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

Method waitForInit

src/node/services/initStateManager.ts:422–513  ·  view source on GitHub ↗

* Wait for workspace initialization to complete. * Used by tools (bash, file_*) to ensure files are ready before executing. * * Behavior: * - No init state: Returns immediately (init not needed or backwards compat) * - Init succeeded/failed: Returns immediately (tools proceed regardle

(workspaceId: string, abortSignal?: AbortSignal)

Source from the content-addressed store, hash-verified

420 * @param abortSignal Optional signal to abort the wait early
421 */
422 async waitForInit(workspaceId: string, abortSignal?: AbortSignal): Promise<void> {
423 const state = this.getInitState(workspaceId);
424
425 // No init state - proceed immediately (backwards compat or init not needed)
426 if (!state) {
427 return;
428 }
429
430 // Init already completed (success or failure) - proceed immediately
431 // Tools should work regardless of init outcome
432 if (state.status !== "running") {
433 return;
434 }
435
436 // Early exit if already aborted
437 if (abortSignal?.aborted) {
438 return;
439 }
440
441 // Init is running - wait for completion promise with timeout
442 const promiseEntry = this.initPromises.get(workspaceId);
443
444 if (!promiseEntry) {
445 // State says running but no promise exists (shouldn't happen, but handle gracefully)
446 log.error(`Init state is running for ${workspaceId} but no promise found, proceeding`);
447 return;
448 }
449
450 const INIT_HOOK_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes
451
452 // Track cleanup handlers
453 let timeoutId: NodeJS.Timeout | undefined;
454 let abortHandler: (() => void) | undefined;
455
456 try {
457 const abortPromise = new Promise<void>((resolve) => {
458 if (!abortSignal) return; // Never resolves if no signal
459 if (abortSignal.aborted) {
460 resolve();
461 return;
462 }
463 abortHandler = () => resolve();
464 abortSignal.addEventListener("abort", abortHandler, { once: true });
465 });
466
467 // Intentional: provisioning (Coder/devcontainer/etc.) can be long-running, so we
468 // avoid timeouts until .mux/init begins. The wait is still interruptible via
469 // abortSignal or workspace deletion (clearInMemoryState).
470 const phase = state.phase ?? "runtime_setup";
471 if (phase === "runtime_setup") {
472 const first = await Promise.race([
473 promiseEntry.promise.then(() => "complete"),
474 promiseEntry.hookPhasePromise.then(() => "hook"),
475 abortPromise.then(() => "abort"),
476 ]);
477 if (first !== "hook") {
478 return;
479 }

Callers

nothing calls this directly

Calls 6

getInitStateMethod · 0.95
getErrorMessageFunction · 0.90
removeEventListenerMethod · 0.80
resolveFunction · 0.70
getMethod · 0.65
addEventListenerMethod · 0.45

Tested by

no test coverage detected