@cluesmith/codev 3.0.3 → 3.0.6
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/dashboard-dist/assets/{index-Dn9zmcKp.js → index-CR-uu485.js} +29 -29
- package/dashboard-dist/assets/index-CR-uu485.js.map +1 -0
- package/dashboard-dist/assets/{index-BWYZtvwm.css → index-nCeIzV_d.css} +1 -1
- package/dashboard-dist/index.html +2 -2
- package/dist/agent-farm/cli.d.ts.map +1 -1
- package/dist/agent-farm/cli.js +16 -1
- package/dist/agent-farm/cli.js.map +1 -1
- package/dist/agent-farm/commands/cleanup.js +13 -0
- package/dist/agent-farm/commands/cleanup.js.map +1 -1
- package/dist/agent-farm/commands/dev.d.ts.map +1 -1
- package/dist/agent-farm/commands/dev.js +10 -3
- package/dist/agent-farm/commands/dev.js.map +1 -1
- package/dist/agent-farm/commands/spawn-worktree.d.ts +12 -3
- package/dist/agent-farm/commands/spawn-worktree.d.ts.map +1 -1
- package/dist/agent-farm/commands/spawn-worktree.js +17 -6
- package/dist/agent-farm/commands/spawn-worktree.js.map +1 -1
- package/dist/agent-farm/commands/spawn.d.ts.map +1 -1
- package/dist/agent-farm/commands/spawn.js +82 -36
- package/dist/agent-farm/commands/spawn.js.map +1 -1
- package/dist/agent-farm/commands/stop.d.ts.map +1 -1
- package/dist/agent-farm/commands/stop.js +15 -8
- package/dist/agent-farm/commands/stop.js.map +1 -1
- package/dist/agent-farm/commands/workspace-add-architect.d.ts +18 -0
- package/dist/agent-farm/commands/workspace-add-architect.d.ts.map +1 -0
- package/dist/agent-farm/commands/workspace-add-architect.js +54 -0
- package/dist/agent-farm/commands/workspace-add-architect.js.map +1 -0
- package/dist/agent-farm/db/index.d.ts.map +1 -1
- package/dist/agent-farm/db/index.js +88 -1
- package/dist/agent-farm/db/index.js.map +1 -1
- package/dist/agent-farm/db/migrate.js +2 -2
- package/dist/agent-farm/db/migrate.js.map +1 -1
- package/dist/agent-farm/db/schema.d.ts +1 -1
- package/dist/agent-farm/db/schema.d.ts.map +1 -1
- package/dist/agent-farm/db/schema.js +4 -3
- package/dist/agent-farm/db/schema.js.map +1 -1
- package/dist/agent-farm/db/types.d.ts +7 -2
- package/dist/agent-farm/db/types.d.ts.map +1 -1
- package/dist/agent-farm/db/types.js +2 -0
- package/dist/agent-farm/db/types.js.map +1 -1
- package/dist/agent-farm/servers/analytics.d.ts.map +1 -1
- package/dist/agent-farm/servers/analytics.js +1 -0
- package/dist/agent-farm/servers/analytics.js.map +1 -1
- package/dist/agent-farm/servers/overview.d.ts +25 -3
- package/dist/agent-farm/servers/overview.d.ts.map +1 -1
- package/dist/agent-farm/servers/overview.js +72 -10
- package/dist/agent-farm/servers/overview.js.map +1 -1
- package/dist/agent-farm/servers/tower-instances.d.ts +18 -0
- package/dist/agent-farm/servers/tower-instances.d.ts.map +1 -1
- package/dist/agent-farm/servers/tower-instances.js +260 -16
- package/dist/agent-farm/servers/tower-instances.js.map +1 -1
- package/dist/agent-farm/servers/tower-messages.d.ts +10 -1
- package/dist/agent-farm/servers/tower-messages.d.ts.map +1 -1
- package/dist/agent-farm/servers/tower-messages.js +137 -7
- package/dist/agent-farm/servers/tower-messages.js.map +1 -1
- package/dist/agent-farm/servers/tower-routes.d.ts.map +1 -1
- package/dist/agent-farm/servers/tower-routes.js +124 -20
- package/dist/agent-farm/servers/tower-routes.js.map +1 -1
- package/dist/agent-farm/servers/tower-terminals.d.ts.map +1 -1
- package/dist/agent-farm/servers/tower-terminals.js +58 -28
- package/dist/agent-farm/servers/tower-terminals.js.map +1 -1
- package/dist/agent-farm/servers/tower-tunnel.d.ts.map +1 -1
- package/dist/agent-farm/servers/tower-tunnel.js +3 -2
- package/dist/agent-farm/servers/tower-tunnel.js.map +1 -1
- package/dist/agent-farm/servers/tower-types.d.ts +9 -2
- package/dist/agent-farm/servers/tower-types.d.ts.map +1 -1
- package/dist/agent-farm/state.d.ts +50 -4
- package/dist/agent-farm/state.d.ts.map +1 -1
- package/dist/agent-farm/state.js +118 -12
- package/dist/agent-farm/state.js.map +1 -1
- package/dist/agent-farm/types.d.ts +3 -1
- package/dist/agent-farm/types.d.ts.map +1 -1
- package/dist/agent-farm/utils/agent-names.d.ts.map +1 -1
- package/dist/agent-farm/utils/agent-names.js +3 -0
- package/dist/agent-farm/utils/agent-names.js.map +1 -1
- package/dist/agent-farm/utils/architect-name.d.ts +37 -0
- package/dist/agent-farm/utils/architect-name.d.ts.map +1 -0
- package/dist/agent-farm/utils/architect-name.js +63 -0
- package/dist/agent-farm/utils/architect-name.js.map +1 -0
- package/dist/agent-farm/utils/git.d.ts +29 -0
- package/dist/agent-farm/utils/git.d.ts.map +1 -0
- package/dist/agent-farm/utils/git.js +38 -0
- package/dist/agent-farm/utils/git.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/consult/index.d.ts.map +1 -1
- package/dist/commands/consult/index.js +2 -0
- package/dist/commands/consult/index.js.map +1 -1
- package/dist/commands/porch/artifacts.d.ts +23 -1
- package/dist/commands/porch/artifacts.d.ts.map +1 -1
- package/dist/commands/porch/artifacts.js +51 -62
- package/dist/commands/porch/artifacts.js.map +1 -1
- package/dist/commands/porch/index.d.ts +23 -2
- package/dist/commands/porch/index.d.ts.map +1 -1
- package/dist/commands/porch/index.js +83 -4
- package/dist/commands/porch/index.js.map +1 -1
- package/dist/commands/porch/notify.d.ts +27 -5
- package/dist/commands/porch/notify.d.ts.map +1 -1
- package/dist/commands/porch/notify.js +23 -12
- package/dist/commands/porch/notify.js.map +1 -1
- package/dist/commands/porch/state.d.ts +1 -1
- package/dist/commands/porch/state.d.ts.map +1 -1
- package/dist/commands/porch/state.js +8 -6
- package/dist/commands/porch/state.js.map +1 -1
- package/dist/lib/github.d.ts +8 -0
- package/dist/lib/github.d.ts.map +1 -1
- package/dist/lib/github.js +29 -2
- package/dist/lib/github.js.map +1 -1
- package/dist/lib/team-github.d.ts +4 -0
- package/dist/lib/team-github.d.ts.map +1 -1
- package/dist/lib/team-github.js +14 -3
- package/dist/lib/team-github.js.map +1 -1
- package/package.json +5 -5
- package/skeleton/protocols/air/builder-prompt.md +6 -0
- package/skeleton/protocols/air/consult-types/impl-review.md +6 -0
- package/skeleton/protocols/air/consult-types/pr-review.md +6 -0
- package/skeleton/protocols/air/prompts/implement.md +6 -0
- package/skeleton/protocols/air/protocol.md +4 -0
- package/skeleton/protocols/aspir/builder-prompt.md +19 -3
- package/skeleton/protocols/aspir/consult-types/plan-review.md +6 -0
- package/skeleton/protocols/aspir/consult-types/spec-review.md +6 -0
- package/skeleton/protocols/aspir/prompts/specify.md +6 -0
- package/skeleton/protocols/aspir/protocol.md +4 -0
- package/skeleton/protocols/bugfix/consult-types/impl-review.md +44 -37
- package/skeleton/protocols/bugfix/consult-types/pr-review.md +65 -40
- package/skeleton/protocols/pir/builder-prompt.md +92 -0
- package/skeleton/protocols/pir/consult-types/impl-review.md +78 -0
- package/skeleton/protocols/pir/consult-types/pr-review.md +65 -0
- package/skeleton/protocols/pir/prompts/implement.md +156 -0
- package/skeleton/protocols/pir/prompts/plan.md +133 -0
- package/skeleton/protocols/pir/prompts/review.md +261 -0
- package/skeleton/protocols/pir/protocol.json +125 -0
- package/skeleton/protocols/pir/protocol.md +204 -0
- package/skeleton/protocols/spir/builder-prompt.md +19 -3
- package/skeleton/protocols/spir/consult-types/plan-review.md +6 -0
- package/skeleton/protocols/spir/consult-types/spec-review.md +6 -0
- package/skeleton/protocols/spir/prompts/specify.md +6 -0
- package/skeleton/protocols/spir/protocol.md +4 -0
- package/skeleton/templates/AGENTS.md +24 -0
- package/skeleton/templates/CLAUDE.md +24 -0
- package/dashboard-dist/assets/index-Dn9zmcKp.js.map +0 -1
|
@@ -9,6 +9,24 @@
|
|
|
9
9
|
import path from 'node:path';
|
|
10
10
|
import { parseAddress, stripLeadingZeros } from '../utils/agent-names.js';
|
|
11
11
|
import { getWorkspaceTerminals } from './tower-terminals.js';
|
|
12
|
+
import { lookupBuilderSpawningArchitect } from '../state.js';
|
|
13
|
+
import { DEFAULT_ARCHITECT_NAME } from '../utils/architect-name.js';
|
|
14
|
+
// ============================================================================
|
|
15
|
+
// Spec 755 error messages — exported as constants so producer and asserter
|
|
16
|
+
// share a single source of truth. Tests assert these texts verbatim; changing
|
|
17
|
+
// the wording without updating both sides will break the build.
|
|
18
|
+
// ============================================================================
|
|
19
|
+
export function legacyBuilderErrorMessage(builderId, registered) {
|
|
20
|
+
const names = registered.length ? registered.join(', ') : '<none>';
|
|
21
|
+
return `legacy builder ${builderId} has no spawning-architect identity and no 'main' architect is registered (registered: ${names})`;
|
|
22
|
+
}
|
|
23
|
+
export function architectGoneErrorMessage(builderId, missingArchitect, registered) {
|
|
24
|
+
const names = registered.length ? registered.join(', ') : '<none>';
|
|
25
|
+
return `builder ${builderId} was spawned by architect ${missingArchitect}, which is no longer registered, and no 'main' architect is registered (registered: ${names})`;
|
|
26
|
+
}
|
|
27
|
+
export function addressSpoofingErrorMessage(builderId) {
|
|
28
|
+
return `builder ${builderId} may only address its own spawning architect`;
|
|
29
|
+
}
|
|
12
30
|
/** Active WebSocket subscribers for the message bus. */
|
|
13
31
|
const messageSubscribers = new Set();
|
|
14
32
|
/**
|
|
@@ -50,11 +68,17 @@ export function getSubscriberCount() {
|
|
|
50
68
|
* - Exact match (case-insensitive) first, then tail match
|
|
51
69
|
* - Multiple tail matches → AMBIGUOUS error
|
|
52
70
|
*
|
|
71
|
+
* Spec 755: when `sender` is supplied and refers to a builder, sender-affinity
|
|
72
|
+
* routing applies to `architect` and `architect:<name>` targets. Non-builder
|
|
73
|
+
* senders see the legacy main-first behavior, unchanged.
|
|
74
|
+
*
|
|
53
75
|
* @param target - Address string: "agent" or "project:agent"
|
|
54
76
|
* @param fallbackWorkspace - Workspace path when no project: prefix is given
|
|
77
|
+
* @param sender - Optional sender identity (a builder ID or 'architect').
|
|
78
|
+
* Enables affinity-aware architect routing per Spec 755.
|
|
55
79
|
* @returns ResolveResult on success, ResolveError on failure
|
|
56
80
|
*/
|
|
57
|
-
export function resolveTarget(target, fallbackWorkspace) {
|
|
81
|
+
export function resolveTarget(target, fallbackWorkspace, sender) {
|
|
58
82
|
const { project, agent } = parseAddress(target);
|
|
59
83
|
// Validate: empty or whitespace-only agent is a malformed address
|
|
60
84
|
if (!agent || !agent.trim()) {
|
|
@@ -63,6 +87,20 @@ export function resolveTarget(target, fallbackWorkspace) {
|
|
|
63
87
|
message: 'Malformed address: agent name is empty.',
|
|
64
88
|
};
|
|
65
89
|
}
|
|
90
|
+
// Spec 755: `architect:<name>` is a per-architect address WITHIN the
|
|
91
|
+
// current workspace, not a `project:agent` cross-workspace address.
|
|
92
|
+
// parseAddress can't distinguish them (the grammar is overloaded), so we
|
|
93
|
+
// intercept here. The resolver below applies the spoofing check when
|
|
94
|
+
// sender is a builder.
|
|
95
|
+
if (project && project.toLowerCase() === 'architect') {
|
|
96
|
+
if (!fallbackWorkspace) {
|
|
97
|
+
return {
|
|
98
|
+
code: 'NO_CONTEXT',
|
|
99
|
+
message: 'Cannot resolve architect:<name> address without workspace context.',
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
return resolveArchitectByName(agent, fallbackWorkspace, sender);
|
|
103
|
+
}
|
|
66
104
|
// Determine the workspace path
|
|
67
105
|
let workspacePath;
|
|
68
106
|
if (project) {
|
|
@@ -81,7 +119,43 @@ export function resolveTarget(target, fallbackWorkspace) {
|
|
|
81
119
|
workspacePath = fallbackWorkspace;
|
|
82
120
|
}
|
|
83
121
|
// Resolve agent within the workspace
|
|
84
|
-
return resolveAgentInWorkspace(agent, workspacePath);
|
|
122
|
+
return resolveAgentInWorkspace(agent, workspacePath, sender);
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Resolve `architect:<name>` to a terminal ID within the given workspace.
|
|
126
|
+
*
|
|
127
|
+
* Spec 755 enforcement when sender is a builder:
|
|
128
|
+
* - If the builder's `spawned_by_architect` matches `<name>`: allowed.
|
|
129
|
+
* - If it doesn't match: rejected with the spoofing error.
|
|
130
|
+
* Non-builder senders may address any architect by name.
|
|
131
|
+
*/
|
|
132
|
+
function resolveArchitectByName(name, workspacePath, sender) {
|
|
133
|
+
const allWorkspaces = getWorkspaceTerminals();
|
|
134
|
+
const entry = allWorkspaces.get(workspacePath);
|
|
135
|
+
if (!entry) {
|
|
136
|
+
return {
|
|
137
|
+
code: 'NOT_FOUND',
|
|
138
|
+
message: `Workspace '${workspacePath}' has no registered terminals.`,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
// Spoofing check: builder senders can only address their own architect.
|
|
142
|
+
if (sender) {
|
|
143
|
+
const spawningArchitect = lookupBuilderSpawningArchitect(sender, workspacePath);
|
|
144
|
+
if (spawningArchitect !== undefined && spawningArchitect !== name) {
|
|
145
|
+
return {
|
|
146
|
+
code: 'NOT_FOUND',
|
|
147
|
+
message: addressSpoofingErrorMessage(sender),
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
const terminalId = entry.architects.get(name);
|
|
152
|
+
if (!terminalId) {
|
|
153
|
+
return {
|
|
154
|
+
code: 'NOT_FOUND',
|
|
155
|
+
message: `Architect '${name}' not found in workspace '${path.basename(workspacePath)}'.`,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
return { terminalId, workspacePath, agent: name };
|
|
85
159
|
}
|
|
86
160
|
/**
|
|
87
161
|
* Find a workspace path by matching the basename of registered workspace paths.
|
|
@@ -111,9 +185,23 @@ function findWorkspaceByBasename(projectName) {
|
|
|
111
185
|
/**
|
|
112
186
|
* Resolve an agent name to a terminal ID within a specific workspace.
|
|
113
187
|
*
|
|
114
|
-
* Checks architect first
|
|
188
|
+
* Checks architect first (with Spec 755 affinity-aware routing when `sender`
|
|
189
|
+
* is a builder), then builders by exact match, then builders by tail match.
|
|
190
|
+
*
|
|
191
|
+
* Spec 755 architect resolution:
|
|
192
|
+
* - Fast path: single-architect workspace with name 'main' — return that
|
|
193
|
+
* terminal directly. Identical to today's behavior; avoids the state.db
|
|
194
|
+
* read entirely for the common case.
|
|
195
|
+
* - Builder sender, target 'architect': look up the builder's
|
|
196
|
+
* spawnedByArchitect. If that architect is registered, route there.
|
|
197
|
+
* Otherwise fall back to 'main', else error with the spec-mandated
|
|
198
|
+
* legacy-builder / architect-gone messages.
|
|
199
|
+
* - Builder sender, target 'architect:<name>': allowed only if <name>
|
|
200
|
+
* matches the sender's spawnedByArchitect. Otherwise rejected (spoofing).
|
|
201
|
+
* - Non-builder sender (or no sender), target 'architect': route to 'main'
|
|
202
|
+
* (or first registered if 'main' is absent). Unchanged from today.
|
|
115
203
|
*/
|
|
116
|
-
function resolveAgentInWorkspace(agent, workspacePath) {
|
|
204
|
+
function resolveAgentInWorkspace(agent, workspacePath, sender) {
|
|
117
205
|
const allWorkspaces = getWorkspaceTerminals();
|
|
118
206
|
const entry = allWorkspaces.get(workspacePath);
|
|
119
207
|
if (!entry) {
|
|
@@ -122,16 +210,58 @@ function resolveAgentInWorkspace(agent, workspacePath) {
|
|
|
122
210
|
message: `Workspace '${workspacePath}' has no registered terminals.`,
|
|
123
211
|
};
|
|
124
212
|
}
|
|
125
|
-
// Check architect
|
|
213
|
+
// Check architect (Spec 755 affinity-aware path).
|
|
126
214
|
if (agent === 'architect' || agent === 'arch') {
|
|
127
|
-
if (
|
|
215
|
+
if (entry.architects.size === 0) {
|
|
128
216
|
return {
|
|
129
217
|
code: 'NOT_FOUND',
|
|
130
218
|
message: `No architect terminal found in workspace '${path.basename(workspacePath)}'.`,
|
|
131
219
|
};
|
|
132
220
|
}
|
|
133
|
-
|
|
221
|
+
// Single-architect fast path: most workspaces have only 'main'.
|
|
222
|
+
// Identical answer to the full resolution path, but skips the state.db
|
|
223
|
+
// read entirely. Guarantees latency parity for solo-architect users.
|
|
224
|
+
if (entry.architects.size === 1 && entry.architects.has(DEFAULT_ARCHITECT_NAME)) {
|
|
225
|
+
const terminalId = entry.architects.get(DEFAULT_ARCHITECT_NAME);
|
|
226
|
+
return { terminalId, workspacePath, agent: 'architect' };
|
|
227
|
+
}
|
|
228
|
+
// Builder-context detection via state.db row presence. Three-valued
|
|
229
|
+
// result distinguishes "builder with explicit name" / "legacy builder"
|
|
230
|
+
// / "not a builder" — see lookupBuilderSpawningArchitect.
|
|
231
|
+
const spawningArchitect = sender ? lookupBuilderSpawningArchitect(sender, workspacePath) : undefined;
|
|
232
|
+
if (spawningArchitect !== undefined && sender) {
|
|
233
|
+
// Sender is a builder.
|
|
234
|
+
if (spawningArchitect === null) {
|
|
235
|
+
// Legacy builder: row exists, spawnedByArchitect is null. Route to
|
|
236
|
+
// 'main' if present; else fail with the spec's verbatim message.
|
|
237
|
+
const main = entry.architects.get(DEFAULT_ARCHITECT_NAME);
|
|
238
|
+
if (main)
|
|
239
|
+
return { terminalId: main, workspacePath, agent: 'architect' };
|
|
240
|
+
return {
|
|
241
|
+
code: 'NOT_FOUND',
|
|
242
|
+
message: legacyBuilderErrorMessage(sender, [...entry.architects.keys()]),
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
// Builder has an explicit spawning architect.
|
|
246
|
+
const target = entry.architects.get(spawningArchitect);
|
|
247
|
+
if (target)
|
|
248
|
+
return { terminalId: target, workspacePath, agent: 'architect' };
|
|
249
|
+
// Architect-gone fallback: route to 'main' if present; else fail.
|
|
250
|
+
const main = entry.architects.get(DEFAULT_ARCHITECT_NAME);
|
|
251
|
+
if (main)
|
|
252
|
+
return { terminalId: main, workspacePath, agent: 'architect' };
|
|
253
|
+
return {
|
|
254
|
+
code: 'NOT_FOUND',
|
|
255
|
+
message: architectGoneErrorMessage(sender, spawningArchitect, [...entry.architects.keys()]),
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
// Non-builder sender (or no sender): 'main' first, then first registered.
|
|
259
|
+
const terminalId = entry.architects.get(DEFAULT_ARCHITECT_NAME) ?? entry.architects.values().next().value;
|
|
260
|
+
return { terminalId, workspacePath, agent: 'architect' };
|
|
134
261
|
}
|
|
262
|
+
// `architect:<name>` addressing is handled earlier in resolveTarget via
|
|
263
|
+
// resolveArchitectByName. Reaching this point means the agent is a plain
|
|
264
|
+
// workspace-local name (builder/shell), so we fall through to those.
|
|
135
265
|
// Check builders — exact match (case-insensitive)
|
|
136
266
|
for (const [builderId, terminalId] of entry.builders) {
|
|
137
267
|
if (builderId.toLowerCase() === agent) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tower-messages.js","sourceRoot":"","sources":["../../../src/agent-farm/servers/tower-messages.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC1E,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"tower-messages.js","sourceRoot":"","sources":["../../../src/agent-farm/servers/tower-messages.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tower-routes.d.ts","sourceRoot":"","sources":["../../../src/agent-farm/servers/tower-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,IAAI,MAAM,WAAW,CAAC;AAa7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAIxE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"tower-routes.d.ts","sourceRoot":"","sources":["../../../src/agent-farm/servers/tower-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,IAAI,MAAM,WAAW,CAAC;AAa7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAIxE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAqElD,gFAAgF;AAChF,wBAAgB,eAAe,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAMtG;AAED,sGAAsG;AACtG,wBAAgB,cAAc,IAAI,IAAI,CAErC;AAMD,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACjE,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iBAAiB,EAAE,OAAO,CAAC;IAC3B,kBAAkB,EAAE,MAAM,cAAc,GAAG,IAAI,CAAC;IAChD,qBAAqB,EAAE,CAAC,YAAY,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACjH,YAAY,EAAE,CAAC,MAAM,EAAE,SAAS,KAAK,IAAI,CAAC;IAC1C,eAAe,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CACvC;AAuCD,wBAAsB,aAAa,CACjC,GAAG,EAAE,IAAI,CAAC,eAAe,EACzB,GAAG,EAAE,IAAI,CAAC,cAAc,EACxB,GAAG,EAAE,YAAY,GAChB,OAAO,CAAC,IAAI,CAAC,CAmFf"}
|
|
@@ -32,8 +32,9 @@ import { resolveTarget, broadcastMessage, isResolveError } from './tower-message
|
|
|
32
32
|
import { formatArchitectMessage, formatBuilderMessage } from '../utils/message-format.js';
|
|
33
33
|
import { SendBuffer } from './send-buffer.js';
|
|
34
34
|
import { writeMessageToSession } from './message-write.js';
|
|
35
|
-
import { getKnownWorkspacePaths, getInstances, getDirectorySuggestions, launchInstance, killTerminalWithShellper, stopInstance, } from './tower-instances.js';
|
|
35
|
+
import { getKnownWorkspacePaths, getInstances, getDirectorySuggestions, launchInstance, killTerminalWithShellper, stopInstance, addArchitect, } from './tower-instances.js';
|
|
36
36
|
import { OverviewCache } from './overview.js';
|
|
37
|
+
import { fetchIssue } from '../../lib/github.js';
|
|
37
38
|
import { computeAnalytics } from './analytics.js';
|
|
38
39
|
import { getAllTasks, executeTask, getTaskId } from './tower-cron.js';
|
|
39
40
|
import { getGlobalDb } from '../db/index.js';
|
|
@@ -66,6 +67,7 @@ const ROUTES = {
|
|
|
66
67
|
'GET /api/terminals': (_req, res) => handleTerminalList(res),
|
|
67
68
|
'GET /api/status': (_req, res) => handleStatus(res),
|
|
68
69
|
'GET /api/overview': (_req, res, url) => handleOverview(res, url),
|
|
70
|
+
'GET /api/issue': (_req, res, url) => handleIssueView(res, url),
|
|
69
71
|
'GET /api/analytics': (_req, res, url) => handleAnalytics(res, url),
|
|
70
72
|
'POST /api/overview/refresh': (_req, res, _url, ctx) => handleOverviewRefresh(res, ctx),
|
|
71
73
|
'GET /api/events': (req, res, _url, ctx) => handleSSEEvents(req, res, ctx),
|
|
@@ -124,6 +126,11 @@ export async function handleRequest(req, res, ctx) {
|
|
|
124
126
|
if (workspaceApiMatch) {
|
|
125
127
|
return await handleWorkspaceAction(req, res, ctx, workspaceApiMatch);
|
|
126
128
|
}
|
|
129
|
+
// Workspace API: /api/workspaces/:encodedPath/architects (Spec 755 — multi-architect)
|
|
130
|
+
const architectsMatch = url.pathname.match(/^\/api\/workspaces\/([^/]+)\/architects$/);
|
|
131
|
+
if (architectsMatch) {
|
|
132
|
+
return await handleAddArchitect(req, res, architectsMatch);
|
|
133
|
+
}
|
|
127
134
|
// Terminal-specific routes: /api/terminals/:id/* (Spec 0090 Phase 2)
|
|
128
135
|
const terminalRouteMatch = url.pathname.match(/^\/api\/terminals\/([^/]+)(\/.*)?$/);
|
|
129
136
|
if (terminalRouteMatch) {
|
|
@@ -176,6 +183,54 @@ async function handleListWorkspaces(res) {
|
|
|
176
183
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
177
184
|
res.end(JSON.stringify({ workspaces }));
|
|
178
185
|
}
|
|
186
|
+
/**
|
|
187
|
+
* POST /api/workspaces/:encodedPath/architects (Spec 755)
|
|
188
|
+
* Body: { name?: string }
|
|
189
|
+
* Adds a named architect terminal to an active workspace.
|
|
190
|
+
* Returns 200 { success: true, name, terminalId } on success,
|
|
191
|
+
* 400 / 404 with { success: false, error } otherwise.
|
|
192
|
+
*/
|
|
193
|
+
async function handleAddArchitect(req, res, match) {
|
|
194
|
+
if (req.method !== 'POST') {
|
|
195
|
+
res.writeHead(405, { 'Content-Type': 'application/json' });
|
|
196
|
+
res.end(JSON.stringify({ error: 'Method not allowed' }));
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
const [, encodedPath] = match;
|
|
200
|
+
let workspacePath;
|
|
201
|
+
try {
|
|
202
|
+
workspacePath = decodeWorkspacePath(encodedPath);
|
|
203
|
+
if (!workspacePath || (!workspacePath.startsWith('/') && !/^[A-Za-z]:[\\/]/.test(workspacePath))) {
|
|
204
|
+
throw new Error('Invalid path');
|
|
205
|
+
}
|
|
206
|
+
workspacePath = normalizeWorkspacePath(workspacePath);
|
|
207
|
+
}
|
|
208
|
+
catch {
|
|
209
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
210
|
+
res.end(JSON.stringify({ error: 'Invalid workspace path encoding' }));
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
let body;
|
|
214
|
+
try {
|
|
215
|
+
body = (await parseJsonBody(req));
|
|
216
|
+
}
|
|
217
|
+
catch {
|
|
218
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
219
|
+
res.end(JSON.stringify({ error: 'Invalid JSON body' }));
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
const result = await addArchitect(workspacePath, body.name);
|
|
223
|
+
if (result.success) {
|
|
224
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
225
|
+
res.end(JSON.stringify({ success: true, name: result.name, terminalId: result.terminalId }));
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
// Distinguish "workspace not active" (404) from validation errors (400).
|
|
229
|
+
const status = result.error?.includes('not running') ? 404 : 400;
|
|
230
|
+
res.writeHead(status, { 'Content-Type': 'application/json' });
|
|
231
|
+
res.end(JSON.stringify({ success: false, error: result.error }));
|
|
232
|
+
}
|
|
233
|
+
}
|
|
179
234
|
async function handleWorkspaceAction(req, res, ctx, match) {
|
|
180
235
|
const [, encodedPath, action] = match;
|
|
181
236
|
let workspacePath;
|
|
@@ -549,6 +604,30 @@ async function handleOverview(res, url, workspaceOverride) {
|
|
|
549
604
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
550
605
|
res.end(JSON.stringify(data));
|
|
551
606
|
}
|
|
607
|
+
async function handleIssueView(res, url) {
|
|
608
|
+
// Workspace resolution mirrors handleOverview: ?workspace= param, else
|
|
609
|
+
// the first known non-builder workspace path.
|
|
610
|
+
let workspaceRoot = url.searchParams.get('workspace');
|
|
611
|
+
if (!workspaceRoot) {
|
|
612
|
+
const knownPaths = getKnownWorkspacePaths();
|
|
613
|
+
workspaceRoot = knownPaths.find(p => !p.includes('/.builders/')) || null;
|
|
614
|
+
}
|
|
615
|
+
const number = url.searchParams.get('number');
|
|
616
|
+
if (!workspaceRoot || !number) {
|
|
617
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
618
|
+
res.end(JSON.stringify({ error: 'Missing workspace or number' }));
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
// Routes through the `issue-view` forge concept (forge-agnostic).
|
|
622
|
+
const issue = await fetchIssue(number, { cwd: workspaceRoot });
|
|
623
|
+
if (!issue) {
|
|
624
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
625
|
+
res.end(JSON.stringify({ error: `Issue #${number} not found or forge unavailable` }));
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
629
|
+
res.end(JSON.stringify(issue));
|
|
630
|
+
}
|
|
552
631
|
function handleOverviewRefresh(res, ctx) {
|
|
553
632
|
overviewCache.invalidate();
|
|
554
633
|
// Bugfix #388: Broadcast SSE event so all connected dashboard clients
|
|
@@ -659,8 +738,10 @@ async function handleSend(req, res, ctx) {
|
|
|
659
738
|
const raw = options.raw === true;
|
|
660
739
|
const noEnter = options.noEnter === true;
|
|
661
740
|
const interrupt = options.interrupt === true;
|
|
662
|
-
// Resolve the target address to a terminal ID
|
|
663
|
-
|
|
741
|
+
// Resolve the target address to a terminal ID.
|
|
742
|
+
// Spec 755: pass `from` so architect resolution is sender-affinity-aware
|
|
743
|
+
// when the sender is a builder. Non-builder senders see unchanged behavior.
|
|
744
|
+
const result = resolveTarget(to, workspace, from);
|
|
664
745
|
if (isResolveError(result)) {
|
|
665
746
|
const statusCode = result.code === 'AMBIGUOUS' ? 409
|
|
666
747
|
: result.code === 'NO_CONTEXT' ? 400
|
|
@@ -1120,8 +1201,11 @@ async function handleWorkspaceState(res, workspacePath) {
|
|
|
1120
1201
|
// entry — see getRehydratedTerminalsEntry doc.
|
|
1121
1202
|
const entry = await getRehydratedTerminalsEntry(workspacePath);
|
|
1122
1203
|
const manager = getTerminalManager();
|
|
1204
|
+
// Spec 761: type as DashboardState directly to prevent inline-literal drift.
|
|
1205
|
+
// ArchitectState.name (Spec 761) carries the architect's stable identity.
|
|
1123
1206
|
const state = {
|
|
1124
1207
|
architect: null,
|
|
1208
|
+
architects: [],
|
|
1125
1209
|
builders: [],
|
|
1126
1210
|
utils: [],
|
|
1127
1211
|
annotations: [],
|
|
@@ -1135,18 +1219,32 @@ async function handleWorkspaceState(res, workspacePath) {
|
|
|
1135
1219
|
} })(),
|
|
1136
1220
|
teamEnabled: await hasTeam(path.join(workspacePath, 'codev', 'team')),
|
|
1137
1221
|
};
|
|
1138
|
-
//
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1222
|
+
// Spec 761: build the architects collection from entry.architects.
|
|
1223
|
+
// - Skip entries whose session is unavailable (race / stale registration).
|
|
1224
|
+
// - Move 'main' to index 0 when present so consumers can rely on
|
|
1225
|
+
// architects[0] as the default architect.
|
|
1226
|
+
// Spec 755: the scalar `state.architect` is preserved as a backward-compat
|
|
1227
|
+
// pointer to the same default architect (architects[0] when present).
|
|
1228
|
+
const collectedArchitects = [];
|
|
1229
|
+
for (const [architectName, terminalId] of entry.architects) {
|
|
1230
|
+
const session = manager.getSession(terminalId);
|
|
1231
|
+
if (!session)
|
|
1232
|
+
continue;
|
|
1233
|
+
collectedArchitects.push({
|
|
1234
|
+
name: architectName,
|
|
1235
|
+
port: 0,
|
|
1236
|
+
pid: session.pid || 0,
|
|
1237
|
+
terminalId,
|
|
1238
|
+
persistent: isSessionPersistent(terminalId, session),
|
|
1239
|
+
});
|
|
1149
1240
|
}
|
|
1241
|
+
const mainIdx = collectedArchitects.findIndex(a => a.name === 'main');
|
|
1242
|
+
if (mainIdx > 0) {
|
|
1243
|
+
const [mainEntry] = collectedArchitects.splice(mainIdx, 1);
|
|
1244
|
+
collectedArchitects.unshift(mainEntry);
|
|
1245
|
+
}
|
|
1246
|
+
state.architects = collectedArchitects;
|
|
1247
|
+
state.architect = collectedArchitects[0] ?? null;
|
|
1150
1248
|
// Add shells from refreshed cache
|
|
1151
1249
|
for (const [shellId, terminalId] of entry.shells) {
|
|
1152
1250
|
const session = manager.getSession(terminalId);
|
|
@@ -1506,9 +1604,14 @@ async function handleWorkspaceTabDelete(res, ctx, workspacePath, tabId) {
|
|
|
1506
1604
|
}
|
|
1507
1605
|
}
|
|
1508
1606
|
else if (tabId === 'architect') {
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1607
|
+
// Spec 755: tabId 'architect' targets the architect surfaced by /api/state
|
|
1608
|
+
// — prefer 'main', else the first registered.
|
|
1609
|
+
const name = entry.architects.has('main')
|
|
1610
|
+
? 'main'
|
|
1611
|
+
: entry.architects.keys().next().value;
|
|
1612
|
+
if (name) {
|
|
1613
|
+
terminalId = entry.architects.get(name);
|
|
1614
|
+
entry.architects.delete(name);
|
|
1512
1615
|
}
|
|
1513
1616
|
}
|
|
1514
1617
|
if (terminalId) {
|
|
@@ -1527,9 +1630,10 @@ async function handleWorkspaceTabDelete(res, ctx, workspacePath, tabId) {
|
|
|
1527
1630
|
async function handleWorkspaceStopAll(res, workspacePath) {
|
|
1528
1631
|
const entry = getWorkspaceTerminalsEntry(workspacePath);
|
|
1529
1632
|
const manager = getTerminalManager();
|
|
1530
|
-
// Kill all terminals (disable shellper auto-restart if applicable)
|
|
1531
|
-
|
|
1532
|
-
|
|
1633
|
+
// Kill all terminals (disable shellper auto-restart if applicable).
|
|
1634
|
+
// Spec 755: iterate all named architects, not just the singleton.
|
|
1635
|
+
for (const terminalId of entry.architects.values()) {
|
|
1636
|
+
await killTerminalWithShellper(manager, terminalId);
|
|
1533
1637
|
}
|
|
1534
1638
|
for (const terminalId of entry.shells.values()) {
|
|
1535
1639
|
await killTerminalWithShellper(manager, terminalId);
|