@aexol/spectral 0.9.229 → 0.9.231

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 (189) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/dist/auth-helper.d.ts.map +1 -1
  3. package/dist/auth-helper.js +14 -8
  4. package/dist/commands/login.d.ts +12 -4
  5. package/dist/commands/login.d.ts.map +1 -1
  6. package/dist/commands/login.js +36 -15
  7. package/dist/commands/serve.d.ts.map +1 -1
  8. package/dist/commands/serve.js +3 -0
  9. package/dist/config.d.ts +15 -0
  10. package/dist/config.d.ts.map +1 -1
  11. package/dist/config.js +10 -1
  12. package/dist/designer/data/skills/blog-post.d.ts +3 -0
  13. package/dist/designer/data/skills/blog-post.d.ts.map +1 -0
  14. package/dist/designer/data/skills/blog-post.js +84 -0
  15. package/dist/designer/data/skills/blog-post.ts +87 -0
  16. package/dist/designer/data/skills/critique.d.ts +3 -0
  17. package/dist/designer/data/skills/critique.d.ts.map +1 -0
  18. package/dist/designer/data/skills/critique.js +109 -0
  19. package/dist/designer/data/skills/critique.ts +112 -0
  20. package/dist/designer/data/skills/dashboard.d.ts +3 -0
  21. package/dist/designer/data/skills/dashboard.d.ts.map +1 -0
  22. package/dist/designer/data/skills/dashboard.js +165 -0
  23. package/dist/designer/data/skills/dashboard.ts +168 -0
  24. package/dist/designer/data/skills/design-brief.d.ts +3 -0
  25. package/dist/designer/data/skills/design-brief.d.ts.map +1 -0
  26. package/dist/designer/data/skills/design-brief.js +141 -0
  27. package/dist/designer/data/skills/design-brief.ts +144 -0
  28. package/dist/designer/data/skills/design-skill.d.ts +10 -0
  29. package/dist/designer/data/skills/design-skill.d.ts.map +1 -0
  30. package/dist/designer/data/skills/design-skill.js +1 -0
  31. package/dist/designer/data/skills/design-skill.ts +9 -0
  32. package/dist/designer/data/skills/docs-page.d.ts +3 -0
  33. package/dist/designer/data/skills/docs-page.d.ts.map +1 -0
  34. package/dist/designer/data/skills/docs-page.js +68 -0
  35. package/dist/designer/data/skills/docs-page.ts +71 -0
  36. package/dist/designer/data/skills/index.d.ts +3 -0
  37. package/dist/designer/data/skills/index.d.ts.map +1 -0
  38. package/dist/designer/data/skills/index.js +28 -0
  39. package/dist/designer/data/skills/index.ts +30 -0
  40. package/dist/designer/data/skills/magazine-poster.d.ts +3 -0
  41. package/dist/designer/data/skills/magazine-poster.d.ts.map +1 -0
  42. package/dist/designer/data/skills/magazine-poster.js +70 -0
  43. package/dist/designer/data/skills/magazine-poster.ts +73 -0
  44. package/dist/designer/data/skills/mobile-app.d.ts +3 -0
  45. package/dist/designer/data/skills/mobile-app.d.ts.map +1 -0
  46. package/dist/designer/data/skills/mobile-app.js +229 -0
  47. package/dist/designer/data/skills/mobile-app.ts +232 -0
  48. package/dist/designer/data/skills/motion-frames.d.ts +3 -0
  49. package/dist/designer/data/skills/motion-frames.d.ts.map +1 -0
  50. package/dist/designer/data/skills/motion-frames.js +121 -0
  51. package/dist/designer/data/skills/motion-frames.ts +124 -0
  52. package/dist/designer/data/skills/pricing-page.d.ts +3 -0
  53. package/dist/designer/data/skills/pricing-page.d.ts.map +1 -0
  54. package/dist/designer/data/skills/pricing-page.js +76 -0
  55. package/dist/designer/data/skills/pricing-page.ts +79 -0
  56. package/dist/designer/data/skills/saas-landing.d.ts +3 -0
  57. package/dist/designer/data/skills/saas-landing.d.ts.map +1 -0
  58. package/dist/designer/data/skills/saas-landing.js +71 -0
  59. package/dist/designer/data/skills/saas-landing.ts +74 -0
  60. package/dist/designer/data/skills/slide-deck.d.ts +3 -0
  61. package/dist/designer/data/skills/slide-deck.d.ts.map +1 -0
  62. package/dist/designer/data/skills/slide-deck.js +213 -0
  63. package/dist/designer/data/skills/slide-deck.ts +216 -0
  64. package/dist/designer/data/skills/social-carousel.d.ts +3 -0
  65. package/dist/designer/data/skills/social-carousel.d.ts.map +1 -0
  66. package/dist/designer/data/skills/social-carousel.js +135 -0
  67. package/dist/designer/data/skills/social-carousel.ts +138 -0
  68. package/dist/designer/data/skills/web-prototype.d.ts +3 -0
  69. package/dist/designer/data/skills/web-prototype.d.ts.map +1 -0
  70. package/dist/designer/data/skills/web-prototype.js +159 -0
  71. package/dist/designer/data/skills/web-prototype.ts +162 -0
  72. package/dist/designer/skills.d.ts +4 -10
  73. package/dist/designer/skills.d.ts.map +1 -1
  74. package/dist/designer/skills.js +2 -1643
  75. package/dist/extensions/wizard/index.js +9 -1
  76. package/dist/generated/parser/ast/nodes.d.ts +115 -0
  77. package/dist/generated/parser/ast/nodes.d.ts.map +1 -0
  78. package/dist/generated/parser/ast/nodes.js +8 -0
  79. package/dist/generated/parser/lexer/lexer.d.ts +28 -0
  80. package/dist/generated/parser/lexer/lexer.d.ts.map +1 -0
  81. package/dist/generated/parser/{lexer.js → lexer/lexer.js} +1 -180
  82. package/dist/generated/parser/{lexer.d.ts → lexer/tokens.d.ts} +5 -24
  83. package/dist/generated/parser/lexer/tokens.d.ts.map +1 -0
  84. package/dist/generated/parser/lexer/tokens.js +187 -0
  85. package/dist/generated/parser/mod.d.ts +5 -7
  86. package/dist/generated/parser/mod.d.ts.map +1 -1
  87. package/dist/generated/parser/mod.js +2 -1
  88. package/dist/generated/parser/parser/cursor.d.ts +47 -0
  89. package/dist/generated/parser/parser/cursor.d.ts.map +1 -0
  90. package/dist/generated/parser/parser/cursor.js +12 -0
  91. package/dist/generated/parser/parser/definitions.d.ts +16 -0
  92. package/dist/generated/parser/parser/definitions.d.ts.map +1 -0
  93. package/dist/generated/parser/parser/definitions.js +353 -0
  94. package/dist/generated/parser/parser/expressions.d.ts +17 -0
  95. package/dist/generated/parser/parser/expressions.d.ts.map +1 -0
  96. package/dist/generated/parser/parser/expressions.js +165 -0
  97. package/dist/generated/parser/parser.d.ts +18 -133
  98. package/dist/generated/parser/parser.d.ts.map +1 -1
  99. package/dist/generated/parser/parser.js +9 -505
  100. package/dist/generated/parser/tokenizer.d.ts +1 -2
  101. package/dist/generated/parser/tokenizer.d.ts.map +1 -1
  102. package/dist/generated/parser/tokenizer.js +2 -1
  103. package/dist/generated/parser/validator.d.ts +1 -1
  104. package/dist/generated/parser/validator.d.ts.map +1 -1
  105. package/dist/generated/zeus/const.d.ts.map +1 -1
  106. package/dist/generated/zeus/const.js +21 -0
  107. package/dist/generated/zeus/index.d.ts +98 -0
  108. package/dist/generated/zeus/index.d.ts.map +1 -1
  109. package/dist/mcp/glimpse-ui.js +9 -1
  110. package/dist/relay/dispatcher.d.ts +11 -104
  111. package/dist/relay/dispatcher.d.ts.map +1 -1
  112. package/dist/relay/dispatcher.js +10 -1130
  113. package/dist/relay/registration.d.ts +10 -2
  114. package/dist/relay/registration.d.ts.map +1 -1
  115. package/dist/relay/registration.js +32 -3
  116. package/dist/relay/rest.d.ts +31 -0
  117. package/dist/relay/rest.d.ts.map +1 -0
  118. package/dist/relay/rest.js +85 -0
  119. package/dist/relay/routing.d.ts +109 -0
  120. package/dist/relay/routing.d.ts.map +1 -0
  121. package/dist/relay/routing.js +669 -0
  122. package/dist/relay/wizard-login.d.ts +17 -0
  123. package/dist/relay/wizard-login.d.ts.map +1 -0
  124. package/dist/relay/wizard-login.js +419 -0
  125. package/dist/sdk/agent-core/agent-loop.d.ts +2 -9
  126. package/dist/sdk/agent-core/agent-loop.d.ts.map +1 -1
  127. package/dist/sdk/agent-core/agent-loop.js +4 -288
  128. package/dist/sdk/agent-core/tool-execution.d.ts +33 -0
  129. package/dist/sdk/agent-core/tool-execution.d.ts.map +1 -0
  130. package/dist/sdk/agent-core/tool-execution.js +300 -0
  131. package/dist/sdk/ai/env-api-keys.js +9 -1
  132. package/dist/sdk/ai/providers/openai/reasoning-replay.d.ts +48 -0
  133. package/dist/sdk/ai/providers/openai/reasoning-replay.d.ts.map +1 -0
  134. package/dist/sdk/ai/providers/openai/reasoning-replay.js +209 -0
  135. package/dist/sdk/ai/providers/openai-completions.d.ts.map +1 -1
  136. package/dist/sdk/ai/providers/openai-completions.js +1 -203
  137. package/dist/sdk/coding-agent/core/compaction/compaction.d.ts +6 -118
  138. package/dist/sdk/coding-agent/core/compaction/compaction.d.ts.map +1 -1
  139. package/dist/sdk/coding-agent/core/compaction/compaction.js +11 -733
  140. package/dist/sdk/coding-agent/core/compaction/cut-points.d.ts +58 -0
  141. package/dist/sdk/coding-agent/core/compaction/cut-points.d.ts.map +1 -0
  142. package/dist/sdk/coding-agent/core/compaction/cut-points.js +191 -0
  143. package/dist/sdk/coding-agent/core/compaction/dedupe.d.ts +28 -0
  144. package/dist/sdk/coding-agent/core/compaction/dedupe.d.ts.map +1 -0
  145. package/dist/sdk/coding-agent/core/compaction/dedupe.js +156 -0
  146. package/dist/sdk/coding-agent/core/compaction/summarize.d.ts +21 -0
  147. package/dist/sdk/coding-agent/core/compaction/summarize.d.ts.map +1 -0
  148. package/dist/sdk/coding-agent/core/compaction/summarize.js +181 -0
  149. package/dist/sdk/coding-agent/core/compaction/tokens.d.ts +47 -0
  150. package/dist/sdk/coding-agent/core/compaction/tokens.d.ts.map +1 -0
  151. package/dist/sdk/coding-agent/core/compaction/tokens.js +224 -0
  152. package/dist/sdk/coding-agent/core/extensions/session-events.d.ts +157 -0
  153. package/dist/sdk/coding-agent/core/extensions/session-events.d.ts.map +1 -0
  154. package/dist/sdk/coding-agent/core/extensions/session-events.js +13 -0
  155. package/dist/sdk/coding-agent/core/extensions/types.d.ts +6 -365
  156. package/dist/sdk/coding-agent/core/extensions/types.d.ts.map +1 -1
  157. package/dist/sdk/coding-agent/core/extensions/types.js +2 -10
  158. package/dist/sdk/coding-agent/core/extensions/ui-types.d.ts +219 -0
  159. package/dist/sdk/coding-agent/core/extensions/ui-types.d.ts.map +1 -0
  160. package/dist/sdk/coding-agent/core/extensions/ui-types.js +4 -0
  161. package/dist/sdk/coding-agent/core/package/default-package-manager.d.ts +115 -0
  162. package/dist/sdk/coding-agent/core/package/default-package-manager.d.ts.map +1 -0
  163. package/dist/sdk/coding-agent/core/package/default-package-manager.js +1184 -0
  164. package/dist/sdk/coding-agent/core/package/discovery.d.ts +20 -0
  165. package/dist/sdk/coding-agent/core/package/discovery.d.ts.map +1 -0
  166. package/dist/sdk/coding-agent/core/package/discovery.js +283 -0
  167. package/dist/sdk/coding-agent/core/package/ignore.d.ts +29 -0
  168. package/dist/sdk/coding-agent/core/package/ignore.d.ts.map +1 -0
  169. package/dist/sdk/coding-agent/core/package/ignore.js +235 -0
  170. package/dist/sdk/coding-agent/core/package/types.d.ts +128 -0
  171. package/dist/sdk/coding-agent/core/package/types.d.ts.map +1 -0
  172. package/dist/sdk/coding-agent/core/package/types.js +18 -0
  173. package/dist/sdk/coding-agent/core/package/update-check.d.ts +43 -0
  174. package/dist/sdk/coding-agent/core/package/update-check.d.ts.map +1 -0
  175. package/dist/sdk/coding-agent/core/package/update-check.js +253 -0
  176. package/dist/sdk/coding-agent/core/package-manager.d.ts +2 -188
  177. package/dist/sdk/coding-agent/core/package-manager.d.ts.map +1 -1
  178. package/dist/sdk/coding-agent/core/package-manager.js +1 -1940
  179. package/dist/sdk/coding-agent/core/session/migrate.d.ts +18 -0
  180. package/dist/sdk/coding-agent/core/session/migrate.d.ts.map +1 -0
  181. package/dist/sdk/coding-agent/core/session/migrate.js +78 -0
  182. package/dist/sdk/coding-agent/core/session/types.d.ts +165 -0
  183. package/dist/sdk/coding-agent/core/session/types.d.ts.map +1 -0
  184. package/dist/sdk/coding-agent/core/session/types.js +4 -0
  185. package/dist/sdk/coding-agent/core/session-manager.d.ts +3 -161
  186. package/dist/sdk/coding-agent/core/session-manager.d.ts.map +1 -1
  187. package/dist/sdk/coding-agent/core/session-manager.js +4 -72
  188. package/package.json +2 -2
  189. package/dist/generated/parser/lexer.d.ts.map +0 -1
