* Append output line from init hook. * Accumulates in state (with truncation for long output) and emits init-output event. * * Truncation strategy: Keep only the most recent INIT_HOOK_MAX_LINES lines (tail). * Older lines are dropped to prevent OOM with large rsync/build output.
(workspaceId: string, line: string, isError: boolean)
| 245 | * Older lines are dropped to prevent OOM with large rsync/build output. |
| 246 | */ |
| 247 | appendOutput(workspaceId: string, line: string, isError: boolean): void { |
| 248 | const state = this.store.getState(workspaceId); |
| 249 | |
| 250 | if (!state) { |
| 251 | log.error(`appendOutput called for workspace ${workspaceId} with no active init state`); |
| 252 | return; |
| 253 | } |
| 254 | |
| 255 | const timestamp = Date.now(); |
| 256 | const lineNumber = (state.truncatedLines ?? 0) + state.lines.length; |
| 257 | const timedLine: TimedLine = { line, isError, timestamp }; |
| 258 | |
| 259 | // Truncation: keep only the most recent MAX_LINES |
| 260 | if (state.lines.length >= INIT_HOOK_MAX_LINES) { |
| 261 | state.lines.shift(); // Drop oldest line |
| 262 | state.truncatedLines = (state.truncatedLines ?? 0) + 1; |
| 263 | } |
| 264 | state.lines.push(timedLine); |
| 265 | |
| 266 | // Emit init-output event (always emit for live streaming, even if truncated from storage) |
| 267 | this.emit("init-output", { |
| 268 | type: "init-output", |
| 269 | workspaceId, |
| 270 | line, |
| 271 | isError, |
| 272 | timestamp, |
| 273 | lineNumber, |
| 274 | } satisfies WorkspaceInitEvent & { workspaceId: string }); |
| 275 | } |
| 276 | |
| 277 | /** |
| 278 | * Finalize init hook execution. |
no test coverage detected