@aexol/spectral 0.9.230 → 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/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/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/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
|
@@ -0,0 +1,669 @@
|
|
|
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 { homedir } from "node:os";
|
|
13
|
+
import { BadRequestError } from "../server/handlers/errors.js";
|
|
14
|
+
import { handleListMcpStatus } from "../server/handlers/mcp-status.js";
|
|
15
|
+
import { handleReloadMcpConfig } from "../server/handlers/mcp-reload.js";
|
|
16
|
+
import { handlePathAutocomplete } from "../server/handlers/paths-autocomplete.js";
|
|
17
|
+
import { handleGetHomeDirectory } from "../server/handlers/paths-home.js";
|
|
18
|
+
import { handleListDirectory } from "../server/handlers/paths-list.js";
|
|
19
|
+
import { handleSearchProjectFiles } from "../server/handlers/files-search.js";
|
|
20
|
+
import { handleBindStudioProject, handleUnbindStudioProject, handleCleanupProjectSessions, handleCreateProject, handleDeleteProject, handleGetProjectObservations, handleListProjectsAsync, handleListSessionsByProjectAsync, handleUpdateProject, } from "../server/handlers/projects.js";
|
|
21
|
+
import { handleCompactSession, handleCleanupAllSessions, handleCreateSession, handleDeleteSession, handleForkSession, handleRememberAndDeleteSession, handleGetSessionDetailAsync, handleGetSessionMessagesAsync, handleGetSessionMemoryDetails, handleGetSessionMemoryStatus, handleUpdateSession, handleGetSessionActiveAgentAsync, handleUpdateSessionActiveAgentAsync, } from "../server/handlers/sessions.js";
|
|
22
|
+
import { handleClearPromptQueue, handleEnqueuePrompt, handleGetPromptQueue, handleRemovePrompt, } from "../server/handlers/queue.js";
|
|
23
|
+
import { handleGetAgentSettingsAsync, handleListAgents, handlePutAgentSettingsAsync, } from "../server/handlers/agent-settings.js";
|
|
24
|
+
import { handleGetSettingsAsync, handlePutSettingsAsync, } from "../server/handlers/settings.js";
|
|
25
|
+
import { handleGetPromptMutationSettings, handlePutPromptMutationSettings, } from "../server/handlers/prompt-mutation-settings.js";
|
|
26
|
+
import { handleKillDevProcess, handleKillDetectedDevServer, handleListDevProcesses, handleRestartDevProcess, handleScanDevServers, handleStartDevProcess, } from "../server/handlers/dev-processes.js";
|
|
27
|
+
import { startWizardLogin } from "./wizard-login.js";
|
|
28
|
+
/**
|
|
29
|
+
* Inline path matcher. Returns `null` for any path/method combination we
|
|
30
|
+
* don't recognise; the caller turns that into a `404 Unknown route`.
|
|
31
|
+
*
|
|
32
|
+
* Intentionally literal — a regex table would be marginally fancier but
|
|
33
|
+
* also marginally slower and harder to read for ~11 routes.
|
|
34
|
+
*/
|
|
35
|
+
export function matchRoute(method, path) {
|
|
36
|
+
// Strip query string for path matching but keep it for the handler.
|
|
37
|
+
const qIdx = path.indexOf("?");
|
|
38
|
+
const cleanPath = qIdx === -1 ? path : path.slice(0, qIdx);
|
|
39
|
+
const query = qIdx >= 0 ? new URLSearchParams(path.slice(qIdx + 1)) : undefined;
|
|
40
|
+
// /api/mcp-status
|
|
41
|
+
if (cleanPath === "/api/mcp-status") {
|
|
42
|
+
if (method === "GET")
|
|
43
|
+
return { route: "list_mcp_status" };
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
if (cleanPath === "/api/mcp-reload") {
|
|
47
|
+
if (method === "POST")
|
|
48
|
+
return { route: "reload_mcp_config" };
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
if (cleanPath === "/api/native-extensions/refresh") {
|
|
52
|
+
if (method === "POST")
|
|
53
|
+
return { route: "refresh_native_extensions" };
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
if (cleanPath === "/api/cloud-agents/refresh") {
|
|
57
|
+
if (method === "POST")
|
|
58
|
+
return { route: "refresh_cloud_agents" };
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
if (cleanPath === "/api/wizard/login") {
|
|
62
|
+
if (method === "POST")
|
|
63
|
+
return { route: "wizard_login" };
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
// /api/settings
|
|
67
|
+
if (cleanPath === "/api/settings") {
|
|
68
|
+
if (method === "GET")
|
|
69
|
+
return { route: "get_settings" };
|
|
70
|
+
if (method === "PUT")
|
|
71
|
+
return { route: "put_settings" };
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
// /api/agent-settings
|
|
75
|
+
if (cleanPath === "/api/agent-settings") {
|
|
76
|
+
if (method === "GET")
|
|
77
|
+
return { route: "get_agent_settings" };
|
|
78
|
+
if (method === "PUT")
|
|
79
|
+
return { route: "put_agent_settings" };
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
if (cleanPath === "/api/prompt-mutation-settings") {
|
|
83
|
+
if (method === "GET")
|
|
84
|
+
return { route: "get_prompt_mutation_settings" };
|
|
85
|
+
if (method === "PUT")
|
|
86
|
+
return { route: "put_prompt_mutation_settings" };
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
// /api/agents
|
|
90
|
+
if (cleanPath === "/api/agents") {
|
|
91
|
+
if (method === "GET")
|
|
92
|
+
return { route: "list_agents", query };
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
// /api/paths/autocomplete
|
|
96
|
+
if (cleanPath === "/api/paths/autocomplete") {
|
|
97
|
+
if (method === "GET")
|
|
98
|
+
return { route: "list_path_autocomplete", query };
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
// /api/paths/list
|
|
102
|
+
if (cleanPath === "/api/paths/list") {
|
|
103
|
+
if (method === "POST")
|
|
104
|
+
return { route: "list_directory" };
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
// /api/paths/home
|
|
108
|
+
if (cleanPath === "/api/paths/home") {
|
|
109
|
+
if (method === "POST")
|
|
110
|
+
return { route: "get_home_directory" };
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
// /api/projects
|
|
114
|
+
if (cleanPath === "/api/projects") {
|
|
115
|
+
if (method === "GET")
|
|
116
|
+
return { route: "list_projects" };
|
|
117
|
+
if (method === "POST")
|
|
118
|
+
return { route: "create_project" };
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
// /api/sessions
|
|
122
|
+
if (cleanPath === "/api/sessions") {
|
|
123
|
+
if (method === "POST")
|
|
124
|
+
return { route: "create_session" };
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
// /api/projects/:id/files/search
|
|
128
|
+
const projectFilesMatch = /^\/api\/projects\/([^/]+)\/files\/search$/.exec(cleanPath);
|
|
129
|
+
if (projectFilesMatch) {
|
|
130
|
+
const id = decodeURIComponent(projectFilesMatch[1]);
|
|
131
|
+
if (method === "GET")
|
|
132
|
+
return { route: "search_project_files", id, query };
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
// /api/projects/:id/bind-studio and /api/projects/:id/unbind-studio
|
|
136
|
+
const bindStudioMatch = /^\/api\/projects\/([^/]+)\/bind-studio$/.exec(cleanPath);
|
|
137
|
+
if (bindStudioMatch) {
|
|
138
|
+
const id = decodeURIComponent(bindStudioMatch[1]);
|
|
139
|
+
if (method === "POST")
|
|
140
|
+
return { route: "bind_studio", id };
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
const unbindStudioMatch = /^\/api\/projects\/([^/]+)\/unbind-studio$/.exec(cleanPath);
|
|
144
|
+
if (unbindStudioMatch) {
|
|
145
|
+
const id = decodeURIComponent(unbindStudioMatch[1]);
|
|
146
|
+
if (method === "POST")
|
|
147
|
+
return { route: "unbind_studio", id };
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
// /api/projects/:id/observations
|
|
151
|
+
const projectObsMatch = /^\/api\/projects\/([^/]+)\/observations$/.exec(cleanPath);
|
|
152
|
+
if (projectObsMatch) {
|
|
153
|
+
const id = decodeURIComponent(projectObsMatch[1]);
|
|
154
|
+
if (method === "GET")
|
|
155
|
+
return { route: "get_project_observations", id };
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
// /api/projects/:id and /api/projects/:id/sessions
|
|
159
|
+
const projectMatch = /^\/api\/projects\/([^/]+)(\/sessions)?$/.exec(cleanPath);
|
|
160
|
+
if (projectMatch) {
|
|
161
|
+
const id = decodeURIComponent(projectMatch[1]);
|
|
162
|
+
const isSessions = projectMatch[2] === "/sessions";
|
|
163
|
+
if (isSessions) {
|
|
164
|
+
if (method === "GET")
|
|
165
|
+
return { route: "list_project_sessions", id };
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
if (method === "PATCH")
|
|
169
|
+
return { route: "update_project", id };
|
|
170
|
+
if (method === "DELETE")
|
|
171
|
+
return { route: "delete_project", id };
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
// /api/projects/:id/cleanup-sessions
|
|
175
|
+
const projectCleanupMatch = /^\/api\/projects\/([^/]+)\/cleanup-sessions$/.exec(cleanPath);
|
|
176
|
+
if (projectCleanupMatch) {
|
|
177
|
+
const id = decodeURIComponent(projectCleanupMatch[1]);
|
|
178
|
+
if (method === "POST")
|
|
179
|
+
return { route: "cleanup_project_sessions", id };
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
// /api/sessions/cleanup (must precede the /api/sessions/:id matches —
|
|
183
|
+
// "cleanup" would otherwise be captured as an :id)
|
|
184
|
+
if (cleanPath === "/api/sessions/cleanup") {
|
|
185
|
+
if (method === "POST")
|
|
186
|
+
return { route: "cleanup_all_sessions" };
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
// /api/sessions/:id
|
|
190
|
+
const sessionMessagesMatch = /^\/api\/sessions\/([^/]+)\/messages$/.exec(cleanPath);
|
|
191
|
+
if (sessionMessagesMatch) {
|
|
192
|
+
const id = decodeURIComponent(sessionMessagesMatch[1]);
|
|
193
|
+
if (method === "GET")
|
|
194
|
+
return { route: "get_session_messages", id, query };
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
const sessionMatch = /^\/api\/sessions\/([^/]+)$/.exec(cleanPath);
|
|
198
|
+
if (sessionMatch) {
|
|
199
|
+
const id = decodeURIComponent(sessionMatch[1]);
|
|
200
|
+
if (method === "GET")
|
|
201
|
+
return { route: "get_session", id };
|
|
202
|
+
if (method === "PATCH")
|
|
203
|
+
return { route: "update_session", id };
|
|
204
|
+
if (method === "DELETE")
|
|
205
|
+
return { route: "delete_session", id };
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
const sessionAgentMatch = /^\/api\/sessions\/([^/]+)\/active-agent$/.exec(cleanPath);
|
|
209
|
+
if (sessionAgentMatch) {
|
|
210
|
+
const id = decodeURIComponent(sessionAgentMatch[1]);
|
|
211
|
+
if (method === "GET")
|
|
212
|
+
return { route: "get_session_active_agent", id };
|
|
213
|
+
if (method === "PUT")
|
|
214
|
+
return { route: "update_session_active_agent", id };
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
// /api/sessions/:id/memory/details and /api/sessions/:id/memory and /api/sessions/:id/compact
|
|
218
|
+
const sessionActionMatch = /^\/api\/sessions\/([^/]+)\/(memory(?:\/details)?|compact)$/.exec(cleanPath);
|
|
219
|
+
if (sessionActionMatch) {
|
|
220
|
+
const id = decodeURIComponent(sessionActionMatch[1]);
|
|
221
|
+
const action = sessionActionMatch[2];
|
|
222
|
+
if (action === "memory" && method === "GET")
|
|
223
|
+
return { route: "get_session_memory", id };
|
|
224
|
+
if (action === "memory/details" && method === "GET")
|
|
225
|
+
return { route: "get_session_memory_details", id };
|
|
226
|
+
if (action === "compact" && method === "POST")
|
|
227
|
+
return { route: "compact_session", id };
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
// /api/sessions/:id/remember-and-delete
|
|
231
|
+
const rememberDeleteMatch = /^\/api\/sessions\/([^/]+)\/remember-and-delete$/.exec(cleanPath);
|
|
232
|
+
if (rememberDeleteMatch) {
|
|
233
|
+
const id = decodeURIComponent(rememberDeleteMatch[1]);
|
|
234
|
+
if (method === "POST")
|
|
235
|
+
return { route: "remember_and_delete_session", id };
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
// /api/sessions/:id/fork
|
|
239
|
+
const forkMatch = /^\/api\/sessions\/([^/]+)\/fork$/.exec(cleanPath);
|
|
240
|
+
if (forkMatch) {
|
|
241
|
+
const id = decodeURIComponent(forkMatch[1]);
|
|
242
|
+
if (method === "POST")
|
|
243
|
+
return { route: "fork_session", id };
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
// /api/sessions/:id/queue and /api/sessions/:id/queue/:itemId
|
|
247
|
+
const queueMatch = /^\/api\/sessions\/([^/]+)\/queue(\/([^/]+))?$/.exec(cleanPath);
|
|
248
|
+
if (queueMatch) {
|
|
249
|
+
const id = decodeURIComponent(queueMatch[1]);
|
|
250
|
+
const itemId = queueMatch[3] ? decodeURIComponent(queueMatch[3]) : undefined;
|
|
251
|
+
if (!itemId) {
|
|
252
|
+
if (method === "GET")
|
|
253
|
+
return { route: "get_prompt_queue", id };
|
|
254
|
+
if (method === "POST")
|
|
255
|
+
return { route: "enqueue_prompt", id };
|
|
256
|
+
if (method === "DELETE")
|
|
257
|
+
return { route: "clear_prompt_queue", id };
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
if (method === "DELETE")
|
|
261
|
+
return { route: "remove_prompt", id, itemId };
|
|
262
|
+
}
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
// /api/dev-processes and /api/dev-processes/:id
|
|
266
|
+
if (cleanPath === "/api/dev-processes") {
|
|
267
|
+
if (method === "GET")
|
|
268
|
+
return { route: "list_dev_processes" };
|
|
269
|
+
if (method === "POST")
|
|
270
|
+
return { route: "start_dev_process" };
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
const restartDevProcessMatch = /^\/api\/dev-processes\/([^/]+)\/restart$/.exec(cleanPath);
|
|
274
|
+
if (restartDevProcessMatch) {
|
|
275
|
+
const id = decodeURIComponent(restartDevProcessMatch[1]);
|
|
276
|
+
if (method === "POST")
|
|
277
|
+
return { route: "restart_dev_process", id };
|
|
278
|
+
return null;
|
|
279
|
+
}
|
|
280
|
+
const devProcessMatch = /^\/api\/dev-processes\/([^/]+)$/.exec(cleanPath);
|
|
281
|
+
if (devProcessMatch) {
|
|
282
|
+
const id = decodeURIComponent(devProcessMatch[1]);
|
|
283
|
+
if (method === "DELETE")
|
|
284
|
+
return { route: "kill_dev_process", id };
|
|
285
|
+
return null;
|
|
286
|
+
}
|
|
287
|
+
// /api/dev-servers and /api/dev-servers/:pid
|
|
288
|
+
if (cleanPath === "/api/dev-servers") {
|
|
289
|
+
if (method === "GET")
|
|
290
|
+
return { route: "scan_dev_servers" };
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
const devServerMatch = /^\/api\/dev-servers\/([^/]+)$/.exec(cleanPath);
|
|
294
|
+
if (devServerMatch) {
|
|
295
|
+
const id = decodeURIComponent(devServerMatch[1]);
|
|
296
|
+
if (method === "DELETE") {
|
|
297
|
+
const rawPort = query?.get("port");
|
|
298
|
+
const parsedPort = rawPort === null ? Number.NaN : Number(rawPort);
|
|
299
|
+
const port = Number.isFinite(parsedPort) ? parsedPort : undefined;
|
|
300
|
+
return port === undefined
|
|
301
|
+
? { route: "kill_dev_server", id }
|
|
302
|
+
: { route: "kill_dev_server", id, port };
|
|
303
|
+
}
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
306
|
+
return null;
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Inner switch: route → handler call. Separated so `handleRestRequest`
|
|
310
|
+
* owns ONE try/catch boundary for all error mapping.
|
|
311
|
+
*
|
|
312
|
+
* Meta publish is fired AFTER the handler returns successfully (i.e.
|
|
313
|
+
* after SQLite has committed) so subscribers never see a "create" event
|
|
314
|
+
* for a row the next read won't find. Publish failures are swallowed
|
|
315
|
+
* inside `safePublish` — the wire response goes back regardless.
|
|
316
|
+
*
|
|
317
|
+
* The body is `unknown` from the wire; each handler validates its own
|
|
318
|
+
* shape via the `BadRequestError`-throwing checks they already do.
|
|
319
|
+
*/
|
|
320
|
+
export async function dispatchRoute(match, body, deps) {
|
|
321
|
+
const { store, manager, publishMetaEvent, logger } = deps;
|
|
322
|
+
const id = match.id ?? "";
|
|
323
|
+
switch (match.route) {
|
|
324
|
+
case "list_projects": {
|
|
325
|
+
const busyIds = manager.getBusySessionIds();
|
|
326
|
+
const projects = await handleListProjectsAsync(store);
|
|
327
|
+
// Batch all per-project session lookups into a SINGLE query (LEFT JOIN +
|
|
328
|
+
// GROUP BY) instead of calling listSessionsByProject(p.id) in a loop,
|
|
329
|
+
// which issued O(projects) queries each with a per-session correlated
|
|
330
|
+
// COUNT(*) subquery and blocked the event loop past the 30s relay
|
|
331
|
+
// deadline on large projects.
|
|
332
|
+
const sessionsByProject = await store.listSessionsByProjectIdsAsync(projects.map((p) => p.id));
|
|
333
|
+
return projects.map((p) => {
|
|
334
|
+
const sessions = sessionsByProject.get(p.id) ?? [];
|
|
335
|
+
const hasActiveSession = sessions.some((s) => busyIds.has(s.id));
|
|
336
|
+
return { ...p, hasActiveSession, busy: hasActiveSession };
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
case "create_project": {
|
|
340
|
+
const project = handleCreateProject(store, asObject(body));
|
|
341
|
+
safePublish(publishMetaEvent, logger, {
|
|
342
|
+
type: "project_created",
|
|
343
|
+
projectId: project.id,
|
|
344
|
+
});
|
|
345
|
+
return project;
|
|
346
|
+
}
|
|
347
|
+
case "update_project": {
|
|
348
|
+
const project = handleUpdateProject(store, id, asObject(body));
|
|
349
|
+
safePublish(publishMetaEvent, logger, {
|
|
350
|
+
type: "project_renamed",
|
|
351
|
+
projectId: project.id,
|
|
352
|
+
});
|
|
353
|
+
return project;
|
|
354
|
+
}
|
|
355
|
+
case "delete_project": {
|
|
356
|
+
// Order matters: tear down live streams BEFORE the row is gone so
|
|
357
|
+
// the bridges don't try to write to a deleted FK target. Mirrors
|
|
358
|
+
// the ordering the deleted Hono route used.
|
|
359
|
+
const project = await store.getProjectAsync(id);
|
|
360
|
+
if (project) {
|
|
361
|
+
const sessions = await store.listSessionsByProjectAsync(id);
|
|
362
|
+
manager.disposeProjectStreams(sessions.map((s) => s.id));
|
|
363
|
+
}
|
|
364
|
+
const result = handleDeleteProject(store, id);
|
|
365
|
+
// Publish AFTER the cascade has committed. We use `id` (the URL
|
|
366
|
+
// param) as the projectId — `handleDeleteProject` would have
|
|
367
|
+
// thrown 404 above if it didn't exist.
|
|
368
|
+
safePublish(publishMetaEvent, logger, {
|
|
369
|
+
type: "project_deleted",
|
|
370
|
+
projectId: id,
|
|
371
|
+
});
|
|
372
|
+
return result;
|
|
373
|
+
}
|
|
374
|
+
case "bind_studio": {
|
|
375
|
+
const result = await handleBindStudioProject(store, id, asObject(body));
|
|
376
|
+
safePublish(publishMetaEvent, logger, {
|
|
377
|
+
type: "project_studio_bound",
|
|
378
|
+
projectId: id,
|
|
379
|
+
studioProjectId: result.studioProjectId,
|
|
380
|
+
studioProjectName: result.studioProjectName,
|
|
381
|
+
});
|
|
382
|
+
// Refresh the X-Project-Id header on active bridges so agent-session
|
|
383
|
+
// credits are attributed to the newly bound project immediately.
|
|
384
|
+
manager.reloadProjectBindings().catch((err) => {
|
|
385
|
+
logger?.error?.(`[dispatcher] reloadProjectBindings failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
386
|
+
});
|
|
387
|
+
// Reload MCP config so the aexol-mcp extension picks up the new binding.
|
|
388
|
+
if (deps.mcpState) {
|
|
389
|
+
handleReloadMcpConfig(deps.mcpState).catch((err) => {
|
|
390
|
+
logger?.error?.(`[dispatcher] MCP reload after bind failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
return result;
|
|
394
|
+
}
|
|
395
|
+
case "unbind_studio": {
|
|
396
|
+
const result = await handleUnbindStudioProject(store, id);
|
|
397
|
+
if (result.studioProjectId) {
|
|
398
|
+
safePublish(publishMetaEvent, logger, {
|
|
399
|
+
type: "project_studio_unbound",
|
|
400
|
+
projectId: id,
|
|
401
|
+
studioProjectId: result.studioProjectId,
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
// Clear the X-Project-Id header on active bridges.
|
|
405
|
+
manager.reloadProjectBindings().catch((err) => {
|
|
406
|
+
logger?.error?.(`[dispatcher] reloadProjectBindings failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
407
|
+
});
|
|
408
|
+
// Reload MCP config so the aexol-mcp extension clears the old binding.
|
|
409
|
+
if (deps.mcpState) {
|
|
410
|
+
handleReloadMcpConfig(deps.mcpState).catch((err) => {
|
|
411
|
+
logger?.error?.(`[dispatcher] MCP reload after unbind failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
return result;
|
|
415
|
+
}
|
|
416
|
+
case "get_project_observations": {
|
|
417
|
+
const limitVal = Number(match.query?.get("limit") ?? 30);
|
|
418
|
+
return handleGetProjectObservations(store, id, Math.max(1, Math.min(100, limitVal || 30)));
|
|
419
|
+
}
|
|
420
|
+
case "list_project_sessions": {
|
|
421
|
+
const busyIds = manager.getBusySessionIds();
|
|
422
|
+
const sessions = await handleListSessionsByProjectAsync(store, id);
|
|
423
|
+
return sessions.map((s) => ({
|
|
424
|
+
...s,
|
|
425
|
+
hasActiveTurn: busyIds.has(s.id),
|
|
426
|
+
busy: busyIds.has(s.id),
|
|
427
|
+
}));
|
|
428
|
+
}
|
|
429
|
+
case "cleanup_project_sessions": {
|
|
430
|
+
const result = handleCleanupProjectSessions(store, id, asObject(body));
|
|
431
|
+
// Tear down any in-flight streams for sessions that are being deleted.
|
|
432
|
+
for (const sessionId of result.ids) {
|
|
433
|
+
manager.disposeSessionStream(sessionId);
|
|
434
|
+
}
|
|
435
|
+
for (const sessionId of result.ids) {
|
|
436
|
+
safePublish(publishMetaEvent, logger, {
|
|
437
|
+
type: "session_deleted",
|
|
438
|
+
projectId: id,
|
|
439
|
+
sessionId,
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
return { deleted: result.deleted };
|
|
443
|
+
}
|
|
444
|
+
case "cleanup_all_sessions": {
|
|
445
|
+
const result = handleCleanupAllSessions(store, asObject(body));
|
|
446
|
+
// Tear down any in-flight streams for sessions that are being deleted,
|
|
447
|
+
// across every project.
|
|
448
|
+
for (const sessionId of result.ids) {
|
|
449
|
+
manager.disposeSessionStream(sessionId);
|
|
450
|
+
}
|
|
451
|
+
for (const [sessionId, projectId] of Object.entries(result.projectIds)) {
|
|
452
|
+
safePublish(publishMetaEvent, logger, {
|
|
453
|
+
type: "session_deleted",
|
|
454
|
+
projectId,
|
|
455
|
+
sessionId,
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
return { deleted: result.deleted };
|
|
459
|
+
}
|
|
460
|
+
case "create_session": {
|
|
461
|
+
const session = handleCreateSession(store, asObject(body));
|
|
462
|
+
safePublish(publishMetaEvent, logger, {
|
|
463
|
+
type: "session_created",
|
|
464
|
+
projectId: session.projectId,
|
|
465
|
+
sessionId: session.id,
|
|
466
|
+
});
|
|
467
|
+
return session;
|
|
468
|
+
}
|
|
469
|
+
case "get_session": {
|
|
470
|
+
const detail = await handleGetSessionDetailAsync(store, id);
|
|
471
|
+
return {
|
|
472
|
+
...detail,
|
|
473
|
+
busy: manager.isBusy(id),
|
|
474
|
+
runState: manager.getRunState(id),
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
case "get_session_active_agent":
|
|
478
|
+
return await handleGetSessionActiveAgentAsync(store, id, deps.cwd ?? homedir(), deps.agentDir);
|
|
479
|
+
case "update_session_active_agent":
|
|
480
|
+
return await handleUpdateSessionActiveAgentAsync(store, id, asObject(body), manager, deps.cwd ?? homedir(), deps.agentDir);
|
|
481
|
+
case "get_session_messages":
|
|
482
|
+
return await handleGetSessionMessagesAsync(store, id, {
|
|
483
|
+
beforeSeq: match.query?.get("beforeSeq") ?? undefined,
|
|
484
|
+
beforeCreatedAt: match.query?.get("beforeCreatedAt") ?? undefined,
|
|
485
|
+
beforeId: match.query?.get("beforeId") ?? undefined,
|
|
486
|
+
limit: match.query?.get("limit") ?? undefined,
|
|
487
|
+
});
|
|
488
|
+
case "get_session_memory":
|
|
489
|
+
return handleGetSessionMemoryStatus(store, manager, id);
|
|
490
|
+
case "get_session_memory_details":
|
|
491
|
+
return handleGetSessionMemoryDetails(store, manager, id);
|
|
492
|
+
case "update_session": {
|
|
493
|
+
const session = handleUpdateSession(store, id, asObject(body));
|
|
494
|
+
safePublish(publishMetaEvent, logger, {
|
|
495
|
+
type: "session_renamed",
|
|
496
|
+
projectId: session.projectId,
|
|
497
|
+
sessionId: session.id,
|
|
498
|
+
});
|
|
499
|
+
return session;
|
|
500
|
+
}
|
|
501
|
+
case "delete_session": {
|
|
502
|
+
// Capture projectId BEFORE the row is gone — otherwise the
|
|
503
|
+
// post-delete publish has no project to attribute to. Treat a
|
|
504
|
+
// missing session here the same way `handleDeleteSession` does
|
|
505
|
+
// (throws NotFoundError → 404), but capture first.
|
|
506
|
+
const detail = store.getSession(id);
|
|
507
|
+
// Same ordering rationale as delete_project.
|
|
508
|
+
manager.disposeSessionStream(id);
|
|
509
|
+
handleDeleteSession(store, id);
|
|
510
|
+
if (detail) {
|
|
511
|
+
safePublish(publishMetaEvent, logger, {
|
|
512
|
+
type: "session_deleted",
|
|
513
|
+
projectId: detail.projectId,
|
|
514
|
+
sessionId: id,
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
return { ok: true };
|
|
518
|
+
}
|
|
519
|
+
case "compact_session":
|
|
520
|
+
return await handleCompactSession(store, manager, id, body);
|
|
521
|
+
case "remember_and_delete_session": {
|
|
522
|
+
// Compact first — the compaction hook's persistProjectObservations
|
|
523
|
+
// runs inside compactSession, writing reflections to cross-session
|
|
524
|
+
// durable memory. On failure (e.g. no API key), we let the error
|
|
525
|
+
// propagate so the session is NOT deleted.
|
|
526
|
+
await handleRememberAndDeleteSession(store, manager, id);
|
|
527
|
+
const detail = store.getSession(id);
|
|
528
|
+
manager.disposeSessionStream(id);
|
|
529
|
+
handleDeleteSession(store, id);
|
|
530
|
+
if (detail) {
|
|
531
|
+
safePublish(publishMetaEvent, logger, {
|
|
532
|
+
type: "session_deleted",
|
|
533
|
+
projectId: detail.projectId,
|
|
534
|
+
sessionId: id,
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
return { ok: true };
|
|
538
|
+
}
|
|
539
|
+
case "fork_session": {
|
|
540
|
+
const session = handleForkSession(store, id, asObject(body));
|
|
541
|
+
safePublish(publishMetaEvent, logger, {
|
|
542
|
+
type: "session_created",
|
|
543
|
+
projectId: session.projectId,
|
|
544
|
+
sessionId: session.id,
|
|
545
|
+
});
|
|
546
|
+
return session;
|
|
547
|
+
}
|
|
548
|
+
case "list_path_autocomplete": {
|
|
549
|
+
const prefix = match.query?.get("prefix") ?? "";
|
|
550
|
+
return await handlePathAutocomplete(prefix);
|
|
551
|
+
}
|
|
552
|
+
case "list_directory":
|
|
553
|
+
return await handleListDirectory(asObject(body));
|
|
554
|
+
case "get_home_directory":
|
|
555
|
+
return handleGetHomeDirectory();
|
|
556
|
+
case "search_project_files": {
|
|
557
|
+
const searchQuery = match.query?.get("query") ?? "";
|
|
558
|
+
return handleSearchProjectFiles(store, id, searchQuery);
|
|
559
|
+
}
|
|
560
|
+
case "enqueue_prompt":
|
|
561
|
+
return handleEnqueuePrompt(store, manager, id, asObject(body));
|
|
562
|
+
case "get_prompt_queue":
|
|
563
|
+
return handleGetPromptQueue(store, id);
|
|
564
|
+
case "remove_prompt":
|
|
565
|
+
handleRemovePrompt(store, manager, id, match.itemId ?? "");
|
|
566
|
+
return { ok: true };
|
|
567
|
+
case "clear_prompt_queue":
|
|
568
|
+
handleClearPromptQueue(store, manager, id);
|
|
569
|
+
return { ok: true };
|
|
570
|
+
case "list_mcp_status": {
|
|
571
|
+
if (!deps.mcpState) {
|
|
572
|
+
return [];
|
|
573
|
+
}
|
|
574
|
+
return handleListMcpStatus(deps.mcpState);
|
|
575
|
+
}
|
|
576
|
+
case "reload_mcp_config": {
|
|
577
|
+
return await handleReloadMcpConfig(deps.mcpState);
|
|
578
|
+
}
|
|
579
|
+
case "refresh_native_extensions": {
|
|
580
|
+
if (!deps.refreshNativeExtensions) {
|
|
581
|
+
return { ok: true };
|
|
582
|
+
}
|
|
583
|
+
await deps.refreshNativeExtensions();
|
|
584
|
+
return { ok: true };
|
|
585
|
+
}
|
|
586
|
+
case "refresh_cloud_agents": {
|
|
587
|
+
const teamId = asObject(body).teamId;
|
|
588
|
+
if (typeof teamId !== "string" || teamId.length === 0) {
|
|
589
|
+
throw new BadRequestError("teamId is required");
|
|
590
|
+
}
|
|
591
|
+
if (deps.refreshCloudAgents)
|
|
592
|
+
await deps.refreshCloudAgents(teamId);
|
|
593
|
+
return { ok: true };
|
|
594
|
+
}
|
|
595
|
+
case "wizard_login": {
|
|
596
|
+
return await (deps.startWizardLogin ?? (() => startWizardLogin(deps.cwd ?? homedir())))();
|
|
597
|
+
}
|
|
598
|
+
case "get_settings":
|
|
599
|
+
return await handleGetSettingsAsync(deps.cwd ?? homedir());
|
|
600
|
+
case "put_settings":
|
|
601
|
+
return await handlePutSettingsAsync(deps.cwd ?? homedir(), asObject(body));
|
|
602
|
+
case "get_agent_settings":
|
|
603
|
+
return await handleGetAgentSettingsAsync(deps.cwd ?? homedir(), deps.agentDir);
|
|
604
|
+
case "put_agent_settings":
|
|
605
|
+
return await handlePutAgentSettingsAsync(deps.cwd ?? homedir(), asObject(body), deps.agentDir);
|
|
606
|
+
case "get_prompt_mutation_settings":
|
|
607
|
+
return handleGetPromptMutationSettings(deps.cwd ?? homedir(), deps.agentDir);
|
|
608
|
+
case "put_prompt_mutation_settings":
|
|
609
|
+
return handlePutPromptMutationSettings(deps.cwd ?? homedir(), asObject(body), deps.agentDir);
|
|
610
|
+
case "list_agents": {
|
|
611
|
+
const agentCwd = match.query?.get("cwd") || deps.cwd || homedir();
|
|
612
|
+
const teamId = match.query?.get("teamId") || undefined;
|
|
613
|
+
// Kick off cloud refresh in the background without blocking the response.
|
|
614
|
+
// The previous version awaited refreshCloudAgents (network → backend GraphQL)
|
|
615
|
+
// before returning, so every page refresh stalled the entire REST queue
|
|
616
|
+
// behind this one slow call while 5-7 other rest_requests timed out.
|
|
617
|
+
// Background refresh self-dedupes via CloudAgentCacheEntry.inFlight, so
|
|
618
|
+
// bursts coalesce; stale cache renders immediately and the picker catches
|
|
619
|
+
// up on the next meta_event/live refresh.
|
|
620
|
+
if (teamId && deps.refreshCloudAgents) {
|
|
621
|
+
void deps.refreshCloudAgents(teamId).catch((err) => {
|
|
622
|
+
logger?.error?.(`[dispatcher] cloud agent refresh failed:`, err);
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
const agents = handleListAgents(agentCwd, deps.agentDir, teamId);
|
|
626
|
+
return agents;
|
|
627
|
+
}
|
|
628
|
+
case "list_dev_processes":
|
|
629
|
+
return handleListDevProcesses(deps.devProcessRegistry);
|
|
630
|
+
case "start_dev_process":
|
|
631
|
+
return handleStartDevProcess(deps.devProcessRegistry, store, asObject(body), deps.cwd);
|
|
632
|
+
case "kill_dev_process":
|
|
633
|
+
return handleKillDevProcess(deps.devProcessRegistry, id);
|
|
634
|
+
case "restart_dev_process":
|
|
635
|
+
return handleRestartDevProcess(deps.devProcessRegistry, id);
|
|
636
|
+
case "scan_dev_servers":
|
|
637
|
+
return handleScanDevServers(store, deps.devProcessRegistry);
|
|
638
|
+
case "kill_dev_server":
|
|
639
|
+
return await handleKillDetectedDevServer(store, Number(id), match.port);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* Invoke `publishMetaEvent` if provided, swallowing any throw. The
|
|
644
|
+
* caller has already returned a 200 to the browser; a publish failure
|
|
645
|
+
* (e.g. relay socket just dropped) must NEVER turn a successful
|
|
646
|
+
* mutation into a wire-level error.
|
|
647
|
+
*/
|
|
648
|
+
function safePublish(publish, logger, event) {
|
|
649
|
+
if (!publish)
|
|
650
|
+
return;
|
|
651
|
+
try {
|
|
652
|
+
publish(event);
|
|
653
|
+
}
|
|
654
|
+
catch (err) {
|
|
655
|
+
(logger ?? console).error?.(`[dispatcher] publishMetaEvent failed for ${event.type}:`, err);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
/**
|
|
659
|
+
* Coerce an `unknown` body to a plain object for the handler. Returns an
|
|
660
|
+
* empty object when the body is missing/non-object so the handler's own
|
|
661
|
+
* field checks produce the canonical error message ("name (string) is
|
|
662
|
+
* required") instead of a confusing "body must be object" from us.
|
|
663
|
+
*/
|
|
664
|
+
function asObject(body) {
|
|
665
|
+
if (body !== null && typeof body === "object" && !Array.isArray(body)) {
|
|
666
|
+
return body;
|
|
667
|
+
}
|
|
668
|
+
return {};
|
|
669
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wizard login flow (CLI side).
|
|
3
|
+
*
|
|
4
|
+
* Starts the `@aexol/wizard-spectral` login subprocess on behalf of
|
|
5
|
+
* `POST /api/wizard/login` (routed by `relay/routing.ts`). Extracted from
|
|
6
|
+
* `relay/dispatcher.ts` — the dispatcher re-exports `startWizardLogin` and
|
|
7
|
+
* `WizardLoginResult` so existing imports keep working.
|
|
8
|
+
*/
|
|
9
|
+
export interface WizardLoginResult {
|
|
10
|
+
status: "configured" | "login_url_observed" | "started" | "completed" | "already_running" | "package_unavailable" | "spawn_failed" | "exited";
|
|
11
|
+
packageName: string;
|
|
12
|
+
message: string;
|
|
13
|
+
loginUrl?: string;
|
|
14
|
+
startedAt?: number;
|
|
15
|
+
}
|
|
16
|
+
export declare function startWizardLogin(directory?: string): Promise<WizardLoginResult>;
|
|
17
|
+
//# sourceMappingURL=wizard-login.d.ts.map
|