@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
@@ -4,8 +4,12 @@
4
4
  // failure, so the CLI never reports "connected" for a socket that never opened.
5
5
  import { test } from "node:test";
6
6
  import assert from "node:assert/strict";
7
+ import { mkdtempSync, readFileSync, rmSync } from "node:fs";
8
+ import { tmpdir } from "node:os";
9
+ import { join } from "node:path";
7
10
  import WebSocket from "ws";
8
11
  import { CloudRuntime } from "./cloud-runtime.js";
12
+ import { RuntimeTelemetryStore } from "./runtime-telemetry.js";
9
13
  const noopLogger = { info: () => undefined, warn: () => undefined, error: () => undefined, debug: () => undefined };
10
14
  const noopExecutor = { execute: async () => ({}), advertisedProjects: async () => [] };
11
15
  function makeRuntime(cloud = {}) {
@@ -15,74 +19,235 @@ function fakeSocket(readyState = WebSocket.OPEN) {
15
19
  const sent = [];
16
20
  return { readyState, sent, send: (t) => sent.push(t), close: () => undefined };
17
21
  }
18
- test("start()'s first-connect promise resolves only when the relay socket opens", async () => {
19
- const runtime = makeRuntime();
22
+ test("one routing selector drives execution, cancellation, and telemetry", async (t) => {
23
+ const selectors = [];
24
+ const started = [];
25
+ const execution = Promise.withResolvers();
26
+ const telemetry = {
27
+ ...Object.fromEntries([
28
+ "connecting", "connected", "disconnected", "socketError", "received", "sent",
29
+ "projectsAdvertised", "requestFinished", "stopped",
30
+ ].map((name) => [name, () => undefined])),
31
+ requestStarted: (request) => started.push(request),
32
+ };
33
+ const runtime = new CloudRuntime({ gateway_url: "wss://cloud.example.net", device_token: "fixture", runtime_id: "runtime" }, {
34
+ execute: async (toolName, _args, selector) => {
35
+ selectors.push(selector);
36
+ if (toolName === "gsd_cancel")
37
+ return {};
38
+ return execution.promise;
39
+ },
40
+ advertisedProjects: async () => [],
41
+ }, noopLogger, telemetry);
42
+ const internals = runtime;
43
+ t.after(() => runtime.stop());
44
+ internals.advertisedProjects = [
45
+ { alias: "one", path: "/work/one", repoIdentity: "one", markers: [".gsd"] },
46
+ { alias: "two", path: "/work/two", repoIdentity: "two", markers: [".gsd"] },
47
+ ];
48
+ const socket = fakeSocket();
49
+ internals.socket = socket;
50
+ const request = internals.handleSocketMessage(socket, JSON.stringify({
51
+ type: "tool_call",
52
+ requestId: "request-routing",
53
+ toolName: "gsd_status",
54
+ projectAlias: "one",
55
+ args: { projectDir: "/work/two" },
56
+ }));
57
+ await Promise.resolve();
58
+ await internals.handleSocketMessage(socket, JSON.stringify({
59
+ type: "cancel",
60
+ requestId: "request-routing",
61
+ }));
62
+ execution.resolve({});
63
+ await request;
64
+ assert.deepEqual(selectors, ["one", "one"]);
65
+ assert.equal(started[0]?.projectAlias, "one");
66
+ assert.equal(started[0]?.projectPath, "/work/one");
67
+ internals.advertisedProjects = [
68
+ { alias: "app", path: "/work/one/app", repoIdentity: "one", markers: [".gsd"] },
69
+ { alias: "app", path: "/work/two/app", repoIdentity: "two", markers: [".gsd"] },
70
+ ];
71
+ await internals.handleSocketMessage(socket, JSON.stringify({
72
+ type: "tool_call",
73
+ requestId: "request-ambiguous",
74
+ toolName: "gsd_status",
75
+ projectAlias: "app",
76
+ }));
77
+ assert.equal(started[1]?.projectAlias, undefined);
78
+ assert.equal(started[1]?.projectPath, undefined);
79
+ });
80
+ test("queued project bytes are reported only after transmission", async (t) => {
81
+ const sentProjects = [];
82
+ const telemetry = {
83
+ ...Object.fromEntries([
84
+ "connecting", "connected", "disconnected", "socketError", "received",
85
+ "projectsAdvertised", "requestStarted", "requestFinished", "stopped",
86
+ ].map((name) => [name, () => undefined])),
87
+ sent: (_text, projectPath) => sentProjects.push(projectPath),
88
+ };
89
+ const runtime = new CloudRuntime({ gateway_url: "wss://cloud.example.net", device_token: "fixture", runtime_id: "runtime" }, {
90
+ execute: async () => ({ ok: true }),
91
+ advertisedProjects: async () => [
92
+ { alias: "app", path: "/work/one/app", repoIdentity: "one", markers: [".gsd"] },
93
+ { alias: "app", path: "/work/two/app", repoIdentity: "two", markers: [".gsd"] },
94
+ ],
95
+ }, noopLogger, telemetry);
20
96
  const internals = runtime;
21
- try {
22
- const deferred = Promise.withResolvers();
23
- internals.firstConnectDeferred = deferred;
24
- const socket = fakeSocket();
25
- internals.socket = socket;
26
- let settled = false;
27
- void deferred.promise.then(() => (settled = true));
28
- await Promise.resolve(); // let any premature settle flush
29
- assert.equal(settled, false, "promise must not resolve before the socket opens");
30
- internals.handleSocketOpen(socket);
31
- await deferred.promise; // resolves — otherwise this hangs/throws
32
- }
33
- finally {
34
- runtime.stop();
35
- }
97
+ t.after(() => runtime.stop());
98
+ internals.advertisedProjects = [
99
+ { alias: "app", path: "/work/one/app", repoIdentity: "one", markers: [".gsd"] },
100
+ { alias: "app", path: "/work/two/app", repoIdentity: "two", markers: [".gsd"] },
101
+ ];
102
+ internals.socket = fakeSocket(WebSocket.CLOSED);
103
+ await internals.handleSocketMessage(internals.socket, JSON.stringify({
104
+ type: "tool_call",
105
+ requestId: "request-queued",
106
+ toolName: "gsd_status",
107
+ args: { projectDir: "/work/two/app" },
108
+ }));
109
+ assert.deepEqual(sentProjects, []);
110
+ const openSocket = fakeSocket();
111
+ internals.socket = openSocket;
112
+ internals.handleSocketOpen(openSocket);
113
+ assert.deepEqual(sentProjects, ["/work/two/app"]);
36
114
  });
37
- test("an early socket close retries instead of rejecting while attempts remain", async () => {
115
+ test("start()'s first-connect promise resolves only when the relay socket opens", async (t) => {
38
116
  const runtime = makeRuntime();
39
117
  const internals = runtime;
40
- try {
41
- const deferred = Promise.withResolvers();
42
- internals.firstConnectDeferred = deferred;
43
- const socket = fakeSocket();
44
- internals.socket = socket;
45
- let settled = false;
46
- void deferred.promise.then(() => (settled = true), () => (settled = true));
47
- internals.handleSocketClose(socket); // first transient failure
48
- await Promise.resolve();
49
- assert.equal(settled, false, "a single early close must not settle start()");
50
- assert.equal(internals.initialConnectAttempts, 1);
51
- assert.notEqual(internals.reconnect, undefined, "a reconnect must be scheduled");
52
- }
53
- finally {
54
- runtime.stop();
55
- }
118
+ t.after(() => runtime.stop());
119
+ const deferred = Promise.withResolvers();
120
+ internals.firstConnectDeferred = deferred;
121
+ const socket = fakeSocket();
122
+ internals.socket = socket;
123
+ let settled = false;
124
+ void deferred.promise.then(() => (settled = true));
125
+ await Promise.resolve(); // let any premature settle flush
126
+ assert.equal(settled, false, "promise must not resolve before the socket opens");
127
+ internals.handleSocketOpen(socket);
128
+ await deferred.promise; // resolves — otherwise this hangs/throws
56
129
  });
57
- test("start()'s first-connect promise rejects once the initial connect attempts are exhausted", async () => {
130
+ test("an early socket close retries instead of rejecting while attempts remain", async (t) => {
58
131
  const runtime = makeRuntime();
59
132
  const internals = runtime;
60
- try {
61
- const deferred = Promise.withResolvers();
62
- internals.firstConnectDeferred = deferred;
63
- // Simulate having already burned every retry but the last so the next close
64
- // is the one that must give up and reject.
65
- internals.initialConnectAttempts = 4;
66
- const socket = fakeSocket();
67
- internals.socket = socket;
68
- internals.handleSocketClose(socket);
69
- await assert.rejects(deferred.promise, /connection failed/);
70
- }
71
- finally {
72
- runtime.stop();
73
- }
133
+ t.after(() => runtime.stop());
134
+ const deferred = Promise.withResolvers();
135
+ internals.firstConnectDeferred = deferred;
136
+ const socket = fakeSocket();
137
+ internals.socket = socket;
138
+ let settled = false;
139
+ void deferred.promise.then(() => (settled = true), () => (settled = true));
140
+ internals.handleSocketClose(socket); // first transient failure
141
+ await Promise.resolve();
142
+ assert.equal(settled, false, "a single early close must not settle start()");
143
+ assert.equal(internals.initialConnectAttempts, 1);
144
+ assert.notEqual(internals.reconnect, undefined, "a reconnect must be scheduled");
145
+ });
146
+ test("start()'s first-connect promise rejects once the initial connect attempts are exhausted", async (t) => {
147
+ const errors = [];
148
+ const telemetry = {
149
+ ...Object.fromEntries([
150
+ "connecting", "connected", "disconnected", "received", "sent", "projectsAdvertised",
151
+ "requestStarted", "requestFinished", "stopped",
152
+ ].map((name) => [name, () => undefined])),
153
+ socketError: (message) => errors.push(message),
154
+ };
155
+ const runtime = new CloudRuntime({ gateway_url: "wss://cloud.example.net", device_token: "fixture", runtime_id: "runtime" }, noopExecutor, noopLogger, telemetry);
156
+ const internals = runtime;
157
+ t.after(() => runtime.stop());
158
+ const deferred = Promise.withResolvers();
159
+ internals.firstConnectDeferred = deferred;
160
+ // Simulate having already burned every retry but the last so the next close
161
+ // is the one that must give up and reject.
162
+ internals.initialConnectAttempts = 4;
163
+ const socket = fakeSocket();
164
+ internals.socket = socket;
165
+ internals.handleSocketClose(socket);
166
+ await assert.rejects(deferred.promise, /connection failed/);
167
+ assert.deepEqual(errors, ["cloud runtime connection failed after 5 attempt(s)"]);
74
168
  });
75
- test("connect() rejects the first-connect promise when the device token is missing", async () => {
169
+ test("connect() rejects the first-connect promise when the device token is missing", async (t) => {
76
170
  const runtime = makeRuntime({ device_token: "" });
77
171
  const internals = runtime;
78
- try {
79
- const deferred = Promise.withResolvers();
80
- internals.firstConnectDeferred = deferred;
81
- internals.connect();
82
- await assert.rejects(deferred.promise, /missing device token/);
83
- }
84
- finally {
85
- runtime.stop();
86
- }
172
+ t.after(() => runtime.stop());
173
+ const deferred = Promise.withResolvers();
174
+ internals.firstConnectDeferred = deferred;
175
+ internals.connect();
176
+ await assert.rejects(deferred.promise, /missing device token/);
177
+ });
178
+ test("socket activity is reported to runtime telemetry", async (t) => {
179
+ const events = [];
180
+ const telemetry = {
181
+ connecting: () => events.push({ name: "connecting" }),
182
+ connected: () => events.push({ name: "connected" }),
183
+ disconnected: () => events.push({ name: "disconnected" }),
184
+ socketError: () => events.push({ name: "error" }),
185
+ received: () => events.push({ name: "received" }),
186
+ sent: () => events.push({ name: "sent" }),
187
+ projectsAdvertised: (details) => events.push({ name: "projects", details }),
188
+ requestStarted: (details) => events.push({ name: "request-started", details }),
189
+ requestFinished: (details) => events.push({ name: "request-finished", details }),
190
+ stopped: () => events.push({ name: "stopped" }),
191
+ };
192
+ const runtime = new CloudRuntime({ gateway_url: "wss://cloud.example.net", device_token: "fixture", runtime_id: "runtime" }, {
193
+ execute: async () => ({ ok: true }),
194
+ advertisedProjects: async () => [{
195
+ alias: "project-one",
196
+ path: "/work/project-one",
197
+ repoIdentity: "repo-one",
198
+ markers: [".gsd"],
199
+ }],
200
+ }, noopLogger, telemetry);
201
+ const internals = runtime;
202
+ t.after(() => runtime.stop());
203
+ const socket = fakeSocket();
204
+ internals.socket = socket;
205
+ internals.handleSocketOpen(socket);
206
+ await new Promise((resolve) => setImmediate(resolve));
207
+ await internals.handleSocketMessage(socket, JSON.stringify({
208
+ type: "tool_call",
209
+ requestId: "request-1",
210
+ toolName: "gsd_status",
211
+ projectAlias: "project-one",
212
+ }));
213
+ assert.ok(events.some((event) => event.name === "connected"));
214
+ assert.ok(events.some((event) => event.name === "received"));
215
+ const advertised = events.find((event) => event.name === "projects");
216
+ assert.equal((advertised?.details)[0]?.alias, "project-one");
217
+ const started = events.find((event) => event.name === "request-started");
218
+ assert.equal((started?.details).projectAlias, "project-one");
219
+ assert.equal((started?.details).toolName, "gsd_status");
220
+ assert.ok(((started?.details).receivedBytes ?? 0) > 0);
221
+ const finished = events.find((event) => event.name === "request-finished");
222
+ assert.equal((finished?.details).projectAlias, "project-one");
223
+ assert.equal((finished?.details).outcome, "success");
224
+ assert.equal((finished?.details).sentBytes, undefined);
225
+ assert.ok(events.some((event) => event.name === "sent"));
226
+ });
227
+ test("startup failures flush runtime telemetry before rejecting", async (t) => {
228
+ const root = mkdtempSync(join(tmpdir(), "gsd-cloud-startup-error-"));
229
+ t.after(() => rmSync(root, { recursive: true, force: true }));
230
+ const telemetry = new RuntimeTelemetryStore(join(root, "daemon.yaml"), {
231
+ gatewayUrl: "wss://cloud.example.net",
232
+ });
233
+ const runtime = new CloudRuntime({ gateway_url: "wss://cloud.example.net", device_token: "", runtime_id: "runtime" }, noopExecutor, noopLogger, telemetry);
234
+ t.after(() => runtime.stop());
235
+ await assert.rejects(runtime.start(), /missing device token/);
236
+ const status = JSON.parse(readFileSync(join(root, "cloud-runtime-status.json"), "utf8"));
237
+ assert.equal(status.state, "error");
238
+ assert.equal(status.last_error, "cloud runtime missing device token or runtime id");
239
+ });
240
+ test("malformed gateway failures flush runtime telemetry before rejecting", async (t) => {
241
+ const root = mkdtempSync(join(tmpdir(), "gsd-cloud-startup-url-error-"));
242
+ t.after(() => rmSync(root, { recursive: true, force: true }));
243
+ const telemetry = new RuntimeTelemetryStore(join(root, "daemon.yaml"), {
244
+ gatewayUrl: "not-a-url",
245
+ });
246
+ const runtime = new CloudRuntime({ gateway_url: "not-a-url", device_token: "fixture", runtime_id: "runtime" }, noopExecutor, noopLogger, telemetry);
247
+ t.after(() => runtime.stop());
248
+ await assert.rejects(runtime.start(), /absolute HTTP\(S\) URL/);
249
+ const status = JSON.parse(readFileSync(join(root, "cloud-runtime-status.json"), "utf8"));
250
+ assert.equal(status.state, "error");
251
+ assert.match(status.last_error ?? "", /absolute HTTP\(S\) URL/);
87
252
  });
88
253
  //# sourceMappingURL=cloud-runtime.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cloud-runtime.test.js","sourceRoot":"","sources":["../src/cloud-runtime.test.ts"],"names":[],"mappings":"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","sourcesContent":["// Project/App: Open GSD\n// File Purpose: Regression tests for CloudRuntime.start()'s first-connect promise\n// — it must resolve only once the relay is actually up and reject on connect\n// failure, so the CLI never reports \"connected\" for a socket that never opened.\nimport { test } from \"node:test\";\nimport assert from \"node:assert/strict\";\nimport WebSocket from \"ws\";\nimport { CloudRuntime } from \"./cloud-runtime.js\";\n\nconst noopLogger = { info: () => undefined, warn: () => undefined, error: () => undefined, debug: () => undefined };\nconst noopExecutor = { execute: async () => ({}), advertisedProjects: async () => [] };\n\nfunction makeRuntime(cloud: Record<string, unknown> = {}): CloudRuntime {\n return new CloudRuntime(\n { gateway_url: \"wss://cloud.example.net\", device_token: \"fixture\", runtime_id: \"runtime\", ...cloud } as never,\n noopExecutor as never,\n noopLogger as never,\n );\n}\n\ntype FakeSocket = { readyState: number; sent: string[]; send: (t: string) => void; close: () => void };\nfunction fakeSocket(readyState = WebSocket.OPEN): FakeSocket {\n const sent: string[] = [];\n return { readyState, sent, send: (t: string) => sent.push(t), close: () => undefined };\n}\ntype RuntimeInternals = {\n socket: FakeSocket | undefined;\n firstConnectDeferred: PromiseWithResolvers<void> | undefined;\n initialConnectAttempts: number;\n reconnect: ReturnType<typeof setTimeout> | undefined;\n handleSocketOpen: (socket: unknown) => void;\n handleSocketClose: (socket: unknown) => void;\n connect: () => void;\n};\n\ntest(\"start()'s first-connect promise resolves only when the relay socket opens\", async () => {\n const runtime = makeRuntime();\n const internals = runtime as unknown as RuntimeInternals;\n try {\n const deferred = Promise.withResolvers<void>();\n internals.firstConnectDeferred = deferred;\n const socket = fakeSocket();\n internals.socket = socket;\n\n let settled = false;\n void deferred.promise.then(() => (settled = true));\n await Promise.resolve(); // let any premature settle flush\n assert.equal(settled, false, \"promise must not resolve before the socket opens\");\n\n internals.handleSocketOpen(socket);\n await deferred.promise; // resolves — otherwise this hangs/throws\n } finally {\n runtime.stop();\n }\n});\n\ntest(\"an early socket close retries instead of rejecting while attempts remain\", async () => {\n const runtime = makeRuntime();\n const internals = runtime as unknown as RuntimeInternals;\n try {\n const deferred = Promise.withResolvers<void>();\n internals.firstConnectDeferred = deferred;\n const socket = fakeSocket();\n internals.socket = socket;\n\n let settled = false;\n void deferred.promise.then(() => (settled = true), () => (settled = true));\n\n internals.handleSocketClose(socket); // first transient failure\n await Promise.resolve();\n assert.equal(settled, false, \"a single early close must not settle start()\");\n assert.equal(internals.initialConnectAttempts, 1);\n assert.notEqual(internals.reconnect, undefined, \"a reconnect must be scheduled\");\n } finally {\n runtime.stop();\n }\n});\n\ntest(\"start()'s first-connect promise rejects once the initial connect attempts are exhausted\", async () => {\n const runtime = makeRuntime();\n const internals = runtime as unknown as RuntimeInternals;\n try {\n const deferred = Promise.withResolvers<void>();\n internals.firstConnectDeferred = deferred;\n // Simulate having already burned every retry but the last so the next close\n // is the one that must give up and reject.\n internals.initialConnectAttempts = 4;\n const socket = fakeSocket();\n internals.socket = socket;\n\n internals.handleSocketClose(socket);\n await assert.rejects(deferred.promise, /connection failed/);\n } finally {\n runtime.stop();\n }\n});\n\ntest(\"connect() rejects the first-connect promise when the device token is missing\", async () => {\n const runtime = makeRuntime({ device_token: \"\" });\n const internals = runtime as unknown as RuntimeInternals;\n try {\n const deferred = Promise.withResolvers<void>();\n internals.firstConnectDeferred = deferred;\n internals.connect();\n await assert.rejects(deferred.promise, /missing device token/);\n } finally {\n runtime.stop();\n }\n});\n"]}
1
+ {"version":3,"file":"cloud-runtime.test.js","sourceRoot":"","sources":["../src/cloud-runtime.test.ts"],"names":[],"mappings":"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","sourcesContent":["// Project/App: Open GSD\n// File Purpose: Regression tests for CloudRuntime.start()'s first-connect promise\n// — it must resolve only once the relay is actually up and reject on connect\n// failure, so the CLI never reports \"connected\" for a socket that never opened.\nimport { test } from \"node:test\";\nimport assert from \"node:assert/strict\";\nimport { mkdtempSync, readFileSync, rmSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport WebSocket from \"ws\";\nimport { CloudRuntime } from \"./cloud-runtime.js\";\nimport { RuntimeTelemetryStore } from \"./runtime-telemetry.js\";\n\nconst noopLogger = { info: () => undefined, warn: () => undefined, error: () => undefined, debug: () => undefined };\nconst noopExecutor = { execute: async () => ({}), advertisedProjects: async () => [] };\n\nfunction makeRuntime(cloud: Record<string, unknown> = {}): CloudRuntime {\n return new CloudRuntime(\n { gateway_url: \"wss://cloud.example.net\", device_token: \"fixture\", runtime_id: \"runtime\", ...cloud } as never,\n noopExecutor as never,\n noopLogger as never,\n );\n}\n\ntype FakeSocket = { readyState: number; sent: string[]; send: (t: string) => void; close: () => void };\nfunction fakeSocket(readyState: number = WebSocket.OPEN): FakeSocket {\n const sent: string[] = [];\n return { readyState, sent, send: (t: string) => sent.push(t), close: () => undefined };\n}\ntype RuntimeInternals = {\n socket: FakeSocket | undefined;\n advertisedProjects: Array<{ alias: string; path: string; repoIdentity: string; markers: string[] }>;\n firstConnectDeferred: PromiseWithResolvers<void> | undefined;\n initialConnectAttempts: number;\n reconnect: ReturnType<typeof setTimeout> | undefined;\n handleSocketOpen: (socket: unknown) => void;\n handleSocketClose: (socket: unknown) => void;\n handleSocketMessage: (socket: unknown, text: string) => Promise<void>;\n connect: () => void;\n};\n\ntest(\"one routing selector drives execution, cancellation, and telemetry\", async (t) => {\n const selectors: Array<string | undefined> = [];\n const started: Array<{ projectAlias?: string; projectPath?: string }> = [];\n const execution = Promise.withResolvers<unknown>();\n const telemetry = {\n ...Object.fromEntries([\n \"connecting\", \"connected\", \"disconnected\", \"socketError\", \"received\", \"sent\",\n \"projectsAdvertised\", \"requestFinished\", \"stopped\",\n ].map((name) => [name, () => undefined])),\n requestStarted: (request: { projectAlias?: string; projectPath?: string }) => started.push(request),\n } as never;\n const runtime = new CloudRuntime(\n { gateway_url: \"wss://cloud.example.net\", device_token: \"fixture\", runtime_id: \"runtime\" },\n {\n execute: async (toolName: string, _args: unknown, selector?: string) => {\n selectors.push(selector);\n if (toolName === \"gsd_cancel\") return {};\n return execution.promise;\n },\n advertisedProjects: async () => [],\n } as never,\n noopLogger as never,\n telemetry,\n );\n const internals = runtime as unknown as RuntimeInternals;\n t.after(() => runtime.stop());\n internals.advertisedProjects = [\n { alias: \"one\", path: \"/work/one\", repoIdentity: \"one\", markers: [\".gsd\"] },\n { alias: \"two\", path: \"/work/two\", repoIdentity: \"two\", markers: [\".gsd\"] },\n ];\n const socket = fakeSocket();\n internals.socket = socket;\n\n const request = internals.handleSocketMessage(socket, JSON.stringify({\n type: \"tool_call\",\n requestId: \"request-routing\",\n toolName: \"gsd_status\",\n projectAlias: \"one\",\n args: { projectDir: \"/work/two\" },\n }));\n await Promise.resolve();\n await internals.handleSocketMessage(socket, JSON.stringify({\n type: \"cancel\",\n requestId: \"request-routing\",\n }));\n execution.resolve({});\n await request;\n\n assert.deepEqual(selectors, [\"one\", \"one\"]);\n assert.equal(started[0]?.projectAlias, \"one\");\n assert.equal(started[0]?.projectPath, \"/work/one\");\n\n internals.advertisedProjects = [\n { alias: \"app\", path: \"/work/one/app\", repoIdentity: \"one\", markers: [\".gsd\"] },\n { alias: \"app\", path: \"/work/two/app\", repoIdentity: \"two\", markers: [\".gsd\"] },\n ];\n await internals.handleSocketMessage(socket, JSON.stringify({\n type: \"tool_call\",\n requestId: \"request-ambiguous\",\n toolName: \"gsd_status\",\n projectAlias: \"app\",\n }));\n assert.equal(started[1]?.projectAlias, undefined);\n assert.equal(started[1]?.projectPath, undefined);\n\n});\n\ntest(\"queued project bytes are reported only after transmission\", async (t) => {\n const sentProjects: Array<string | undefined> = [];\n const telemetry = {\n ...Object.fromEntries([\n \"connecting\", \"connected\", \"disconnected\", \"socketError\", \"received\",\n \"projectsAdvertised\", \"requestStarted\", \"requestFinished\", \"stopped\",\n ].map((name) => [name, () => undefined])),\n sent: (_text: string, projectPath?: string) => sentProjects.push(projectPath),\n } as never;\n const runtime = new CloudRuntime(\n { gateway_url: \"wss://cloud.example.net\", device_token: \"fixture\", runtime_id: \"runtime\" },\n {\n execute: async () => ({ ok: true }),\n advertisedProjects: async () => [\n { alias: \"app\", path: \"/work/one/app\", repoIdentity: \"one\", markers: [\".gsd\"] },\n { alias: \"app\", path: \"/work/two/app\", repoIdentity: \"two\", markers: [\".gsd\"] },\n ],\n } as never,\n noopLogger as never,\n telemetry,\n );\n const internals = runtime as unknown as RuntimeInternals;\n t.after(() => runtime.stop());\n internals.advertisedProjects = [\n { alias: \"app\", path: \"/work/one/app\", repoIdentity: \"one\", markers: [\".gsd\"] },\n { alias: \"app\", path: \"/work/two/app\", repoIdentity: \"two\", markers: [\".gsd\"] },\n ];\n internals.socket = fakeSocket(WebSocket.CLOSED);\n\n await internals.handleSocketMessage(internals.socket, JSON.stringify({\n type: \"tool_call\",\n requestId: \"request-queued\",\n toolName: \"gsd_status\",\n args: { projectDir: \"/work/two/app\" },\n }));\n assert.deepEqual(sentProjects, []);\n\n const openSocket = fakeSocket();\n internals.socket = openSocket;\n internals.handleSocketOpen(openSocket);\n assert.deepEqual(sentProjects, [\"/work/two/app\"]);\n\n});\n\ntest(\"start()'s first-connect promise resolves only when the relay socket opens\", async (t) => {\n const runtime = makeRuntime();\n const internals = runtime as unknown as RuntimeInternals;\n t.after(() => runtime.stop());\n const deferred = Promise.withResolvers<void>();\n internals.firstConnectDeferred = deferred;\n const socket = fakeSocket();\n internals.socket = socket;\n\n let settled = false;\n void deferred.promise.then(() => (settled = true));\n await Promise.resolve(); // let any premature settle flush\n assert.equal(settled, false, \"promise must not resolve before the socket opens\");\n\n internals.handleSocketOpen(socket);\n await deferred.promise; // resolves — otherwise this hangs/throws\n\n});\n\ntest(\"an early socket close retries instead of rejecting while attempts remain\", async (t) => {\n const runtime = makeRuntime();\n const internals = runtime as unknown as RuntimeInternals;\n t.after(() => runtime.stop());\n const deferred = Promise.withResolvers<void>();\n internals.firstConnectDeferred = deferred;\n const socket = fakeSocket();\n internals.socket = socket;\n\n let settled = false;\n void deferred.promise.then(() => (settled = true), () => (settled = true));\n\n internals.handleSocketClose(socket); // first transient failure\n await Promise.resolve();\n assert.equal(settled, false, \"a single early close must not settle start()\");\n assert.equal(internals.initialConnectAttempts, 1);\n assert.notEqual(internals.reconnect, undefined, \"a reconnect must be scheduled\");\n\n});\n\ntest(\"start()'s first-connect promise rejects once the initial connect attempts are exhausted\", async (t) => {\n const errors: string[] = [];\n const telemetry = {\n ...Object.fromEntries([\n \"connecting\", \"connected\", \"disconnected\", \"received\", \"sent\", \"projectsAdvertised\",\n \"requestStarted\", \"requestFinished\", \"stopped\",\n ].map((name) => [name, () => undefined])),\n socketError: (message: string) => errors.push(message),\n } as never;\n const runtime = new CloudRuntime(\n { gateway_url: \"wss://cloud.example.net\", device_token: \"fixture\", runtime_id: \"runtime\" },\n noopExecutor as never,\n noopLogger as never,\n telemetry,\n );\n const internals = runtime as unknown as RuntimeInternals;\n t.after(() => runtime.stop());\n const deferred = Promise.withResolvers<void>();\n internals.firstConnectDeferred = deferred;\n // Simulate having already burned every retry but the last so the next close\n // is the one that must give up and reject.\n internals.initialConnectAttempts = 4;\n const socket = fakeSocket();\n internals.socket = socket;\n\n internals.handleSocketClose(socket);\n await assert.rejects(deferred.promise, /connection failed/);\n assert.deepEqual(errors, [\"cloud runtime connection failed after 5 attempt(s)\"]);\n\n});\n\ntest(\"connect() rejects the first-connect promise when the device token is missing\", async (t) => {\n const runtime = makeRuntime({ device_token: \"\" });\n const internals = runtime as unknown as RuntimeInternals;\n t.after(() => runtime.stop());\n const deferred = Promise.withResolvers<void>();\n internals.firstConnectDeferred = deferred;\n internals.connect();\n await assert.rejects(deferred.promise, /missing device token/);\n\n});\n\ntest(\"socket activity is reported to runtime telemetry\", async (t) => {\n const events: Array<{ name: string; details?: unknown }> = [];\n const telemetry = {\n connecting: () => events.push({ name: \"connecting\" }),\n connected: () => events.push({ name: \"connected\" }),\n disconnected: () => events.push({ name: \"disconnected\" }),\n socketError: () => events.push({ name: \"error\" }),\n received: () => events.push({ name: \"received\" }),\n sent: () => events.push({ name: \"sent\" }),\n projectsAdvertised: (details: unknown) => events.push({ name: \"projects\", details }),\n requestStarted: (details: unknown) => events.push({ name: \"request-started\", details }),\n requestFinished: (details: unknown) => events.push({ name: \"request-finished\", details }),\n stopped: () => events.push({ name: \"stopped\" }),\n };\n const runtime = new CloudRuntime(\n { gateway_url: \"wss://cloud.example.net\", device_token: \"fixture\", runtime_id: \"runtime\" },\n {\n execute: async () => ({ ok: true }),\n advertisedProjects: async () => [{\n alias: \"project-one\",\n path: \"/work/project-one\",\n repoIdentity: \"repo-one\",\n markers: [\".gsd\"],\n }],\n } as never,\n noopLogger as never,\n telemetry,\n );\n const internals = runtime as unknown as RuntimeInternals;\n t.after(() => runtime.stop());\n const socket = fakeSocket();\n internals.socket = socket;\n\n internals.handleSocketOpen(socket);\n await new Promise((resolve) => setImmediate(resolve));\n await internals.handleSocketMessage(socket, JSON.stringify({\n type: \"tool_call\",\n requestId: \"request-1\",\n toolName: \"gsd_status\",\n projectAlias: \"project-one\",\n }));\n\n assert.ok(events.some((event) => event.name === \"connected\"));\n assert.ok(events.some((event) => event.name === \"received\"));\n const advertised = events.find((event) => event.name === \"projects\");\n assert.equal((advertised?.details as Array<{ alias?: string }>)[0]?.alias, \"project-one\");\n const started = events.find((event) => event.name === \"request-started\");\n assert.equal((started?.details as { projectAlias?: string }).projectAlias, \"project-one\");\n assert.equal((started?.details as { toolName?: string }).toolName, \"gsd_status\");\n assert.ok(((started?.details as { receivedBytes?: number }).receivedBytes ?? 0) > 0);\n const finished = events.find((event) => event.name === \"request-finished\");\n assert.equal((finished?.details as { projectAlias?: string }).projectAlias, \"project-one\");\n assert.equal((finished?.details as { outcome?: string }).outcome, \"success\");\n assert.equal((finished?.details as { sentBytes?: number }).sentBytes, undefined);\n assert.ok(events.some((event) => event.name === \"sent\"));\n\n});\n\ntest(\"startup failures flush runtime telemetry before rejecting\", async (t) => {\n const root = mkdtempSync(join(tmpdir(), \"gsd-cloud-startup-error-\"));\n t.after(() => rmSync(root, { recursive: true, force: true }));\n const telemetry = new RuntimeTelemetryStore(join(root, \"daemon.yaml\"), {\n gatewayUrl: \"wss://cloud.example.net\",\n });\n const runtime = new CloudRuntime(\n { gateway_url: \"wss://cloud.example.net\", device_token: \"\", runtime_id: \"runtime\" },\n noopExecutor as never,\n noopLogger as never,\n telemetry,\n );\n t.after(() => runtime.stop());\n\n await assert.rejects(runtime.start(), /missing device token/);\n const status = JSON.parse(readFileSync(join(root, \"cloud-runtime-status.json\"), \"utf8\")) as {\n state?: string;\n last_error?: string;\n };\n assert.equal(status.state, \"error\");\n assert.equal(status.last_error, \"cloud runtime missing device token or runtime id\");\n\n});\n\ntest(\"malformed gateway failures flush runtime telemetry before rejecting\", async (t) => {\n const root = mkdtempSync(join(tmpdir(), \"gsd-cloud-startup-url-error-\"));\n t.after(() => rmSync(root, { recursive: true, force: true }));\n const telemetry = new RuntimeTelemetryStore(join(root, \"daemon.yaml\"), {\n gatewayUrl: \"not-a-url\",\n });\n const runtime = new CloudRuntime(\n { gateway_url: \"not-a-url\", device_token: \"fixture\", runtime_id: \"runtime\" },\n noopExecutor as never,\n noopLogger as never,\n telemetry,\n );\n t.after(() => runtime.stop());\n\n await assert.rejects(runtime.start(), /absolute HTTP\\(S\\) URL/);\n const status = JSON.parse(readFileSync(join(root, \"cloud-runtime-status.json\"), \"utf8\")) as {\n state?: string;\n last_error?: string;\n };\n assert.equal(status.state, \"error\");\n assert.match(status.last_error ?? \"\", /absolute HTTP\\(S\\) URL/);\n\n});\n"]}
@@ -0,0 +1,3 @@
1
+ export declare function canonicalConfigPath(configPath: string): string;
2
+ export declare function runtimeArtifactPath(configPath: string, artifact: "status" | "state" | "log" | "start.lock"): string;
3
+ //# sourceMappingURL=runtime-artifacts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-artifacts.d.ts","sourceRoot":"","sources":["../src/runtime-artifacts.ts"],"names":[],"mappings":"AAIA,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAO9D;AAED,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,QAAQ,GAAG,OAAO,GAAG,KAAK,GAAG,YAAY,GAClD,MAAM,CAcR"}
@@ -0,0 +1,36 @@
1
+ import { createHash } from "node:crypto";
2
+ import { realpathSync } from "node:fs";
3
+ import { basename, dirname, join, resolve } from "node:path";
4
+ export function canonicalConfigPath(configPath) {
5
+ const absolutePath = resolve(configPath);
6
+ try {
7
+ return realpathSync(absolutePath);
8
+ }
9
+ catch {
10
+ return absolutePath;
11
+ }
12
+ }
13
+ export function runtimeArtifactPath(configPath, artifact) {
14
+ const canonicalPath = canonicalConfigPath(configPath);
15
+ const legacy = basename(canonicalPath) === "daemon.yaml";
16
+ const namespace = legacy
17
+ ? ""
18
+ : `-${createHash("sha256").update(canonicalPath).digest("hex").slice(0, 16)}`;
19
+ let suffix;
20
+ switch (artifact) {
21
+ case "status":
22
+ suffix = "-status.json";
23
+ break;
24
+ case "state":
25
+ suffix = ".json";
26
+ break;
27
+ case "log":
28
+ suffix = ".log";
29
+ break;
30
+ case "start.lock":
31
+ suffix = ".start.lock";
32
+ break;
33
+ }
34
+ return join(dirname(canonicalPath), `cloud-runtime${namespace}${suffix}`);
35
+ }
36
+ //# sourceMappingURL=runtime-artifacts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-artifacts.js","sourceRoot":"","sources":["../src/runtime-artifacts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE7D,MAAM,UAAU,mBAAmB,CAAC,UAAkB;IACpD,MAAM,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IACzC,IAAI,CAAC;QACH,OAAO,YAAY,CAAC,YAAY,CAAC,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,YAAY,CAAC;IACtB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,mBAAmB,CACjC,UAAkB,EAClB,QAAmD;IAEnD,MAAM,aAAa,GAAG,mBAAmB,CAAC,UAAU,CAAC,CAAC;IACtD,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,aAAa,CAAC;IACzD,MAAM,SAAS,GAAG,MAAM;QACtB,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC,IAAI,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;IAChF,IAAI,MAAc,CAAC;IACnB,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,QAAQ;YAAE,MAAM,GAAG,cAAc,CAAC;YAAC,MAAM;QAC9C,KAAK,OAAO;YAAE,MAAM,GAAG,OAAO,CAAC;YAAC,MAAM;QACtC,KAAK,KAAK;YAAE,MAAM,GAAG,MAAM,CAAC;YAAC,MAAM;QACnC,KAAK,YAAY;YAAE,MAAM,GAAG,aAAa,CAAC;YAAC,MAAM;IACnD,CAAC;IACD,OAAO,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,gBAAgB,SAAS,GAAG,MAAM,EAAE,CAAC,CAAC;AAC5E,CAAC","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { realpathSync } from \"node:fs\";\nimport { basename, dirname, join, resolve } from \"node:path\";\n\nexport function canonicalConfigPath(configPath: string): string {\n const absolutePath = resolve(configPath);\n try {\n return realpathSync(absolutePath);\n } catch {\n return absolutePath;\n }\n}\n\nexport function runtimeArtifactPath(\n configPath: string,\n artifact: \"status\" | \"state\" | \"log\" | \"start.lock\",\n): string {\n const canonicalPath = canonicalConfigPath(configPath);\n const legacy = basename(canonicalPath) === \"daemon.yaml\";\n const namespace = legacy\n ? \"\"\n : `-${createHash(\"sha256\").update(canonicalPath).digest(\"hex\").slice(0, 16)}`;\n let suffix: string;\n switch (artifact) {\n case \"status\": suffix = \"-status.json\"; break;\n case \"state\": suffix = \".json\"; break;\n case \"log\": suffix = \".log\"; break;\n case \"start.lock\": suffix = \".start.lock\"; break;\n }\n return join(dirname(canonicalPath), `cloud-runtime${namespace}${suffix}`);\n}\n"]}
@@ -10,6 +10,7 @@ interface StartRuntimeOptions {
10
10
  projectDirs: string[];
11
11
  readyTimeoutMs?: number;
12
12
  verbose?: boolean;
13
+ processIdentityReader?: (pid: number) => string | null;
13
14
  }
14
15
  export declare const BACKGROUND_RUNTIME_READY_TIMEOUT_MS: number;
15
16
  export declare function startBackgroundRuntime(opts: StartRuntimeOptions): Promise<RuntimeProcessStatus>;
@@ -23,5 +24,9 @@ export declare function writeRuntimeState(configPath: string, pid: number, proje
23
24
  export declare function clearRuntimeState(configPath: string): void;
24
25
  export declare function stopBackgroundRuntime(configPath: string): Promise<boolean>;
25
26
  export declare function backgroundRuntimeStatus(configPath: string): RuntimeProcessStatus;
27
+ export declare function runtimeStatePath(configPath: string): string;
28
+ export declare function runtimeLogPath(configPath: string): string;
29
+ export declare function acquireRuntimeStartLock(configPath: string, onRecoveryClaimed?: () => void): Promise<() => void>;
30
+ export declare function commandLineMatchesRuntimeConfig(command: string, configPath: string, platform: NodeJS.Platform): boolean;
26
31
  export {};
27
32
  //# sourceMappingURL=runtime-process.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-process.d.ts","sourceRoot":"","sources":["../src/runtime-process.ts"],"names":[],"mappings":"AAqBA,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,mBAAmB;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAOD,eAAO,MAAM,mCAAmC,QACoB,CAAC;AAErE,wBAAsB,sBAAsB,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAoDrG;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,CAI3F;AAED,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAE1D;AAED,wBAAsB,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAMhF;AAED,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,MAAM,GAAG,oBAAoB,CAoBhF"}
1
+ {"version":3,"file":"runtime-process.d.ts","sourceRoot":"","sources":["../src/runtime-process.ts"],"names":[],"mappings":"AAmCA,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,mBAAmB;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,qBAAqB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;CACxD;AAQD,eAAO,MAAM,mCAAmC,QACoB,CAAC;AAKrE,wBAAsB,sBAAsB,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAwErG;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,CAM3F;AAiBD,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAE1D;AAED,wBAAsB,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAWhF;AAED,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,MAAM,GAAG,oBAAoB,CAoBhF;AAmCD,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAEzD;AAMD,wBAAsB,uBAAuB,CAC3C,UAAU,EAAE,MAAM,EAClB,iBAAiB,CAAC,EAAE,MAAM,IAAI,GAC7B,OAAO,CAAC,MAAM,IAAI,CAAC,CAoCrB;AAgOD,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,CAAC,QAAQ,GACxB,OAAO,CAKT"}