MCPcopy
hub / github.com/coder/mux / endInit

Method endInit

src/node/services/initStateManager.ts:285–337  ·  view source on GitHub ↗

* Finalize init hook execution. * Updates state, persists to disk, emits init-end event, and resolves completion promise. * * IMPORTANT: We persist BEFORE updating in-memory exitCode to prevent a race condition * where replay() sees exitCode !== null but the file doesn't exist yet. This

(workspaceId: string, exitCode: number)

Source from the content-addressed store, hash-verified

283 * the invariant: if init-end is visible (live or replay), the file MUST exist.
284 */
285 async endInit(workspaceId: string, exitCode: number): Promise<void> {
286 const state = this.store.getState(workspaceId);
287
288 if (!state) {
289 log.error(`endInit called for workspace ${workspaceId} with no active init state`);
290 return;
291 }
292
293 const endTime = Date.now();
294 const finalStatus = exitCode === 0 ? "success" : "error";
295
296 // Create complete state for persistence (don't mutate in-memory state yet)
297 const stateToPerist: InitHookState = {
298 ...state,
299 status: finalStatus,
300 exitCode,
301 endTime,
302 };
303
304 // Persist FIRST - ensures file exists before in-memory state shows completion
305 await this.store.persist(workspaceId, stateToPerist, {
306 // If WorkspaceService.remove() cleared init state, do not recreate ~/.mux/sessions/<id>/
307 shouldWrite: () => this.store.hasState(workspaceId),
308 });
309
310 // NOW update in-memory state (replay will now see file exists)
311 state.status = finalStatus;
312 state.exitCode = exitCode;
313 state.endTime = endTime;
314
315 log.info(
316 `Init hook ${state.status} for workspace ${workspaceId} (exit code ${exitCode}, duration ${endTime - state.startTime}ms)`
317 );
318
319 // Emit init-end event
320 this.emit("init-end", {
321 type: "init-end",
322 workspaceId,
323 exitCode,
324 timestamp: endTime,
325 // Include truncation info so frontend can show indicator
326 ...(state.truncatedLines ? { truncatedLines: state.truncatedLines } : {}),
327 } satisfies WorkspaceInitEvent & { workspaceId: string });
328
329 // Resolve completion promise for waiting tools
330 const promiseEntry = this.initPromises.get(workspaceId);
331 if (promiseEntry) {
332 promiseEntry.resolve();
333 this.initPromises.delete(workspaceId);
334 }
335
336 // Keep state in memory for replay (unlike streams which delete immediately)
337 }
338
339 /**
340 * Get current in-memory init state for a workspace.

Callers 3

createInitLoggerMethod · 0.80
startWorkspaceInitMethod · 0.80

Calls 7

getStateMethod · 0.80
persistMethod · 0.80
hasStateMethod · 0.80
resolveMethod · 0.80
emitMethod · 0.65
getMethod · 0.65
deleteMethod · 0.45

Tested by

no test coverage detected