@opengsd/gsd-pi 1.10.0 → 1.11.0

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.
Files changed (218) hide show
  1. package/README.md +10 -9
  2. package/dist/resources/.managed-resources-content-hash +1 -1
  3. package/dist/resources/extensions/gsd/auto-model-selection.js +1 -0
  4. package/dist/resources/extensions/gsd/auto-start.js +1 -1
  5. package/dist/resources/extensions/gsd/auto-verification.js +25 -12
  6. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +4 -3
  7. package/dist/resources/extensions/gsd/db/engine.js +8 -2
  8. package/dist/resources/extensions/gsd/db-canonical-foundation-schema.js +120 -0
  9. package/dist/resources/extensions/gsd/db-migration-steps.js +4 -0
  10. package/dist/resources/extensions/gsd/docs/preferences-reference.md +12 -3
  11. package/dist/resources/extensions/gsd/doctor-engine-checks.js +16 -4
  12. package/dist/resources/extensions/gsd/model-cost-table.js +6 -3
  13. package/dist/resources/extensions/gsd/model-router.js +10 -5
  14. package/dist/resources/extensions/gsd/planning-subagent-registry.js +12 -4
  15. package/dist/resources/extensions/gsd/preferences-models.js +50 -10
  16. package/dist/resources/extensions/gsd/preferences-types.js +1 -0
  17. package/dist/resources/extensions/gsd/preferences-validation.js +41 -6
  18. package/dist/resources/extensions/gsd/preferences.js +7 -0
  19. package/dist/resources/extensions/gsd/templates/PREFERENCES.md +3 -0
  20. package/dist/resources/extensions/gsd/tools/plan-slice.js +5 -1
  21. package/dist/resources/extensions/gsd/verification-evidence.js +5 -3
  22. package/dist/resources/extensions/ollama/ollama-chat-provider.js +2 -2
  23. package/dist/resources/extensions/subagent/index.js +1 -1
  24. package/dist/startup-model-validation.d.ts +1 -1
  25. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  26. package/dist/web/standalone/.next/BUILD_ID +1 -1
  27. package/dist/web/standalone/.next/app-path-routes-manifest.json +7 -7
  28. package/dist/web/standalone/.next/build-manifest.json +2 -2
  29. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  30. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  31. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  32. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  33. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  34. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  35. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  36. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  37. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  38. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  39. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  40. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  41. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  42. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  43. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  44. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  45. package/dist/web/standalone/.next/server/app/index.html +1 -1
  46. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  47. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  48. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  49. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  50. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  51. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  52. package/dist/web/standalone/.next/server/app-paths-manifest.json +7 -7
  53. package/dist/web/standalone/.next/server/chunks/8357.js +2 -2
  54. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  55. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  56. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  57. package/dist/web/standalone/node_modules/@gsd/native/package.json +1 -1
  58. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  59. package/integrations/hermes/open_gsd_hermes/gsd_client.py +1 -1
  60. package/integrations/hermes/pyproject.toml +1 -1
  61. package/package.json +7 -6
  62. package/packages/cloud-mcp-gateway/package.json +2 -2
  63. package/packages/contracts/dist/rpc.d.ts +1 -1
  64. package/packages/contracts/dist/rpc.d.ts.map +1 -1
  65. package/packages/contracts/dist/rpc.js +1 -1
  66. package/packages/contracts/dist/rpc.js.map +1 -1
  67. package/packages/contracts/dist/rpc.test.js +1 -1
  68. package/packages/contracts/dist/rpc.test.js.map +1 -1
  69. package/packages/contracts/package.json +1 -1
  70. package/packages/daemon/package.json +4 -4
  71. package/packages/gsd-agent-core/package.json +5 -5
  72. package/packages/gsd-agent-modes/dist/cli/args.js +2 -2
  73. package/packages/gsd-agent-modes/dist/cli/args.js.map +1 -1
  74. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  75. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.js +1 -0
  76. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.js.map +1 -1
  77. package/packages/gsd-agent-modes/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  78. package/packages/gsd-agent-modes/dist/modes/interactive/components/thinking-selector.js +1 -0
  79. package/packages/gsd-agent-modes/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  80. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-autocomplete.d.ts.map +1 -1
  81. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-autocomplete.js +1 -0
  82. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-autocomplete.js.map +1 -1
  83. package/packages/gsd-agent-modes/package.json +7 -7
  84. package/packages/gsd-cloud/README.md +12 -2
  85. package/packages/gsd-cloud/dist/background-cli.test.js +157 -80
  86. package/packages/gsd-cloud/dist/background-cli.test.js.map +1 -1
  87. package/packages/gsd-cloud/dist/cli.d.ts.map +1 -1
  88. package/packages/gsd-cloud/dist/cli.js +36 -12
  89. package/packages/gsd-cloud/dist/cli.js.map +1 -1
  90. package/packages/gsd-cloud/dist/cloud-runtime.d.ts +6 -1
  91. package/packages/gsd-cloud/dist/cloud-runtime.d.ts.map +1 -1
  92. package/packages/gsd-cloud/dist/cloud-runtime.js +106 -24
  93. package/packages/gsd-cloud/dist/cloud-runtime.js.map +1 -1
  94. package/packages/gsd-cloud/dist/cloud-runtime.test.js +224 -59
  95. package/packages/gsd-cloud/dist/cloud-runtime.test.js.map +1 -1
  96. package/packages/gsd-cloud/dist/runtime-artifacts.d.ts +3 -0
  97. package/packages/gsd-cloud/dist/runtime-artifacts.d.ts.map +1 -0
  98. package/packages/gsd-cloud/dist/runtime-artifacts.js +36 -0
  99. package/packages/gsd-cloud/dist/runtime-artifacts.js.map +1 -0
  100. package/packages/gsd-cloud/dist/runtime-process.d.ts +5 -0
  101. package/packages/gsd-cloud/dist/runtime-process.d.ts.map +1 -1
  102. package/packages/gsd-cloud/dist/runtime-process.js +429 -61
  103. package/packages/gsd-cloud/dist/runtime-process.js.map +1 -1
  104. package/packages/gsd-cloud/dist/runtime-process.test.js +493 -47
  105. package/packages/gsd-cloud/dist/runtime-process.test.js.map +1 -1
  106. package/packages/gsd-cloud/dist/runtime-telemetry.d.ts +117 -0
  107. package/packages/gsd-cloud/dist/runtime-telemetry.d.ts.map +1 -0
  108. package/packages/gsd-cloud/dist/runtime-telemetry.js +291 -0
  109. package/packages/gsd-cloud/dist/runtime-telemetry.js.map +1 -0
  110. package/packages/gsd-cloud/dist/runtime-telemetry.test.d.ts +2 -0
  111. package/packages/gsd-cloud/dist/runtime-telemetry.test.d.ts.map +1 -0
  112. package/packages/gsd-cloud/dist/runtime-telemetry.test.js +434 -0
  113. package/packages/gsd-cloud/dist/runtime-telemetry.test.js.map +1 -0
  114. package/packages/gsd-cloud/package.json +2 -2
  115. package/packages/mcp-server/package.json +4 -4
  116. package/packages/native/package.json +1 -1
  117. package/packages/pi-agent-core/dist/types.d.ts +2 -2
  118. package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
  119. package/packages/pi-agent-core/dist/types.js.map +1 -1
  120. package/packages/pi-agent-core/package.json +1 -1
  121. package/packages/pi-ai/dist/models.d.ts.map +1 -1
  122. package/packages/pi-ai/dist/models.generated.d.ts +113 -2
  123. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  124. package/packages/pi-ai/dist/models.generated.js +84 -9
  125. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  126. package/packages/pi-ai/dist/models.js +18 -6
  127. package/packages/pi-ai/dist/models.js.map +1 -1
  128. package/packages/pi-ai/dist/providers/amazon-bedrock.js +3 -2
  129. package/packages/pi-ai/dist/providers/amazon-bedrock.js.map +1 -1
  130. package/packages/pi-ai/dist/providers/azure-openai-responses.d.ts +1 -1
  131. package/packages/pi-ai/dist/providers/azure-openai-responses.d.ts.map +1 -1
  132. package/packages/pi-ai/dist/providers/azure-openai-responses.js.map +1 -1
  133. package/packages/pi-ai/dist/providers/google-vertex.js.map +1 -1
  134. package/packages/pi-ai/dist/providers/google.js.map +1 -1
  135. package/packages/pi-ai/dist/providers/openai-codex-responses.d.ts +1 -1
  136. package/packages/pi-ai/dist/providers/openai-codex-responses.d.ts.map +1 -1
  137. package/packages/pi-ai/dist/providers/openai-codex-responses.js +1 -1
  138. package/packages/pi-ai/dist/providers/openai-codex-responses.js.map +1 -1
  139. package/packages/pi-ai/dist/providers/openai-completions.d.ts +1 -1
  140. package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
  141. package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
  142. package/packages/pi-ai/dist/providers/openai-responses.d.ts +1 -1
  143. package/packages/pi-ai/dist/providers/openai-responses.d.ts.map +1 -1
  144. package/packages/pi-ai/dist/providers/openai-responses.js +1 -1
  145. package/packages/pi-ai/dist/providers/openai-responses.js.map +1 -1
  146. package/packages/pi-ai/dist/providers/simple-options.d.ts +1 -1
  147. package/packages/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
  148. package/packages/pi-ai/dist/providers/simple-options.js +1 -1
  149. package/packages/pi-ai/dist/providers/simple-options.js.map +1 -1
  150. package/packages/pi-ai/dist/types.d.ts +9 -1
  151. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  152. package/packages/pi-ai/dist/types.js.map +1 -1
  153. package/packages/pi-ai/package.json +1 -1
  154. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  155. package/packages/pi-coding-agent/dist/core/model-registry.js +1 -0
  156. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  157. package/packages/pi-coding-agent/dist/core/model-resolver.js +1 -1
  158. package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
  159. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -3
  160. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  161. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  162. package/packages/pi-coding-agent/dist/theme/theme.d.ts +1 -1
  163. package/packages/pi-coding-agent/dist/theme/theme.d.ts.map +1 -1
  164. package/packages/pi-coding-agent/dist/theme/theme.js +1 -0
  165. package/packages/pi-coding-agent/dist/theme/theme.js.map +1 -1
  166. package/packages/pi-coding-agent/package.json +7 -7
  167. package/packages/pi-tui/package.json +2 -2
  168. package/packages/rpc-client/package.json +2 -2
  169. package/pkg/dist/theme/theme.d.ts +1 -1
  170. package/pkg/dist/theme/theme.d.ts.map +1 -1
  171. package/pkg/dist/theme/theme.js +1 -0
  172. package/pkg/dist/theme/theme.js.map +1 -1
  173. package/pkg/package.json +1 -1
  174. package/src/resources/extensions/gsd/auto-model-selection.ts +1 -0
  175. package/src/resources/extensions/gsd/auto-start.ts +1 -1
  176. package/src/resources/extensions/gsd/auto-verification.ts +46 -12
  177. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +4 -3
  178. package/src/resources/extensions/gsd/db/engine.ts +9 -1
  179. package/src/resources/extensions/gsd/db-canonical-foundation-schema.ts +129 -0
  180. package/src/resources/extensions/gsd/db-migration-steps.ts +5 -0
  181. package/src/resources/extensions/gsd/docs/preferences-reference.md +12 -3
  182. package/src/resources/extensions/gsd/doctor-engine-checks.ts +15 -4
  183. package/src/resources/extensions/gsd/model-cost-table.ts +14 -3
  184. package/src/resources/extensions/gsd/model-router.ts +10 -5
  185. package/src/resources/extensions/gsd/planning-subagent-registry.ts +18 -4
  186. package/src/resources/extensions/gsd/preferences-models.ts +54 -10
  187. package/src/resources/extensions/gsd/preferences-types.ts +10 -2
  188. package/src/resources/extensions/gsd/preferences-validation.ts +49 -6
  189. package/src/resources/extensions/gsd/preferences.ts +13 -0
  190. package/src/resources/extensions/gsd/templates/PREFERENCES.md +3 -0
  191. package/src/resources/extensions/gsd/tests/db-canonical-foundation.test.ts +459 -0
  192. package/src/resources/extensions/gsd/tests/db-engine-migrate-guards.test.ts +3 -3
  193. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +64 -0
  194. package/src/resources/extensions/gsd/tests/model-cost-table.test.ts +22 -7
  195. package/src/resources/extensions/gsd/tests/model-isolation.test.ts +55 -0
  196. package/src/resources/extensions/gsd/tests/model-router.test.ts +1 -1
  197. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +24 -0
  198. package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +38 -1
  199. package/src/resources/extensions/gsd/tests/preferences.test.ts +71 -4
  200. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +3 -0
  201. package/src/resources/extensions/gsd/tests/service-tier.test.ts +2 -2
  202. package/src/resources/extensions/gsd/tests/single-writer-invariant.test.ts +1 -0
  203. package/src/resources/extensions/gsd/tests/thinking-level-resolution.test.ts +1 -1
  204. package/src/resources/extensions/gsd/tests/verification-evidence.test.ts +12 -0
  205. package/src/resources/extensions/gsd/tests/workflow-authority-faults.test.ts +238 -0
  206. package/src/resources/extensions/gsd/tests/workflow-authority-fixture.test.ts +48 -0
  207. package/src/resources/extensions/gsd/tests/workflow-authority-fixture.ts +141 -0
  208. package/src/resources/extensions/gsd/tests/workflow-authority-projection-conflict.test.ts +177 -0
  209. package/src/resources/extensions/gsd/tests/workflow-fault-harness.test.ts +39 -0
  210. package/src/resources/extensions/gsd/tests/workflow-fault-harness.ts +72 -0
  211. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +40 -0
  212. package/src/resources/extensions/gsd/tools/plan-slice.ts +5 -1
  213. package/src/resources/extensions/gsd/verification-evidence.ts +5 -2
  214. package/src/resources/extensions/ollama/ollama-chat-provider.ts +2 -2
  215. package/src/resources/extensions/ollama/tests/ollama-chat-provider-think.test.ts +4 -0
  216. package/src/resources/extensions/subagent/index.ts +1 -1
  217. /package/dist/web/standalone/.next/static/{EAhnhYMt5UVOiG9-iPDjc → 8ub6oiEp2YnlOHQU20LJr}/_buildManifest.js +0 -0
  218. /package/dist/web/standalone/.next/static/{EAhnhYMt5UVOiG9-iPDjc → 8ub6oiEp2YnlOHQU20LJr}/_ssgManifest.js +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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","sourcesContent":["// Project/App: Open GSD\n// File Purpose: Standalone gsd-cloud CLI — login / status / connect / disconnect.\n//\n// Wires device-flow + CloudRuntime + a selected Executor adapter DIRECTLY. It has\n// no dependency on any Daemon or Orchestrator class; the only cloud behaviour is\n// the WS relay client driving the local GSD runtime through the Executor seam.\n\nimport { parseArgs } from \"node:util\";\nimport { realpathSync } from \"node:fs\";\nimport { delimiter, resolve } from \"node:path\";\nimport { resolveConfigPath, loadConfig } from \"./config.js\";\nimport { Logger } from \"./logger.js\";\nimport {\n clearCloudConfig,\n exchangePairingCode,\n redactedCloudStatus,\n saveCloudConfig,\n} from \"./cloud-config.js\";\nimport { runDeviceFlow } from \"./device-flow.js\";\nimport { CloudRuntime } from \"./cloud-runtime.js\";\nimport { selectExecutor } from \"./executors/index.js\";\nimport {\n backgroundRuntimeStatus,\n clearRuntimeState,\n startBackgroundRuntime,\n stopBackgroundRuntime,\n writeRuntimeState,\n} from \"./runtime-process.js\";\nimport type { DaemonConfig } from \"./types.js\";\n\nexport async function handleCloudCommand(argv: string[], opts: {\n binaryName: string;\n}): Promise<void> {\n if (argv[0] === \"--help\" || argv[0] === \"-h\") {\n process.stdout.write(formatUsage(opts.binaryName));\n process.exit(0);\n }\n\n const command = argv[0];\n const { values } = parseArgs({\n args: argv.slice(1),\n options: {\n config: { type: \"string\", short: \"c\" },\n gateway: { type: \"string\" },\n code: { type: \"string\" },\n \"runtime-name\": { type: \"string\" },\n verbose: { type: \"boolean\", short: \"v\", default: false },\n foreground: { type: \"boolean\", default: false },\n help: { type: \"boolean\", short: \"h\", default: false },\n },\n strict: true,\n });\n\n if (values.help || !command) {\n process.stdout.write(formatUsage(opts.binaryName));\n process.exit(0);\n }\n\n const configPath = resolveConfigPath(values.config);\n\n if (command === \"status\") {\n const config = loadConfig(configPath);\n process.stdout.write(`${JSON.stringify({\n ...redactedCloudStatus(config),\n projects: config.projects.scan_roots,\n background: backgroundRuntimeStatus(configPath),\n }, null, 2)}\\n`);\n return;\n }\n\n if (command === \"disconnect\") {\n await stopBackgroundRuntime(configPath);\n clearCloudConfig(configPath);\n process.stdout.write(`${opts.binaryName}: background runtime stopped and cloud credentials removed.\\n`);\n return;\n }\n\n if (command === \"login\") {\n if (!values.gateway) {\n throw new Error(\"login requires --gateway\");\n }\n const runtimeName = values[\"runtime-name\"];\n const { deviceToken, runtimeId, gatewayUrl } = await runDeviceFlow({\n gatewayUrl: values.gateway,\n configPath,\n runtimeName,\n binaryName: opts.binaryName,\n });\n const projectDirs = selectedProjectDirs([]);\n const config = saveCloudConfig(configPath, {\n gateway_url: gatewayUrl,\n device_token: deviceToken,\n runtime_id: runtimeId,\n ...(runtimeName ? { runtime_name: runtimeName } : {}),\n enabled: true,\n }, projectDirs);\n process.stdout.write(`${opts.binaryName}: cloud runtime ${runtimeId} paired — connecting...\\n`);\n if (values.foreground) {\n await stopBackgroundRuntime(configPath);\n await runCloudRuntime(config, opts.binaryName, values.verbose, projectDirs, {\n registerConfigPath: configPath,\n });\n return;\n }\n await startAndReportBackgroundRuntime(configPath, projectDirs, opts.binaryName, values.verbose);\n return;\n }\n\n if (command === \"pair\") {\n if (!values.gateway || !values.code) {\n throw new Error(\"pair requires --gateway and --code\");\n }\n const runtimeName = values[\"runtime-name\"];\n const result = await exchangePairingCode({\n gatewayUrl: values.gateway,\n code: values.code,\n runtimeName,\n });\n saveCloudConfig(configPath, {\n gateway_url: values.gateway,\n device_token: result.deviceToken,\n runtime_id: result.runtimeId,\n ...(runtimeName ? { runtime_name: runtimeName } : {}),\n enabled: true,\n });\n process.stdout.write(`${opts.binaryName}: paired cloud runtime ${result.runtimeId}.\\n`);\n return;\n }\n\n if (command === \"connect\") {\n let config = loadConfig(configPath);\n if (!config.cloud?.device_token || !config.cloud.runtime_id) {\n throw new Error(\"cloud runtime is not paired; run `login` first\");\n }\n const projectDirs = selectedProjectDirs(config.projects.scan_roots);\n config = saveCloudConfig(configPath, config.cloud, projectDirs);\n if (values.foreground) {\n await stopBackgroundRuntime(configPath);\n await runCloudRuntime(config, opts.binaryName, values.verbose, projectDirs, {\n registerConfigPath: configPath,\n });\n return;\n }\n await startAndReportBackgroundRuntime(configPath, projectDirs, opts.binaryName, values.verbose);\n return;\n }\n\n if (command === \"_run\") {\n const config = loadConfig(configPath);\n if (!config.cloud?.device_token || !config.cloud.runtime_id) {\n throw new Error(\"cloud runtime is not paired; run `login` first\");\n }\n const projectDirs = selectedProjectDirs(config.projects.scan_roots);\n await runCloudRuntime(config, opts.binaryName, values.verbose, projectDirs, {\n onConnected: () => {\n process.send?.({ type: \"ready\" });\n },\n });\n return;\n }\n\n throw new Error(`Unknown cloud runtime command: ${command}`);\n}\n\n/**\n * Start the WS relay and block until the process is signalled. This is the whole\n * \"daemon\" for the standalone agent: one CloudRuntime + one Executor, no Discord,\n * no scanner, no session-manager class.\n */\nasync function runCloudRuntime(\n config: DaemonConfig,\n binaryName: string,\n verbose: boolean,\n projectDirs: string[],\n opts: { onConnected?: () => void; registerConfigPath?: string } = {},\n): Promise<void> {\n if (!config.cloud) throw new Error(\"cloud runtime is not configured\");\n if (config.cloud.enabled === false) {\n throw new Error(\"cloud runtime is disabled in config; set cloud.enabled to true to connect\");\n }\n const logger = new Logger({\n filePath: config.log.file,\n level: config.log.level,\n verbose,\n });\n const executor = selectExecutor(logger, { projectDirs });\n const runtime = new CloudRuntime(config.cloud, executor, logger);\n // A foreground session owns the runtime itself, so it records its own PID in\n // the shared state file. That lets a later `connect`/`disconnect` find and\n // stop it, just like a detached background runtime. Background `_run` children\n // do not register here; their launcher records the child PID instead.\n if (opts.registerConfigPath) {\n writeRuntimeState(opts.registerConfigPath, process.pid, projectDirs);\n }\n try {\n await runtime.start();\n } catch (error) {\n if (opts.registerConfigPath) clearRuntimeState(opts.registerConfigPath);\n throw error;\n }\n opts.onConnected?.();\n process.stdout.write(`${binaryName}: connected to ${config.cloud.gateway_url}. Press Ctrl+C to stop.\\n`);\n\n await new Promise<void>((resolve) => {\n const shutdown = () => {\n if (opts.registerConfigPath) clearRuntimeState(opts.registerConfigPath);\n runtime.stop();\n void Promise.resolve(executor.close?.()).finally(() => {\n void logger.close().finally(() => resolve());\n });\n };\n process.once(\"SIGINT\", shutdown);\n process.once(\"SIGTERM\", shutdown);\n });\n}\n\nasync function startAndReportBackgroundRuntime(\n configPath: string,\n projectDirs: string[],\n binaryName: string,\n verbose = false,\n): Promise<void> {\n const binaryPath = process.argv[1];\n if (!binaryPath) throw new Error(\"could not resolve the gsd-cloud executable path\");\n const status = await startBackgroundRuntime({ binaryPath, configPath, projectDirs, verbose });\n process.stdout.write(`${binaryName}: connected in the background (PID ${status.pid}).\\n`);\n for (const project of projectDirs) process.stdout.write(`${binaryName}: project ${project}\\n`);\n process.stdout.write(`${binaryName}: logs ${status.log_file}\\n`);\n}\n\nfunction selectedProjectDirs(savedProjectDirs: string[]): string[] {\n const configured = process.env[\"GSD_CLOUD_PROJECTS\"];\n if (configured?.trim()) {\n const configuredPaths = uniqueResolvedPaths(configured.split(delimiter));\n if (configuredPaths.length > 0) return configuredPaths;\n }\n if (savedProjectDirs.length > 0) {\n const savedPaths = uniqueResolvedPaths(savedProjectDirs);\n if (savedPaths.length > 0) return savedPaths;\n }\n return [canonicalizePath(process.cwd())];\n}\n\nfunction uniqueResolvedPaths(paths: string[]): string[] {\n const resolved = paths\n .map((path) => path.trim())\n .filter(Boolean)\n .map((path) => canonicalizePath(path));\n return [...new Set(resolved)];\n}\n\n/**\n * Resolve a project path to its canonical, symlink-free absolute form. The\n * executor advertises project paths via `resolve()` alone, so canonicalizing\n * here keeps the saved `scan_roots` and the advertised hello paths identical to\n * the real directory (e.g. when the cwd or GSD_CLOUD_PROJECTS points through a\n * symlink). Falls back to the plain resolved path when the target does not yet\n * exist on disk.\n */\nfunction canonicalizePath(path: string): string {\n const resolved = resolve(path);\n try {\n return realpathSync(resolved);\n } catch {\n return resolved;\n }\n}\n\nexport function formatUsage(binaryName: string): string {\n return `Usage: ${binaryName} login [--gateway <url>] [--runtime-name <name>] [--config <path>] [--foreground]\n ${binaryName} status [--config <path>]\n ${binaryName} pair --gateway <url> --code <code> [--runtime-name <name>] [--config <path>]\n ${binaryName} connect [--config <path>] [--verbose] [--foreground]\n ${binaryName} disconnect [--config <path>]\n\nCommands:\n login (Recommended) Browser-based pairing — opens an approval URL, then\n connects the current project in the background. Defaults to the\n public GSD Cloud gateway.\n status Show current cloud runtime configuration and connection status.\n pair Exchange a pairing code for a device token (headless/CI environments).\n connect Start a background connection using saved credentials and projects.\n disconnect Stop the background runtime and remove local cloud credentials.\n\nOptions:\n --config <path> Path to YAML config file (default: ~/.gsd/daemon.yaml)\n --gateway <url> Cloud gateway URL (login defaults to https://cloud.opengsd.net)\n --code <code> Pairing code from the gateway (pair only)\n --runtime-name <name> Friendly name for this local GSD runtime\n --verbose Print log entries to stderr in addition to the log file\n --foreground Keep login/connect attached to this terminal (debugging)\n --help Show this help message and exit\n\nEnvironment:\n GSD_CLOUD_PROJECTS Path-delimiter separated project dirs to advertise\n (default: current working directory)\n GSD_CLI_PATH Path to the gsd binary (default: gsd on PATH)\n GSD_CLOUD_EXECUTOR Backend adapter: gsd-pi (default), codex, claude\n`;\n}\n"]}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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","sourcesContent":["// Project/App: Open GSD\n// File Purpose: Standalone gsd-cloud CLI — login / status / connect / disconnect.\n//\n// Wires device-flow + CloudRuntime + a selected Executor adapter DIRECTLY. It has\n// no dependency on any Daemon or Orchestrator class; the only cloud behaviour is\n// the WS relay client driving the local GSD runtime through the Executor seam.\n\nimport { parseArgs } from \"node:util\";\nimport { realpathSync } from \"node:fs\";\nimport { delimiter, resolve } from \"node:path\";\nimport { resolveConfigPath, loadConfig } from \"./config.js\";\nimport { Logger } from \"./logger.js\";\nimport {\n clearCloudConfig,\n exchangePairingCode,\n redactedCloudStatus,\n saveCloudConfig,\n} from \"./cloud-config.js\";\nimport { runDeviceFlow } from \"./device-flow.js\";\nimport { CloudRuntime } from \"./cloud-runtime.js\";\nimport { selectExecutor } from \"./executors/index.js\";\nimport {\n acquireRuntimeStartLock,\n backgroundRuntimeStatus,\n clearRuntimeState,\n startBackgroundRuntime,\n stopBackgroundRuntime,\n writeRuntimeState,\n} from \"./runtime-process.js\";\nimport { clearRuntimeTelemetry, readRuntimeTelemetry, RuntimeTelemetryStore } from \"./runtime-telemetry.js\";\nimport type { DaemonConfig } from \"./types.js\";\n\nexport async function handleCloudCommand(argv: string[], opts: {\n binaryName: string;\n}): Promise<void> {\n if (argv[0] === \"--help\" || argv[0] === \"-h\") {\n process.stdout.write(formatUsage(opts.binaryName));\n process.exit(0);\n }\n\n const command = argv[0];\n const { values } = parseArgs({\n args: argv.slice(1),\n options: {\n config: { type: \"string\", short: \"c\" },\n gateway: { type: \"string\" },\n code: { type: \"string\" },\n \"runtime-name\": { type: \"string\" },\n verbose: { type: \"boolean\", short: \"v\", default: false },\n foreground: { type: \"boolean\", default: false },\n help: { type: \"boolean\", short: \"h\", default: false },\n },\n strict: true,\n });\n\n if (values.help || !command) {\n process.stdout.write(formatUsage(opts.binaryName));\n process.exit(0);\n }\n\n const configPath = resolveConfigPath(values.config);\n\n if (command === \"status\") {\n const config = loadConfig(configPath);\n process.stdout.write(`${JSON.stringify({\n ...redactedCloudStatus(config),\n projects: config.projects.scan_roots,\n background: backgroundRuntimeStatus(configPath),\n telemetry: readRuntimeTelemetry(configPath),\n }, null, 2)}\\n`);\n return;\n }\n\n if (command === \"disconnect\") {\n await stopBackgroundRuntime(configPath);\n clearCloudConfig(configPath);\n clearRuntimeTelemetry(configPath);\n process.stdout.write(`${opts.binaryName}: background runtime stopped and cloud credentials removed.\\n`);\n return;\n }\n\n if (command === \"stop\") {\n const stopped = await stopBackgroundRuntime(configPath);\n process.stdout.write(stopped\n ? `${opts.binaryName}: background runtime stopped.\\n`\n : `${opts.binaryName}: background runtime is not running.\\n`);\n return;\n }\n\n if (command === \"login\") {\n if (!values.gateway) {\n throw new Error(\"login requires --gateway\");\n }\n const runtimeName = values[\"runtime-name\"];\n const { deviceToken, runtimeId, gatewayUrl } = await runDeviceFlow({\n gatewayUrl: values.gateway,\n configPath,\n runtimeName,\n binaryName: opts.binaryName,\n });\n const projectDirs = selectedProjectDirs([]);\n const config = saveCloudConfig(configPath, {\n gateway_url: gatewayUrl,\n device_token: deviceToken,\n runtime_id: runtimeId,\n ...(runtimeName ? { runtime_name: runtimeName } : {}),\n enabled: true,\n }, projectDirs);\n process.stdout.write(`${opts.binaryName}: cloud runtime ${runtimeId} paired — connecting...\\n`);\n if (values.foreground) {\n await runForegroundCloudRuntime(config, configPath, opts.binaryName, values.verbose, projectDirs);\n return;\n }\n await startAndReportBackgroundRuntime(configPath, projectDirs, opts.binaryName, values.verbose);\n return;\n }\n\n if (command === \"pair\") {\n if (!values.gateway || !values.code) {\n throw new Error(\"pair requires --gateway and --code\");\n }\n const runtimeName = values[\"runtime-name\"];\n const result = await exchangePairingCode({\n gatewayUrl: values.gateway,\n code: values.code,\n runtimeName,\n });\n saveCloudConfig(configPath, {\n gateway_url: values.gateway,\n device_token: result.deviceToken,\n runtime_id: result.runtimeId,\n ...(runtimeName ? { runtime_name: runtimeName } : {}),\n enabled: true,\n });\n process.stdout.write(`${opts.binaryName}: paired cloud runtime ${result.runtimeId}.\\n`);\n return;\n }\n\n if (command === \"connect\") {\n let config = loadConfig(configPath);\n if (!config.cloud?.device_token || !config.cloud.runtime_id) {\n throw new Error(\"cloud runtime is not paired; run `login` first\");\n }\n const projectDirs = selectedProjectDirs(config.projects.scan_roots);\n config = saveCloudConfig(configPath, config.cloud, projectDirs);\n if (values.foreground) {\n await runForegroundCloudRuntime(config, configPath, opts.binaryName, values.verbose, projectDirs);\n return;\n }\n await startAndReportBackgroundRuntime(configPath, projectDirs, opts.binaryName, values.verbose);\n return;\n }\n\n if (command === \"_run\") {\n const config = loadConfig(configPath);\n if (!config.cloud?.device_token || !config.cloud.runtime_id) {\n throw new Error(\"cloud runtime is not paired; run `login` first\");\n }\n const projectDirs = selectedProjectDirs(config.projects.scan_roots);\n await runCloudRuntime(config, configPath, opts.binaryName, values.verbose, projectDirs, {\n onConnected: () => {\n process.send?.({ type: \"ready\" });\n },\n });\n return;\n }\n\n throw new Error(`Unknown cloud runtime command: ${command}`);\n}\n\nasync function runForegroundCloudRuntime(\n config: DaemonConfig,\n configPath: string,\n binaryName: string,\n verbose: boolean,\n projectDirs: string[],\n): Promise<void> {\n const releaseStartLock = await acquireRuntimeStartLock(configPath);\n try {\n await stopBackgroundRuntime(configPath);\n await runCloudRuntime(config, configPath, binaryName, verbose, projectDirs, {\n registerConfigPath: configPath,\n onConnected: releaseStartLock,\n });\n } finally {\n releaseStartLock();\n }\n}\n\n/**\n * Start the WS relay and block until the process is signalled. This is the whole\n * \"daemon\" for the standalone agent: one CloudRuntime + one Executor, no Discord,\n * no scanner, no session-manager class.\n */\nasync function runCloudRuntime(\n config: DaemonConfig,\n configPath: string,\n binaryName: string,\n verbose: boolean,\n projectDirs: string[],\n opts: { onConnected?: () => void; registerConfigPath?: string } = {},\n): Promise<void> {\n if (!config.cloud) throw new Error(\"cloud runtime is not configured\");\n if (config.cloud.enabled === false) {\n throw new Error(\"cloud runtime is disabled in config; set cloud.enabled to true to connect\");\n }\n const logger = new Logger({\n filePath: config.log.file,\n level: config.log.level,\n verbose,\n });\n const executor = selectExecutor(logger, { projectDirs });\n const telemetry = new RuntimeTelemetryStore(configPath, {\n gatewayUrl: config.cloud.gateway_url,\n runtimeId: config.cloud.runtime_id,\n runtimeName: config.cloud.runtime_name,\n });\n const runtime = new CloudRuntime(config.cloud, executor, logger, telemetry);\n // A foreground session owns the runtime itself, so it records its own PID in\n // the shared state file. That lets a later `connect`/`disconnect` find and\n // stop it, just like a detached background runtime. Background `_run` children\n // do not register here; their launcher records the child PID instead.\n if (opts.registerConfigPath) {\n writeRuntimeState(opts.registerConfigPath, process.pid, projectDirs);\n }\n try {\n await runtime.start();\n } catch (error) {\n if (opts.registerConfigPath) clearRuntimeState(opts.registerConfigPath);\n throw error;\n }\n opts.onConnected?.();\n process.stdout.write(`${binaryName}: connected to ${config.cloud.gateway_url}. Press Ctrl+C to stop.\\n`);\n\n await new Promise<void>((resolve) => {\n const shutdown = () => {\n if (opts.registerConfigPath) clearRuntimeState(opts.registerConfigPath);\n runtime.stop();\n void Promise.resolve(executor.close?.()).finally(() => {\n void logger.close().finally(() => resolve());\n });\n };\n process.once(\"SIGINT\", shutdown);\n process.once(\"SIGTERM\", shutdown);\n });\n}\n\nasync function startAndReportBackgroundRuntime(\n configPath: string,\n projectDirs: string[],\n binaryName: string,\n verbose = false,\n): Promise<void> {\n const binaryPath = process.argv[1];\n if (!binaryPath) throw new Error(\"could not resolve the gsd-cloud executable path\");\n const status = await startBackgroundRuntime({ binaryPath, configPath, projectDirs, verbose });\n process.stdout.write(`${binaryName}: connected in the background (PID ${status.pid}).\\n`);\n for (const project of projectDirs) process.stdout.write(`${binaryName}: project ${project}\\n`);\n process.stdout.write(`${binaryName}: logs ${status.log_file}\\n`);\n}\n\nfunction selectedProjectDirs(savedProjectDirs: string[]): string[] {\n const configured = process.env[\"GSD_CLOUD_PROJECTS\"];\n if (configured?.trim()) {\n const configuredPaths = uniqueResolvedPaths(configured.split(delimiter));\n if (configuredPaths.length > 0) return configuredPaths;\n }\n if (savedProjectDirs.length > 0) {\n const savedPaths = uniqueResolvedPaths(savedProjectDirs);\n if (savedPaths.length > 0) return savedPaths;\n }\n return [canonicalizePath(process.cwd())];\n}\n\nfunction uniqueResolvedPaths(paths: string[]): string[] {\n const resolved = paths\n .map((path) => path.trim())\n .filter(Boolean)\n .map((path) => canonicalizePath(path));\n return [...new Set(resolved)];\n}\n\n/**\n * Resolve a project path to its canonical, symlink-free absolute form. The\n * executor advertises project paths via `resolve()` alone, so canonicalizing\n * here keeps the saved `scan_roots` and the advertised hello paths identical to\n * the real directory (e.g. when the cwd or GSD_CLOUD_PROJECTS points through a\n * symlink). Falls back to the plain resolved path when the target does not yet\n * exist on disk.\n */\nfunction canonicalizePath(path: string): string {\n const resolved = resolve(path);\n try {\n return realpathSync(resolved);\n } catch {\n return resolved;\n }\n}\n\nexport function formatUsage(binaryName: string): string {\n return `Usage: ${binaryName} login [--gateway <url>] [--runtime-name <name>] [--config <path>] [--foreground]\n ${binaryName} status [--config <path>]\n ${binaryName} pair --gateway <url> --code <code> [--runtime-name <name>] [--config <path>]\n ${binaryName} connect [--config <path>] [--verbose] [--foreground]\n ${binaryName} stop [--config <path>]\n ${binaryName} disconnect [--config <path>]\n\nCommands:\n login (Recommended) Browser-based pairing — opens an approval URL, then\n connects the current project in the background. Defaults to the\n public GSD Cloud gateway.\n status Show current cloud runtime configuration and connection status.\n pair Exchange a pairing code for a device token (headless/CI environments).\n connect Start a background connection using saved credentials and projects.\n stop Stop the background runtime without removing saved credentials.\n disconnect Stop the background runtime and remove local cloud credentials.\n\nOptions:\n --config <path> Path to YAML config file (default: ~/.gsd/daemon.yaml)\n --gateway <url> Cloud gateway URL (login defaults to https://cloud.opengsd.net)\n --code <code> Pairing code from the gateway (pair only)\n --runtime-name <name> Friendly name for this local GSD runtime\n --verbose Print log entries to stderr in addition to the log file\n --foreground Keep login/connect attached to this terminal (debugging)\n --help Show this help message and exit\n\nEnvironment:\n GSD_CLOUD_PROJECTS Path-delimiter separated project dirs to advertise\n (default: current working directory)\n GSD_CLI_PATH Path to the gsd binary (default: gsd on PATH)\n GSD_CLOUD_EXECUTOR Backend adapter: gsd-pi (default), codex, claude\n`;\n}\n"]}
