@opengsd/gsd-pi 1.9.0-dev.e10b60d4 → 1.10.0-dev.a5e42e99
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -9
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/gsd/auto/loop.js +24 -4
- package/dist/resources/extensions/gsd/auto/run-unit.js +19 -1
- package/dist/resources/extensions/gsd/auto-model-selection.js +1 -0
- package/dist/resources/extensions/gsd/auto-start.js +30 -10
- package/dist/resources/extensions/gsd/auto-verification.js +25 -12
- package/dist/resources/extensions/gsd/auto-worktree-sync.js +7 -3
- package/dist/resources/extensions/gsd/auto.js +12 -1
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +4 -3
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +12 -3
- package/dist/resources/extensions/gsd/doctor-engine-checks.js +37 -6
- package/dist/resources/extensions/gsd/flat-phase-migration.js +12 -0
- package/dist/resources/extensions/gsd/gsd-db.js +75 -31
- package/dist/resources/extensions/gsd/model-cost-table.js +6 -3
- package/dist/resources/extensions/gsd/model-router.js +10 -5
- package/dist/resources/extensions/gsd/planning-subagent-registry.js +12 -4
- package/dist/resources/extensions/gsd/preferences-models.js +50 -10
- package/dist/resources/extensions/gsd/preferences-types.js +1 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +41 -6
- package/dist/resources/extensions/gsd/preferences.js +7 -0
- package/dist/resources/extensions/gsd/templates/PREFERENCES.md +3 -0
- package/dist/resources/extensions/gsd/tools/plan-slice.js +5 -1
- package/dist/resources/extensions/gsd/verification-evidence.js +5 -3
- package/dist/resources/extensions/gsd/worktree-state-projection.js +20 -18
- package/dist/resources/extensions/ollama/ollama-chat-provider.js +2 -2
- package/dist/resources/extensions/subagent/index.js +1 -1
- package/dist/startup-model-validation.d.ts +1 -1
- package/dist/tsconfig.extensions.tsbuildinfo +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +6 -6
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +6 -6
- package/dist/web/standalone/.next/server/chunks/1991.js +2 -2
- package/dist/web/standalone/.next/server/chunks/5124.js +1 -1
- package/dist/web/standalone/.next/server/chunks/8357.js +2 -2
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/node_modules/@gsd/native/package.json +1 -1
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
- package/integrations/hermes/open_gsd_hermes/gsd_client.py +1 -1
- package/integrations/hermes/pyproject.toml +1 -1
- package/package.json +7 -6
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/dist/rpc.d.ts +1 -1
- package/packages/contracts/dist/rpc.d.ts.map +1 -1
- package/packages/contracts/dist/rpc.js +1 -1
- package/packages/contracts/dist/rpc.js.map +1 -1
- package/packages/contracts/dist/rpc.test.js +1 -1
- package/packages/contracts/dist/rpc.test.js.map +1 -1
- package/packages/contracts/package.json +1 -1
- package/packages/daemon/package.json +4 -4
- package/packages/gsd-agent-core/package.json +5 -5
- package/packages/gsd-agent-modes/dist/cli/args.js +2 -2
- package/packages/gsd-agent-modes/dist/cli/args.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.js +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/thinking-selector.js +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/thinking-selector.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-autocomplete.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-autocomplete.js +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-autocomplete.js.map +1 -1
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/gsd-cloud/README.md +21 -5
- package/packages/gsd-cloud/dist/background-cli.test.d.ts +2 -0
- package/packages/gsd-cloud/dist/background-cli.test.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/background-cli.test.js +325 -0
- package/packages/gsd-cloud/dist/background-cli.test.js.map +1 -0
- package/packages/gsd-cloud/dist/cli.d.ts.map +1 -1
- package/packages/gsd-cloud/dist/cli.js +146 -16
- package/packages/gsd-cloud/dist/cli.js.map +1 -1
- package/packages/gsd-cloud/dist/cloud-config.d.ts +1 -1
- package/packages/gsd-cloud/dist/cloud-config.d.ts.map +1 -1
- package/packages/gsd-cloud/dist/cloud-config.js +7 -1
- package/packages/gsd-cloud/dist/cloud-config.js.map +1 -1
- package/packages/gsd-cloud/dist/cloud-runtime.d.ts +7 -4
- package/packages/gsd-cloud/dist/cloud-runtime.d.ts.map +1 -1
- package/packages/gsd-cloud/dist/cloud-runtime.js +115 -31
- package/packages/gsd-cloud/dist/cloud-runtime.js.map +1 -1
- package/packages/gsd-cloud/dist/cloud-runtime.test.js +224 -59
- package/packages/gsd-cloud/dist/cloud-runtime.test.js.map +1 -1
- package/packages/gsd-cloud/dist/runtime-artifacts.d.ts +3 -0
- package/packages/gsd-cloud/dist/runtime-artifacts.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/runtime-artifacts.js +36 -0
- package/packages/gsd-cloud/dist/runtime-artifacts.js.map +1 -0
- package/packages/gsd-cloud/dist/runtime-process.d.ts +32 -0
- package/packages/gsd-cloud/dist/runtime-process.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/runtime-process.js +640 -0
- package/packages/gsd-cloud/dist/runtime-process.js.map +1 -0
- package/packages/gsd-cloud/dist/runtime-process.test.d.ts +2 -0
- package/packages/gsd-cloud/dist/runtime-process.test.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/runtime-process.test.js +543 -0
- package/packages/gsd-cloud/dist/runtime-process.test.js.map +1 -0
- package/packages/gsd-cloud/dist/runtime-telemetry.d.ts +117 -0
- package/packages/gsd-cloud/dist/runtime-telemetry.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/runtime-telemetry.js +291 -0
- package/packages/gsd-cloud/dist/runtime-telemetry.js.map +1 -0
- package/packages/gsd-cloud/dist/runtime-telemetry.test.d.ts +2 -0
- package/packages/gsd-cloud/dist/runtime-telemetry.test.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/runtime-telemetry.test.js +434 -0
- package/packages/gsd-cloud/dist/runtime-telemetry.test.js.map +1 -0
- package/packages/gsd-cloud/package.json +2 -2
- package/packages/mcp-server/dist/cli-runner.d.ts +3 -0
- package/packages/mcp-server/dist/cli-runner.d.ts.map +1 -1
- package/packages/mcp-server/dist/cli-runner.js +119 -33
- package/packages/mcp-server/dist/cli-runner.js.map +1 -1
- package/packages/mcp-server/package.json +4 -4
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/dist/types.d.ts +2 -2
- package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/types.js.map +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/dist/models.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +130 -2
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +101 -9
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/dist/models.js +18 -6
- package/packages/pi-ai/dist/models.js.map +1 -1
- package/packages/pi-ai/dist/providers/amazon-bedrock.js +3 -2
- package/packages/pi-ai/dist/providers/amazon-bedrock.js.map +1 -1
- package/packages/pi-ai/dist/providers/azure-openai-responses.d.ts +1 -1
- package/packages/pi-ai/dist/providers/azure-openai-responses.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/azure-openai-responses.js.map +1 -1
- package/packages/pi-ai/dist/providers/google-vertex.js.map +1 -1
- package/packages/pi-ai/dist/providers/google.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-codex-responses.d.ts +1 -1
- package/packages/pi-ai/dist/providers/openai-codex-responses.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/openai-codex-responses.js +1 -1
- package/packages/pi-ai/dist/providers/openai-codex-responses.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-completions.d.ts +1 -1
- package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-responses.d.ts +1 -1
- package/packages/pi-ai/dist/providers/openai-responses.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/openai-responses.js +1 -1
- package/packages/pi-ai/dist/providers/openai-responses.js.map +1 -1
- package/packages/pi-ai/dist/providers/simple-options.d.ts +1 -1
- package/packages/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/simple-options.js +1 -1
- package/packages/pi-ai/dist/providers/simple-options.js.map +1 -1
- package/packages/pi-ai/dist/types.d.ts +9 -1
- package/packages/pi-ai/dist/types.d.ts.map +1 -1
- package/packages/pi-ai/dist/types.js.map +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.js +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -3
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/skills.d.ts +5 -0
- package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/skills.js +54 -0
- package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
- package/packages/pi-coding-agent/dist/theme/theme.d.ts +1 -1
- package/packages/pi-coding-agent/dist/theme/theme.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/theme/theme.js +1 -0
- package/packages/pi-coding-agent/dist/theme/theme.js.map +1 -1
- package/packages/pi-coding-agent/package.json +7 -7
- package/packages/pi-tui/package.json +2 -2
- package/packages/rpc-client/package.json +2 -2
- package/pkg/dist/theme/theme.d.ts +1 -1
- package/pkg/dist/theme/theme.d.ts.map +1 -1
- package/pkg/dist/theme/theme.js +1 -0
- package/pkg/dist/theme/theme.js.map +1 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/gsd/auto/loop.ts +48 -4
- package/src/resources/extensions/gsd/auto/run-unit.ts +20 -1
- package/src/resources/extensions/gsd/auto-model-selection.ts +1 -0
- package/src/resources/extensions/gsd/auto-start.ts +32 -9
- package/src/resources/extensions/gsd/auto-verification.ts +46 -12
- package/src/resources/extensions/gsd/auto-worktree-sync.ts +10 -3
- package/src/resources/extensions/gsd/auto.ts +13 -1
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +4 -3
- package/src/resources/extensions/gsd/docs/preferences-reference.md +12 -3
- package/src/resources/extensions/gsd/doctor-engine-checks.ts +42 -6
- package/src/resources/extensions/gsd/flat-phase-migration.ts +14 -0
- package/src/resources/extensions/gsd/gsd-db.ts +85 -33
- package/src/resources/extensions/gsd/model-cost-table.ts +14 -3
- package/src/resources/extensions/gsd/model-router.ts +10 -5
- package/src/resources/extensions/gsd/planning-subagent-registry.ts +18 -4
- package/src/resources/extensions/gsd/preferences-models.ts +54 -10
- package/src/resources/extensions/gsd/preferences-types.ts +10 -2
- package/src/resources/extensions/gsd/preferences-validation.ts +49 -6
- package/src/resources/extensions/gsd/preferences.ts +13 -0
- package/src/resources/extensions/gsd/templates/PREFERENCES.md +3 -0
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +156 -0
- package/src/resources/extensions/gsd/tests/auto-start-index-lock.test.ts +21 -1
- package/src/resources/extensions/gsd/tests/auto-wrapup-inflight-guard.test.ts +74 -1
- package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +147 -0
- package/src/resources/extensions/gsd/tests/flat-phase-migration.test.ts +8 -9
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +32 -0
- package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +13 -0
- package/src/resources/extensions/gsd/tests/model-cost-table.test.ts +22 -7
- package/src/resources/extensions/gsd/tests/model-isolation.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/model-router.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/plan-slice.test.ts +24 -0
- package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +38 -1
- package/src/resources/extensions/gsd/tests/preferences.test.ts +71 -4
- package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +3 -0
- package/src/resources/extensions/gsd/tests/service-tier.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/thinking-level-resolution.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/verification-evidence.test.ts +12 -0
- package/src/resources/extensions/gsd/tests/workflow-authority-faults.test.ts +238 -0
- package/src/resources/extensions/gsd/tests/workflow-authority-fixture.test.ts +48 -0
- package/src/resources/extensions/gsd/tests/workflow-authority-fixture.ts +141 -0
- package/src/resources/extensions/gsd/tests/workflow-authority-projection-conflict.test.ts +177 -0
- package/src/resources/extensions/gsd/tests/workflow-fault-harness.test.ts +39 -0
- package/src/resources/extensions/gsd/tests/workflow-fault-harness.ts +72 -0
- package/src/resources/extensions/gsd/tests/worktree-state-projection.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +60 -0
- package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +40 -0
- package/src/resources/extensions/gsd/tools/plan-slice.ts +5 -1
- package/src/resources/extensions/gsd/verification-evidence.ts +5 -2
- package/src/resources/extensions/gsd/worktree-state-projection.ts +21 -24
- package/src/resources/extensions/ollama/ollama-chat-provider.ts +2 -2
- package/src/resources/extensions/ollama/tests/ollama-chat-provider-think.test.ts +4 -0
- package/src/resources/extensions/subagent/index.ts +1 -1
- /package/dist/web/standalone/.next/static/{V9MAVyYVPboQUB9KTTAMi → xwUaOqui04jIIFVsIBLiL}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{V9MAVyYVPboQUB9KTTAMi → xwUaOqui04jIIFVsIBLiL}/_ssgManifest.js +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-process.js","sourceRoot":"","sources":["../src/runtime-process.ts"],"names":[],"mappings":"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","sourcesContent":["// Project/App: Open GSD\n// File Purpose: Detached cloud runtime process lifecycle and status persistence.\nimport { execFileSync, spawn, type ChildProcess } from \"node:child_process\";\nimport { randomUUID } from \"node:crypto\";\nimport {\n chmodSync,\n closeSync,\n linkSync,\n mkdirSync,\n openSync,\n readFileSync,\n statSync,\n unlinkSync,\n writeFileSync,\n} from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { CLOUD_RUNTIME_INITIAL_CONNECT_WINDOW_MS } from \"./cloud-runtime.js\";\nimport { canonicalConfigPath, runtimeArtifactPath } from \"./runtime-artifacts.js\";\n\ninterface RuntimeProcessState {\n pid: number;\n projects: string[];\n process_start_identity?: string;\n}\n\ninterface LocatedRuntimeProcessState {\n path: string;\n state: RuntimeProcessState;\n}\n\ninterface RuntimeStartLockOwner {\n pid: number;\n process_start_identity?: string;\n}\n\nexport interface RuntimeProcessStatus {\n running: boolean;\n pid: number | null;\n projects: string[];\n log_file: string;\n}\n\ninterface StartRuntimeOptions {\n binaryPath: string;\n configPath: string;\n projectDirs: string[];\n readyTimeoutMs?: number;\n verbose?: boolean;\n processIdentityReader?: (pid: number) => string | null;\n}\n\nconst PROCESS_STARTUP_GRACE_MS = 5_000;\nconst FORCED_STOP_TIMEOUT_MS = 5_000;\nconst STOP_GRACE_PERIOD_MS = 5_000;\nconst STOP_POLL_INTERVAL_MS = 50;\nconst MALFORMED_START_LOCK_GRACE_MS = 5_000;\n\nexport const BACKGROUND_RUNTIME_READY_TIMEOUT_MS =\n CLOUD_RUNTIME_INITIAL_CONNECT_WINDOW_MS + PROCESS_STARTUP_GRACE_MS;\nconst START_LOCK_TIMEOUT_MS = BACKGROUND_RUNTIME_READY_TIMEOUT_MS\n + 2 * (STOP_GRACE_PERIOD_MS + FORCED_STOP_TIMEOUT_MS)\n + STOP_POLL_INTERVAL_MS;\n\nexport async function startBackgroundRuntime(opts: StartRuntimeOptions): Promise<RuntimeProcessStatus> {\n const releaseStartLock = await acquireRuntimeStartLock(opts.configPath);\n try {\n await stopBackgroundRuntime(opts.configPath);\n\n const logFile = runtimeLogPath(opts.configPath);\n mkdirSync(dirname(runtimeStatePath(opts.configPath)), { recursive: true });\n const logFd = openSync(logFile, \"a\", 0o600);\n chmodSync(logFile, 0o600);\n const runArgs = [opts.binaryPath, \"_run\", \"--config\", opts.configPath];\n if (opts.verbose) runArgs.push(\"--verbose\");\n let child: ChildProcess;\n try {\n child = spawn(process.execPath, runArgs, {\n cwd: opts.projectDirs[0] ?? process.cwd(),\n detached: true,\n env: process.env,\n stdio: [\"ignore\", logFd, logFd, \"ipc\"],\n });\n } finally {\n closeSync(logFd);\n }\n\n if (child.pid == null) {\n child.kill();\n throw new Error(`could not start the background runtime; see ${logFile}`);\n }\n const pid = child.pid;\n\n let processStartIdentity: string | null = null;\n try {\n processStartIdentity = (opts.processIdentityReader ?? readProcessStartIdentity)(pid);\n if (!processStartIdentity) {\n throw new Error(`could not determine process identity for PID ${pid}`);\n }\n writeRuntimeStateWithIdentity(\n opts.configPath,\n pid,\n opts.projectDirs,\n processStartIdentity,\n );\n } catch (error) {\n if (child.connected) child.disconnect();\n if (processStartIdentity) {\n await terminateProcess(pid, processStartIdentity);\n } else {\n await terminateKnownChild(child);\n }\n throw error;\n }\n\n try {\n await waitUntilReady(child, opts.readyTimeoutMs ?? BACKGROUND_RUNTIME_READY_TIMEOUT_MS);\n } catch (error) {\n if (child.connected) child.disconnect();\n await terminateProcess(pid, processStartIdentity);\n removeRuntimeState(opts.configPath);\n throw error;\n }\n\n if (child.connected) child.disconnect();\n child.unref();\n\n return {\n running: true,\n pid,\n projects: opts.projectDirs,\n log_file: logFile,\n };\n } finally {\n releaseStartLock();\n }\n}\n\n/**\n * Record a running runtime so a later launch can find and stop it. Used both by\n * the detached launcher (child PID) and by a `--foreground` session (its own\n * PID), so the two modes share one source of truth and never coexist on the\n * same device token.\n */\nexport function writeRuntimeState(configPath: string, pid: number, projects: string[]): void {\n const processStartIdentity = readProcessStartIdentity(pid);\n if (!processStartIdentity) {\n throw new Error(`could not determine process identity for PID ${pid}`);\n }\n writeRuntimeStateWithIdentity(configPath, pid, projects, processStartIdentity);\n}\n\nfunction writeRuntimeStateWithIdentity(\n configPath: string,\n pid: number,\n projects: string[],\n processStartIdentity: string,\n): void {\n const statePath = runtimeStatePath(configPath);\n mkdirSync(dirname(statePath), { recursive: true });\n writePrivateJson(statePath, {\n pid,\n projects,\n process_start_identity: processStartIdentity,\n } satisfies RuntimeProcessState);\n}\n\nexport function clearRuntimeState(configPath: string): void {\n removeRuntimeState(configPath);\n}\n\nexport async function stopBackgroundRuntime(configPath: string): Promise<boolean> {\n const located = readRuntimeState(configPath);\n if (!located) return false;\n const processStartIdentity = located.state.process_start_identity;\n if (!processStartIdentity || !runtimeProcessMatches(located.state)) {\n removeRuntimeStateFile(located.path);\n return false;\n }\n await terminateProcess(located.state.pid, processStartIdentity);\n removeRuntimeStateFile(located.path);\n return true;\n}\n\nexport function backgroundRuntimeStatus(configPath: string): RuntimeProcessStatus {\n const located = readRuntimeState(configPath);\n if (!located) {\n return { running: false, pid: null, projects: [], log_file: runtimeLogPath(configPath) };\n }\n if (!runtimeProcessMatches(located.state)) {\n removeRuntimeStateFile(located.path);\n return {\n running: false,\n pid: null,\n projects: located.state.projects,\n log_file: runtimeLogPath(configPath),\n };\n }\n return {\n running: true,\n pid: located.state.pid,\n projects: located.state.projects,\n log_file: runtimeLogPath(configPath),\n };\n}\n\nfunction waitUntilReady(child: ChildProcess, timeoutMs: number): Promise<void> {\n return new Promise((resolve, reject) => {\n let timeout: ReturnType<typeof setTimeout>;\n const cleanup = (): void => {\n clearTimeout(timeout);\n child.removeListener(\"error\", onError);\n child.removeListener(\"exit\", onExit);\n child.removeListener(\"message\", onMessage);\n };\n const onError = (error: Error): void => {\n cleanup();\n reject(error);\n };\n const onExit = (code: number | null): void => {\n cleanup();\n reject(new Error(`background runtime exited before connecting (exit ${code ?? \"unknown\"})`));\n };\n const onMessage = (message: unknown): void => {\n if (!message || typeof message !== \"object\" || (message as { type?: unknown }).type !== \"ready\") return;\n cleanup();\n resolve();\n };\n timeout = setTimeout(() => {\n cleanup();\n reject(new Error(`background runtime did not connect within ${timeoutMs}ms`));\n }, timeoutMs);\n\n child.once(\"error\", onError);\n child.once(\"exit\", onExit);\n child.on(\"message\", onMessage);\n });\n}\n\nexport function runtimeStatePath(configPath: string): string {\n return runtimeArtifactPath(configPath, \"state\");\n}\n\nexport function runtimeLogPath(configPath: string): string {\n return runtimeArtifactPath(configPath, \"log\");\n}\n\nfunction runtimeStartLockPath(configPath: string): string {\n return runtimeArtifactPath(configPath, \"start.lock\");\n}\n\nexport async function acquireRuntimeStartLock(\n configPath: string,\n onRecoveryClaimed?: () => void,\n): Promise<() => void> {\n const lockPath = runtimeStartLockPath(configPath);\n mkdirSync(dirname(lockPath), { recursive: true });\n const processStartIdentity = readProcessStartIdentity(process.pid);\n if (!processStartIdentity) {\n throw new Error(`could not determine process identity for PID ${process.pid}`);\n }\n const owner: RuntimeStartLockOwner = {\n pid: process.pid,\n process_start_identity: processStartIdentity,\n };\n // Match worst-case `startBackgroundRuntime` hold time: stop prior runtime, wait for\n // ready, and tear down the child if ready fails, plus one poll interval.\n const deadline = Date.now() + START_LOCK_TIMEOUT_MS;\n while (Date.now() < deadline) {\n try {\n createRuntimeStartLock(lockPath, owner);\n let released = false;\n return () => {\n if (released) return;\n released = true;\n releaseRuntimeStartLock(lockPath, owner);\n };\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"EEXIST\") throw error;\n try {\n if (recoverRuntimeStartLock(lockPath, configPath, onRecoveryClaimed)) {\n continue;\n }\n } catch {\n // Another process may have released the lock; retry.\n }\n await new Promise((resolve) => setTimeout(resolve, STOP_POLL_INTERVAL_MS));\n }\n }\n throw new Error(\"timed out waiting for the background runtime start lock\");\n}\n\nfunction createRuntimeStartLock(lockPath: string, owner: RuntimeStartLockOwner): void {\n const temporaryPath = `${lockPath}.${process.pid}.${randomUUID()}.tmp`;\n try {\n writePrivateJson(temporaryPath, owner);\n linkSync(temporaryPath, lockPath);\n } finally {\n removeRuntimeStateFile(temporaryPath);\n }\n}\n\nfunction releaseRuntimeStartLock(lockPath: string, owner: RuntimeStartLockOwner): void {\n if (!runtimeStartLockOwnersEqual(readRuntimeStartLockOwner(lockPath), owner)) return;\n removeRuntimeStateFile(lockPath);\n}\n\nfunction readRuntimeStartLockOwner(lockPath: string): RuntimeStartLockOwner | null {\n try {\n const text = readFileSync(lockPath, \"utf8\").trim();\n if (/^\\d+$/.test(text)) {\n const pid = Number.parseInt(text, 10);\n return pid > 0 ? { pid } : null;\n }\n const value = JSON.parse(text) as Partial<RuntimeStartLockOwner>;\n if (!Number.isInteger(value.pid) || (value.pid ?? 0) <= 0) return null;\n if (typeof value.process_start_identity !== \"string\") return null;\n return { pid: value.pid!, process_start_identity: value.process_start_identity };\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n return null;\n }\n}\n\nfunction runtimeStartLockOwnerIsRunning(owner: RuntimeStartLockOwner): boolean {\n if (!owner.process_start_identity) return processIsRunning(owner.pid);\n return processMatchesIdentity(owner.pid, owner.process_start_identity);\n}\n\nfunction runtimeStartLockCanBeRecovered(lockPath: string, configPath: string): boolean {\n const owner = readRuntimeStartLockOwner(lockPath);\n if (owner?.process_start_identity) return !runtimeStartLockOwnerIsRunning(owner);\n const age = Date.now() - statSync(lockPath).mtimeMs;\n if (owner) {\n if (!runtimeStartLockOwnerIsRunning(owner)) return true;\n return age > START_LOCK_TIMEOUT_MS\n && inspectProcessCommandConfig(owner.pid, configPath) === false;\n }\n return age > MALFORMED_START_LOCK_GRACE_MS;\n}\n\nfunction recoverRuntimeStartLock(\n lockPath: string,\n configPath: string,\n onRecoveryClaimed?: () => void,\n): boolean {\n const recoveryPath = `${lockPath}.recovery.${process.pid}.${randomUUID()}`;\n try {\n linkSync(lockPath, recoveryPath);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return false;\n throw error;\n }\n\n try {\n if (!runtimeStartLockCanBeRecovered(recoveryPath, configPath)) return false;\n onRecoveryClaimed?.();\n if (!pathsReferenceSameFile(lockPath, recoveryPath)) return false;\n unlinkSync(lockPath);\n return true;\n } finally {\n removeRuntimeStateFile(recoveryPath);\n }\n}\n\nfunction pathsReferenceSameFile(firstPath: string, secondPath: string): boolean {\n try {\n const first = statSync(firstPath, { bigint: true });\n const second = statSync(secondPath, { bigint: true });\n return first.dev === second.dev && first.ino === second.ino;\n } catch {\n return false;\n }\n}\n\nfunction runtimeStartLockOwnersEqual(\n first: RuntimeStartLockOwner | null,\n second: RuntimeStartLockOwner,\n): boolean {\n return first?.pid === second.pid\n && first.process_start_identity === second.process_start_identity;\n}\n\nfunction readRuntimeState(configPath: string): LocatedRuntimeProcessState | null {\n const statePath = runtimeStatePath(configPath);\n const current = readRuntimeStateFile(statePath);\n if (current) {\n if (typeof current.process_start_identity === \"string\"\n && statePath !== legacyRuntimeStatePath(configPath)) {\n return { path: statePath, state: current };\n }\n return migrateLegacyRuntimeState(configPath, statePath, current);\n }\n const legacyPath = legacyRuntimeStatePath(configPath);\n if (legacyPath === statePath) return null;\n const legacy = readRuntimeStateFile(legacyPath);\n if (!legacy) return null;\n return migrateLegacyRuntimeState(configPath, legacyPath, legacy);\n}\n\nfunction readRuntimeStateFile(path: string): RuntimeProcessState | null {\n try {\n const value = JSON.parse(readFileSync(path, \"utf8\")) as Partial<RuntimeProcessState>;\n const pid = value.pid;\n if (typeof pid !== \"number\" || !Number.isInteger(pid) || pid <= 0\n || !Array.isArray(value.projects)) return null;\n return {\n pid,\n projects: value.projects.filter((project): project is string => typeof project === \"string\"),\n process_start_identity: value.process_start_identity,\n };\n } catch {\n return null;\n }\n}\n\nfunction migrateLegacyRuntimeState(\n configPath: string,\n sourcePath: string,\n state: RuntimeProcessState,\n): LocatedRuntimeProcessState | null {\n const destinationPath = runtimeStatePath(configPath);\n if (sourcePath === legacyRuntimeStatePath(configPath)\n && !processCommandMatchesConfig(state.pid, configPath)) return null;\n if (typeof state.process_start_identity === \"string\") {\n if (sourcePath !== destinationPath) {\n writeRuntimeStateWithIdentity(\n configPath,\n state.pid,\n state.projects,\n state.process_start_identity,\n );\n removeRuntimeStateFile(sourcePath);\n }\n return { path: destinationPath, state };\n }\n const processStartIdentity = readProcessStartIdentity(state.pid);\n if (!processStartIdentity) return null;\n writeRuntimeStateWithIdentity(configPath, state.pid, state.projects, processStartIdentity);\n if (sourcePath !== destinationPath) removeRuntimeStateFile(sourcePath);\n return {\n path: destinationPath,\n state: { ...state, process_start_identity: processStartIdentity },\n };\n}\n\nfunction legacyRuntimeStatePath(configPath: string): string {\n return runtimeArtifactPath(`${dirname(canonicalConfigPath(configPath))}/daemon.yaml`, \"state\");\n}\n\nfunction runtimeProcessMatches(state: RuntimeProcessState): boolean {\n return typeof state.process_start_identity === \"string\"\n && processIsRunning(state.pid)\n && readProcessStartIdentity(state.pid) === state.process_start_identity;\n}\n\nfunction readProcessStartIdentity(pid: number): string | null {\n try {\n if (process.platform === \"linux\") {\n const stat = readFileSync(`/proc/${pid}/stat`, \"utf8\");\n const fields = stat.slice(stat.lastIndexOf(\")\") + 2).split(\" \");\n return fields[19] ?? null;\n }\n if (process.platform === \"darwin\" || process.platform === \"freebsd\") {\n return execFileSync(\"/bin/ps\", [\"-o\", \"lstart=\", \"-p\", String(pid)], {\n encoding: \"utf8\",\n }).trim() || null;\n }\n if (process.platform === \"win32\") {\n return execFileSync(\"powershell.exe\", [\n \"-NoProfile\",\n \"-NonInteractive\",\n \"-Command\",\n `(Get-Process -Id ${pid}).StartTime.ToUniversalTime().Ticks`,\n ], { encoding: \"utf8\" }).trim() || null;\n }\n } catch {\n return null;\n }\n return null;\n}\n\nfunction processCommandMatchesConfig(pid: number, configPath: string): boolean {\n return inspectProcessCommandConfig(pid, configPath) === true;\n}\n\nfunction inspectProcessCommandConfig(pid: number, configPath: string): boolean | null {\n const expectedConfigPath = canonicalConfigPath(configPath);\n try {\n if (process.platform === \"linux\") {\n const args = readFileSync(`/proc/${pid}/cmdline`, \"utf8\").split(\"\\0\").filter(Boolean);\n return commandArgsMatchConfig(args, expectedConfigPath);\n }\n if (process.platform === \"darwin\" || process.platform === \"freebsd\") {\n const command = execFileSync(\"/bin/ps\", [\"-ww\", \"-o\", \"command=\", \"-p\", String(pid)], {\n encoding: \"utf8\",\n }).trim();\n return commandLineMatchesRuntimeConfig(command, expectedConfigPath, process.platform);\n }\n if (process.platform === \"win32\") {\n const command = execFileSync(\"powershell.exe\", [\n \"-NoProfile\",\n \"-NonInteractive\",\n \"-Command\",\n `(Get-CimInstance Win32_Process -Filter \\\"ProcessId = ${pid}\\\").CommandLine`,\n ], { encoding: \"utf8\" }).trim();\n return commandLineMatchesRuntimeConfig(command, expectedConfigPath, process.platform);\n }\n } catch {\n return null;\n }\n return null;\n}\n\nexport function commandLineMatchesRuntimeConfig(\n command: string,\n configPath: string,\n platform: NodeJS.Platform,\n): boolean {\n if (platform === \"win32\") {\n return commandArgsMatchConfig(splitWindowsCommandLine(command), canonicalConfigPath(configPath));\n }\n return commandStringMatchesConfig(command, canonicalConfigPath(configPath));\n}\n\nfunction splitWindowsCommandLine(command: string): string[] {\n const args: string[] = [];\n let index = 0;\n while (index < command.length) {\n while (/\\s/.test(command[index] ?? \"\")) index += 1;\n if (index >= command.length) break;\n let argument = \"\";\n let inQuotes = false;\n while (index < command.length) {\n if (!inQuotes && /\\s/.test(command[index] ?? \"\")) break;\n let backslashes = 0;\n while (command[index] === \"\\\\\") {\n backslashes += 1;\n index += 1;\n }\n if (command[index] === '\"') {\n argument += \"\\\\\".repeat(Math.floor(backslashes / 2));\n if (backslashes % 2 === 0) {\n inQuotes = !inQuotes;\n } else {\n argument += '\"';\n }\n index += 1;\n continue;\n }\n argument += \"\\\\\".repeat(backslashes);\n if (index < command.length) {\n argument += command[index];\n index += 1;\n }\n }\n args.push(argument);\n }\n return args;\n}\n\nfunction commandStringMatchesConfig(command: string, configPath: string): boolean {\n const runtimeCommand = findRuntimeCommand(command);\n if (!runtimeCommand) return false;\n const executablePrefix = command.slice(0, runtimeCommand.index);\n if (!commandPrefixHasCloudRuntimeLauncher(executablePrefix)) return false;\n if (runtimeCommand.command === \"login\"\n && !/(?:^|\\s)--foreground(?:\\s|$)/.test(command.slice(runtimeCommand.index))) return false;\n const match = command.slice(runtimeCommand.index).match(\n /(?:^|\\s)(?:--config|-c)(?:=|\\s+)(.*?)(?=\\s+-{1,2}[A-Za-z](?:[\\w-]*)(?:=|\\s|$)|$)/,\n );\n const configuredPath = match?.[1]?.trim().replace(/^(?:\"([\\s\\S]*)\"|'([\\s\\S]*)')$/, \"$1$2\");\n return configuredPath !== undefined && canonicalConfigPath(configuredPath) === configPath;\n}\n\nfunction commandArgsMatchConfig(args: string[], configPath: string): boolean {\n const runtimeCommand = findRuntimeCommandInArgs(args);\n if (!runtimeCommand) return false;\n if (!argsHaveCloudRuntimeLauncher(args.slice(0, runtimeCommand.index))) return false;\n const runtimeArgs = args.slice(runtimeCommand.index + 1);\n if (runtimeCommand.command === \"login\" && !runtimeArgs.includes(\"--foreground\")) return false;\n const configIndex = runtimeArgs.findIndex((arg) => arg === \"--config\" || arg === \"-c\");\n const configuredPath = configIndex >= 0 ? runtimeArgs[configIndex + 1] : undefined;\n const equalsArgument = runtimeArgs.find((arg) => arg.startsWith(\"--config=\") || arg.startsWith(\"-c=\"))\n ?.replace(/^(?:--config|-c)=/, \"\");\n return [configuredPath, equalsArgument].some(\n (value) => value !== undefined && canonicalConfigPath(value) === configPath,\n );\n}\n\nfunction argsHaveCloudRuntimeLauncher(args: string[]): boolean {\n if (args.length === 1) return isCloudRuntimeExecutable(args[0]!);\n return args.length === 2\n && isNodeExecutable(args[0]!)\n && isCloudRuntimeExecutable(args[1]!);\n}\n\nfunction commandPrefixHasCloudRuntimeLauncher(prefix: string): boolean {\n const args = splitFlattenedCommandPrefix(prefix);\n return args !== null && argsHaveCloudRuntimeLauncher(args);\n}\n\nfunction splitFlattenedCommandPrefix(prefix: string): string[] | null {\n const args: string[] = [];\n const token = /\\s*(?:\"([^\"]*)\"|'([^']*)'|(\\S+))/gy;\n while (token.lastIndex < prefix.length) {\n const match = token.exec(prefix);\n if (!match) return null;\n args.push(match[1] ?? match[2] ?? match[3]!);\n }\n return args;\n}\n\nfunction findRuntimeCommand(command: string): { command: string; index: number } | null {\n const match = /(?:^|\\s)(_run|connect|login)(?=\\s|$)/.exec(command);\n return match ? { command: match[1]!, index: match.index } : null;\n}\n\nfunction findRuntimeCommandInArgs(args: string[]): { command: string; index: number } | null {\n const index = args.findIndex((arg) => arg === \"_run\" || arg === \"connect\" || arg === \"login\");\n return index < 0 ? null : { command: args[index]!, index };\n}\n\nfunction isCloudRuntimeExecutable(argument: string): boolean {\n return /(?:^|[\\\\/])gsd-cloud(?:\\.[cm]?js)?$/.test(argument);\n}\n\nfunction isNodeExecutable(argument: string): boolean {\n return /(?:^|[\\\\/])node(?:\\.exe)?$/i.test(argument);\n}\n\nfunction processIsRunning(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (error) {\n return (error as NodeJS.ErrnoException).code === \"EPERM\";\n }\n}\n\nasync function waitForProcessExit(\n pid: number,\n expectedIdentity: string,\n timeoutMs: number,\n): Promise<boolean> {\n const deadline = Date.now() + timeoutMs;\n while (processMatchesIdentity(pid, expectedIdentity)) {\n if (Date.now() >= deadline) return false;\n await new Promise((resolve) => setTimeout(resolve, STOP_POLL_INTERVAL_MS));\n }\n return true;\n}\n\nasync function terminateProcess(pid: number, expectedIdentity: string): Promise<void> {\n if (!signalProcess(pid, expectedIdentity, \"SIGTERM\")) return;\n if (await waitForProcessExit(pid, expectedIdentity, STOP_GRACE_PERIOD_MS)) return;\n if (!signalProcess(pid, expectedIdentity, \"SIGKILL\")) return;\n if (!await waitForProcessExit(pid, expectedIdentity, FORCED_STOP_TIMEOUT_MS)) {\n throw new Error(`background runtime PID ${pid} did not stop`);\n }\n}\n\nasync function terminateKnownChild(child: ChildProcess): Promise<void> {\n if (child.exitCode !== null || child.signalCode !== null) return;\n child.kill(\"SIGTERM\");\n if (await waitForChildExit(child, STOP_GRACE_PERIOD_MS)) return;\n child.kill(\"SIGKILL\");\n if (!await waitForChildExit(child, FORCED_STOP_TIMEOUT_MS)) {\n throw new Error(`background runtime PID ${child.pid ?? \"unknown\"} did not stop`);\n }\n}\n\nfunction waitForChildExit(child: ChildProcess, timeoutMs: number): Promise<boolean> {\n if (child.exitCode !== null || child.signalCode !== null) return Promise.resolve(true);\n return new Promise((resolve) => {\n const onExit = (): void => {\n clearTimeout(timeout);\n resolve(true);\n };\n const timeout = setTimeout(() => {\n child.removeListener(\"exit\", onExit);\n resolve(false);\n }, timeoutMs);\n child.once(\"exit\", onExit);\n });\n}\n\nfunction signalProcess(pid: number, expectedIdentity: string, signal: NodeJS.Signals): boolean {\n if (!processMatchesIdentity(pid, expectedIdentity)) return false;\n try {\n process.kill(pid, signal);\n return true;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ESRCH\") return false;\n throw error;\n }\n}\n\nfunction processMatchesIdentity(pid: number, expectedIdentity: string): boolean {\n return processIsRunning(pid) && readProcessStartIdentity(pid) === expectedIdentity;\n}\n\nfunction removeRuntimeState(configPath: string): void {\n removeRuntimeStateFile(runtimeStatePath(configPath));\n}\n\nfunction removeRuntimeStateFile(path: string): void {\n try {\n unlinkSync(path);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") throw error;\n }\n}\n\nfunction writePrivateJson(path: string, value: unknown): void {\n writeFileSync(path, `${JSON.stringify(value, null, 2)}\\n`, { encoding: \"utf8\", mode: 0o600 });\n chmodSync(path, 0o600);\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-process.test.d.ts","sourceRoot":"","sources":["../src/runtime-process.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,543 @@
|
|
|
1
|
+
// Project/App: Open GSD
|
|
2
|
+
// File Purpose: Regression coverage for detached cloud runtime process timing and shutdown.
|
|
3
|
+
import { spawn } from "node:child_process";
|
|
4
|
+
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, statSync, utimesSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import { test } from "node:test";
|
|
8
|
+
import assert from "node:assert/strict";
|
|
9
|
+
import { CLOUD_RUNTIME_INITIAL_CONNECT_WINDOW_MS } from "./cloud-runtime.js";
|
|
10
|
+
import { BACKGROUND_RUNTIME_READY_TIMEOUT_MS, acquireRuntimeStartLock, backgroundRuntimeStatus, commandLineMatchesRuntimeConfig, runtimeLogPath, runtimeStatePath, startBackgroundRuntime, stopBackgroundRuntime, writeRuntimeState, } from "./runtime-process.js";
|
|
11
|
+
import { runtimeArtifactPath } from "./runtime-artifacts.js";
|
|
12
|
+
import { runtimeTelemetryPath } from "./runtime-telemetry.js";
|
|
13
|
+
test("background startup allows the cloud runtime's full initial reconnect window", () => {
|
|
14
|
+
assert.ok(BACKGROUND_RUNTIME_READY_TIMEOUT_MS > CLOUD_RUNTIME_INITIAL_CONNECT_WINDOW_MS);
|
|
15
|
+
});
|
|
16
|
+
test("runtime artifacts are namespaced by config path while daemon.yaml stays legacy-compatible", (t) => {
|
|
17
|
+
const root = mkdtempSync(join(tmpdir(), "gsd-cloud-artifacts-"));
|
|
18
|
+
t.after(() => rmSync(root, { recursive: true, force: true }));
|
|
19
|
+
const defaultConfig = join(root, "daemon.yaml");
|
|
20
|
+
const firstConfig = join(root, "first.yaml");
|
|
21
|
+
const secondConfig = join(root, "second.yaml");
|
|
22
|
+
assert.equal(runtimeStatePath(defaultConfig), join(root, "cloud-runtime.json"));
|
|
23
|
+
assert.equal(runtimeLogPath(defaultConfig), join(root, "cloud-runtime.log"));
|
|
24
|
+
assert.equal(runtimeTelemetryPath(defaultConfig), join(root, "cloud-runtime-status.json"));
|
|
25
|
+
assert.notEqual(runtimeStatePath(firstConfig), runtimeStatePath(secondConfig));
|
|
26
|
+
assert.notEqual(runtimeLogPath(firstConfig), runtimeLogPath(secondConfig));
|
|
27
|
+
assert.notEqual(runtimeTelemetryPath(firstConfig), runtimeTelemetryPath(secondConfig));
|
|
28
|
+
});
|
|
29
|
+
test("runtime artifact namespace is stable across monitor implementations", () => {
|
|
30
|
+
assert.equal(runtimeStatePath("/work/state/first.yaml"), "/work/state/cloud-runtime-58cb3ff924131c6e.json");
|
|
31
|
+
});
|
|
32
|
+
test("stop refuses to signal a process whose identity does not match state", async (t) => {
|
|
33
|
+
const root = mkdtempSync(join(tmpdir(), "gsd-cloud-reused-pid-"));
|
|
34
|
+
const configPath = join(root, "daemon.yaml");
|
|
35
|
+
const statePath = join(root, "cloud-runtime.json");
|
|
36
|
+
const child = spawn(process.execPath, ["-e", "setInterval(()=>{},1000)"]);
|
|
37
|
+
t.after(() => {
|
|
38
|
+
child.kill("SIGKILL");
|
|
39
|
+
rmSync(root, { recursive: true, force: true });
|
|
40
|
+
});
|
|
41
|
+
assert.ok(child.pid);
|
|
42
|
+
writeFileSync(statePath, `${JSON.stringify({
|
|
43
|
+
pid: child.pid,
|
|
44
|
+
projects: [root],
|
|
45
|
+
process_start_identity: "not-this-process",
|
|
46
|
+
})}\n`);
|
|
47
|
+
assert.equal(await stopBackgroundRuntime(configPath), false);
|
|
48
|
+
assert.equal(processIsRunning(child.pid), true);
|
|
49
|
+
assert.equal(existsSync(statePath), true);
|
|
50
|
+
});
|
|
51
|
+
test("custom configs migrate matching live legacy runtime state", (t) => {
|
|
52
|
+
const root = mkdtempSync(join(tmpdir(), "gsd-cloud-legacy-state-"));
|
|
53
|
+
const configPath = join(root, "custom.yaml");
|
|
54
|
+
const legacyStatePath = join(root, "cloud-runtime.json");
|
|
55
|
+
const child = spawn(process.execPath, [
|
|
56
|
+
writeLegacyRuntime(root, "gsd-cloud.js"),
|
|
57
|
+
"_run",
|
|
58
|
+
"--config",
|
|
59
|
+
configPath,
|
|
60
|
+
]);
|
|
61
|
+
t.after(() => {
|
|
62
|
+
child.kill("SIGKILL");
|
|
63
|
+
rmSync(root, { recursive: true, force: true });
|
|
64
|
+
});
|
|
65
|
+
assert.ok(child.pid);
|
|
66
|
+
writeFileSync(legacyStatePath, `${JSON.stringify({ pid: child.pid, projects: [root] })}\n`);
|
|
67
|
+
const status = backgroundRuntimeStatus(configPath);
|
|
68
|
+
assert.equal(status.running, true);
|
|
69
|
+
assert.equal(status.pid, child.pid);
|
|
70
|
+
assert.equal(existsSync(runtimeStatePath(configPath)), true);
|
|
71
|
+
assert.equal(existsSync(legacyStatePath), false);
|
|
72
|
+
});
|
|
73
|
+
for (const runtimeArgs of [
|
|
74
|
+
["connect"],
|
|
75
|
+
["login", "--foreground"],
|
|
76
|
+
]) {
|
|
77
|
+
test(`custom configs migrate legacy ${runtimeArgs.join(" ")} runtime state`, async (t) => {
|
|
78
|
+
const root = mkdtempSync(join(tmpdir(), "gsd-cloud-foreground-legacy-"));
|
|
79
|
+
const configPath = join(root, "custom runtime.yaml");
|
|
80
|
+
const legacyStatePath = join(root, "cloud-runtime.json");
|
|
81
|
+
const binaryPath = writeLegacyRuntime(root, "gsd-cloud.js");
|
|
82
|
+
const child = spawn(process.execPath, [
|
|
83
|
+
binaryPath,
|
|
84
|
+
...runtimeArgs,
|
|
85
|
+
"--config",
|
|
86
|
+
configPath,
|
|
87
|
+
]);
|
|
88
|
+
t.after(() => {
|
|
89
|
+
child.kill("SIGKILL");
|
|
90
|
+
rmSync(root, { recursive: true, force: true });
|
|
91
|
+
});
|
|
92
|
+
assert.ok(child.pid);
|
|
93
|
+
await waitForCondition(() => processIsRunning(child.pid));
|
|
94
|
+
writeFileSync(legacyStatePath, `${JSON.stringify({ pid: child.pid, projects: [root] })}\n`);
|
|
95
|
+
const status = backgroundRuntimeStatus(configPath);
|
|
96
|
+
assert.equal(status.running, true);
|
|
97
|
+
assert.equal(status.pid, child.pid);
|
|
98
|
+
assert.equal(existsSync(runtimeStatePath(configPath)), true);
|
|
99
|
+
assert.equal(existsSync(legacyStatePath), false);
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
test("legacy runtime state for another config is not migrated or signalled", async (t) => {
|
|
103
|
+
const root = mkdtempSync(join(tmpdir(), "gsd-cloud-foreign-legacy-state-"));
|
|
104
|
+
const requestedConfig = join(root, "requested.yaml");
|
|
105
|
+
const actualConfig = join(root, "actual.yaml");
|
|
106
|
+
const legacyStatePath = join(root, "cloud-runtime.json");
|
|
107
|
+
const child = spawn(process.execPath, [
|
|
108
|
+
"-e",
|
|
109
|
+
"setInterval(()=>{},1000)",
|
|
110
|
+
"_run",
|
|
111
|
+
"--config",
|
|
112
|
+
actualConfig,
|
|
113
|
+
]);
|
|
114
|
+
t.after(() => {
|
|
115
|
+
child.kill("SIGKILL");
|
|
116
|
+
rmSync(root, { recursive: true, force: true });
|
|
117
|
+
});
|
|
118
|
+
assert.ok(child.pid);
|
|
119
|
+
writeFileSync(legacyStatePath, `${JSON.stringify({ pid: child.pid, projects: [root] })}\n`);
|
|
120
|
+
assert.equal(await stopBackgroundRuntime(requestedConfig), false);
|
|
121
|
+
assert.equal(processIsRunning(child.pid), true);
|
|
122
|
+
assert.equal(existsSync(legacyStatePath), true);
|
|
123
|
+
});
|
|
124
|
+
test("default config preserves shared legacy state owned by a custom config", async (t) => {
|
|
125
|
+
const root = mkdtempSync(join(tmpdir(), "gsd-cloud-shared-legacy-state-"));
|
|
126
|
+
const requestedConfig = join(root, "daemon.yaml");
|
|
127
|
+
const actualConfig = join(root, "custom.yaml");
|
|
128
|
+
const legacyStatePath = join(root, "cloud-runtime.json");
|
|
129
|
+
const binaryPath = writeLegacyRuntime(root, "gsd-cloud.js");
|
|
130
|
+
const child = spawn(process.execPath, [binaryPath, "connect", "--config", actualConfig]);
|
|
131
|
+
t.after(() => {
|
|
132
|
+
child.kill("SIGKILL");
|
|
133
|
+
rmSync(root, { recursive: true, force: true });
|
|
134
|
+
});
|
|
135
|
+
assert.ok(child.pid);
|
|
136
|
+
writeFileSync(legacyStatePath, `${JSON.stringify({ pid: child.pid, projects: [root] })}\n`);
|
|
137
|
+
assert.equal(await stopBackgroundRuntime(requestedConfig), false);
|
|
138
|
+
assert.equal(processIsRunning(child.pid), true);
|
|
139
|
+
assert.equal(existsSync(legacyStatePath), true);
|
|
140
|
+
});
|
|
141
|
+
test("legacy migration rejects unrelated executables", async (t) => {
|
|
142
|
+
const root = mkdtempSync(join(tmpdir(), "gsd-cloud-unrelated-legacy-state-"));
|
|
143
|
+
const configPath = join(root, "daemon.yaml");
|
|
144
|
+
const legacyStatePath = join(root, "cloud-runtime.json");
|
|
145
|
+
const binaryPath = writeLegacyRuntime(root, "other-tool.js");
|
|
146
|
+
const child = spawn(process.execPath, [binaryPath, "connect", "--config", configPath]);
|
|
147
|
+
t.after(() => {
|
|
148
|
+
child.kill("SIGKILL");
|
|
149
|
+
rmSync(root, { recursive: true, force: true });
|
|
150
|
+
});
|
|
151
|
+
assert.ok(child.pid);
|
|
152
|
+
writeFileSync(legacyStatePath, `${JSON.stringify({ pid: child.pid, projects: [root] })}\n`);
|
|
153
|
+
assert.equal(await stopBackgroundRuntime(configPath), false);
|
|
154
|
+
assert.equal(processIsRunning(child.pid), true);
|
|
155
|
+
assert.equal(existsSync(legacyStatePath), true);
|
|
156
|
+
});
|
|
157
|
+
test("legacy migration preserves config paths with spaces before short options", async (t) => {
|
|
158
|
+
const root = mkdtempSync(join(tmpdir(), "gsd-cloud-argv-legacy-"));
|
|
159
|
+
const configPath = join(root, "custom runtime.yaml");
|
|
160
|
+
const legacyStatePath = join(root, "cloud-runtime.json");
|
|
161
|
+
const binaryPath = writeLegacyRuntime(root, "gsd-cloud.js");
|
|
162
|
+
const child = spawn(process.execPath, [
|
|
163
|
+
binaryPath,
|
|
164
|
+
"connect",
|
|
165
|
+
"--config",
|
|
166
|
+
configPath,
|
|
167
|
+
"-v",
|
|
168
|
+
"--foreground",
|
|
169
|
+
]);
|
|
170
|
+
t.after(() => {
|
|
171
|
+
child.kill("SIGKILL");
|
|
172
|
+
rmSync(root, { recursive: true, force: true });
|
|
173
|
+
});
|
|
174
|
+
assert.ok(child.pid);
|
|
175
|
+
await waitForCondition(() => processIsRunning(child.pid));
|
|
176
|
+
writeFileSync(legacyStatePath, `${JSON.stringify({ pid: child.pid, projects: [root] })}\n`);
|
|
177
|
+
const status = backgroundRuntimeStatus(configPath);
|
|
178
|
+
assert.equal(status.running, true);
|
|
179
|
+
assert.equal(status.pid, child.pid);
|
|
180
|
+
assert.equal(existsSync(runtimeStatePath(configPath)), true);
|
|
181
|
+
assert.equal(existsSync(legacyStatePath), false);
|
|
182
|
+
});
|
|
183
|
+
test("Windows command matching accepts quoted gsd-cloud.js paths", () => {
|
|
184
|
+
const configPath = "/work/runtime/daemon.yaml";
|
|
185
|
+
const command = [
|
|
186
|
+
'"C:\\Program Files\\nodejs\\node.exe"',
|
|
187
|
+
'"C:\\Program Files\\gsd-cloud\\gsd-cloud.js"',
|
|
188
|
+
"connect",
|
|
189
|
+
"--config",
|
|
190
|
+
`"${configPath}"`,
|
|
191
|
+
].join(" ");
|
|
192
|
+
assert.equal(commandLineMatchesRuntimeConfig(command, configPath, "win32"), true);
|
|
193
|
+
});
|
|
194
|
+
test("command matching rejects gsd-cloud decoys before another launcher", () => {
|
|
195
|
+
const configPath = "/work/runtime/daemon.yaml";
|
|
196
|
+
assert.equal(commandLineMatchesRuntimeConfig(`other-tool /tmp/gsd-cloud.js connect --config ${configPath}`, configPath, "linux"), false);
|
|
197
|
+
assert.equal(commandLineMatchesRuntimeConfig(`"C:\\Tools\\other-tool.exe" "C:\\tmp\\gsd-cloud.js" connect --config "${configPath}"`, configPath, "win32"), false);
|
|
198
|
+
assert.equal(commandLineMatchesRuntimeConfig(`/usr/bin/node /tmp/gsd-cloud.js --config ${configPath} connect --config /other.yaml`, configPath, "linux"), false);
|
|
199
|
+
assert.equal(commandLineMatchesRuntimeConfig(`/usr/bin/other /tmp/gsd-cloud.js connect --config ${configPath}`, configPath, "darwin"), false);
|
|
200
|
+
assert.equal(commandLineMatchesRuntimeConfig(`/usr/bin/node /usr/bin/other /tmp/gsd-cloud.js connect --config ${configPath}`, configPath, "darwin"), false);
|
|
201
|
+
});
|
|
202
|
+
test("command matching accepts a directly launched bare gsd-cloud executable", () => {
|
|
203
|
+
const configPath = "/work/runtime/daemon.yaml";
|
|
204
|
+
assert.equal(commandLineMatchesRuntimeConfig(`gsd-cloud connect --config ${configPath}`, configPath, "darwin"), true);
|
|
205
|
+
assert.equal(commandLineMatchesRuntimeConfig(`"/Applications/GSD Cloud/gsd-cloud" connect --config ${configPath}`, configPath, "darwin"), true);
|
|
206
|
+
});
|
|
207
|
+
test("start lock atomically records PID and process identity", { timeout: 5_000 }, async (t) => {
|
|
208
|
+
const root = mkdtempSync(join(tmpdir(), "gsd-cloud-lock-owner-"));
|
|
209
|
+
const configPath = join(root, "daemon.yaml");
|
|
210
|
+
const binaryPath = writeReadyRuntime(root, 500);
|
|
211
|
+
const lockPath = runtimeArtifactPath(configPath, "start.lock");
|
|
212
|
+
t.after(async () => {
|
|
213
|
+
await stopBackgroundRuntime(configPath).catch(() => undefined);
|
|
214
|
+
rmSync(root, { recursive: true, force: true });
|
|
215
|
+
});
|
|
216
|
+
const starting = startBackgroundRuntime({ binaryPath, configPath, projectDirs: [root] });
|
|
217
|
+
await waitForCondition(() => existsSync(lockPath));
|
|
218
|
+
const owner = JSON.parse(readFileSync(lockPath, "utf8"));
|
|
219
|
+
assert.equal(owner.pid, process.pid);
|
|
220
|
+
assert.equal(typeof owner.process_start_identity, "string");
|
|
221
|
+
assert.ok(owner.process_start_identity);
|
|
222
|
+
await starting;
|
|
223
|
+
});
|
|
224
|
+
test("start lock recovers immediately when its PID identity is stale", { timeout: 5_000 }, async (t) => {
|
|
225
|
+
const root = mkdtempSync(join(tmpdir(), "gsd-cloud-stale-lock-"));
|
|
226
|
+
const configPath = join(root, "daemon.yaml");
|
|
227
|
+
const binaryPath = writeReadyRuntime(root);
|
|
228
|
+
const lockPath = runtimeArtifactPath(configPath, "start.lock");
|
|
229
|
+
writeFileSync(lockPath, `${JSON.stringify({
|
|
230
|
+
pid: process.pid,
|
|
231
|
+
process_start_identity: "reused-process",
|
|
232
|
+
})}\n`);
|
|
233
|
+
t.after(async () => {
|
|
234
|
+
await stopBackgroundRuntime(configPath).catch(() => undefined);
|
|
235
|
+
rmSync(root, { recursive: true, force: true });
|
|
236
|
+
});
|
|
237
|
+
const starting = startBackgroundRuntime({ binaryPath, configPath, projectDirs: [root] });
|
|
238
|
+
await new Promise((resolveImmediate) => setImmediate(resolveImmediate));
|
|
239
|
+
const owner = JSON.parse(readFileSync(lockPath, "utf8"));
|
|
240
|
+
assert.notEqual(owner.process_start_identity, "reused-process");
|
|
241
|
+
await starting;
|
|
242
|
+
});
|
|
243
|
+
test("start lock recovery never removes a replacement owner", { timeout: 5_000 }, async (t) => {
|
|
244
|
+
const root = mkdtempSync(join(tmpdir(), "gsd-cloud-replaced-lock-"));
|
|
245
|
+
const ownerConfigPath = join(root, "owner.yaml");
|
|
246
|
+
const configPath = join(root, "daemon.yaml");
|
|
247
|
+
const ownerLockPath = runtimeArtifactPath(ownerConfigPath, "start.lock");
|
|
248
|
+
const lockPath = runtimeArtifactPath(configPath, "start.lock");
|
|
249
|
+
const releaseOwner = await acquireRuntimeStartLock(ownerConfigPath);
|
|
250
|
+
const replacementOwner = readFileSync(ownerLockPath, "utf8");
|
|
251
|
+
releaseOwner();
|
|
252
|
+
writeFileSync(lockPath, "stale");
|
|
253
|
+
const old = new Date(0);
|
|
254
|
+
utimesSync(lockPath, old, old);
|
|
255
|
+
let release;
|
|
256
|
+
t.after(async () => {
|
|
257
|
+
rmSync(lockPath, { force: true });
|
|
258
|
+
release ??= await acquiring.catch(() => undefined);
|
|
259
|
+
release?.();
|
|
260
|
+
rmSync(root, { recursive: true, force: true });
|
|
261
|
+
});
|
|
262
|
+
let replacementInstalled;
|
|
263
|
+
let replacementInode;
|
|
264
|
+
const installed = new Promise((resolve) => {
|
|
265
|
+
replacementInstalled = resolve;
|
|
266
|
+
});
|
|
267
|
+
const acquiring = acquireRuntimeStartLock(configPath, () => {
|
|
268
|
+
rmSync(lockPath);
|
|
269
|
+
writeFileSync(lockPath, replacementOwner);
|
|
270
|
+
replacementInode = statSync(lockPath, { bigint: true }).ino;
|
|
271
|
+
replacementInstalled();
|
|
272
|
+
});
|
|
273
|
+
await installed;
|
|
274
|
+
assert.equal(readFileSync(lockPath, "utf8"), replacementOwner);
|
|
275
|
+
assert.equal(statSync(lockPath, { bigint: true }).ino, replacementInode);
|
|
276
|
+
rmSync(lockPath);
|
|
277
|
+
release = await acquiring;
|
|
278
|
+
release();
|
|
279
|
+
});
|
|
280
|
+
test("start lock recovery ignores an abandoned recovery claim", { timeout: 5_000 }, async (t) => {
|
|
281
|
+
const root = mkdtempSync(join(tmpdir(), "gsd-cloud-abandoned-recovery-"));
|
|
282
|
+
const configPath = join(root, "daemon.yaml");
|
|
283
|
+
const lockPath = runtimeArtifactPath(configPath, "start.lock");
|
|
284
|
+
const recoveryPath = `${lockPath}.recovery`;
|
|
285
|
+
writeFileSync(lockPath, "stale");
|
|
286
|
+
writeFileSync(recoveryPath, "abandoned");
|
|
287
|
+
const old = new Date(0);
|
|
288
|
+
utimesSync(lockPath, old, old);
|
|
289
|
+
utimesSync(recoveryPath, old, old);
|
|
290
|
+
let release;
|
|
291
|
+
const acquiring = acquireRuntimeStartLock(configPath);
|
|
292
|
+
t.after(async () => {
|
|
293
|
+
rmSync(lockPath, { force: true });
|
|
294
|
+
rmSync(recoveryPath, { force: true });
|
|
295
|
+
release ??= await acquiring.catch(() => undefined);
|
|
296
|
+
release?.();
|
|
297
|
+
rmSync(root, { recursive: true, force: true });
|
|
298
|
+
});
|
|
299
|
+
release = await Promise.race([
|
|
300
|
+
acquiring,
|
|
301
|
+
new Promise((_resolve, reject) => {
|
|
302
|
+
setTimeout(() => reject(new Error("abandoned recovery claim blocked start")), 500);
|
|
303
|
+
}),
|
|
304
|
+
]);
|
|
305
|
+
release();
|
|
306
|
+
assert.equal(existsSync(lockPath), false);
|
|
307
|
+
assert.equal(readFileSync(recoveryPath, "utf8"), "abandoned");
|
|
308
|
+
});
|
|
309
|
+
test("start lock does not reclaim a live legacy owner by age", { timeout: 5_000 }, async (t) => {
|
|
310
|
+
const root = mkdtempSync(join(tmpdir(), "gsd-cloud-live-legacy-lock-"));
|
|
311
|
+
const configPath = join(root, "daemon.yaml");
|
|
312
|
+
const binaryPath = writeReadyRuntime(root);
|
|
313
|
+
const lockPath = runtimeArtifactPath(configPath, "start.lock");
|
|
314
|
+
writeFileSync(lockPath, `${process.pid}\n`);
|
|
315
|
+
t.after(async () => {
|
|
316
|
+
await stopBackgroundRuntime(configPath).catch(() => undefined);
|
|
317
|
+
rmSync(root, { recursive: true, force: true });
|
|
318
|
+
});
|
|
319
|
+
const starting = startBackgroundRuntime({ binaryPath, configPath, projectDirs: [root] });
|
|
320
|
+
await new Promise((resolveDelay) => setTimeout(resolveDelay, 1_200));
|
|
321
|
+
assert.equal(readFileSync(lockPath, "utf8"), `${process.pid}\n`);
|
|
322
|
+
rmSync(lockPath);
|
|
323
|
+
await starting;
|
|
324
|
+
});
|
|
325
|
+
test("start lock preserves an aged legacy owner running the same config", { timeout: 5_000 }, async (t) => {
|
|
326
|
+
const root = mkdtempSync(join(tmpdir(), "gsd-cloud-live-runtime-lock-"));
|
|
327
|
+
const configPath = join(root, "daemon.yaml");
|
|
328
|
+
const binaryPath = writeReadyRuntime(root);
|
|
329
|
+
const legacyBinaryPath = writeLegacyRuntime(root, "gsd-cloud");
|
|
330
|
+
const lockPath = runtimeArtifactPath(configPath, "start.lock");
|
|
331
|
+
const owner = spawn(process.execPath, [legacyBinaryPath, "connect", "--config", configPath]);
|
|
332
|
+
t.after(async () => {
|
|
333
|
+
owner.kill("SIGKILL");
|
|
334
|
+
await stopBackgroundRuntime(configPath).catch(() => undefined);
|
|
335
|
+
rmSync(root, { recursive: true, force: true });
|
|
336
|
+
});
|
|
337
|
+
assert.ok(owner.pid);
|
|
338
|
+
await waitForCondition(() => processIsRunning(owner.pid));
|
|
339
|
+
writeFileSync(lockPath, `${owner.pid}\n`);
|
|
340
|
+
const old = new Date(0);
|
|
341
|
+
utimesSync(lockPath, old, old);
|
|
342
|
+
const starting = startBackgroundRuntime({ binaryPath, configPath, projectDirs: [root] });
|
|
343
|
+
await new Promise((resolveDelay) => setTimeout(resolveDelay, 200));
|
|
344
|
+
assert.equal(readFileSync(lockPath, "utf8"), `${owner.pid}\n`);
|
|
345
|
+
rmSync(lockPath);
|
|
346
|
+
await starting;
|
|
347
|
+
});
|
|
348
|
+
for (const staleOwner of ["", "not-json", `${process.pid}\n`]) {
|
|
349
|
+
test(`start lock recovers stale legacy owner ${JSON.stringify(staleOwner)}`, { timeout: 2_000 }, async (t) => {
|
|
350
|
+
const root = mkdtempSync(join(tmpdir(), "gsd-cloud-stale-legacy-lock-"));
|
|
351
|
+
const configPath = join(root, "daemon.yaml");
|
|
352
|
+
const lockPath = runtimeArtifactPath(configPath, "start.lock");
|
|
353
|
+
writeFileSync(lockPath, staleOwner);
|
|
354
|
+
const old = new Date(0);
|
|
355
|
+
utimesSync(lockPath, old, old);
|
|
356
|
+
let release;
|
|
357
|
+
const acquiring = acquireRuntimeStartLock(configPath);
|
|
358
|
+
t.after(async () => {
|
|
359
|
+
if (!release) {
|
|
360
|
+
rmSync(lockPath, { force: true });
|
|
361
|
+
release = await acquiring.catch(() => undefined);
|
|
362
|
+
}
|
|
363
|
+
release?.();
|
|
364
|
+
rmSync(root, { recursive: true, force: true });
|
|
365
|
+
});
|
|
366
|
+
release = await Promise.race([
|
|
367
|
+
acquiring,
|
|
368
|
+
new Promise((_resolve, reject) => {
|
|
369
|
+
setTimeout(() => reject(new Error("stale start lock was not recovered promptly")), 500);
|
|
370
|
+
}),
|
|
371
|
+
]);
|
|
372
|
+
release();
|
|
373
|
+
assert.equal(existsSync(lockPath), false);
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
test("stop waits for the detached runtime to exit before removing its state", { timeout: 5_000 }, async (t) => {
|
|
377
|
+
const root = mkdtempSync(join(tmpdir(), "gsd-cloud-stop-"));
|
|
378
|
+
const configPath = join(root, "daemon.yaml");
|
|
379
|
+
const statePath = join(root, "cloud-runtime.json");
|
|
380
|
+
const binaryPath = join(root, "gsd-cloud.js");
|
|
381
|
+
writeFileSync(binaryPath, [
|
|
382
|
+
"process.on('SIGTERM',()=>setTimeout(()=>process.exit(0),200));",
|
|
383
|
+
"process.send?.('ready');",
|
|
384
|
+
"setInterval(()=>{},1000);",
|
|
385
|
+
].join("\n"));
|
|
386
|
+
const child = spawn(process.execPath, [
|
|
387
|
+
binaryPath,
|
|
388
|
+
"_run",
|
|
389
|
+
"--config",
|
|
390
|
+
configPath,
|
|
391
|
+
], { stdio: ["ignore", "ignore", "ignore", "ipc"] });
|
|
392
|
+
t.after(() => {
|
|
393
|
+
child.kill("SIGKILL");
|
|
394
|
+
rmSync(root, { recursive: true, force: true });
|
|
395
|
+
});
|
|
396
|
+
await new Promise((resolve, reject) => {
|
|
397
|
+
child.once("error", reject);
|
|
398
|
+
child.once("message", () => resolve());
|
|
399
|
+
});
|
|
400
|
+
assert.ok(child.pid);
|
|
401
|
+
writeRuntimeState(configPath, child.pid, [root]);
|
|
402
|
+
const startedAt = Date.now();
|
|
403
|
+
assert.equal(await stopBackgroundRuntime(configPath), true);
|
|
404
|
+
assert.ok(Date.now() - startedAt >= 150);
|
|
405
|
+
assert.equal(existsSync(statePath), false);
|
|
406
|
+
});
|
|
407
|
+
test("background startup terminates its child when state registration fails", { timeout: 5_000 }, async (t) => {
|
|
408
|
+
const root = mkdtempSync(join(tmpdir(), "gsd-cloud-registration-failure-"));
|
|
409
|
+
const configPath = join(root, "daemon.yaml");
|
|
410
|
+
const pidPath = join(root, "runtime-pid.txt");
|
|
411
|
+
const binaryPath = join(root, "runtime.mjs");
|
|
412
|
+
writeFileSync(binaryPath, [
|
|
413
|
+
'import { writeFileSync } from "node:fs";',
|
|
414
|
+
`writeFileSync(${JSON.stringify(pidPath)}, String(process.pid));`,
|
|
415
|
+
'process.on("SIGTERM", () => process.exit(0));',
|
|
416
|
+
'setInterval(() => undefined, 1_000);',
|
|
417
|
+
].join("\n"));
|
|
418
|
+
mkdirSync(runtimeStatePath(configPath));
|
|
419
|
+
t.after(() => {
|
|
420
|
+
if (existsSync(pidPath)) {
|
|
421
|
+
const pid = Number.parseInt(readFileSync(pidPath, "utf8"), 10);
|
|
422
|
+
if (processIsRunning(pid))
|
|
423
|
+
process.kill(pid, "SIGKILL");
|
|
424
|
+
}
|
|
425
|
+
rmSync(root, { recursive: true, force: true });
|
|
426
|
+
});
|
|
427
|
+
await assert.rejects(startBackgroundRuntime({ binaryPath, configPath, projectDirs: [root] }));
|
|
428
|
+
await new Promise((resolve) => setTimeout(resolve, 250));
|
|
429
|
+
if (existsSync(pidPath)) {
|
|
430
|
+
const pid = Number.parseInt(readFileSync(pidPath, "utf8"), 10);
|
|
431
|
+
await waitForCondition(() => !processIsRunning(pid));
|
|
432
|
+
assert.equal(processIsRunning(pid), false);
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
test("background startup force-stops its child when identity registration fails", { timeout: 8_000 }, async (t) => {
|
|
436
|
+
const root = mkdtempSync(join(tmpdir(), "gsd-cloud-registration-force-stop-"));
|
|
437
|
+
const configPath = join(root, "daemon.yaml");
|
|
438
|
+
const pidPath = join(root, "runtime-pid.txt");
|
|
439
|
+
const binaryPath = join(root, "runtime.mjs");
|
|
440
|
+
writeFileSync(binaryPath, [
|
|
441
|
+
'import { writeFileSync } from "node:fs";',
|
|
442
|
+
'process.on("SIGTERM", () => undefined);',
|
|
443
|
+
`writeFileSync(${JSON.stringify(pidPath)}, String(process.pid));`,
|
|
444
|
+
'setInterval(() => undefined, 1_000);',
|
|
445
|
+
].join("\n"));
|
|
446
|
+
mkdirSync(runtimeStatePath(configPath));
|
|
447
|
+
t.after(() => {
|
|
448
|
+
if (existsSync(pidPath)) {
|
|
449
|
+
const pid = Number.parseInt(readFileSync(pidPath, "utf8"), 10);
|
|
450
|
+
if (processIsRunning(pid))
|
|
451
|
+
process.kill(pid, "SIGKILL");
|
|
452
|
+
}
|
|
453
|
+
rmSync(root, { recursive: true, force: true });
|
|
454
|
+
});
|
|
455
|
+
await assert.rejects(startBackgroundRuntime({
|
|
456
|
+
binaryPath,
|
|
457
|
+
configPath,
|
|
458
|
+
projectDirs: [root],
|
|
459
|
+
processIdentityReader: () => {
|
|
460
|
+
const deadline = Date.now() + 1_000;
|
|
461
|
+
while (!existsSync(pidPath) && Date.now() < deadline) {
|
|
462
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 10);
|
|
463
|
+
}
|
|
464
|
+
return null;
|
|
465
|
+
},
|
|
466
|
+
}));
|
|
467
|
+
if (existsSync(pidPath)) {
|
|
468
|
+
const pid = Number.parseInt(readFileSync(pidPath, "utf8"), 10);
|
|
469
|
+
await waitForCondition(() => !processIsRunning(pid), 7_000);
|
|
470
|
+
assert.equal(processIsRunning(pid), false);
|
|
471
|
+
}
|
|
472
|
+
});
|
|
473
|
+
test("concurrent starts serialize and leave only the newest runtime running", { timeout: 15_000 }, async (t) => {
|
|
474
|
+
const root = mkdtempSync(join(tmpdir(), "gsd-cloud-concurrent-start-"));
|
|
475
|
+
const configPath = join(root, "daemon.yaml");
|
|
476
|
+
const binaryPath = writeReadyRuntime(root);
|
|
477
|
+
t.after(async () => {
|
|
478
|
+
await stopBackgroundRuntime(configPath).catch(() => undefined);
|
|
479
|
+
rmSync(root, { recursive: true, force: true });
|
|
480
|
+
});
|
|
481
|
+
const [first, second] = await Promise.all([
|
|
482
|
+
startBackgroundRuntime({ binaryPath, configPath, projectDirs: [root] }),
|
|
483
|
+
startBackgroundRuntime({ binaryPath, configPath, projectDirs: [root] }),
|
|
484
|
+
]);
|
|
485
|
+
assert.notEqual(first.pid, second.pid);
|
|
486
|
+
assert.ok(first.pid);
|
|
487
|
+
assert.equal(processIsRunning(first.pid), false);
|
|
488
|
+
assert.equal(backgroundRuntimeStatus(configPath).pid, second.pid);
|
|
489
|
+
});
|
|
490
|
+
test("verbose background starts forward the flag to the runtime child", { timeout: 10_000 }, async (t) => {
|
|
491
|
+
const root = mkdtempSync(join(tmpdir(), "gsd-cloud-verbose-start-"));
|
|
492
|
+
const configPath = join(root, "daemon.yaml");
|
|
493
|
+
const binaryPath = writeReadyRuntime(root);
|
|
494
|
+
t.after(async () => {
|
|
495
|
+
await stopBackgroundRuntime(configPath).catch(() => undefined);
|
|
496
|
+
rmSync(root, { recursive: true, force: true });
|
|
497
|
+
});
|
|
498
|
+
await startBackgroundRuntime({
|
|
499
|
+
binaryPath,
|
|
500
|
+
configPath,
|
|
501
|
+
projectDirs: [root],
|
|
502
|
+
verbose: true,
|
|
503
|
+
});
|
|
504
|
+
const args = JSON.parse(readFileSync(join(root, "runtime-args.json"), "utf8"));
|
|
505
|
+
assert.ok(args.includes("--verbose"));
|
|
506
|
+
});
|
|
507
|
+
function writeReadyRuntime(root, readyDelayMs = 0) {
|
|
508
|
+
const binaryPath = join(root, "gsd-cloud.js");
|
|
509
|
+
writeFileSync(binaryPath, [
|
|
510
|
+
'import { writeFileSync } from "node:fs";',
|
|
511
|
+
`writeFileSync(${JSON.stringify(join(root, "runtime-args.json"))}, JSON.stringify(process.argv.slice(2)));`,
|
|
512
|
+
'process.on("SIGTERM", () => process.exit(0));',
|
|
513
|
+
`setTimeout(() => process.send?.({ type: "ready" }), ${readyDelayMs});`,
|
|
514
|
+
'setInterval(() => undefined, 1_000);',
|
|
515
|
+
].join("\n"));
|
|
516
|
+
return binaryPath;
|
|
517
|
+
}
|
|
518
|
+
function writeLegacyRuntime(root, name) {
|
|
519
|
+
const binaryPath = join(root, name);
|
|
520
|
+
writeFileSync(binaryPath, [
|
|
521
|
+
"process.on('SIGTERM', () => process.exit(0));",
|
|
522
|
+
"setInterval(() => undefined, 1_000);",
|
|
523
|
+
].join("\n"));
|
|
524
|
+
return binaryPath;
|
|
525
|
+
}
|
|
526
|
+
function processIsRunning(pid) {
|
|
527
|
+
try {
|
|
528
|
+
process.kill(pid, 0);
|
|
529
|
+
return true;
|
|
530
|
+
}
|
|
531
|
+
catch {
|
|
532
|
+
return false;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
async function waitForCondition(condition, timeoutMs = 2_000) {
|
|
536
|
+
const deadline = Date.now() + timeoutMs;
|
|
537
|
+
while (!condition()) {
|
|
538
|
+
if (Date.now() >= deadline)
|
|
539
|
+
throw new Error("timed out waiting for test condition");
|
|
540
|
+
await new Promise((resolve) => setTimeout(resolve, 25));
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
//# sourceMappingURL=runtime-process.test.js.map
|