@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.
- package/CHANGELOG.md +2 -0
- package/dist/auth-helper.d.ts.map +1 -1
- package/dist/auth-helper.js +14 -8
- package/dist/commands/login.d.ts +12 -4
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +36 -15
- package/dist/commands/serve.d.ts.map +1 -1
- package/dist/commands/serve.js +3 -0
- package/dist/config.d.ts +15 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +10 -1
- package/dist/designer/data/skills/blog-post.d.ts +3 -0
- package/dist/designer/data/skills/blog-post.d.ts.map +1 -0
- package/dist/designer/data/skills/blog-post.js +84 -0
- package/dist/designer/data/skills/blog-post.ts +87 -0
- package/dist/designer/data/skills/critique.d.ts +3 -0
- package/dist/designer/data/skills/critique.d.ts.map +1 -0
- package/dist/designer/data/skills/critique.js +109 -0
- package/dist/designer/data/skills/critique.ts +112 -0
- package/dist/designer/data/skills/dashboard.d.ts +3 -0
- package/dist/designer/data/skills/dashboard.d.ts.map +1 -0
- package/dist/designer/data/skills/dashboard.js +165 -0
- package/dist/designer/data/skills/dashboard.ts +168 -0
- package/dist/designer/data/skills/design-brief.d.ts +3 -0
- package/dist/designer/data/skills/design-brief.d.ts.map +1 -0
- package/dist/designer/data/skills/design-brief.js +141 -0
- package/dist/designer/data/skills/design-brief.ts +144 -0
- package/dist/designer/data/skills/design-skill.d.ts +10 -0
- package/dist/designer/data/skills/design-skill.d.ts.map +1 -0
- package/dist/designer/data/skills/design-skill.js +1 -0
- package/dist/designer/data/skills/design-skill.ts +9 -0
- package/dist/designer/data/skills/docs-page.d.ts +3 -0
- package/dist/designer/data/skills/docs-page.d.ts.map +1 -0
- package/dist/designer/data/skills/docs-page.js +68 -0
- package/dist/designer/data/skills/docs-page.ts +71 -0
- package/dist/designer/data/skills/index.d.ts +3 -0
- package/dist/designer/data/skills/index.d.ts.map +1 -0
- package/dist/designer/data/skills/index.js +28 -0
- package/dist/designer/data/skills/index.ts +30 -0
- package/dist/designer/data/skills/magazine-poster.d.ts +3 -0
- package/dist/designer/data/skills/magazine-poster.d.ts.map +1 -0
- package/dist/designer/data/skills/magazine-poster.js +70 -0
- package/dist/designer/data/skills/magazine-poster.ts +73 -0
- package/dist/designer/data/skills/mobile-app.d.ts +3 -0
- package/dist/designer/data/skills/mobile-app.d.ts.map +1 -0
- package/dist/designer/data/skills/mobile-app.js +229 -0
- package/dist/designer/data/skills/mobile-app.ts +232 -0
- package/dist/designer/data/skills/motion-frames.d.ts +3 -0
- package/dist/designer/data/skills/motion-frames.d.ts.map +1 -0
- package/dist/designer/data/skills/motion-frames.js +121 -0
- package/dist/designer/data/skills/motion-frames.ts +124 -0
- package/dist/designer/data/skills/pricing-page.d.ts +3 -0
- package/dist/designer/data/skills/pricing-page.d.ts.map +1 -0
- package/dist/designer/data/skills/pricing-page.js +76 -0
- package/dist/designer/data/skills/pricing-page.ts +79 -0
- package/dist/designer/data/skills/saas-landing.d.ts +3 -0
- package/dist/designer/data/skills/saas-landing.d.ts.map +1 -0
- package/dist/designer/data/skills/saas-landing.js +71 -0
- package/dist/designer/data/skills/saas-landing.ts +74 -0
- package/dist/designer/data/skills/slide-deck.d.ts +3 -0
- package/dist/designer/data/skills/slide-deck.d.ts.map +1 -0
- package/dist/designer/data/skills/slide-deck.js +213 -0
- package/dist/designer/data/skills/slide-deck.ts +216 -0
- package/dist/designer/data/skills/social-carousel.d.ts +3 -0
- package/dist/designer/data/skills/social-carousel.d.ts.map +1 -0
- package/dist/designer/data/skills/social-carousel.js +135 -0
- package/dist/designer/data/skills/social-carousel.ts +138 -0
- package/dist/designer/data/skills/web-prototype.d.ts +3 -0
- package/dist/designer/data/skills/web-prototype.d.ts.map +1 -0
- package/dist/designer/data/skills/web-prototype.js +159 -0
- package/dist/designer/data/skills/web-prototype.ts +162 -0
- package/dist/designer/skills.d.ts +4 -10
- package/dist/designer/skills.d.ts.map +1 -1
- package/dist/designer/skills.js +2 -1643
- package/dist/extensions/wizard/index.js +9 -1
- package/dist/generated/parser/ast/nodes.d.ts +115 -0
- package/dist/generated/parser/ast/nodes.d.ts.map +1 -0
- package/dist/generated/parser/ast/nodes.js +8 -0
- package/dist/generated/parser/lexer/lexer.d.ts +28 -0
- package/dist/generated/parser/lexer/lexer.d.ts.map +1 -0
- package/dist/generated/parser/{lexer.js → lexer/lexer.js} +1 -180
- package/dist/generated/parser/{lexer.d.ts → lexer/tokens.d.ts} +5 -24
- package/dist/generated/parser/lexer/tokens.d.ts.map +1 -0
- package/dist/generated/parser/lexer/tokens.js +187 -0
- package/dist/generated/parser/mod.d.ts +5 -7
- package/dist/generated/parser/mod.d.ts.map +1 -1
- package/dist/generated/parser/mod.js +2 -1
- package/dist/generated/parser/parser/cursor.d.ts +47 -0
- package/dist/generated/parser/parser/cursor.d.ts.map +1 -0
- package/dist/generated/parser/parser/cursor.js +12 -0
- package/dist/generated/parser/parser/definitions.d.ts +16 -0
- package/dist/generated/parser/parser/definitions.d.ts.map +1 -0
- package/dist/generated/parser/parser/definitions.js +353 -0
- package/dist/generated/parser/parser/expressions.d.ts +17 -0
- package/dist/generated/parser/parser/expressions.d.ts.map +1 -0
- package/dist/generated/parser/parser/expressions.js +165 -0
- package/dist/generated/parser/parser.d.ts +18 -133
- package/dist/generated/parser/parser.d.ts.map +1 -1
- package/dist/generated/parser/parser.js +9 -505
- package/dist/generated/parser/tokenizer.d.ts +1 -2
- package/dist/generated/parser/tokenizer.d.ts.map +1 -1
- package/dist/generated/parser/tokenizer.js +2 -1
- package/dist/generated/parser/validator.d.ts +1 -1
- package/dist/generated/parser/validator.d.ts.map +1 -1
- package/dist/generated/zeus/const.d.ts.map +1 -1
- package/dist/generated/zeus/const.js +21 -0
- package/dist/generated/zeus/index.d.ts +98 -0
- package/dist/generated/zeus/index.d.ts.map +1 -1
- package/dist/mcp/glimpse-ui.js +9 -1
- package/dist/relay/dispatcher.d.ts +11 -104
- package/dist/relay/dispatcher.d.ts.map +1 -1
- package/dist/relay/dispatcher.js +10 -1130
- package/dist/relay/registration.d.ts +10 -2
- package/dist/relay/registration.d.ts.map +1 -1
- package/dist/relay/registration.js +32 -3
- package/dist/relay/rest.d.ts +31 -0
- package/dist/relay/rest.d.ts.map +1 -0
- package/dist/relay/rest.js +85 -0
- package/dist/relay/routing.d.ts +109 -0
- package/dist/relay/routing.d.ts.map +1 -0
- package/dist/relay/routing.js +669 -0
- package/dist/relay/wizard-login.d.ts +17 -0
- package/dist/relay/wizard-login.d.ts.map +1 -0
- package/dist/relay/wizard-login.js +419 -0
- package/dist/sdk/agent-core/agent-loop.d.ts +2 -9
- package/dist/sdk/agent-core/agent-loop.d.ts.map +1 -1
- package/dist/sdk/agent-core/agent-loop.js +4 -288
- package/dist/sdk/agent-core/tool-execution.d.ts +33 -0
- package/dist/sdk/agent-core/tool-execution.d.ts.map +1 -0
- package/dist/sdk/agent-core/tool-execution.js +300 -0
- package/dist/sdk/ai/env-api-keys.js +9 -1
- package/dist/sdk/ai/providers/openai/reasoning-replay.d.ts +48 -0
- package/dist/sdk/ai/providers/openai/reasoning-replay.d.ts.map +1 -0
- package/dist/sdk/ai/providers/openai/reasoning-replay.js +209 -0
- package/dist/sdk/ai/providers/openai-completions.d.ts.map +1 -1
- package/dist/sdk/ai/providers/openai-completions.js +1 -203
- package/dist/sdk/coding-agent/core/compaction/compaction.d.ts +6 -118
- package/dist/sdk/coding-agent/core/compaction/compaction.d.ts.map +1 -1
- package/dist/sdk/coding-agent/core/compaction/compaction.js +11 -733
- package/dist/sdk/coding-agent/core/compaction/cut-points.d.ts +58 -0
- package/dist/sdk/coding-agent/core/compaction/cut-points.d.ts.map +1 -0
- package/dist/sdk/coding-agent/core/compaction/cut-points.js +191 -0
- package/dist/sdk/coding-agent/core/compaction/dedupe.d.ts +28 -0
- package/dist/sdk/coding-agent/core/compaction/dedupe.d.ts.map +1 -0
- package/dist/sdk/coding-agent/core/compaction/dedupe.js +156 -0
- package/dist/sdk/coding-agent/core/compaction/summarize.d.ts +21 -0
- package/dist/sdk/coding-agent/core/compaction/summarize.d.ts.map +1 -0
- package/dist/sdk/coding-agent/core/compaction/summarize.js +181 -0
- package/dist/sdk/coding-agent/core/compaction/tokens.d.ts +47 -0
- package/dist/sdk/coding-agent/core/compaction/tokens.d.ts.map +1 -0
- package/dist/sdk/coding-agent/core/compaction/tokens.js +224 -0
- package/dist/sdk/coding-agent/core/extensions/session-events.d.ts +157 -0
- package/dist/sdk/coding-agent/core/extensions/session-events.d.ts.map +1 -0
- package/dist/sdk/coding-agent/core/extensions/session-events.js +13 -0
- package/dist/sdk/coding-agent/core/extensions/types.d.ts +6 -365
- package/dist/sdk/coding-agent/core/extensions/types.d.ts.map +1 -1
- package/dist/sdk/coding-agent/core/extensions/types.js +2 -10
- package/dist/sdk/coding-agent/core/extensions/ui-types.d.ts +219 -0
- package/dist/sdk/coding-agent/core/extensions/ui-types.d.ts.map +1 -0
- package/dist/sdk/coding-agent/core/extensions/ui-types.js +4 -0
- package/dist/sdk/coding-agent/core/package/default-package-manager.d.ts +115 -0
- package/dist/sdk/coding-agent/core/package/default-package-manager.d.ts.map +1 -0
- package/dist/sdk/coding-agent/core/package/default-package-manager.js +1184 -0
- package/dist/sdk/coding-agent/core/package/discovery.d.ts +20 -0
- package/dist/sdk/coding-agent/core/package/discovery.d.ts.map +1 -0
- package/dist/sdk/coding-agent/core/package/discovery.js +283 -0
- package/dist/sdk/coding-agent/core/package/ignore.d.ts +29 -0
- package/dist/sdk/coding-agent/core/package/ignore.d.ts.map +1 -0
- package/dist/sdk/coding-agent/core/package/ignore.js +235 -0
- package/dist/sdk/coding-agent/core/package/types.d.ts +128 -0
- package/dist/sdk/coding-agent/core/package/types.d.ts.map +1 -0
- package/dist/sdk/coding-agent/core/package/types.js +18 -0
- package/dist/sdk/coding-agent/core/package/update-check.d.ts +43 -0
- package/dist/sdk/coding-agent/core/package/update-check.d.ts.map +1 -0
- package/dist/sdk/coding-agent/core/package/update-check.js +253 -0
- package/dist/sdk/coding-agent/core/package-manager.d.ts +2 -188
- package/dist/sdk/coding-agent/core/package-manager.d.ts.map +1 -1
- package/dist/sdk/coding-agent/core/package-manager.js +1 -1940
- package/dist/sdk/coding-agent/core/session/migrate.d.ts +18 -0
- package/dist/sdk/coding-agent/core/session/migrate.d.ts.map +1 -0
- package/dist/sdk/coding-agent/core/session/migrate.js +78 -0
- package/dist/sdk/coding-agent/core/session/types.d.ts +165 -0
- package/dist/sdk/coding-agent/core/session/types.d.ts.map +1 -0
- package/dist/sdk/coding-agent/core/session/types.js +4 -0
- package/dist/sdk/coding-agent/core/session-manager.d.ts +3 -161
- package/dist/sdk/coding-agent/core/session-manager.d.ts.map +1 -1
- package/dist/sdk/coding-agent/core/session-manager.js +4 -72
- package/package.json +2 -2
- 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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 ${
|
|
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":"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"}
|