@@ -106,6 +106,10 @@ type DevServerConfig = {
106106 * ```
107107 */
108108export class DevServerManager extends EventEmitter {
109+ // AC2: PID file path for orphaned-process recovery (CLI / Code Builder) — static before instance per member-ordering
110+ private static readonly PID_DIR = '.sf' ;
111+ private static readonly PID_FILENAME = 'webapp-dev-server.pid' ;
112+
109113 private options : DevServerConfig ;
110114 private process : ChildProcess | null = null ;
111115 private detectedUrl : string | null = null ;
@@ -115,10 +119,6 @@ export class DevServerManager extends EventEmitter {
115119 private stderrBuffer : string [ ] = [ ] ; // Buffer to store stderr lines for error parsing
116120 private readonly maxStderrLines = 100 ; // Keep last 100 lines
117121
118- // AC2: PID file path for orphaned-process recovery (CLI / Code Builder)
119- private static readonly PID_DIR = '.sf' ;
120- private static readonly PID_FILENAME = 'webapp-dev-server.pid' ;
121-
122122 /**
123123 * Creates a new DevServerManager instance
124124 *
@@ -130,48 +130,6 @@ export class DevServerManager extends EventEmitter {
130130 this . logger = Logger . childFromRoot ( 'DevServerManager' ) ;
131131 }
132132
133- // --- AC2: PID file persistence (for CLI / direct browser path) ---
134-
135- /**
136- * Get the PID file path. Uses the project root's .sf/ directory.
137- */
138- private getPidFilePath ( ) : string {
139- return join ( this . options . cwd , DevServerManager . PID_DIR , DevServerManager . PID_FILENAME ) ;
140- }
141-
142- /**
143- * Save the dev server PID to a file on disk.
144- * This allows orphan cleanup if the CLI process crashes or Code Builder disconnects.
145- */
146- private savePidFile ( pid : number ) : void {
147- try {
148- const dir = join ( this . options . cwd , DevServerManager . PID_DIR ) ;
149- if ( ! existsSync ( dir ) ) {
150- mkdirSync ( dir , { recursive : true } ) ;
151- }
152- const pidData = JSON . stringify ( { pid, url : this . detectedUrl , timestamp : Date . now ( ) } ) ;
153- writeFileSync ( this . getPidFilePath ( ) , pidData , 'utf-8' ) ;
154- this . logger . debug ( `Saved dev server PID file: ${ pid } ` ) ;
155- } catch ( error ) {
156- this . logger . warn ( `Failed to write PID file: ${ error instanceof Error ? error . message : String ( error ) } ` ) ;
157- }
158- }
159-
160- /**
161- * Remove the PID file (on clean shutdown).
162- */
163- private removePidFile ( ) : void {
164- try {
165- const pidPath = this . getPidFilePath ( ) ;
166- if ( existsSync ( pidPath ) ) {
167- unlinkSync ( pidPath ) ;
168- this . logger . debug ( 'Removed dev server PID file' ) ;
169- }
170- } catch ( error ) {
171- this . logger . warn ( `Failed to remove PID file: ${ error instanceof Error ? error . message : String ( error ) } ` ) ;
172- }
173- }
174-
175133 /**
176134 * Read saved PID from the PID file.
177135 * Returns null if no PID file exists or it's unreadable.
@@ -214,7 +172,7 @@ export class DevServerManager extends EventEmitter {
214172 return false ;
215173 }
216174
217- logger ?. debug ( `Found saved PID file: PID=${ saved . pid } , URL=${ saved . url } ` ) ;
175+ logger ?. debug ( `Found saved PID file: PID=${ saved . pid } , URL=${ String ( saved . url ?? 'null' ) } ` ) ;
218176
219177 try {
220178 // Signal 0 just checks if the process exists
@@ -252,13 +210,6 @@ export class DevServerManager extends EventEmitter {
252210 return false ;
253211 }
254212
255- /**
256- * Get the PID of the running dev server process (if any).
257- */
258- public getPid ( ) : number | undefined {
259- return this . process ?. pid ?? undefined ;
260- }
261-
262213 /**
263214 * Strips ANSI color codes from a string
264215 *
@@ -304,6 +255,13 @@ export class DevServerManager extends EventEmitter {
304255 return null ;
305256 }
306257
258+ /**
259+ * Get the PID of the running dev server process (if any).
260+ */
261+ public getPid ( ) : number | undefined {
262+ return this . process ?. pid ?? undefined ;
263+ }
264+
307265 /**
308266 * Starts the dev server process
309267 *
@@ -431,6 +389,46 @@ export class DevServerManager extends EventEmitter {
431389 } ) ;
432390 }
433391
392+ /**
393+ * Get the PID file path. Uses the project root's .sf/ directory.
394+ */
395+ private getPidFilePath ( ) : string {
396+ return join ( this . options . cwd , DevServerManager . PID_DIR , DevServerManager . PID_FILENAME ) ;
397+ }
398+
399+ /**
400+ * Save the dev server PID to a file on disk.
401+ * This allows orphan cleanup if the CLI process crashes or Code Builder disconnects.
402+ */
403+ private savePidFile ( pid : number ) : void {
404+ try {
405+ const dir = join ( this . options . cwd , DevServerManager . PID_DIR ) ;
406+ if ( ! existsSync ( dir ) ) {
407+ mkdirSync ( dir , { recursive : true } ) ;
408+ }
409+ const pidData = JSON . stringify ( { pid, url : this . detectedUrl , timestamp : Date . now ( ) } ) ;
410+ writeFileSync ( this . getPidFilePath ( ) , pidData , 'utf-8' ) ;
411+ this . logger . debug ( `Saved dev server PID file: ${ pid } ` ) ;
412+ } catch ( error ) {
413+ this . logger . warn ( `Failed to write PID file: ${ error instanceof Error ? error . message : String ( error ) } ` ) ;
414+ }
415+ }
416+
417+ /**
418+ * Remove the PID file (on clean shutdown).
419+ */
420+ private removePidFile ( ) : void {
421+ try {
422+ const pidPath = this . getPidFilePath ( ) ;
423+ if ( existsSync ( pidPath ) ) {
424+ unlinkSync ( pidPath ) ;
425+ this . logger . debug ( 'Removed dev server PID file' ) ;
426+ }
427+ } catch ( error ) {
428+ this . logger . warn ( `Failed to remove PID file: ${ error instanceof Error ? error . message : String ( error ) } ` ) ;
429+ }
430+ }
431+
434432 /**
435433 * Sets up event handlers for the spawned process
436434 *
0 commit comments