* Start tracking a new init hook execution. * Creates in-memory state, completion promise, and emits init-start event.
(workspaceId: string, hookPath: string)
| 167 | * Creates in-memory state, completion promise, and emits init-start event. |
| 168 | */ |
| 169 | startInit(workspaceId: string, hookPath: string): void { |
| 170 | const startTime = Date.now(); |
| 171 | |
| 172 | const state: InitHookState = { |
| 173 | status: "running", |
| 174 | phase: "runtime_setup", |
| 175 | hookPath, |
| 176 | startTime, |
| 177 | lines: [], |
| 178 | exitCode: null, |
| 179 | endTime: null, |
| 180 | }; |
| 181 | |
| 182 | this.store.setState(workspaceId, state); |
| 183 | |
| 184 | // Create completion promise for this init |
| 185 | // This allows multiple tools to await the same init without event listeners |
| 186 | let resolve: () => void; |
| 187 | let reject: (error: Error) => void; |
| 188 | let resolveHookPhase: () => void; |
| 189 | const promise = new Promise<void>((res, rej) => { |
| 190 | resolve = res; |
| 191 | reject = rej; |
| 192 | }); |
| 193 | // Prevent unhandled rejections if a workspace is deleted before any waiters attach. |
| 194 | promise.catch(() => undefined); |
| 195 | const hookPhasePromise = new Promise<void>((res) => { |
| 196 | resolveHookPhase = res; |
| 197 | }); |
| 198 | |
| 199 | this.initPromises.set(workspaceId, { |
| 200 | promise, |
| 201 | resolve: resolve!, |
| 202 | reject: reject!, |
| 203 | hookPhasePromise, |
| 204 | resolveHookPhase: resolveHookPhase!, |
| 205 | }); |
| 206 | |
| 207 | log.debug(`Init hook started for workspace ${workspaceId}: ${hookPath}`); |
| 208 | |
| 209 | // Emit init-start event |
| 210 | this.emit("init-start", { |
| 211 | type: "init-start", |
| 212 | workspaceId, |
| 213 | hookPath, |
| 214 | timestamp: startTime, |
| 215 | } satisfies WorkspaceInitEvent & { workspaceId: string }); |
| 216 | } |
| 217 | |
| 218 | /** |
| 219 | * Signal that the .mux/init hook is starting. |
no test coverage detected