@@ -36,18 +36,16 @@ private async Task<int> RunAsync(ParseResult parseResult, CancellationToken canc
3636
3737 if ( pid is not null )
3838 {
39- var state = await StateManager . LoadStateByPidAsync ( pid . Value , cancellationToken ) ;
40- if ( state is null )
41- {
42- Console . WriteLine ( $ "No running Dev Proxy instance with PID { pid . Value } .") ;
43- return 1 ;
44- }
45-
46- return await StopInstanceAsync ( state , force , cancellationToken ) ;
39+ return await StopByPidAsync ( pid . Value , force , cancellationToken ) ;
4740 }
4841
42+ // Reconcile any system-proxy registrations left behind by crashed
43+ // instances first — this must run before LoadAllStatesAsync, which prunes
44+ // (deletes) the stale state files it depends on.
45+ var reconciliation = await SystemProxyManager . ReconcileOrphanedSystemProxiesAsync ( cancellationToken ) ;
46+
4947 var states = await StateManager . LoadAllStatesAsync ( cancellationToken ) ;
50- if ( states . Count == 0 )
48+ if ( states . Count == 0 && reconciliation . Orphans . Count == 0 )
5149 {
5250 Console . WriteLine ( "Dev Proxy is not running." ) ;
5351 return 1 ;
@@ -63,9 +61,55 @@ private async Task<int> RunAsync(ParseResult parseResult, CancellationToken canc
6361 }
6462 }
6563
64+ ReportReconciledOrphans ( reconciliation ) ;
65+
6666 return exitCode ;
6767 }
6868
69+ private static async Task < int > StopByPidAsync ( int pid , bool force , CancellationToken cancellationToken )
70+ {
71+ // Capture a potential orphaned system-proxy record before LoadStateByPidAsync
72+ // prunes (deletes) the stale state file for a dead PID.
73+ var orphan = ( await StateManager . GetOrphanedSystemProxyStatesAsync ( cancellationToken ) )
74+ . Find ( o => o . Pid == pid ) ;
75+
76+ var state = await StateManager . LoadStateByPidAsync ( pid , cancellationToken ) ;
77+ if ( state is not null )
78+ {
79+ return await StopInstanceAsync ( state , force , cancellationToken ) ;
80+ }
81+
82+ if ( orphan is not null )
83+ {
84+ var liveOwner = await StateManager . FindSystemProxyInstanceAsync ( cancellationToken ) ;
85+ if ( liveOwner is null )
86+ {
87+ new SystemProxyManager ( NullLogger < SystemProxyManager > . Instance ) . Disable ( ) ;
88+ Console . WriteLine ( $ "Restored system proxy left by crashed Dev Proxy (PID: { pid } ).") ;
89+ }
90+ else
91+ {
92+ Console . WriteLine ( $ "Removed stale record for crashed Dev Proxy (PID: { pid } ); system proxy is owned by a running instance (PID: { liveOwner . Pid } ).") ;
93+ }
94+
95+ await StateManager . DeleteStateAsync ( pid , cancellationToken ) ;
96+ return 0 ;
97+ }
98+
99+ Console . WriteLine ( $ "No running Dev Proxy instance with PID { pid } .") ;
100+ return 1 ;
101+ }
102+
103+ private static void ReportReconciledOrphans ( SystemProxyManager . OrphanReconciliation reconciliation )
104+ {
105+ foreach ( var orphan in reconciliation . Orphans )
106+ {
107+ Console . WriteLine ( reconciliation . SystemProxyDisabled
108+ ? $ "Restored system proxy left by crashed Dev Proxy (PID: { orphan . Pid } )."
109+ : $ "Removed stale record for crashed Dev Proxy (PID: { orphan . Pid } ); system proxy is owned by a running instance.") ;
110+ }
111+ }
112+
69113 private static async Task < int > StopInstanceAsync ( ProxyInstanceState state , bool force , CancellationToken cancellationToken )
70114 {
71115 if ( force )
@@ -148,10 +192,12 @@ private static async Task<int> StopInstanceAsync(ProxyInstanceState state, bool
148192
149193 private static async Task < int > ForceStopAsync ( ProxyInstanceState state , CancellationToken cancellationToken )
150194 {
151- // Best-effort: restore the OS proxy in case the daemon is killed before it can
152- // deregister itself (SIGKILL can't be caught; a crashed daemon never cleans up).
153- // Engine-agnostic and cross-platform (Windows WinINET + macOS toggle-proxy.sh).
154- new SystemProxyManager ( NullLogger < SystemProxyManager > . Instance ) . Disable ( ) ;
195+ // A killed process can't run its own cleanup, so restore the system proxy
196+ // on its behalf before terminating it.
197+ if ( state . AsSystemProxy )
198+ {
199+ new SystemProxyManager ( NullLogger < SystemProxyManager > . Instance ) . Disable ( ) ;
200+ }
155201
156202 try
157203 {
0 commit comments