@@ -22,6 +22,7 @@ export interface SpawnedAgentFixture {
2222 readonly connection : acp . ClientSideConnection ;
2323 readonly workspaceDir : string ;
2424 createSession ( ) : Promise < acp . NewSessionResponse > ;
25+ restart ( ) : Promise < SpawnedAgentFixture > ;
2526 writeSkill ( skill : TestSkill ) : void ;
2627 setPermissionResponder ( responder : PermissionResponder ) : void ;
2728 expectPromptText (
@@ -38,16 +39,13 @@ export interface SpawnedAgentFixture {
3839 dispose ( ) : Promise < void > ;
3940}
4041
41- export function createSpawnedAgentFixture (
42- extraEnv ?: NodeJS . ProcessEnv ,
43- ) : SpawnedAgentFixture {
44- const paths = RuntimePaths . createTemporary ( ) ;
45- writeCodexHomeConfig ( paths . codexHome , {
46- model : DEFAULT_TEST_MODEL_ID . model ,
47- model_reasoning_effort : DEFAULT_TEST_MODEL_ID . effort ,
48- web_search : "disabled" ,
49- } ) ;
42+ type ConnectionInitializer = ( connection : acp . ClientSideConnection ) => Promise < void > ;
5043
44+ export async function createSpawnedAgentFixture (
45+ initializeConnection : ConnectionInitializer ,
46+ extraEnv ?: NodeJS . ProcessEnv ,
47+ paths = RuntimePaths . createTemporary ( ) ,
48+ ) : Promise < SpawnedAgentFixture > {
5149 const agentProcess = spawn ( "npm" , [ "run" , "--silent" , "start" ] , {
5250 cwd : process . cwd ( ) ,
5351 env : {
@@ -59,7 +57,15 @@ export function createSpawnedAgentFixture(
5957 stdio : [ "pipe" , "pipe" , "pipe" ] ,
6058 } ) ;
6159
62- return new SpawnedAgentFixtureImpl ( new RecordingClient ( ) , agentProcess , paths ) ;
60+ const fixture = new SpawnedAgentFixtureImpl (
61+ new RecordingClient ( ) ,
62+ agentProcess ,
63+ paths ,
64+ initializeConnection ,
65+ extraEnv ,
66+ ) ;
67+ await initializeConnection ( fixture . connection ) ;
68+ return fixture ;
6369}
6470
6571class RuntimePaths {
@@ -79,6 +85,11 @@ class RuntimePaths {
7985 for ( const dir of [ paths . codexHome , paths . workspaceDir , paths . appServerLogsDir ] ) {
8086 fs . mkdirSync ( dir , { recursive : true } ) ;
8187 }
88+ writeCodexHomeConfig ( paths . codexHome , {
89+ model : DEFAULT_TEST_MODEL_ID . model ,
90+ model_reasoning_effort : DEFAULT_TEST_MODEL_ID . effort ,
91+ web_search : "disabled" ,
92+ } ) ;
8293 return paths ;
8394 }
8495}
@@ -128,11 +139,14 @@ class RecordingClient implements acp.Client {
128139
129140class SpawnedAgentFixtureImpl implements SpawnedAgentFixture {
130141 readonly connection : acp . ClientSideConnection ;
142+ private disposed = false ;
131143
132144 constructor (
133145 private readonly client : RecordingClient ,
134146 private readonly agentProcess : ChildProcessWithoutNullStreams ,
135147 private readonly paths : RuntimePaths ,
148+ private readonly initializeConnection : ConnectionInitializer ,
149+ private readonly extraEnv ?: NodeJS . ProcessEnv ,
136150 ) {
137151 const output = Readable . toWeb ( agentProcess . stdout ) as ReadableStream < Uint8Array > ;
138152 this . connection = new acp . ClientSideConnection (
@@ -152,6 +166,11 @@ class SpawnedAgentFixtureImpl implements SpawnedAgentFixture {
152166 } ) ;
153167 }
154168
169+ async restart ( ) : Promise < SpawnedAgentFixture > {
170+ await this . stopProcess ( false ) ;
171+ return await createSpawnedAgentFixture ( this . initializeConnection , this . extraEnv , this . paths ) ;
172+ }
173+
155174 writeSkill ( skill : TestSkill ) : void {
156175 const skillDirectory = path . join ( this . paths . codexHome , "skills" , skill . name ) ;
157176 fs . mkdirSync ( skillDirectory , { recursive : true } ) ;
@@ -211,6 +230,15 @@ class SpawnedAgentFixtureImpl implements SpawnedAgentFixture {
211230 }
212231
213232 async dispose ( ) : Promise < void > {
233+ if ( this . disposed ) {
234+ return ;
235+ }
236+ this . disposed = true ;
237+ await this . stopProcess ( true ) ;
238+ removeDirectoryWithRetry ( this . paths . rootDir ) ;
239+ }
240+
241+ private async stopProcess ( printLogs : boolean ) : Promise < void > {
214242 if ( ! this . agentProcess . stdin . destroyed && ! this . agentProcess . stdin . writableEnded ) {
215243 this . agentProcess . stdin . end ( ) ;
216244 }
@@ -221,12 +249,16 @@ class SpawnedAgentFixtureImpl implements SpawnedAgentFixture {
221249 await waitForProcessExit ( this . agentProcess , 4_000 ) ;
222250 }
223251
224- printLogDirectory ( this . paths . appServerLogsDir ) ;
225- removeDirectoryWithRetry ( this . paths . rootDir ) ;
252+ if ( printLogs ) {
253+ printLogDirectory ( this . paths . appServerLogsDir ) ;
254+ }
226255 }
227256}
228257
229258function printLogDirectory ( logDirectory : string ) : void {
259+ if ( ! fs . existsSync ( logDirectory ) ) {
260+ return ;
261+ }
230262 fs . readdirSync ( logDirectory , { withFileTypes : true } )
231263 . filter ( ( entry ) => entry . isFile ( ) )
232264 . forEach ( ( entry ) => {
0 commit comments