@@ -1,21 +1,24 @@
1
1
  import type { Logger } from "./logger.js";
2
2
  import type { DaemonConfig } from "./types.js";
3
3
  import type { Executor } from "./executors/executor.js";
4
+ import { type RuntimeTelemetryReporter } from "./runtime-telemetry.js";
4
5
  export declare const CLOUD_RUNTIME_INITIAL_CONNECT_WINDOW_MS: number;
5
6
  export declare class CloudRuntime {
6
7
  private readonly cloud;
7
8
  private readonly executor;
8
9
  private readonly logger;
10
+ private readonly telemetry;
9
11
  private static readonly MAX_OUTBOX;
10
12
  private socket;
11
13
  private heartbeat;
12
14
  private reconnect;
13
15
  private readonly inFlight;
14
16
  private outbox;
17
+ private advertisedProjects;
15
18
  private stopped;
16
19
  private firstConnectDeferred;
17
20
  private initialConnectAttempts;
18
- constructor(cloud: NonNullable<DaemonConfig["cloud"]>, executor: Executor, logger: Logger);
21
+ constructor(cloud: NonNullable<DaemonConfig["cloud"]>, executor: Executor, logger: Logger, telemetry?: RuntimeTelemetryReporter);
19
22
  start(): Promise<void>;
20
23
  stop(): void;
21
24
  private connect;
@@ -25,6 +28,8 @@ export declare class CloudRuntime {
25
28
  private handleSocketError;
26
29
  private advertiseProjects;
27
30
  private handleMessage;
31
+ private resolveRoutingKey;
32
+ private resolveProject;
28
33
  private cancelInFlight;
29
34
  private resolveFirstConnect;
30
35
  private rejectFirstConnect;
@@ -1 +1 @@
1
- {"version":3,"file":"cloud-runtime.d.ts","sourceRoot":"","sources":["../src/cloud-runtime.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAOxD,eAAO,MAAM,uCAAuC,QAEG,CAAC;AAUxD,qBAAa,YAAY;IAiBrB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAlBzB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAO;IAMzC,OAAO,CAAC,MAAM,CAAwB;IACtC,OAAO,CAAC,SAAS,CAA6C;IAC9D,OAAO,CAAC,SAAS,CAA4C;IAC7D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqC;IAC9D,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,oBAAoB,CAAyC;IACrE,OAAO,CAAC,sBAAsB,CAAK;gBAGhB,KAAK,EAAE,WAAW,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,EACzC,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,MAAM;IAGjC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAQtB,IAAI,IAAI,IAAI;IAcZ,OAAO,CAAC,OAAO;IAkDf,OAAO,CAAC,gBAAgB;YAmBV,mBAAmB;IAKjC,OAAO,CAAC,iBAAiB;IA+BzB,OAAO,CAAC,iBAAiB;YAKX,iBAAiB;YAUjB,aAAa;YA6Bb,cAAc;IAqB5B,OAAO,CAAC,mBAAmB;IAO3B,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,IAAI;CAcb"}
1
+ {"version":3,"file":"cloud-runtime.d.ts","sourceRoot":"","sources":["../src/cloud-runtime.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,KAAK,EAAqB,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAE3E,OAAO,EAEL,KAAK,wBAAwB,EAC9B,MAAM,wBAAwB,CAAC;AAMhC,eAAO,MAAM,uCAAuC,QAEG,CAAC;AAoBxD,qBAAa,YAAY;IAkBrB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,SAAS;IApB5B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAO;IAMzC,OAAO,CAAC,MAAM,CAAwB;IACtC,OAAO,CAAC,SAAS,CAA6C;IAC9D,OAAO,CAAC,SAAS,CAA4C;IAC7D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsC;IAC/D,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,kBAAkB,CAA2B;IACrD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,oBAAoB,CAAyC;IACrE,OAAO,CAAC,sBAAsB,CAAK;gBAGhB,KAAK,EAAE,WAAW,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,EACzC,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,MAAM,EACd,SAAS,GAAE,wBAA+C;IAG7E,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAoBtB,IAAI,IAAI,IAAI;IAeZ,OAAO,CAAC,OAAO;IAsDf,OAAO,CAAC,gBAAgB;YAuBV,mBAAmB;IAMjC,OAAO,CAAC,iBAAiB;IAgCzB,OAAO,CAAC,iBAAiB;YAMX,iBAAiB;YAYjB,aAAa;IAgE3B,OAAO,CAAC,iBAAiB;IAMzB,OAAO,CAAC,cAAc;YAWR,cAAc;IA4B5B,OAAO,CAAC,mBAAmB;IAO3B,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,IAAI;CAeb"}
@@ -1,5 +1,6 @@
1
1
  import WebSocket from "ws";
2
2
  import { createGatewayLookup, parseCloudGatewayUrl, validateGatewayNetworkTarget } from "./cloud-config.js";
3
+ import { noopRuntimeTelemetry, } from "./runtime-telemetry.js";
3
4
  const INITIAL_CONNECT_ATTEMPTS = 5;
4
5
  const INITIAL_CONNECT_HANDSHAKE_TIMEOUT_MS = 30_000;
5
6
  const RECONNECT_DELAY_MS = 5_000;
@@ -9,6 +10,7 @@ export class CloudRuntime {
9
10
  cloud;
10
11
  executor;
11
12
  logger;
13
+ telemetry;
12
14
  static MAX_OUTBOX = 200;
13
15
  // How many times to retry the initial connect before rejecting start(). A
14
16
  // transient handshake failure (gateway briefly unreachable, DNS hiccup) should
@@ -20,23 +22,39 @@ export class CloudRuntime {
20
22
  reconnect;
21
23
  inFlight = new Map();
22
24
  outbox = [];
25
+ advertisedProjects = [];
23
26
  stopped = false;
24
27
  firstConnectDeferred;
25
28
  initialConnectAttempts = 0;
26
- constructor(cloud, executor, logger) {
29
+ constructor(cloud, executor, logger, telemetry = noopRuntimeTelemetry) {
27
30
  this.cloud = cloud;
28
31
  this.executor = executor;
29
32
  this.logger = logger;
33
+ this.telemetry = telemetry;
30
34
  }
31
35
  start() {
32
36
  this.stopped = false;
33
37
  this.initialConnectAttempts = 0;
34
- this.firstConnectDeferred = Promise.withResolvers();
35
- this.connect();
36
- return this.firstConnectDeferred.promise;
38
+ const firstConnect = Promise.withResolvers();
39
+ this.firstConnectDeferred = firstConnect;
40
+ const result = firstConnect.promise.catch(async (error) => {
41
+ this.telemetry.failed?.();
42
+ await this.telemetry.flush?.();
43
+ throw error;
44
+ });
45
+ try {
46
+ this.connect();
47
+ }
48
+ catch (error) {
49
+ const message = error instanceof Error ? error.message : String(error);
50
+ this.telemetry.socketError(message);
51
+ this.rejectFirstConnect(error instanceof Error ? error : new Error(message));
52
+ }
53
+ return result;
37
54
  }
38
55
  stop() {
39
56
  this.stopped = true;
57
+ this.telemetry.stopped();
40
58
  this.rejectFirstConnect(new Error("cloud runtime stopped"));
41
59
  if (this.reconnect)
42
60
  clearTimeout(this.reconnect);
@@ -54,9 +72,12 @@ export class CloudRuntime {
54
72
  if (this.reconnect)
55
73
  clearTimeout(this.reconnect);
56
74
  this.reconnect = undefined;
75
+ this.telemetry.connecting();
57
76
  if (!this.cloud.device_token || !this.cloud.runtime_id) {
77
+ const message = "cloud runtime missing device token or runtime id";
58
78
  this.logger.warn("cloud runtime skipped — missing device token or runtime id");
59
- this.rejectFirstConnect(new Error("cloud runtime missing device token or runtime id"));
79
+ this.telemetry.socketError(message);
80
+ this.rejectFirstConnect(new Error(message));
60
81
  return;
61
82
  }
62
83
  const gatewayUrl = parseCloudGatewayUrl(this.cloud.gateway_url);
@@ -66,6 +87,7 @@ export class CloudRuntime {
66
87
  catch (err) {
67
88
  const message = err instanceof Error ? err.message : String(err);
68
89
  this.logger.warn("cloud runtime skipped unsafe gateway URL", { error: message });
90
+ this.telemetry.socketError(message);
69
91
  this.rejectFirstConnect(new Error(`cloud runtime unsafe gateway URL: ${message}`));
70
92
  return;
71
93
  }
@@ -103,6 +125,7 @@ export class CloudRuntime {
103
125
  handleSocketOpen(socket) {
104
126
  if (socket !== this.socket)
105
127
  return;
128
+ this.telemetry.connected();
106
129
  this.resolveFirstConnect();
107
130
  this.logger.info("cloud runtime connected", { gateway_url: this.cloud.gateway_url, runtime_id: this.cloud.runtime_id });
108
131
  // Re-advertise projects (async: the hello is sent on a later microtask), then
@@ -112,11 +135,13 @@ export class CloudRuntime {
112
135
  void this.advertiseProjects();
113
136
  const pending = this.outbox;
114
137
  this.outbox = [];
115
- for (const text of pending) {
116
- if (this.socket?.readyState === WebSocket.OPEN)
117
- this.socket.send(text);
138
+ for (const frame of pending) {
139
+ if (this.socket?.readyState === WebSocket.OPEN) {
140
+ this.socket.send(frame.text);
141
+ this.telemetry.sent(frame.text, frame.projectPath);
142
+ }
118
143
  else
119
- this.outbox.push(text);
144
+ this.outbox.push(frame);
120
145
  }
121
146
  if (this.heartbeat)
122
147
  clearInterval(this.heartbeat);
@@ -125,6 +150,7 @@ export class CloudRuntime {
125
150
  async handleSocketMessage(socket, text) {
126
151
  if (socket !== this.socket)
127
152
  return;
153
+ this.telemetry.received(text);
128
154
  await this.handleMessage(text);
129
155
  }
130
156
  handleSocketClose(socket) {
@@ -136,6 +162,7 @@ export class CloudRuntime {
136
162
  this.socket = undefined;
137
163
  if (this.stopped)
138
164
  return;
165
+ this.telemetry.disconnected();
139
166
  if (this.firstConnectDeferred) {
140
167
  // Still trying to establish the first connection: retry transient
141
168
  // handshake failures (like the daemon's reconnect loop) and only reject
@@ -143,7 +170,9 @@ export class CloudRuntime {
143
170
  // not kill the runtime while a persistent outage still surfaces an error.
144
171
  this.initialConnectAttempts += 1;
145
172
  if (this.initialConnectAttempts >= INITIAL_CONNECT_ATTEMPTS) {
146
- this.rejectFirstConnect(new Error(`cloud runtime connection failed after ${this.initialConnectAttempts} attempt(s)`));
173
+ const error = new Error(`cloud runtime connection failed after ${this.initialConnectAttempts} attempt(s)`);
174
+ this.telemetry.socketError(error.message);
175
+ this.rejectFirstConnect(error);
147
176
  return;
148
177
  }
149
178
  this.logger.warn("cloud runtime initial connect failed; retrying", {
@@ -162,9 +191,12 @@ export class CloudRuntime {
162
191
  if (socket !== this.socket)
163
192
  return;
164
193
  this.logger.warn("cloud runtime socket error", { error: err.message });
194
+ this.telemetry.socketError(err.message);
165
195
  }
166
196
  async advertiseProjects() {
167
197
  const projects = await this.executor.advertisedProjects();
198
+ this.advertisedProjects = projects;
199
+ this.telemetry.projectsAdvertised(projects);
168
200
  this.send({
169
201
  type: "hello",
170
202
  runtimeId: this.cloud.runtime_id,
@@ -186,25 +218,75 @@ export class CloudRuntime {
186
218
  }
187
219
  if (message.type !== "tool_call" || !message.requestId || !message.toolName)
188
220
  return;
189
- this.inFlight.set(message.requestId, message);
221
+ const routingKey = this.resolveRoutingKey(message);
222
+ const project = this.resolveProject(routingKey);
223
+ const projectAlias = project?.alias;
224
+ const projectPath = project?.path;
225
+ const startedAt = Date.now();
226
+ const receivedBytes = Buffer.byteLength(text);
227
+ this.inFlight.set(message.requestId, {
228
+ message,
229
+ ...(routingKey !== undefined ? { routingKey } : {}),
230
+ });
231
+ this.telemetry.requestStarted({
232
+ requestId: message.requestId,
233
+ ...(projectAlias ? { projectAlias } : {}),
234
+ ...(projectPath ? { projectPath } : {}),
235
+ toolName: message.toolName,
236
+ receivedBytes,
237
+ });
238
+ let outcome = "success";
239
+ let errorMessage;
190
240
  try {
191
- const result = await this.executor.execute(message.toolName, message.args ?? {}, message.projectAlias);
192
- if (!this.inFlight.has(message.requestId))
241
+ const result = await this.executor.execute(message.toolName, message.args ?? {}, routingKey);
242
+ if (!this.inFlight.has(message.requestId)) {
243
+ outcome = "cancelled";
193
244
  return;
194
- this.send({ type: "tool_result", requestId: message.requestId, result });
245
+ }
246
+ this.send({ type: "tool_result", requestId: message.requestId, result }, projectPath);
195
247
  }
196
248
  catch (err) {
197
- if (!this.inFlight.has(message.requestId))
249
+ if (!this.inFlight.has(message.requestId)) {
250
+ outcome = "cancelled";
198
251
  return;
252
+ }
253
+ outcome = "error";
254
+ errorMessage = err instanceof Error ? err.message : String(err);
199
255
  this.send({
200
256
  type: "tool_result",
201
257
  requestId: message.requestId,
202
- error: err instanceof Error ? err.message : String(err),
203
- });
258
+ error: errorMessage,
259
+ }, projectPath);
204
260
  }
205
261
  finally {
206
262
  this.inFlight.delete(message.requestId);
263
+ this.telemetry.requestFinished({
264
+ requestId: message.requestId,
265
+ ...(projectAlias ? { projectAlias } : {}),
266
+ ...(projectPath ? { projectPath } : {}),
267
+ toolName: message.toolName,
268
+ durationMs: Date.now() - startedAt,
269
+ outcome,
270
+ ...(errorMessage ? { error: errorMessage } : {}),
271
+ });
272
+ }
273
+ }
274
+ resolveRoutingKey(message) {
275
+ return message.projectAlias
276
+ ?? (typeof message.args?.projectDir === "string" ? message.args.projectDir : undefined)
277
+ ?? (typeof message.args?.projectAlias === "string" ? message.args.projectAlias : undefined);
278
+ }
279
+ resolveProject(routingKey) {
280
+ if (routingKey !== undefined) {
281
+ const exact = this.advertisedProjects.find((project) => project.path === routingKey);
282
+ if (exact)
283
+ return exact;
284
+ const matches = this.advertisedProjects.filter((project) => project.alias === routingKey);
285
+ return matches.length === 1 ? matches[0] : undefined;
207
286
  }
287
+ if (this.advertisedProjects.length === 1)
288
+ return this.advertisedProjects[0];
289
+ return undefined;
208
290
  }
209
291
  async cancelInFlight(requestId) {
210
292
  const pending = this.inFlight.get(requestId);
@@ -212,13 +294,12 @@ export class CloudRuntime {
212
294
  return;
213
295
  this.inFlight.delete(requestId);
214
296
  try {
215
- if (typeof pending.args?.sessionId === "string") {
216
- await this.executor.execute("gsd_cancel", { sessionId: pending.args.sessionId }, pending.projectAlias);
297
+ if (typeof pending.message.args?.sessionId === "string") {
298
+ await this.executor.execute("gsd_cancel", { sessionId: pending.message.args.sessionId }, pending.routingKey);
217
299
  return;
218
300
  }
219
- const projectDir = typeof pending.args?.projectDir === "string" ? pending.args.projectDir : pending.projectAlias;
220
- if (projectDir) {
221
- await this.executor.execute("gsd_cancel", { projectDir });
301
+ if (pending.routingKey !== undefined) {
302
+ await this.executor.execute("gsd_cancel", { projectDir: pending.routingKey }, pending.routingKey);
222
303
  }
223
304
  }
224
305
  catch (err) {
@@ -242,16 +323,17 @@ export class CloudRuntime {
242
323
  this.firstConnectDeferred = undefined;
243
324
  deferred.reject(err);
244
325
  }
245
- send(message) {
326
+ send(message, projectPath) {
246
327
  const text = JSON.stringify(message);
247
328
  if (this.socket?.readyState === WebSocket.OPEN) {
248
329
  this.socket.send(text);
330
+ this.telemetry.sent(text, projectPath);
249
331
  return;
250
332
  }
251
333
  // Buffer while disconnected; flushed on reconnect in handleSocketOpen. Bounded
252
334
  // so a long outage cannot grow memory without limit — a stale heartbeat is
253
335
  // worth less than a fresh tool_result, so drop oldest first.
254
- this.outbox.push(text);
336
+ this.outbox.push({ text, ...(projectPath ? { projectPath } : {}) });
255
337
  if (this.outbox.length > CloudRuntime.MAX_OUTBOX) {
256
338
  this.outbox.shift();
257
339
  }
@@ -1 +1 @@
1
- {"version":3,"file":"cloud-runtime.js","sourceRoot":"","sources":["../src/cloud-runtime.ts"],"names":[],"mappings":"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","sourcesContent":["import WebSocket from \"ws\";\nimport type { Logger } from \"./logger.js\";\nimport type { DaemonConfig } from \"./types.js\";\nimport type { Executor } from \"./executors/executor.js\";\nimport { createGatewayLookup, parseCloudGatewayUrl, validateGatewayNetworkTarget } from \"./cloud-config.js\";\n\nconst INITIAL_CONNECT_ATTEMPTS = 5;\nconst INITIAL_CONNECT_HANDSHAKE_TIMEOUT_MS = 30_000;\nconst RECONNECT_DELAY_MS = 5_000;\n\nexport const CLOUD_RUNTIME_INITIAL_CONNECT_WINDOW_MS =\n INITIAL_CONNECT_ATTEMPTS * INITIAL_CONNECT_HANDSHAKE_TIMEOUT_MS\n + (INITIAL_CONNECT_ATTEMPTS - 1) * RECONNECT_DELAY_MS;\n\ninterface GatewayMessage {\n type: string;\n requestId?: string;\n toolName?: string;\n args?: Record<string, unknown>;\n projectAlias?: string;\n}\n\nexport class CloudRuntime {\n private static readonly MAX_OUTBOX = 200;\n // How many times to retry the initial connect before rejecting start(). A\n // transient handshake failure (gateway briefly unreachable, DNS hiccup) should\n // retry like the daemon's reconnect loop rather than kill the runtime; a\n // persistent failure (gateway down, session rejected) must eventually reject so\n // the CLI reports an error instead of hanging or exiting silently.\n private socket: WebSocket | undefined;\n private heartbeat: ReturnType<typeof setInterval> | undefined;\n private reconnect: ReturnType<typeof setTimeout> | undefined;\n private readonly inFlight = new Map<string, GatewayMessage>();\n private outbox: string[] = [];\n private stopped = false;\n private firstConnectDeferred: PromiseWithResolvers<void> | undefined;\n private initialConnectAttempts = 0;\n\n constructor(\n private readonly cloud: NonNullable<DaemonConfig[\"cloud\"]>,\n private readonly executor: Executor,\n private readonly logger: Logger,\n ) {}\n\n start(): Promise<void> {\n this.stopped = false;\n this.initialConnectAttempts = 0;\n this.firstConnectDeferred = Promise.withResolvers<void>();\n this.connect();\n return this.firstConnectDeferred.promise;\n }\n\n stop(): void {\n this.stopped = true;\n this.rejectFirstConnect(new Error(\"cloud runtime stopped\"));\n if (this.reconnect) clearTimeout(this.reconnect);\n this.reconnect = undefined;\n if (this.heartbeat) clearInterval(this.heartbeat);\n this.heartbeat = undefined;\n this.inFlight.clear();\n this.outbox = [];\n const socket = this.socket;\n this.socket = undefined;\n socket?.close();\n }\n\n private connect(): void {\n if (this.reconnect) clearTimeout(this.reconnect);\n this.reconnect = undefined;\n if (!this.cloud.device_token || !this.cloud.runtime_id) {\n this.logger.warn(\"cloud runtime skipped — missing device token or runtime id\");\n this.rejectFirstConnect(new Error(\"cloud runtime missing device token or runtime id\"));\n return;\n }\n const gatewayUrl = parseCloudGatewayUrl(this.cloud.gateway_url);\n try {\n validateGatewayNetworkTarget(gatewayUrl);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n this.logger.warn(\"cloud runtime skipped unsafe gateway URL\", { error: message });\n this.rejectFirstConnect(new Error(`cloud runtime unsafe gateway URL: ${message}`));\n return;\n }\n const url = new URL(\"/runtime/connect\", gatewayUrl);\n url.protocol = url.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n const socket = new WebSocket(url, {\n headers: { Authorization: `Bearer ${this.cloud.device_token}` },\n lookup: createGatewayLookup(gatewayUrl),\n handshakeTimeout: INITIAL_CONNECT_HANDSHAKE_TIMEOUT_MS,\n });\n const previousSocket = this.socket;\n this.socket = socket;\n if (previousSocket) {\n // Detach the old socket's handlers before closing so its listeners don't\n // linger on a socket we've already replaced (handlers also guard on\n // identity, but this releases them eagerly for GC).\n previousSocket.removeAllListeners();\n if (previousSocket.readyState !== WebSocket.CLOSING && previousSocket.readyState !== WebSocket.CLOSED) {\n previousSocket.close();\n }\n }\n\n socket.on(\"open\", () => {\n this.handleSocketOpen(socket);\n });\n socket.on(\"message\", (data) => {\n void this.handleSocketMessage(socket, data.toString(\"utf8\"));\n });\n socket.on(\"close\", () => {\n this.handleSocketClose(socket);\n });\n socket.on(\"error\", (err) => {\n this.handleSocketError(socket, err);\n });\n }\n\n private handleSocketOpen(socket: WebSocket): void {\n if (socket !== this.socket) return;\n this.resolveFirstConnect();\n this.logger.info(\"cloud runtime connected\", { gateway_url: this.cloud.gateway_url, runtime_id: this.cloud.runtime_id });\n // Re-advertise projects (async: the hello is sent on a later microtask), then\n // drain any messages buffered while disconnected. tool_results route by\n // requestId on the authenticated connection, so drain order vs the hello is\n // not significant.\n void this.advertiseProjects();\n const pending = this.outbox;\n this.outbox = [];\n for (const text of pending) {\n if (this.socket?.readyState === WebSocket.OPEN) this.socket.send(text);\n else this.outbox.push(text);\n }\n if (this.heartbeat) clearInterval(this.heartbeat);\n this.heartbeat = setInterval(() => this.send({ type: \"heartbeat\", at: Date.now() }), 30_000);\n }\n\n private async handleSocketMessage(socket: WebSocket, text: string): Promise<void> {\n if (socket !== this.socket) return;\n await this.handleMessage(text);\n }\n\n private handleSocketClose(socket: WebSocket): void {\n if (socket !== this.socket) return;\n if (this.heartbeat) clearInterval(this.heartbeat);\n this.heartbeat = undefined;\n this.socket = undefined;\n if (this.stopped) return;\n if (this.firstConnectDeferred) {\n // Still trying to establish the first connection: retry transient\n // handshake failures (like the daemon's reconnect loop) and only reject\n // start() once the bounded attempts are exhausted, so a brief blip does\n // not kill the runtime while a persistent outage still surfaces an error.\n this.initialConnectAttempts += 1;\n if (this.initialConnectAttempts >= INITIAL_CONNECT_ATTEMPTS) {\n this.rejectFirstConnect(\n new Error(\n `cloud runtime connection failed after ${this.initialConnectAttempts} attempt(s)`,\n ),\n );\n return;\n }\n this.logger.warn(\"cloud runtime initial connect failed; retrying\", {\n attempt: this.initialConnectAttempts,\n max: INITIAL_CONNECT_ATTEMPTS,\n });\n } else {\n this.logger.warn(\"cloud runtime disconnected; reconnecting\");\n }\n if (this.reconnect) clearTimeout(this.reconnect);\n this.reconnect = setTimeout(() => this.connect(), RECONNECT_DELAY_MS);\n }\n\n private handleSocketError(socket: WebSocket, err: Error): void {\n if (socket !== this.socket) return;\n this.logger.warn(\"cloud runtime socket error\", { error: err.message });\n }\n\n private async advertiseProjects(): Promise<void> {\n const projects = await this.executor.advertisedProjects();\n this.send({\n type: \"hello\",\n runtimeId: this.cloud.runtime_id,\n runtimeName: this.cloud.runtime_name,\n projects,\n });\n }\n\n private async handleMessage(text: string): Promise<void> {\n let message: GatewayMessage;\n try {\n message = JSON.parse(text) as GatewayMessage;\n } catch {\n return;\n }\n if (message.type === \"cancel\" && message.requestId) {\n void this.cancelInFlight(message.requestId);\n return;\n }\n if (message.type !== \"tool_call\" || !message.requestId || !message.toolName) return;\n this.inFlight.set(message.requestId, message);\n try {\n const result = await this.executor.execute(message.toolName, message.args ?? {}, message.projectAlias);\n if (!this.inFlight.has(message.requestId)) return;\n this.send({ type: \"tool_result\", requestId: message.requestId, result });\n } catch (err) {\n if (!this.inFlight.has(message.requestId)) return;\n this.send({\n type: \"tool_result\",\n requestId: message.requestId,\n error: err instanceof Error ? err.message : String(err),\n });\n } finally {\n this.inFlight.delete(message.requestId);\n }\n }\n\n private async cancelInFlight(requestId: string): Promise<void> {\n const pending = this.inFlight.get(requestId);\n if (!pending) return;\n this.inFlight.delete(requestId);\n try {\n if (typeof pending.args?.sessionId === \"string\") {\n await this.executor.execute(\"gsd_cancel\", { sessionId: pending.args.sessionId }, pending.projectAlias);\n return;\n }\n const projectDir = typeof pending.args?.projectDir === \"string\" ? pending.args.projectDir : pending.projectAlias;\n if (projectDir) {\n await this.executor.execute(\"gsd_cancel\", { projectDir });\n }\n } catch (err) {\n this.logger.warn(\"cloud runtime cancel failed\", {\n requestId,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n private resolveFirstConnect(): void {\n const deferred = this.firstConnectDeferred;\n if (!deferred) return;\n this.firstConnectDeferred = undefined;\n deferred.resolve();\n }\n\n private rejectFirstConnect(err: Error): void {\n const deferred = this.firstConnectDeferred;\n if (!deferred) return;\n this.firstConnectDeferred = undefined;\n deferred.reject(err);\n }\n\n private send(message: unknown): void {\n const text = JSON.stringify(message);\n if (this.socket?.readyState === WebSocket.OPEN) {\n this.socket.send(text);\n return;\n }\n // Buffer while disconnected; flushed on reconnect in handleSocketOpen. Bounded\n // so a long outage cannot grow memory without limit — a stale heartbeat is\n // worth less than a fresh tool_result, so drop oldest first.\n this.outbox.push(text);\n if (this.outbox.length > CloudRuntime.MAX_OUTBOX) {\n this.outbox.shift();\n }\n }\n}\n"]}
1
+ {"version":3,"file":"cloud-runtime.js","sourceRoot":"","sources":["../src/cloud-runtime.ts"],"names":[],"mappings":"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","sourcesContent":["import WebSocket from \"ws\";\nimport type { Logger } from \"./logger.js\";\nimport type { DaemonConfig } from \"./types.js\";\nimport type { AdvertisedProject, Executor } from \"./executors/executor.js\";\nimport { createGatewayLookup, parseCloudGatewayUrl, validateGatewayNetworkTarget } from \"./cloud-config.js\";\nimport {\n noopRuntimeTelemetry,\n type RuntimeTelemetryReporter,\n} from \"./runtime-telemetry.js\";\n\nconst INITIAL_CONNECT_ATTEMPTS = 5;\nconst INITIAL_CONNECT_HANDSHAKE_TIMEOUT_MS = 30_000;\nconst RECONNECT_DELAY_MS = 5_000;\n\nexport const CLOUD_RUNTIME_INITIAL_CONNECT_WINDOW_MS =\n INITIAL_CONNECT_ATTEMPTS * INITIAL_CONNECT_HANDSHAKE_TIMEOUT_MS\n + (INITIAL_CONNECT_ATTEMPTS - 1) * RECONNECT_DELAY_MS;\n\ninterface GatewayMessage {\n type: string;\n requestId?: string;\n toolName?: string;\n args?: Record<string, unknown>;\n projectAlias?: string;\n}\n\ninterface QueuedFrame {\n text: string;\n projectPath?: string;\n}\n\ninterface InFlightRequest {\n message: GatewayMessage;\n routingKey?: string;\n}\n\nexport class CloudRuntime {\n private static readonly MAX_OUTBOX = 200;\n // How many times to retry the initial connect before rejecting start(). A\n // transient handshake failure (gateway briefly unreachable, DNS hiccup) should\n // retry like the daemon's reconnect loop rather than kill the runtime; a\n // persistent failure (gateway down, session rejected) must eventually reject so\n // the CLI reports an error instead of hanging or exiting silently.\n private socket: WebSocket | undefined;\n private heartbeat: ReturnType<typeof setInterval> | undefined;\n private reconnect: ReturnType<typeof setTimeout> | undefined;\n private readonly inFlight = new Map<string, InFlightRequest>();\n private outbox: QueuedFrame[] = [];\n private advertisedProjects: AdvertisedProject[] = [];\n private stopped = false;\n private firstConnectDeferred: PromiseWithResolvers<void> | undefined;\n private initialConnectAttempts = 0;\n\n constructor(\n private readonly cloud: NonNullable<DaemonConfig[\"cloud\"]>,\n private readonly executor: Executor,\n private readonly logger: Logger,\n private readonly telemetry: RuntimeTelemetryReporter = noopRuntimeTelemetry,\n ) {}\n\n start(): Promise<void> {\n this.stopped = false;\n this.initialConnectAttempts = 0;\n const firstConnect = Promise.withResolvers<void>();\n this.firstConnectDeferred = firstConnect;\n const result = firstConnect.promise.catch(async (error: unknown) => {\n this.telemetry.failed?.();\n await this.telemetry.flush?.();\n throw error;\n });\n try {\n this.connect();\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n this.telemetry.socketError(message);\n this.rejectFirstConnect(error instanceof Error ? error : new Error(message));\n }\n return result;\n }\n\n stop(): void {\n this.stopped = true;\n this.telemetry.stopped();\n this.rejectFirstConnect(new Error(\"cloud runtime stopped\"));\n if (this.reconnect) clearTimeout(this.reconnect);\n this.reconnect = undefined;\n if (this.heartbeat) clearInterval(this.heartbeat);\n this.heartbeat = undefined;\n this.inFlight.clear();\n this.outbox = [];\n const socket = this.socket;\n this.socket = undefined;\n socket?.close();\n }\n\n private connect(): void {\n if (this.reconnect) clearTimeout(this.reconnect);\n this.reconnect = undefined;\n this.telemetry.connecting();\n if (!this.cloud.device_token || !this.cloud.runtime_id) {\n const message = \"cloud runtime missing device token or runtime id\";\n this.logger.warn(\"cloud runtime skipped — missing device token or runtime id\");\n this.telemetry.socketError(message);\n this.rejectFirstConnect(new Error(message));\n return;\n }\n const gatewayUrl = parseCloudGatewayUrl(this.cloud.gateway_url);\n try {\n validateGatewayNetworkTarget(gatewayUrl);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n this.logger.warn(\"cloud runtime skipped unsafe gateway URL\", { error: message });\n this.telemetry.socketError(message);\n this.rejectFirstConnect(new Error(`cloud runtime unsafe gateway URL: ${message}`));\n return;\n }\n const url = new URL(\"/runtime/connect\", gatewayUrl);\n url.protocol = url.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n const socket = new WebSocket(url, {\n headers: { Authorization: `Bearer ${this.cloud.device_token}` },\n lookup: createGatewayLookup(gatewayUrl),\n handshakeTimeout: INITIAL_CONNECT_HANDSHAKE_TIMEOUT_MS,\n });\n const previousSocket = this.socket;\n this.socket = socket;\n if (previousSocket) {\n // Detach the old socket's handlers before closing so its listeners don't\n // linger on a socket we've already replaced (handlers also guard on\n // identity, but this releases them eagerly for GC).\n previousSocket.removeAllListeners();\n if (previousSocket.readyState !== WebSocket.CLOSING && previousSocket.readyState !== WebSocket.CLOSED) {\n previousSocket.close();\n }\n }\n\n socket.on(\"open\", () => {\n this.handleSocketOpen(socket);\n });\n socket.on(\"message\", (data) => {\n void this.handleSocketMessage(socket, data.toString(\"utf8\"));\n });\n socket.on(\"close\", () => {\n this.handleSocketClose(socket);\n });\n socket.on(\"error\", (err) => {\n this.handleSocketError(socket, err);\n });\n }\n\n private handleSocketOpen(socket: WebSocket): void {\n if (socket !== this.socket) return;\n this.telemetry.connected();\n this.resolveFirstConnect();\n this.logger.info(\"cloud runtime connected\", { gateway_url: this.cloud.gateway_url, runtime_id: this.cloud.runtime_id });\n // Re-advertise projects (async: the hello is sent on a later microtask), then\n // drain any messages buffered while disconnected. tool_results route by\n // requestId on the authenticated connection, so drain order vs the hello is\n // not significant.\n void this.advertiseProjects();\n const pending = this.outbox;\n this.outbox = [];\n for (const frame of pending) {\n if (this.socket?.readyState === WebSocket.OPEN) {\n this.socket.send(frame.text);\n this.telemetry.sent(frame.text, frame.projectPath);\n }\n else this.outbox.push(frame);\n }\n if (this.heartbeat) clearInterval(this.heartbeat);\n this.heartbeat = setInterval(() => this.send({ type: \"heartbeat\", at: Date.now() }), 30_000);\n }\n\n private async handleSocketMessage(socket: WebSocket, text: string): Promise<void> {\n if (socket !== this.socket) return;\n this.telemetry.received(text);\n await this.handleMessage(text);\n }\n\n private handleSocketClose(socket: WebSocket): void {\n if (socket !== this.socket) return;\n if (this.heartbeat) clearInterval(this.heartbeat);\n this.heartbeat = undefined;\n this.socket = undefined;\n if (this.stopped) return;\n this.telemetry.disconnected();\n if (this.firstConnectDeferred) {\n // Still trying to establish the first connection: retry transient\n // handshake failures (like the daemon's reconnect loop) and only reject\n // start() once the bounded attempts are exhausted, so a brief blip does\n // not kill the runtime while a persistent outage still surfaces an error.\n this.initialConnectAttempts += 1;\n if (this.initialConnectAttempts >= INITIAL_CONNECT_ATTEMPTS) {\n const error = new Error(\n `cloud runtime connection failed after ${this.initialConnectAttempts} attempt(s)`,\n );\n this.telemetry.socketError(error.message);\n this.rejectFirstConnect(error);\n return;\n }\n this.logger.warn(\"cloud runtime initial connect failed; retrying\", {\n attempt: this.initialConnectAttempts,\n max: INITIAL_CONNECT_ATTEMPTS,\n });\n } else {\n this.logger.warn(\"cloud runtime disconnected; reconnecting\");\n }\n if (this.reconnect) clearTimeout(this.reconnect);\n this.reconnect = setTimeout(() => this.connect(), RECONNECT_DELAY_MS);\n }\n\n private handleSocketError(socket: WebSocket, err: Error): void {\n if (socket !== this.socket) return;\n this.logger.warn(\"cloud runtime socket error\", { error: err.message });\n this.telemetry.socketError(err.message);\n }\n\n private async advertiseProjects(): Promise<void> {\n const projects = await this.executor.advertisedProjects();\n this.advertisedProjects = projects;\n this.telemetry.projectsAdvertised(projects);\n this.send({\n type: \"hello\",\n runtimeId: this.cloud.runtime_id,\n runtimeName: this.cloud.runtime_name,\n projects,\n });\n }\n\n private async handleMessage(text: string): Promise<void> {\n let message: GatewayMessage;\n try {\n message = JSON.parse(text) as GatewayMessage;\n } catch {\n return;\n }\n if (message.type === \"cancel\" && message.requestId) {\n void this.cancelInFlight(message.requestId);\n return;\n }\n if (message.type !== \"tool_call\" || !message.requestId || !message.toolName) return;\n const routingKey = this.resolveRoutingKey(message);\n const project = this.resolveProject(routingKey);\n const projectAlias = project?.alias;\n const projectPath = project?.path;\n const startedAt = Date.now();\n const receivedBytes = Buffer.byteLength(text);\n this.inFlight.set(message.requestId, {\n message,\n ...(routingKey !== undefined ? { routingKey } : {}),\n });\n this.telemetry.requestStarted({\n requestId: message.requestId,\n ...(projectAlias ? { projectAlias } : {}),\n ...(projectPath ? { projectPath } : {}),\n toolName: message.toolName,\n receivedBytes,\n });\n let outcome: \"success\" | \"error\" | \"cancelled\" = \"success\";\n let errorMessage: string | undefined;\n try {\n const result = await this.executor.execute(message.toolName, message.args ?? {}, routingKey);\n if (!this.inFlight.has(message.requestId)) {\n outcome = \"cancelled\";\n return;\n }\n this.send({ type: \"tool_result\", requestId: message.requestId, result }, projectPath);\n } catch (err) {\n if (!this.inFlight.has(message.requestId)) {\n outcome = \"cancelled\";\n return;\n }\n outcome = \"error\";\n errorMessage = err instanceof Error ? err.message : String(err);\n this.send({\n type: \"tool_result\",\n requestId: message.requestId,\n error: errorMessage,\n }, projectPath);\n } finally {\n this.inFlight.delete(message.requestId);\n this.telemetry.requestFinished({\n requestId: message.requestId,\n ...(projectAlias ? { projectAlias } : {}),\n ...(projectPath ? { projectPath } : {}),\n toolName: message.toolName,\n durationMs: Date.now() - startedAt,\n outcome,\n ...(errorMessage ? { error: errorMessage } : {}),\n });\n }\n }\n\n private resolveRoutingKey(message: GatewayMessage): string | undefined {\n return message.projectAlias\n ?? (typeof message.args?.projectDir === \"string\" ? message.args.projectDir : undefined)\n ?? (typeof message.args?.projectAlias === \"string\" ? message.args.projectAlias : undefined);\n }\n\n private resolveProject(routingKey?: string): AdvertisedProject | undefined {\n if (routingKey !== undefined) {\n const exact = this.advertisedProjects.find((project) => project.path === routingKey);\n if (exact) return exact;\n const matches = this.advertisedProjects.filter((project) => project.alias === routingKey);\n return matches.length === 1 ? matches[0] : undefined;\n }\n if (this.advertisedProjects.length === 1) return this.advertisedProjects[0];\n return undefined;\n }\n\n private async cancelInFlight(requestId: string): Promise<void> {\n const pending = this.inFlight.get(requestId);\n if (!pending) return;\n this.inFlight.delete(requestId);\n try {\n if (typeof pending.message.args?.sessionId === \"string\") {\n await this.executor.execute(\n \"gsd_cancel\",\n { sessionId: pending.message.args.sessionId },\n pending.routingKey,\n );\n return;\n }\n if (pending.routingKey !== undefined) {\n await this.executor.execute(\n \"gsd_cancel\",\n { projectDir: pending.routingKey },\n pending.routingKey,\n );\n }\n } catch (err) {\n this.logger.warn(\"cloud runtime cancel failed\", {\n requestId,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n private resolveFirstConnect(): void {\n const deferred = this.firstConnectDeferred;\n if (!deferred) return;\n this.firstConnectDeferred = undefined;\n deferred.resolve();\n }\n\n private rejectFirstConnect(err: Error): void {\n const deferred = this.firstConnectDeferred;\n if (!deferred) return;\n this.firstConnectDeferred = undefined;\n deferred.reject(err);\n }\n\n private send(message: unknown, projectPath?: string): void {\n const text = JSON.stringify(message);\n if (this.socket?.readyState === WebSocket.OPEN) {\n this.socket.send(text);\n this.telemetry.sent(text, projectPath);\n return;\n }\n // Buffer while disconnected; flushed on reconnect in handleSocketOpen. Bounded\n // so a long outage cannot grow memory without limit — a stale heartbeat is\n // worth less than a fresh tool_result, so drop oldest first.\n this.outbox.push({ text, ...(projectPath ? { projectPath } : {}) });\n if (this.outbox.length > CloudRuntime.MAX_OUTBOX) {\n this.outbox.shift();\n }\n }\n}\n"]}