(exitCode = 0)
| 311 | |
| 312 | // Unified shutdown handler |
| 313 | const shutdown = (exitCode = 0): void => { |
| 314 | if (isShuttingDown) { |
| 315 | return; |
| 316 | } |
| 317 | isShuttingDown = true; |
| 318 | |
| 319 | if (idleCheckTimer) { |
| 320 | clearInterval(idleCheckTimer); |
| 321 | idleCheckTimer = null; |
| 322 | } |
| 323 | |
| 324 | recordDaemonLifecycleMetric('shutdown'); |
| 325 | log('info', '[Daemon] Shutting down...'); |
| 326 | |
| 327 | const cleanupArtifacts = (): ReturnType<typeof cleanupOwnedWorkspaceFilesystemArtifacts> => |
| 328 | cleanupOwnedWorkspaceFilesystemArtifacts({ |
| 329 | workspaceKey, |
| 330 | trigger: 'shutdown', |
| 331 | daemonCleanup: { |
| 332 | pid: process.pid, |
| 333 | socketPath, |
| 334 | instanceId: daemonInstanceId, |
| 335 | allowLiveOwner: true, |
| 336 | }, |
| 337 | }); |
| 338 | |
| 339 | let cleanupStarted = false; |
| 340 | let forcedShutdownTimer: NodeJS.Timeout | null = null; |
| 341 | const finishShutdown = (finalExitCode: number, flushTimeoutMs: number): void => { |
| 342 | if (cleanupStarted) { |
| 343 | return; |
| 344 | } |
| 345 | cleanupStarted = true; |
| 346 | if (forcedShutdownTimer) { |
| 347 | clearTimeout(forcedShutdownTimer); |
| 348 | forcedShutdownTimer = null; |
| 349 | } |
| 350 | void cleanupArtifacts() |
| 351 | .then( |
| 352 | (result) => { |
| 353 | if (result.errors.length > 0) { |
| 354 | log('error', `[Daemon] Cleanup failed: ${result.errors.join('; ')}`, { |
| 355 | sentry: true, |
| 356 | }); |
| 357 | return; |
| 358 | } |
| 359 | log('info', '[Daemon] Cleanup complete'); |
| 360 | }, |
| 361 | (error) => { |
| 362 | const message = error instanceof Error ? error.message : String(error); |
| 363 | log('error', `[Daemon] Cleanup failed: ${message}`, { sentry: true }); |
| 364 | }, |
| 365 | ) |
| 366 | .finally(() => { |
| 367 | void flushAndCloseSentry(flushTimeoutMs).finally(() => { |
| 368 | process.exit(finalExitCode); |
| 369 | }); |
| 370 | }); |
no test coverage detected