@@ -118,6 +118,11 @@ const resolveWebHostSource = extractFunctionBySignature(
118118 'function resolveWebHost(options = {}) {' ,
119119 'resolveWebHost'
120120) ;
121+ const cmdStartSource = extractFunctionBySignature (
122+ cliContent ,
123+ 'function cmdStart(options = {}) {' ,
124+ 'cmdStart'
125+ ) ;
121126const releaseRunPortIfNeededSource = extractFunctionBySignature (
122127 cliContent ,
123128 'function releaseRunPortIfNeeded(port, deps = {}) {' ,
@@ -180,24 +185,45 @@ test('releaseRunPortIfNeeded skips non-default ports', () => {
180185 assert . deepStrictEqual ( calls , [ ] ) ;
181186} ) ;
182187
183- test ( 'releaseRunPortIfNeeded clears default port via fuser on linux ' , ( ) => {
188+ test ( 'releaseRunPortIfNeeded clears default port only after lsof pids map to managed run processes ' , ( ) => {
184189 const calls = [ ] ;
190+ const killed = [ ] ;
185191 const logs = [ ] ;
186192 const releaseRunPortIfNeeded = instantiateFunction ( releaseRunPortIfNeededSource , 'releaseRunPortIfNeeded' , {
187193 DEFAULT_WEB_PORT : 3737 ,
188194 spawnSync ( command , args ) {
189195 calls . push ( [ command , args ] ) ;
190- if ( command === 'fuser' ) {
191- return { status : 0 , stdout : '1234\n' , stderr : '' } ;
196+ if ( command === 'lsof' ) {
197+ return { status : 0 , stdout : '1234\n8888\n' , stderr : '' } ;
198+ }
199+ if ( command === 'ps' ) {
200+ return {
201+ status : 0 ,
202+ stdout : [
203+ 'UID PID PPID C STIME TTY TIME CMD' ,
204+ 'u0_a876 1234 1000 0 1970 ? 00:00:00 node /repo/cli.js run --no-browser' ,
205+ 'u0_a876 8888 1000 0 1970 ? 00:00:00 node /repo/other-server.js'
206+ ] . join ( '\n' ) ,
207+ stderr : ''
208+ } ;
192209 }
193210 throw new Error ( `unexpected command: ${ command } ` ) ;
194211 } ,
195- process : { platform : 'linux' } ,
212+ process : {
213+ platform : 'linux' ,
214+ kill ( pid , signal ) {
215+ killed . push ( [ pid , signal ] ) ;
216+ }
217+ } ,
196218 console : { log ( message ) { logs . push ( message ) ; } }
197219 } ) ;
198220
199221 const result = releaseRunPortIfNeeded ( 3737 ) ;
200- assert . deepStrictEqual ( calls , [ [ 'fuser' , [ '-k' , '3737/tcp' ] ] ] ) ;
222+ assert . deepStrictEqual ( calls , [
223+ [ 'lsof' , [ '-ti' , 'tcp:3737' ] ] ,
224+ [ 'ps' , [ '-ef' ] ]
225+ ] ) ;
226+ assert . deepStrictEqual ( killed , [ [ 1234 , 'SIGKILL' ] ] ) ;
201227 assert . deepStrictEqual ( result , {
202228 attempted : true ,
203229 released : true ,
@@ -206,18 +232,26 @@ test('releaseRunPortIfNeeded clears default port via fuser on linux', () => {
206232 assert . deepStrictEqual ( logs , [ '~ 已释放端口 3737 占用' ] ) ;
207233} ) ;
208234
209- test ( 'releaseRunPortIfNeeded falls back to lsof pids when fuser is unavailable' , ( ) => {
235+ test ( 'releaseRunPortIfNeeded falls back to ps scan when lsof is unavailable' , ( ) => {
210236 const calls = [ ] ;
211237 const killed = [ ] ;
212238 const releaseRunPortIfNeeded = instantiateFunction ( releaseRunPortIfNeededSource , 'releaseRunPortIfNeeded' , {
213239 DEFAULT_WEB_PORT : 3737 ,
214240 spawnSync ( command , args ) {
215241 calls . push ( [ command , args ] ) ;
216- if ( command === 'fuser ' ) {
242+ if ( command === 'lsof ' ) {
217243 return { error : { code : 'ENOENT' } , status : null , stdout : '' , stderr : '' } ;
218244 }
219- if ( command === 'lsof' ) {
220- return { status : 0 , stdout : '2222\n3333\n' , stderr : '' } ;
245+ if ( command === 'ps' ) {
246+ return {
247+ status : 0 ,
248+ stdout : [
249+ 'UID PID PPID C STIME TTY TIME CMD' ,
250+ 'u0_a876 2222 1000 0 1970 ? 00:00:00 node /repo/cli.js run --no-browser' ,
251+ 'u0_a876 3333 1000 0 1970 ? 00:00:00 /usr/bin/codexmate run'
252+ ] . join ( '\n' ) ,
253+ stderr : ''
254+ } ;
221255 }
222256 throw new Error ( `unexpected command: ${ command } ` ) ;
223257 } ,
@@ -232,8 +266,8 @@ test('releaseRunPortIfNeeded falls back to lsof pids when fuser is unavailable',
232266
233267 const result = releaseRunPortIfNeeded ( 3737 ) ;
234268 assert . deepStrictEqual ( calls , [
235- [ 'fuser ' , [ '-k ' , '3737/ tcp' ] ] ,
236- [ 'lsof ' , [ '-ti' , 'tcp:3737 '] ]
269+ [ 'lsof ' , [ '-ti ' , 'tcp:3737 ' ] ] ,
270+ [ 'ps ' , [ '-ef ' ] ]
237271 ] ) ;
238272 assert . deepStrictEqual ( killed , [
239273 [ 2222 , 'SIGKILL' ] ,
@@ -253,11 +287,8 @@ test('releaseRunPortIfNeeded falls back to ps scan for managed run processes', (
253287 DEFAULT_WEB_PORT : 3737 ,
254288 spawnSync ( command , args ) {
255289 calls . push ( [ command , args ] ) ;
256- if ( command === 'fuser' ) {
257- return { error : { code : 'EACCES' } , status : 1 , stdout : '' , stderr : 'Permission denied' } ;
258- }
259290 if ( command === 'lsof' ) {
260- return { error : { code : 'ENOENT' } , status : null , stdout : '' , stderr : '' } ;
291+ return { status : 0 , stdout : '9001\n9002\n ' , stderr : '' } ;
261292 }
262293 if ( command === 'ps' ) {
263294 return {
@@ -285,7 +316,6 @@ test('releaseRunPortIfNeeded falls back to ps scan for managed run processes', (
285316
286317 const result = releaseRunPortIfNeeded ( 3737 ) ;
287318 assert . deepStrictEqual ( calls , [
288- [ 'fuser' , [ '-k' , '3737/tcp' ] ] ,
289319 [ 'lsof' , [ '-ti' , 'tcp:3737' ] ] ,
290320 [ 'ps' , [ '-ef' ] ]
291321 ] ) ;
@@ -298,7 +328,15 @@ test('releaseRunPortIfNeeded falls back to ps scan for managed run processes', (
298328} ) ;
299329
300330test ( 'cmdStart releases the resolved port before creating the web server' , ( ) => {
301- assert . match ( cliContent , / c o n s t p o r t = r e s o l v e W e b P o r t \( \) ; \s * c o n s t h o s t = r e s o l v e W e b H o s t \( o p t i o n s \) ; \s * r e l e a s e R u n P o r t I f N e e d e d \( p o r t \) ; \s * l e t s e r v e r H a n d l e = c r e a t e W e b S e r v e r \( / s) ;
331+ const resolveIndex = cmdStartSource . indexOf ( 'resolveWebPort(' ) ;
332+ const releaseIndex = cmdStartSource . indexOf ( 'releaseRunPortIfNeeded(' ) ;
333+ const createIndex = cmdStartSource . indexOf ( 'createWebServer(' ) ;
334+
335+ assert ( resolveIndex >= 0 , 'cmdStart should resolve the web port' ) ;
336+ assert ( releaseIndex >= 0 , 'cmdStart should release the run port before startup' ) ;
337+ assert ( createIndex >= 0 , 'cmdStart should create the web server' ) ;
338+ assert ( resolveIndex < releaseIndex , 'cmdStart should resolve the port before releasing it' ) ;
339+ assert ( releaseIndex < createIndex , 'cmdStart should release the port before creating the web server' ) ;
302340} ) ;
303341
304342const getCodexSkillsDirSource = extractFunctionBySignature (
0 commit comments