@@ -3,12 +3,14 @@
3
3
  *
4
4
  * Contract (Batch 1 backend):
5
5
  * POST <backend>/api/machines/register
6
- * Headers: Authorization: Bearer <teamApiKey|userJwt>
6
+ * Headers: Authorization: Bearer <userJwt|userApiKey|teamApiKey>
7
7
  * Body: { name?: string, hostname: string, version: string, pid: number }
8
- * 200: { machineId: string, jwt: string, name: string, ownerId?: string }
8
+ * 200: { machineId: string, jwt: string, name: string, teamId?, ownerId?, visibility? }
9
9
  *
10
10
  * Auth priority:
11
11
  * - If `userJwt` is set → used as Bearer token (machine gets ownerId)
12
+ * - Else if `userApiKey` is set → used as Bearer token (machine is PRIVATE
13
+ * with ownerId derived from the key by the backend)
12
14
  * - Else → `apiKey` (team API key, machine gets no owner)
13
15
  *
14
16
  * Why this lives in its own module:
@@ -30,6 +32,12 @@ export interface RegistrationDeps {
30
32
  backendUrl: string;
31
33
  /** Team API key — used when `userJwt` is not set. */
32
34
  apiKey: string;
35
+ /**
36
+ * Optional user API key ("sk-aexol-user-"), pinned to a team but owned by a
37
+ * single user. Used when `userJwt` is not set; takes priority over `apiKey`.
38
+ * The backend registers the machine as PRIVATE with ownerId from the key.
39
+ */
40
+ userApiKey?: string;
33
41
  /** Optional user JWT from OAuth login. When set, used instead of `apiKey` for registration. */
34
42
  userJwt?: string;
35
43
  /** Display name override (from `--machine-name`). Falls back to hostname. */
@@ -1 +1 @@
1
- {"version":3,"file":"registration.d.ts","sourceRoot":"","sources":["../../src/relay/registration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAIH,OAAO,EAA4B,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAElF,MAAM,WAAW,gBAAgB;IAC/B,wEAAwE;IACxE,UAAU,EAAE,MAAM,CAAC;IACnB,qDAAqD;IACrD,MAAM,EAAE,MAAM,CAAC;IACf,+FAA+F;IAC/F,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6EAA6E;IAC7E,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,uDAAuD;IACvD,OAAO,EAAE,MAAM,CAAC;IAChB,oEAAoE;IACpE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,8BAA8B;IAC9B,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC/B,0EAA0E;IAC1E,MAAM,EAAE,OAAO,CAAC;IAChB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,aAAa,CAAC;CACvB;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAWvD;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAUvD;AAED,mFAAmF;AACnF,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,SAAK,GAAG,OAAO,CAI/D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC,gBAAgB,CAAC,CA+G3B"}
1
+ {"version":3,"file":"registration.d.ts","sourceRoot":"","sources":["../../src/relay/registration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAIH,OAAO,EAA4B,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAElF,MAAM,WAAW,gBAAgB;IAC/B,wEAAwE;IACxE,UAAU,EAAE,MAAM,CAAC;IACnB,qDAAqD;IACrD,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+FAA+F;IAC/F,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6EAA6E;IAC7E,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,uDAAuD;IACvD,OAAO,EAAE,MAAM,CAAC;IAChB,oEAAoE;IACpE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,8BAA8B;IAC9B,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC/B,0EAA0E;IAC1E,MAAM,EAAE,OAAO,CAAC;IAChB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,aAAa,CAAC;CACvB;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAWvD;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAUvD;AAED,mFAAmF;AACnF,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,SAAK,GAAG,OAAO,CAI/D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC,gBAAgB,CAAC,CA2I3B"}
@@ -3,12 +3,14 @@
3
3
  *
4
4
  * Contract (Batch 1 backend):
5
5
  * POST <backend>/api/machines/register
6
- * Headers: Authorization: Bearer <teamApiKey|userJwt>
6
+ * Headers: Authorization: Bearer <userJwt|userApiKey|teamApiKey>
7
7
  * Body: { name?: string, hostname: string, version: string, pid: number }
8
- * 200: { machineId: string, jwt: string, name: string, ownerId?: string }
8
+ * 200: { machineId: string, jwt: string, name: string, teamId?, ownerId?, visibility? }
9
9
  *
10
10
  * Auth priority:
11
11
  * - If `userJwt` is set → used as Bearer token (machine gets ownerId)
12
+ * - Else if `userApiKey` is set → used as Bearer token (machine is PRIVATE
13
+ * with ownerId derived from the key by the backend)
12
14
  * - Else → `apiKey` (team API key, machine gets no owner)
13
15
  *
14
16
  * Why this lives in its own module:
@@ -92,6 +94,7 @@ export async function ensureMachineRegistered(deps) {
92
94
  const skew = deps.expirySkewSec ?? 60;
93
95
  const existing = await loadMachine();
94
96
  const forceRefresh = deps.forceRefresh === true;
97
+ const usingUserApiKey = !deps.userJwt && !!deps.userApiKey;
95
98
  if (!forceRefresh && existing && !isJwtExpired(existing.machineJwt, skew)) {
96
99
  // The cached machine JWT is still valid. If the caller authenticated with a
97
100
  // user JWT (OAuth login) and the cached machine was previously owned by a
@@ -106,6 +109,21 @@ export async function ensureMachineRegistered(deps) {
106
109
  shouldReuse = false;
107
110
  }
108
111
  }
112
+ else if (usingUserApiKey) {
113
+ // A user API key carries no `sub` claim, so identity is tracked by the
114
+ // key itself: `ensureMachineRegistered` records the key used for the
115
+ // last registration in `machine.json` extra. A different key means a
116
+ // different user (the backend derives `ownerId` from the key), so the
117
+ // machine must be re-registered to transfer ownership and flip
118
+ // visibility to PRIVATE. A missing marker means the cached machine was
119
+ // registered with a team key — re-register so the backend can make it
120
+ // PRIVATE for this user.
121
+ const previousKey = existing.extra?.userApiKey;
122
+ if (previousKey !== deps.userApiKey) {
123
+ console.log("Machine registered under a different user API key, re-registering to transfer ownership...");
124
+ shouldReuse = false;
125
+ }
126
+ }
109
127
  if (shouldReuse) {
110
128
  return { reused: true, record: existing };
111
129
  }
@@ -114,6 +132,8 @@ export async function ensureMachineRegistered(deps) {
114
132
  console.warn("Re-registering machine: cached JWT was rejected by the backend (forceRefresh).");
115
133
  }
116
134
  const fetchImpl = deps.fetchImpl ?? fetch;
135
+ // Token priority: userJwt (OAuth) > userApiKey (user-pinned key) > team key.
136
+ const authToken = deps.userJwt ?? deps.userApiKey ?? deps.apiKey;
117
137
  const url = `${deps.backendUrl.replace(/\/$/, "")}/api/machines/register`;
118
138
  const body = {
119
139
  // Reuse the cached display name on JWT-expiry refresh so the backend's
@@ -129,7 +149,7 @@ export async function ensureMachineRegistered(deps) {
129
149
  res = await fetchImpl(url, {
130
150
  method: "POST",
131
151
  headers: {
132
- Authorization: `Bearer ${deps.userJwt ?? deps.apiKey}`,
152
+ Authorization: `Bearer ${authToken}`,
133
153
  "Content-Type": "application/json",
134
154
  },
135
155
  body: JSON.stringify(body),
@@ -163,6 +183,14 @@ export async function ensureMachineRegistered(deps) {
163
183
  typeof obj.name !== "string") {
164
184
  throw new Error(`Machine registration: backend response missing required fields (machineId, jwt, name)`);
165
185
  }
186
+ const extra = { ...(existing?.extra ?? {}) };
187
+ delete extra.userApiKey;
188
+ if (usingUserApiKey) {
189
+ // Record which key was used so a later run with a different user key
190
+ // (a different account) forces re-registration. machine.json is 0600,
191
+ // same as config.json where the key already lives.
192
+ extra.userApiKey = deps.userApiKey;
193
+ }
166
194
  const record = {
167
195
  machineId: obj.machineId,
168
196
  machineName: obj.name,
@@ -173,6 +201,7 @@ export async function ensureMachineRegistered(deps) {
173
201
  registeredAt: Date.now(),
174
202
  hostname: hostname(),
175
203
  version: deps.version,
204
+ extra: Object.keys(extra).length > 0 ? extra : undefined,
176
205
  };
177
206
  await saveMachine(record);
178
207
  return { reused: false, forced: forceRefresh, record };
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Relay REST request handler (CLI side).
3
+ *
4
+ * Translates an inbound `RestRequestFrame` into a `dispatchRoute` call and
5
+ * maps handler errors to status codes (400/404/500). Errors thrown by
6
+ * handlers are returned in the body, never re-thrown — the backend has a
7
+ * 30 s pending-request timeout and we want every request to terminate with
8
+ * a wire response so the browser doesn't sit on a stale spinner.
9
+ *
10
+ * Extracted from `relay/dispatcher.ts` — the dispatcher re-exports
11
+ * `handleRestRequest` so existing imports keep working.
12
+ */
13
+ import type { RestRequestFrame, RestResponseFrame } from "@aexol/relay-protocol";
14
+ import { type RestRequestDeps } from "./routing.js";
15
+ /**
16
+ * Dispatch a `rest_request` frame. Always resolves with a
17
+ * `RestResponseFrame` — handler exceptions are caught and translated.
18
+ *
19
+ * Status mapping:
20
+ * - 200: handler returned successfully (body is the handler's return value
21
+ * or `{ ok: true }` for void returns)
22
+ * - 400: `BadRequestError` thrown by handler, OR malformed body for a
23
+ * route that requires one, OR unknown route
24
+ * - 404: `NotFoundError` thrown by handler
25
+ * - 405: route matches but method doesn't (returned as 404 since we don't
26
+ * distinguish — the matcher treats it as "no route", consistent
27
+ * with the original Hono router which also 404'd)
28
+ * - 500: anything else; `error` field carries a sanitized message
29
+ */
30
+ export declare function handleRestRequest(frame: RestRequestFrame, deps: RestRequestDeps): Promise<RestResponseFrame>;
31
+ //# sourceMappingURL=rest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rest.d.ts","sourceRoot":"","sources":["../../src/relay/rest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EACV,gBAAgB,EAChB,iBAAiB,EAClB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EAA6B,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/E;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,gBAAgB,EACvB,IAAI,EAAE,eAAe,GACpB,OAAO,CAAC,iBAAiB,CAAC,CAoE5B"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Relay REST request handler (CLI side).
3
+ *
4
+ * Translates an inbound `RestRequestFrame` into a `dispatchRoute` call and
5
+ * maps handler errors to status codes (400/404/500). Errors thrown by
6
+ * handlers are returned in the body, never re-thrown — the backend has a
7
+ * 30 s pending-request timeout and we want every request to terminate with
8
+ * a wire response so the browser doesn't sit on a stale spinner.
9
+ *
10
+ * Extracted from `relay/dispatcher.ts` — the dispatcher re-exports
11
+ * `handleRestRequest` so existing imports keep working.
12
+ */
13
+ import { BadRequestError, NotFoundError } from "../server/handlers/errors.js";
14
+ import { dispatchRoute, matchRoute } from "./routing.js";
15
+ /**
16
+ * Dispatch a `rest_request` frame. Always resolves with a
17
+ * `RestResponseFrame` — handler exceptions are caught and translated.
18
+ *
19
+ * Status mapping:
20
+ * - 200: handler returned successfully (body is the handler's return value
21
+ * or `{ ok: true }` for void returns)
22
+ * - 400: `BadRequestError` thrown by handler, OR malformed body for a
23
+ * route that requires one, OR unknown route
24
+ * - 404: `NotFoundError` thrown by handler
25
+ * - 405: route matches but method doesn't (returned as 404 since we don't
26
+ * distinguish — the matcher treats it as "no route", consistent
27
+ * with the original Hono router which also 404'd)
28
+ * - 500: anything else; `error` field carries a sanitized message
29
+ */
30
+ export async function handleRestRequest(frame, deps) {
31
+ const { reqId, method, path, body } = frame;
32
+ const logger = deps.logger ?? console;
33
+ const startedAt = performance.now();
34
+ logger.info?.(`[rest] start machineId=${frame.machineId} method=${method} path=${path} reqId=${reqId}`);
35
+ const match = matchRoute(method, path);
36
+ if (!match) {
37
+ logger.info?.(`[rest] finish machineId=${frame.machineId} method=${method} path=${path} reqId=${reqId} status=404 durationMs=${Math.round(performance.now() - startedAt)}`);
38
+ return {
39
+ kind: "rest_response",
40
+ reqId,
41
+ status: 404,
42
+ body: { error: `Unknown route: ${method} ${path}` },
43
+ };
44
+ }
45
+ try {
46
+ const result = await dispatchRoute(match, body, deps);
47
+ logger.info?.(`[rest] finish machineId=${frame.machineId} method=${method} path=${path} reqId=${reqId} status=200 durationMs=${Math.round(performance.now() - startedAt)}`);
48
+ return {
49
+ kind: "rest_response",
50
+ reqId,
51
+ status: 200,
52
+ body: result,
53
+ };
54
+ }
55
+ catch (err) {
56
+ if (err instanceof BadRequestError) {
57
+ logger.info?.(`[rest] finish machineId=${frame.machineId} method=${method} path=${path} reqId=${reqId} status=400 durationMs=${Math.round(performance.now() - startedAt)}`);
58
+ return {
59
+ kind: "rest_response",
60
+ reqId,
61
+ status: 400,
62
+ body: { error: err.message, code: err.code },
63
+ };
64
+ }
65
+ if (err instanceof NotFoundError) {
66
+ logger.info?.(`[rest] finish machineId=${frame.machineId} method=${method} path=${path} reqId=${reqId} status=404 durationMs=${Math.round(performance.now() - startedAt)}`);
67
+ return {
68
+ kind: "rest_response",
69
+ reqId,
70
+ status: 404,
71
+ body: { error: err.message, code: err.code },
72
+ };
73
+ }
74
+ // Unexpected — log locally so operators can correlate, but only
75
+ // return a sanitized message to the caller.
76
+ logger.error?.(`[dispatcher] unexpected error in ${method} ${path}:`, err);
77
+ logger.info?.(`[rest] finish machineId=${frame.machineId} method=${method} path=${path} reqId=${reqId} status=500 durationMs=${Math.round(performance.now() - startedAt)}`);
78
+ return {
79
+ kind: "rest_response",
80
+ reqId,
81
+ status: 500,
82
+ body: { error: "Internal error" },
83
+ };
84
+ }
85
+ }
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Relay route table (CLI side).
3
+ *
4
+ * `matchRoute` is the pure path matcher translating `method + path` into a
5
+ * `RouteMatch`; `dispatchRoute` is the inner switch that calls the pure REST
6
+ * handlers (`handlers/projects.ts`, `handlers/sessions.ts`, ...). Extracted
7
+ * from `relay/dispatcher.ts` — `handleRestRequest` (`relay/rest.ts`) owns the
8
+ * single try/catch boundary around `dispatchRoute`, and the dispatcher
9
+ * re-exports `matchRoute` + the request-deps types so existing imports keep
10
+ * working.
11
+ */
12
+ import type { MetaEvent } from "@aexol/relay-protocol";
13
+ import type { McpExtensionState } from "../mcp/state.js";
14
+ import type { SessionStreamManager } from "../server/session-stream.js";
15
+ import type { DevProcessRegistry } from "../server/dev-process-registry.js";
16
+ import type { SessionStore } from "../server/storage.js";
17
+ import { type WizardLoginResult } from "./wizard-login.js";
18
+ /**
19
+ * Result of matching a request path against a route table entry.
20
+ * `id` is populated when the route had an `:id` placeholder.
21
+ */
22
+ interface RouteMatch {
23
+ route: "list_projects" | "create_project" | "update_project" | "delete_project" | "bind_studio" | "unbind_studio" | "get_project_observations" | "list_project_sessions" | "cleanup_project_sessions" | "cleanup_all_sessions" | "create_session" | "get_session" | "get_session_active_agent" | "update_session_active_agent" | "get_session_messages" | "update_session" | "delete_session" | "get_session_memory" | "get_session_memory_details" | "compact_session" | "remember_and_delete_session" | "fork_session" | "list_path_autocomplete" | "list_directory" | "get_home_directory" | "search_project_files" | "enqueue_prompt" | "get_prompt_queue" | "remove_prompt" | "clear_prompt_queue" | "list_mcp_status" | "reload_mcp_config" | "refresh_native_extensions" | "refresh_cloud_agents" | "get_settings" | "put_settings" | "get_agent_settings" | "put_agent_settings" | "get_prompt_mutation_settings" | "put_prompt_mutation_settings" | "wizard_login" | "list_agents" | "list_dev_processes" | "start_dev_process" | "kill_dev_process" | "restart_dev_process" | "scan_dev_servers" | "kill_dev_server";
24
+ id?: string;
25
+ /** Parsed query params, if the path carried a `?...` suffix. */
26
+ query?: URLSearchParams;
27
+ /** For `remove_prompt`: the queue item id extracted from the path. */
28
+ itemId?: string;
29
+ /** For `kill_dev_server`: optional target port from the query string. */
30
+ port?: number;
31
+ }
32
+ /**
33
+ * Inline path matcher. Returns `null` for any path/method combination we
34
+ * don't recognise; the caller turns that into a `404 Unknown route`.
35
+ *
36
+ * Intentionally literal — a regex table would be marginally fancier but
37
+ * also marginally slower and harder to read for ~11 routes.
38
+ */
39
+ export declare function matchRoute(method: string, path: string): RouteMatch | null;
40
+ /**
41
+ * Best-effort publisher for project/session lifecycle meta events. Bound
42
+ * by `serve.ts` to `relay.send({kind:"meta_event", machineId, event})`;
43
+ * tests pass a recording stub. Always called inside a try/catch by the
44
+ * dispatcher so a publish failure never breaks the REST response — the
45
+ * mutation already succeeded.
46
+ *
47
+ * Omit (`undefined`) to disable publishing entirely (used in the handler
48
+ * unit tests where there's no relay).
49
+ */
50
+ export type PublishMetaEvent = (event: MetaEvent) => void;
51
+ export interface RestRequestDeps {
52
+ store: SessionStore;
53
+ manager: SessionStreamManager;
54
+ /** Working directory for this machine. Used by settings persistence. Defaults to homedir. */
55
+ cwd?: string;
56
+ /** Agent config dir (`~/.spectral/agent` by default). Passed through to agent-settings persistence. */
57
+ agentDir?: string;
58
+ /**
59
+ * Optional logger for unexpected errors and per-request timing.
60
+ * Defaults to `console`; optional methods are guarded so a minimal
61
+ * `{ error }` logger (used by tests) remains valid.
62
+ */
63
+ logger?: {
64
+ error?: (...args: unknown[]) => void;
65
+ info?: (...args: unknown[]) => void;
66
+ };
67
+ /**
68
+ * Optional MCP extension state. Passed through by `serve.ts` from the
69
+ * `AgentBridge`. When set, `GET /api/mcp-status` returns the current
70
+ * machine-local MCP snapshot.
71
+ */
72
+ mcpState?: McpExtensionState;
73
+ /** Machine-level dev process registry. Used by `/api/dev-processes`. */
74
+ devProcessRegistry?: DevProcessRegistry;
75
+ /**
76
+ * Optional meta-event publisher. Invoked AFTER each successful mutation
77
+ * (create/update/delete project, create/update/delete session) so all
78
+ * tabs of all browsers subscribed to this machine refetch. Read paths
79
+ * (`list_*`, `get_*`) never publish.
80
+ *
81
+ * Failure handling: any throw is caught + logged; we never propagate
82
+ * publish failures into the REST response (the SQLite mutation has
83
+ * already committed and the caller deserves the success it asked for).
84
+ */
85
+ publishMetaEvent?: PublishMetaEvent;
86
+ refreshNativeExtensions?: () => Promise<void>;
87
+ /**
88
+ * Refresh the cloud-agent cache. Returns a result describing whether the
89
+ * backend rejected auth; callers that just need a cache warm may ignore it
90
+ * (the bridge handles auth-rejection separately via onCloudAgentsAuthRejected).
91
+ */
92
+ refreshCloudAgents?: (teamId: string) => Promise<unknown>;
93
+ startWizardLogin?: () => Promise<WizardLoginResult>;
94
+ }
95
+ /**
96
+ * Inner switch: route → handler call. Separated so `handleRestRequest`
97
+ * owns ONE try/catch boundary for all error mapping.
98
+ *
99
+ * Meta publish is fired AFTER the handler returns successfully (i.e.
100
+ * after SQLite has committed) so subscribers never see a "create" event
101
+ * for a row the next read won't find. Publish failures are swallowed
102
+ * inside `safePublish` — the wire response goes back regardless.
103
+ *
104
+ * The body is `unknown` from the wire; each handler validates its own
105
+ * shape via the `BadRequestError`-throwing checks they already do.
106
+ */
107
+ export declare function dispatchRoute(match: RouteMatch, body: unknown, deps: RestRequestDeps): Promise<unknown>;
108
+ export {};
109
+ //# sourceMappingURL=routing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../../src/relay/routing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAKvD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAkDzD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACxE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AAC5E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AASzD,OAAO,EAAoB,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAM7E;;;GAGG;AACH,UAAU,UAAU;IAClB,KAAK,EACD,eAAe,GACf,gBAAgB,GAChB,gBAAgB,GAChB,gBAAgB,GAChB,aAAa,GACb,eAAe,GACf,0BAA0B,GAC1B,uBAAuB,GACvB,0BAA0B,GAC1B,sBAAsB,GACtB,gBAAgB,GAChB,aAAa,GACb,0BAA0B,GAC1B,6BAA6B,GAC7B,sBAAsB,GACtB,gBAAgB,GAChB,gBAAgB,GAChB,oBAAoB,GACpB,4BAA4B,GAC5B,iBAAiB,GACjB,6BAA6B,GAC7B,cAAc,GACd,wBAAwB,GACxB,gBAAgB,GAChB,oBAAoB,GACpB,sBAAsB,GACtB,gBAAgB,GAChB,kBAAkB,GAClB,eAAe,GACf,oBAAoB,GACpB,iBAAiB,GACjB,mBAAmB,GACnB,2BAA2B,GAC3B,sBAAsB,GACtB,cAAc,GACd,cAAc,GACd,oBAAoB,GACpB,oBAAoB,GACpB,8BAA8B,GAC9B,8BAA8B,GAC9B,cAAc,GACd,aAAa,GACb,oBAAoB,GACpB,mBAAmB,GACnB,kBAAkB,GAClB,qBAAqB,GACrB,kBAAkB,GAClB,iBAAiB,CAAC;IACtB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,gEAAgE;IAChE,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,sEAAsE;IACtE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,GACX,UAAU,GAAG,IAAI,CAuPnB;AAMD;;;;;;;;;GASG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;AAE1D,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,YAAY,CAAC;IACpB,OAAO,EAAE,oBAAoB,CAAC;IAC9B,6FAA6F;IAC7F,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,uGAAuG;IACvG,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,MAAM,CAAC,EAAE;QACP,KAAK,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;QACrC,IAAI,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;KACrC,CAAC;IACF;;;;OAIG;IACH,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,wEAAwE;IACxE,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IACxC;;;;;;;;;OASG;IACH,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,uBAAuB,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1D,gBAAgB,CAAC,EAAE,MAAM,OAAO,CAAC,iBAAiB,CAAC,CAAC;CACrD;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,UAAU,EACjB,IAAI,EAAE,OAAO,EACb,IAAI,EAAE,eAAe,GACpB,OAAO,CAAC,OAAO,CAAC,CA6YlB"}