@alisio/plugin-swarm 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/agents/swarm-architect.md +20 -0
- package/.agents/agents/swarm-cleaner.md +20 -0
- package/.agents/agents/swarm-coder.md +20 -0
- package/.agents/agents/swarm-hardener.md +20 -0
- package/.agents/agents/swarm-lieutenant.md +20 -0
- package/.agents/agents/swarm-qa.md +20 -0
- package/.agents/agents/swarm-refactorer.md +20 -0
- package/.agents/agents/swarm-specifier.md +20 -0
- package/.agents/skills/swarm-architecture-rules/SKILL.md +43 -0
- package/.agents/skills/swarm-clarification/SKILL.md +42 -0
- package/.agents/skills/swarm-cleaner-metrics/SKILL.md +44 -0
- package/.agents/skills/swarm-engineering-constitution/SKILL.md +44 -0
- package/.agents/skills/swarm-gherkin-spec/SKILL.md +43 -0
- package/.agents/skills/swarm-handoff-protocol/SKILL.md +45 -0
- package/.agents/skills/swarm-lieutenant/SKILL.md +41 -0
- package/.agents/skills/swarm-mutation-hardening/SKILL.md +43 -0
- package/.agents/skills/swarm-qa-acceptance/SKILL.md +42 -0
- package/.agents/skills/swarm-tdd-slice/SKILL.md +44 -0
- package/.agents/skills/swarm-toolchain-profile/SKILL.md +41 -0
- package/LICENSE +21 -0
- package/README.es.md +337 -0
- package/README.md +322 -2
- package/assets/approval-flow.svg +1 -0
- package/assets/architecture.svg +1 -0
- package/assets/audit-states.svg +1 -0
- package/assets/dashboard.css +964 -0
- package/assets/dashboard.html +123 -0
- package/assets/dashboard.js +965 -0
- package/assets/dashboard.svg +1 -0
- package/assets/handoff-sequence.svg +1 -0
- package/assets/pack-pipelines.svg +1 -0
- package/assets/packs/four-pack/pack.json +18 -0
- package/assets/packs/six-pack/pack.json +22 -0
- package/assets/packs/two-pack/pack.json +14 -0
- package/assets/screenshots/dashboard-board-dark.png +0 -0
- package/assets/screenshots/dashboard-board-light.png +0 -0
- package/assets/screenshots/dashboard-documents-diff.png +0 -0
- package/assets/screenshots/dashboard-empty-state.png +0 -0
- package/assets/screenshots/dashboard-phone.png +0 -0
- package/assets/screenshots/dashboard-reject-dialog.png +0 -0
- package/assets/screenshots/dashboard-transcript-tail.png +0 -0
- package/assets/screenshots/dashboard-work-queue.png +0 -0
- package/assets/task-states.svg +1 -0
- package/assets/toolchains/node-ts.json +61 -0
- package/cover.svg +46 -0
- package/dist/adapters/child-session-runner.d.ts +36 -0
- package/dist/adapters/child-session-runner.d.ts.map +1 -0
- package/dist/adapters/child-session-runner.js +156 -0
- package/dist/adapters/child-session-runner.js.map +1 -0
- package/dist/adapters/fs-board-store.d.ts +11 -0
- package/dist/adapters/fs-board-store.d.ts.map +1 -0
- package/dist/adapters/fs-board-store.js +27 -0
- package/dist/adapters/fs-board-store.js.map +1 -0
- package/dist/adapters/fs-handoff-store.d.ts +36 -0
- package/dist/adapters/fs-handoff-store.d.ts.map +1 -0
- package/dist/adapters/fs-handoff-store.js +254 -0
- package/dist/adapters/fs-handoff-store.js.map +1 -0
- package/dist/adapters/fs-task-state-store.d.ts +16 -0
- package/dist/adapters/fs-task-state-store.d.ts.map +1 -0
- package/dist/adapters/fs-task-state-store.js +66 -0
- package/dist/adapters/fs-task-state-store.js.map +1 -0
- package/dist/adapters/git-worktree.d.ts +31 -0
- package/dist/adapters/git-worktree.d.ts.map +1 -0
- package/dist/adapters/git-worktree.js +209 -0
- package/dist/adapters/git-worktree.js.map +1 -0
- package/dist/adapters/process-exec.d.ts +36 -0
- package/dist/adapters/process-exec.d.ts.map +1 -0
- package/dist/adapters/process-exec.js +138 -0
- package/dist/adapters/process-exec.js.map +1 -0
- package/dist/adapters/process-gate-runner.d.ts +39 -0
- package/dist/adapters/process-gate-runner.d.ts.map +1 -0
- package/dist/adapters/process-gate-runner.js +242 -0
- package/dist/adapters/process-gate-runner.js.map +1 -0
- package/dist/adapters/unavailable-runner.d.ts +4 -0
- package/dist/adapters/unavailable-runner.d.ts.map +1 -0
- package/dist/adapters/unavailable-runner.js +12 -0
- package/dist/adapters/unavailable-runner.js.map +1 -0
- package/dist/app/audit.d.ts +23 -0
- package/dist/app/audit.d.ts.map +1 -0
- package/dist/app/audit.js +9 -0
- package/dist/app/audit.js.map +1 -0
- package/dist/app/budget.d.ts +29 -0
- package/dist/app/budget.d.ts.map +1 -0
- package/dist/app/budget.js +78 -0
- package/dist/app/budget.js.map +1 -0
- package/dist/app/chat-store.d.ts +18 -0
- package/dist/app/chat-store.d.ts.map +1 -0
- package/dist/app/chat-store.js +48 -0
- package/dist/app/chat-store.js.map +1 -0
- package/dist/app/documents.d.ts +33 -0
- package/dist/app/documents.d.ts.map +1 -0
- package/dist/app/documents.js +63 -0
- package/dist/app/documents.js.map +1 -0
- package/dist/app/forge.d.ts +106 -0
- package/dist/app/forge.d.ts.map +1 -0
- package/dist/app/forge.js +420 -0
- package/dist/app/forge.js.map +1 -0
- package/dist/app/operator.d.ts +46 -0
- package/dist/app/operator.d.ts.map +1 -0
- package/dist/app/operator.js +168 -0
- package/dist/app/operator.js.map +1 -0
- package/dist/app/pidfile.d.ts +18 -0
- package/dist/app/pidfile.d.ts.map +1 -0
- package/dist/app/pidfile.js +56 -0
- package/dist/app/pidfile.js.map +1 -0
- package/dist/app/project.d.ts +93 -0
- package/dist/app/project.d.ts.map +1 -0
- package/dist/app/project.js +291 -0
- package/dist/app/project.js.map +1 -0
- package/dist/app/prompts.d.ts +48 -0
- package/dist/app/prompts.d.ts.map +1 -0
- package/dist/app/prompts.js +88 -0
- package/dist/app/prompts.js.map +1 -0
- package/dist/app/pump.d.ts +144 -0
- package/dist/app/pump.d.ts.map +1 -0
- package/dist/app/pump.js +707 -0
- package/dist/app/pump.js.map +1 -0
- package/dist/app/services.d.ts +112 -0
- package/dist/app/services.d.ts.map +1 -0
- package/dist/app/services.js +323 -0
- package/dist/app/services.js.map +1 -0
- package/dist/app/state.d.ts +70 -0
- package/dist/app/state.d.ts.map +1 -0
- package/dist/app/state.js +96 -0
- package/dist/app/state.js.map +1 -0
- package/dist/args.d.ts +10 -0
- package/dist/args.d.ts.map +1 -0
- package/dist/args.js +33 -0
- package/dist/args.js.map +1 -0
- package/dist/coordinator.d.ts +80 -0
- package/dist/coordinator.d.ts.map +1 -0
- package/dist/coordinator.js +481 -0
- package/dist/coordinator.js.map +1 -0
- package/dist/dashboard/api.d.ts +28 -0
- package/dist/dashboard/api.d.ts.map +1 -0
- package/dist/dashboard/api.js +178 -0
- package/dist/dashboard/api.js.map +1 -0
- package/dist/dashboard/auth.d.ts +13 -0
- package/dist/dashboard/auth.d.ts.map +1 -0
- package/dist/dashboard/auth.js +36 -0
- package/dist/dashboard/auth.js.map +1 -0
- package/dist/dashboard/open.d.ts +7 -0
- package/dist/dashboard/open.d.ts.map +1 -0
- package/dist/dashboard/open.js +17 -0
- package/dist/dashboard/open.js.map +1 -0
- package/dist/dashboard/server.d.ts +28 -0
- package/dist/dashboard/server.d.ts.map +1 -0
- package/dist/dashboard/server.js +176 -0
- package/dist/dashboard/server.js.map +1 -0
- package/dist/doctor.d.ts +28 -0
- package/dist/doctor.d.ts.map +1 -0
- package/dist/doctor.js +108 -0
- package/dist/doctor.js.map +1 -0
- package/dist/domain/attention.d.ts +20 -0
- package/dist/domain/attention.d.ts.map +1 -0
- package/dist/domain/attention.js +41 -0
- package/dist/domain/attention.js.map +1 -0
- package/dist/domain/envelope.d.ts +37 -0
- package/dist/domain/envelope.d.ts.map +1 -0
- package/dist/domain/envelope.js +106 -0
- package/dist/domain/envelope.js.map +1 -0
- package/dist/domain/handoff.d.ts +57 -0
- package/dist/domain/handoff.d.ts.map +1 -0
- package/dist/domain/handoff.js +191 -0
- package/dist/domain/handoff.js.map +1 -0
- package/dist/domain/identifiers.d.ts +18 -0
- package/dist/domain/identifiers.d.ts.map +1 -0
- package/dist/domain/identifiers.js +70 -0
- package/dist/domain/identifiers.js.map +1 -0
- package/dist/domain/pack.d.ts +45 -0
- package/dist/domain/pack.d.ts.map +1 -0
- package/dist/domain/pack.js +207 -0
- package/dist/domain/pack.js.map +1 -0
- package/dist/domain/pipeline.d.ts +33 -0
- package/dist/domain/pipeline.d.ts.map +1 -0
- package/dist/domain/pipeline.js +50 -0
- package/dist/domain/pipeline.js.map +1 -0
- package/dist/domain/task.d.ts +26 -0
- package/dist/domain/task.d.ts.map +1 -0
- package/dist/domain/task.js +154 -0
- package/dist/domain/task.js.map +1 -0
- package/dist/domain/taskstate.d.ts +37 -0
- package/dist/domain/taskstate.d.ts.map +1 -0
- package/dist/domain/taskstate.js +98 -0
- package/dist/domain/taskstate.js.map +1 -0
- package/dist/gates/dry.d.ts +14 -0
- package/dist/gates/dry.d.ts.map +1 -0
- package/dist/gates/dry.js +71 -0
- package/dist/gates/dry.js.map +1 -0
- package/dist/gates/parsers.d.ts +34 -0
- package/dist/gates/parsers.d.ts.map +1 -0
- package/dist/gates/parsers.js +120 -0
- package/dist/gates/parsers.js.map +1 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +207 -0
- package/dist/index.js.map +1 -0
- package/dist/options.d.ts +13 -0
- package/dist/options.d.ts.map +1 -0
- package/dist/options.js +27 -0
- package/dist/options.js.map +1 -0
- package/dist/panel.d.ts +5 -0
- package/dist/panel.d.ts.map +1 -0
- package/dist/panel.js +52 -0
- package/dist/panel.js.map +1 -0
- package/dist/ports/agent-runner.d.ts +51 -0
- package/dist/ports/agent-runner.d.ts.map +1 -0
- package/dist/ports/agent-runner.js +2 -0
- package/dist/ports/agent-runner.js.map +1 -0
- package/dist/ports/board-store.d.ts +7 -0
- package/dist/ports/board-store.d.ts.map +1 -0
- package/dist/ports/board-store.js +2 -0
- package/dist/ports/board-store.js.map +1 -0
- package/dist/ports/clock.d.ts +5 -0
- package/dist/ports/clock.d.ts.map +1 -0
- package/dist/ports/clock.js +2 -0
- package/dist/ports/clock.js.map +1 -0
- package/dist/ports/gate-runner.d.ts +34 -0
- package/dist/ports/gate-runner.d.ts.map +1 -0
- package/dist/ports/gate-runner.js +2 -0
- package/dist/ports/gate-runner.js.map +1 -0
- package/dist/ports/handoff-store.d.ts +39 -0
- package/dist/ports/handoff-store.d.ts.map +1 -0
- package/dist/ports/handoff-store.js +2 -0
- package/dist/ports/handoff-store.js.map +1 -0
- package/dist/ports/isolation.d.ts +43 -0
- package/dist/ports/isolation.d.ts.map +1 -0
- package/dist/ports/isolation.js +2 -0
- package/dist/ports/isolation.js.map +1 -0
- package/dist/ports/notifier.d.ts +28 -0
- package/dist/ports/notifier.d.ts.map +1 -0
- package/dist/ports/notifier.js +2 -0
- package/dist/ports/notifier.js.map +1 -0
- package/dist/ports/task-state-store.d.ts +10 -0
- package/dist/ports/task-state-store.d.ts.map +1 -0
- package/dist/ports/task-state-store.js +2 -0
- package/dist/ports/task-state-store.js.map +1 -0
- package/dist/refs.d.ts +10 -0
- package/dist/refs.d.ts.map +1 -0
- package/dist/refs.js +19 -0
- package/dist/refs.js.map +1 -0
- package/dist/resources.d.ts +55 -0
- package/dist/resources.d.ts.map +1 -0
- package/dist/resources.js +255 -0
- package/dist/resources.js.map +1 -0
- package/dist/status.d.ts +18 -0
- package/dist/status.d.ts.map +1 -0
- package/dist/status.js +60 -0
- package/dist/status.js.map +1 -0
- package/dist/storage.d.ts +18 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +79 -0
- package/dist/storage.js.map +1 -0
- package/dist/toolchains/profile.d.ts +21 -0
- package/dist/toolchains/profile.d.ts.map +1 -0
- package/dist/toolchains/profile.js +60 -0
- package/dist/toolchains/profile.js.map +1 -0
- package/dist/toolchains/thresholds.d.ts +29 -0
- package/dist/toolchains/thresholds.d.ts.map +1 -0
- package/dist/toolchains/thresholds.js +45 -0
- package/dist/toolchains/thresholds.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +3 -0
- package/dist/version.js.map +1 -0
- package/package.json +51 -3
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { rejectActions } from "../app/operator.js";
|
|
2
|
+
import { validateProjectName, validateRole, validateTaskName } from "../domain/identifiers.js";
|
|
3
|
+
/** A failure with an HTTP status; the message is safe to show the operator. */
|
|
4
|
+
export class ApiError extends Error {
|
|
5
|
+
status;
|
|
6
|
+
constructor(status, message) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.status = status;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
const MAX_TEXT = 8000;
|
|
12
|
+
/** Map a service error to an HTTP status from its message: our own messages are the contract. */
|
|
13
|
+
export function statusFor(error) {
|
|
14
|
+
if (error instanceof ApiError)
|
|
15
|
+
return error.status;
|
|
16
|
+
const message = error instanceof Error ? error.message : "";
|
|
17
|
+
if (/^Unknown /i.test(message))
|
|
18
|
+
return 404;
|
|
19
|
+
if (/already exists/i.test(message))
|
|
20
|
+
return 409;
|
|
21
|
+
return 400;
|
|
22
|
+
}
|
|
23
|
+
const isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
24
|
+
function bodyOf(request) {
|
|
25
|
+
if (!isRecord(request.body))
|
|
26
|
+
throw new ApiError(400, "Expected a JSON object body");
|
|
27
|
+
return request.body;
|
|
28
|
+
}
|
|
29
|
+
function text(source, key, options = {}) {
|
|
30
|
+
const value = source[key];
|
|
31
|
+
if (value === undefined && options.optional)
|
|
32
|
+
return "";
|
|
33
|
+
if (typeof value !== "string")
|
|
34
|
+
throw new ApiError(400, `"${key}" must be a string`);
|
|
35
|
+
if (value.length > (options.max ?? MAX_TEXT))
|
|
36
|
+
throw new ApiError(400, `"${key}" is too long`);
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
const project = (source) => {
|
|
40
|
+
const value = source instanceof URLSearchParams ? source.get("project") : source.project;
|
|
41
|
+
if (typeof value !== "string")
|
|
42
|
+
throw new ApiError(400, "A project is required");
|
|
43
|
+
return validateProjectName(value);
|
|
44
|
+
};
|
|
45
|
+
const task = (source) => {
|
|
46
|
+
const value = source instanceof URLSearchParams ? source.get("task") : source.task;
|
|
47
|
+
if (typeof value !== "string")
|
|
48
|
+
throw new ApiError(400, "A task is required");
|
|
49
|
+
return validateTaskName(value);
|
|
50
|
+
};
|
|
51
|
+
function itemId(id, kind) {
|
|
52
|
+
const parts = id.split(":");
|
|
53
|
+
if (parts.length !== 3 || parts[0] !== kind) {
|
|
54
|
+
throw new ApiError(400, `Expected an id like ${kind}:<project>:<task>`);
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
project: validateProjectName(parts[1]),
|
|
58
|
+
task: validateTaskName(parts[2]),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
const ok = (body = { ok: true }) => ({ status: 200, body });
|
|
62
|
+
const routes = [
|
|
63
|
+
{
|
|
64
|
+
method: "GET",
|
|
65
|
+
pattern: ["state"],
|
|
66
|
+
handler: async ({ services, workspace }) => ok(await services.state(workspace)),
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
method: "GET",
|
|
70
|
+
pattern: ["mission"],
|
|
71
|
+
handler: async ({ services, workspace }, request) => {
|
|
72
|
+
const name = project(request.query);
|
|
73
|
+
return ok({ project: name, mission: await services.mission(workspace, name) });
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
method: "GET",
|
|
78
|
+
pattern: ["doc"],
|
|
79
|
+
handler: async ({ services, workspace }, request) => ok(await services.documents(workspace, project(request.query), task(request.query))),
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
method: "GET",
|
|
83
|
+
pattern: ["agents", ":role", "tail"],
|
|
84
|
+
handler: async ({ services, workspace }, request, [role]) => {
|
|
85
|
+
validateRole(role);
|
|
86
|
+
return ok(await services.agentTail(workspace, project(request.query), role));
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
...["delete", "retry"].map((action) => ({
|
|
90
|
+
method: "POST",
|
|
91
|
+
pattern: ["tasks", action],
|
|
92
|
+
handler: async ({ services, workspace }, request) => {
|
|
93
|
+
const body = bodyOf(request);
|
|
94
|
+
const args = [workspace, project(body), task(body)];
|
|
95
|
+
if (action === "delete")
|
|
96
|
+
await services.deleteTask(...args);
|
|
97
|
+
else
|
|
98
|
+
await services.retryTask(...args);
|
|
99
|
+
return ok();
|
|
100
|
+
},
|
|
101
|
+
})),
|
|
102
|
+
{
|
|
103
|
+
method: "POST",
|
|
104
|
+
pattern: ["approvals", ":id", ":action"],
|
|
105
|
+
handler: async ({ services, workspace }, request, [id, action]) => {
|
|
106
|
+
if (action !== "approve" && action !== "comments" && action !== "reject") {
|
|
107
|
+
throw new ApiError(404, "Unknown approval action");
|
|
108
|
+
}
|
|
109
|
+
const ref = itemId(id, "approval");
|
|
110
|
+
const body = bodyOf(request);
|
|
111
|
+
if (action === "approve") {
|
|
112
|
+
await services.approve(workspace, ref.project, ref.task);
|
|
113
|
+
}
|
|
114
|
+
else if (action === "comments") {
|
|
115
|
+
if (body.clear === true) {
|
|
116
|
+
await services.clearComments(workspace, ref.project, ref.task);
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
await services.addComment(workspace, ref.project, ref.task, text(body, "doc", { max: 300 }), text(body, "text"));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
const chosen = text(body, "action", { max: 20 });
|
|
124
|
+
if (!rejectActions.includes(chosen)) {
|
|
125
|
+
throw new ApiError(400, "Reject needs one of: retry, delete, accept");
|
|
126
|
+
}
|
|
127
|
+
const comments = body.comments === undefined ? undefined : text(body, "comments");
|
|
128
|
+
await services.reject(workspace, ref.project, ref.task, chosen, comments);
|
|
129
|
+
}
|
|
130
|
+
return ok();
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
method: "POST",
|
|
135
|
+
pattern: ["clarifications", ":id", "answer"],
|
|
136
|
+
handler: async ({ services, workspace }, request, [id]) => {
|
|
137
|
+
const ref = itemId(id, "clarification");
|
|
138
|
+
const answer = text(bodyOf(request), "text");
|
|
139
|
+
if (!answer.trim())
|
|
140
|
+
throw new ApiError(400, "An answer needs text");
|
|
141
|
+
await services.answer(workspace, ref.project, ref.task, answer);
|
|
142
|
+
return ok();
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
];
|
|
146
|
+
/** The mutating routes, for the "complementary only" test: gate decisions and nothing else. */
|
|
147
|
+
export const mutatingRoutes = () => routes
|
|
148
|
+
.filter((route) => route.method === "POST")
|
|
149
|
+
.map((route) => `POST /api/${route.pattern.join("/")}`)
|
|
150
|
+
.sort();
|
|
151
|
+
function match(pattern, segments) {
|
|
152
|
+
if (pattern.length !== segments.length)
|
|
153
|
+
return undefined;
|
|
154
|
+
const params = [];
|
|
155
|
+
for (const [index, part] of pattern.entries()) {
|
|
156
|
+
if (part.startsWith(":"))
|
|
157
|
+
params.push(segments[index]);
|
|
158
|
+
else if (part !== segments[index])
|
|
159
|
+
return undefined;
|
|
160
|
+
}
|
|
161
|
+
return params;
|
|
162
|
+
}
|
|
163
|
+
/** Dispatch one API request. Every failure is an `ApiError` or a service `Error`; the caller maps it. */
|
|
164
|
+
export async function handleApi(deps, request) {
|
|
165
|
+
let pathMatched = false;
|
|
166
|
+
for (const route of routes) {
|
|
167
|
+
const params = match(route.pattern, request.segments);
|
|
168
|
+
if (!params)
|
|
169
|
+
continue;
|
|
170
|
+
pathMatched = true;
|
|
171
|
+
if (route.method === request.method)
|
|
172
|
+
return route.handler(deps, request, params);
|
|
173
|
+
}
|
|
174
|
+
if (pathMatched)
|
|
175
|
+
throw new ApiError(405, "Method not allowed");
|
|
176
|
+
throw new ApiError(404, "Unknown route");
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/dashboard/api.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare const TOKEN_COOKIE = "swarm_token";
|
|
2
|
+
export declare const TOKEN_HEADER = "x-swarm-token";
|
|
3
|
+
/** A random 256-bit token, hex encoded. One per dashboard run (AD-9). */
|
|
4
|
+
export declare const generateToken: () => string;
|
|
5
|
+
/** Constant-time comparison; a missing or differently sized value never matches. */
|
|
6
|
+
export declare function tokenMatches(expected: string, provided: string | undefined): boolean;
|
|
7
|
+
export declare function parseCookies(header: string | undefined): Record<string, string>;
|
|
8
|
+
/** The only `Host` values accepted: the loopback names with the bound port (DNS rebinding guard). */
|
|
9
|
+
export declare const allowedHosts: (port: number) => Set<string>;
|
|
10
|
+
export declare const hostAllowed: (host: string | undefined, port: number) => boolean;
|
|
11
|
+
/** A missing `Origin` is fine (same-origin navigation, scripts); a present one must be ours. */
|
|
12
|
+
export declare function originAllowed(origin: string | undefined, port: number): boolean;
|
|
13
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/dashboard/auth.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,YAAY,gBAAgB,CAAC;AAC1C,eAAO,MAAM,YAAY,kBAAkB,CAAC;AAE5C,yEAAyE;AACzE,eAAO,MAAM,aAAa,QAAO,MAAyC,CAAC;AAE3E,oFAAoF;AACpF,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAKpF;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAU/E;AAED,qGAAqG;AACrG,eAAO,MAAM,YAAY,SAAU,MAAM,KAAG,GAAG,CAAC,MAAM,CACD,CAAC;AAEtD,eAAO,MAAM,WAAW,SAAU,MAAM,GAAG,SAAS,QAAQ,MAAM,KAAG,OACH,CAAC;AAEnE,gGAAgG;AAChG,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAG/E"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { randomBytes, timingSafeEqual } from "node:crypto";
|
|
2
|
+
export const TOKEN_COOKIE = "swarm_token";
|
|
3
|
+
export const TOKEN_HEADER = "x-swarm-token";
|
|
4
|
+
/** A random 256-bit token, hex encoded. One per dashboard run (AD-9). */
|
|
5
|
+
export const generateToken = () => randomBytes(32).toString("hex");
|
|
6
|
+
/** Constant-time comparison; a missing or differently sized value never matches. */
|
|
7
|
+
export function tokenMatches(expected, provided) {
|
|
8
|
+
if (!provided)
|
|
9
|
+
return false;
|
|
10
|
+
const a = Buffer.from(expected);
|
|
11
|
+
const b = Buffer.from(provided);
|
|
12
|
+
return a.length === b.length && timingSafeEqual(a, b);
|
|
13
|
+
}
|
|
14
|
+
export function parseCookies(header) {
|
|
15
|
+
const out = {};
|
|
16
|
+
if (!header)
|
|
17
|
+
return out;
|
|
18
|
+
for (const part of header.split(";")) {
|
|
19
|
+
const cookie = part.trim();
|
|
20
|
+
const index = cookie.indexOf("=");
|
|
21
|
+
if (index <= 0)
|
|
22
|
+
continue;
|
|
23
|
+
out[cookie.slice(0, index)] = cookie.slice(index + 1).trim();
|
|
24
|
+
}
|
|
25
|
+
return out;
|
|
26
|
+
}
|
|
27
|
+
/** The only `Host` values accepted: the loopback names with the bound port (DNS rebinding guard). */
|
|
28
|
+
export const allowedHosts = (port) => new Set([`127.0.0.1:${port}`, `localhost:${port}`]);
|
|
29
|
+
export const hostAllowed = (host, port) => host !== undefined && allowedHosts(port).has(host.toLowerCase());
|
|
30
|
+
/** A missing `Origin` is fine (same-origin navigation, scripts); a present one must be ours. */
|
|
31
|
+
export function originAllowed(origin, port) {
|
|
32
|
+
if (origin === undefined)
|
|
33
|
+
return true;
|
|
34
|
+
return [...allowedHosts(port)].some((host) => origin === `http://${host}`);
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/dashboard/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE3D,MAAM,CAAC,MAAM,YAAY,GAAG,aAAa,CAAC;AAC1C,MAAM,CAAC,MAAM,YAAY,GAAG,eAAe,CAAC;AAE5C,yEAAyE;AACzE,MAAM,CAAC,MAAM,aAAa,GAAG,GAAW,EAAE,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AAE3E,oFAAoF;AACpF,MAAM,UAAU,YAAY,CAAC,QAAgB,EAAE,QAA4B;IACzE,IAAI,CAAC,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5B,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAChC,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAChC,OAAO,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,IAAI,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AACxD,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,MAA0B;IACrD,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,IAAI,CAAC,MAAM;QAAE,OAAO,GAAG,CAAC;IACxB,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACrC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,KAAK,IAAI,CAAC;YAAE,SAAS;QACzB,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC/D,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,qGAAqG;AACrG,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,IAAY,EAAe,EAAE,CACxD,IAAI,GAAG,CAAC,CAAC,aAAa,IAAI,EAAE,EAAE,aAAa,IAAI,EAAE,CAAC,CAAC,CAAC;AAEtD,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,IAAwB,EAAE,IAAY,EAAW,EAAE,CAC7E,IAAI,KAAK,SAAS,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;AAEnE,gGAAgG;AAChG,MAAM,UAAU,aAAa,CAAC,MAA0B,EAAE,IAAY;IACpE,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACtC,OAAO,CAAC,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,KAAK,UAAU,IAAI,EAAE,CAAC,CAAC;AAC7E,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Open a URL in the default browser, best effort. The URL is built by the plugin (loopback host,
|
|
3
|
+
* numeric port, hex token), the command is an argument array and every failure is swallowed:
|
|
4
|
+
* the operator can always open the printed link by hand.
|
|
5
|
+
*/
|
|
6
|
+
export declare function openInBrowser(url: string): void;
|
|
7
|
+
//# sourceMappingURL=open.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"open.d.ts","sourceRoot":"","sources":["../../src/dashboard/open.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAU/C"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
/**
|
|
3
|
+
* Open a URL in the default browser, best effort. The URL is built by the plugin (loopback host,
|
|
4
|
+
* numeric port, hex token), the command is an argument array and every failure is swallowed:
|
|
5
|
+
* the operator can always open the printed link by hand.
|
|
6
|
+
*/
|
|
7
|
+
export function openInBrowser(url) {
|
|
8
|
+
const [command, args] = process.platform === "darwin"
|
|
9
|
+
? ["open", [url]]
|
|
10
|
+
: process.platform === "win32"
|
|
11
|
+
? ["rundll32", ["url.dll,FileProtocolHandler", url]]
|
|
12
|
+
: ["xdg-open", [url]];
|
|
13
|
+
const child = spawn(command, args, { stdio: "ignore", detached: true });
|
|
14
|
+
child.on("error", () => undefined);
|
|
15
|
+
child.unref();
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=open.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"open.js","sourceRoot":"","sources":["../../src/dashboard/open.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE3C;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,GAAW;IACvC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,GACnB,OAAO,CAAC,QAAQ,KAAK,QAAQ;QAC3B,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC;QACjB,CAAC,CAAC,OAAO,CAAC,QAAQ,KAAK,OAAO;YAC5B,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,6BAA6B,EAAE,GAAG,CAAC,CAAC;YACpD,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5B,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IACxE,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IACnC,KAAK,CAAC,KAAK,EAAE,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { SwarmServices } from "../app/services.js";
|
|
2
|
+
import { type DashboardAsset } from "../resources.js";
|
|
3
|
+
export declare const MAX_BODY_BYTES: number;
|
|
4
|
+
export interface DashboardOptions {
|
|
5
|
+
services: SwarmServices;
|
|
6
|
+
workspace: string;
|
|
7
|
+
/** Test seam: a fixed token. Production generates a random 256-bit one. */
|
|
8
|
+
token?: string;
|
|
9
|
+
/** Test seam: where the static files come from. */
|
|
10
|
+
assets?: (name: DashboardAsset) => Promise<string>;
|
|
11
|
+
/** Called once when the server stops, however that happens. */
|
|
12
|
+
onClosed?: () => void;
|
|
13
|
+
}
|
|
14
|
+
export interface DashboardHandle {
|
|
15
|
+
/** The URL to open: it carries the token once, exchanged for a cookie and then dropped. */
|
|
16
|
+
url: string;
|
|
17
|
+
baseUrl: string;
|
|
18
|
+
port: number;
|
|
19
|
+
token: string;
|
|
20
|
+
close(): Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The dashboard server: static page plus JSON API on `127.0.0.1` (AD-9). Every request needs the
|
|
24
|
+
* per-run token, a loopback `Host` and (when present) a local `Origin`; there is no CORS and the
|
|
25
|
+
* CSP forbids anything but the page's own origin. Mutations call the same services as the commands.
|
|
26
|
+
*/
|
|
27
|
+
export declare function startDashboard(options: DashboardOptions): Promise<DashboardHandle>;
|
|
28
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/dashboard/server.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,KAAK,cAAc,EAAsB,MAAM,iBAAiB,CAAC;AAY1E,eAAO,MAAM,cAAc,QAAa,CAAC;AAezC,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,aAAa,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,2EAA2E;IAC3E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACnD,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;CACvB;AAED,MAAM,WAAW,eAAe;IAC9B,2FAA2F;IAC3F,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAyDD;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC,CAgHxF"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { createServer } from "node:http";
|
|
2
|
+
import { loadDashboardAsset } from "../resources.js";
|
|
3
|
+
import { ApiError, handleApi, statusFor } from "./api.js";
|
|
4
|
+
import { generateToken, hostAllowed, originAllowed, parseCookies, TOKEN_COOKIE, TOKEN_HEADER, tokenMatches, } from "./auth.js";
|
|
5
|
+
export const MAX_BODY_BYTES = 256 * 1024;
|
|
6
|
+
const CSP = [
|
|
7
|
+
"default-src 'self'",
|
|
8
|
+
"script-src 'self'",
|
|
9
|
+
"style-src 'self'",
|
|
10
|
+
"img-src 'self' data:",
|
|
11
|
+
"connect-src 'self'",
|
|
12
|
+
"base-uri 'none'",
|
|
13
|
+
"form-action 'none'",
|
|
14
|
+
"frame-ancestors 'none'",
|
|
15
|
+
].join("; ");
|
|
16
|
+
const TOKEN_PLACEHOLDER = "__SWARM_TOKEN__";
|
|
17
|
+
const STATIC = {
|
|
18
|
+
"/": { asset: "dashboard.html", type: "text/html; charset=utf-8" },
|
|
19
|
+
"/dashboard.css": { asset: "dashboard.css", type: "text/css; charset=utf-8" },
|
|
20
|
+
"/dashboard.js": { asset: "dashboard.js", type: "text/javascript; charset=utf-8" },
|
|
21
|
+
};
|
|
22
|
+
function send(res, status, body, type, extra = {}) {
|
|
23
|
+
res.writeHead(status, {
|
|
24
|
+
"content-type": type,
|
|
25
|
+
"content-length": Buffer.byteLength(body),
|
|
26
|
+
"content-security-policy": CSP,
|
|
27
|
+
"x-content-type-options": "nosniff",
|
|
28
|
+
"referrer-policy": "no-referrer",
|
|
29
|
+
"cache-control": "no-store",
|
|
30
|
+
"cross-origin-resource-policy": "same-origin",
|
|
31
|
+
...extra,
|
|
32
|
+
});
|
|
33
|
+
res.end(body);
|
|
34
|
+
}
|
|
35
|
+
const sendJson = (res, status, value, extra = {}) => send(res, status, JSON.stringify(value), "application/json; charset=utf-8", extra);
|
|
36
|
+
const fail = (res, status, message, extra = {}) => sendJson(res, status, { error: message }, extra);
|
|
37
|
+
function readBody(req) {
|
|
38
|
+
return new Promise((resolve, reject) => {
|
|
39
|
+
const chunks = [];
|
|
40
|
+
let size = 0;
|
|
41
|
+
let tooLarge = false;
|
|
42
|
+
req.on("data", (chunk) => {
|
|
43
|
+
if (tooLarge)
|
|
44
|
+
return;
|
|
45
|
+
size += chunk.length;
|
|
46
|
+
if (size > MAX_BODY_BYTES) {
|
|
47
|
+
tooLarge = true;
|
|
48
|
+
chunks.length = 0;
|
|
49
|
+
reject(new ApiError(413, "The request body is too large (limit 256 KiB)"));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
chunks.push(chunk);
|
|
53
|
+
});
|
|
54
|
+
req.on("end", () => {
|
|
55
|
+
if (!tooLarge)
|
|
56
|
+
resolve(Buffer.concat(chunks).toString("utf8"));
|
|
57
|
+
});
|
|
58
|
+
req.on("error", reject);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The dashboard server: static page plus JSON API on `127.0.0.1` (AD-9). Every request needs the
|
|
63
|
+
* per-run token, a loopback `Host` and (when present) a local `Origin`; there is no CORS and the
|
|
64
|
+
* CSP forbids anything but the page's own origin. Mutations call the same services as the commands.
|
|
65
|
+
*/
|
|
66
|
+
export async function startDashboard(options) {
|
|
67
|
+
const token = options.token ?? generateToken();
|
|
68
|
+
const loadAsset = options.assets ?? loadDashboardAsset;
|
|
69
|
+
const deps = { services: options.services, workspace: options.workspace };
|
|
70
|
+
let port = 0;
|
|
71
|
+
const authorised = (req, mutating) => {
|
|
72
|
+
const header = req.headers[TOKEN_HEADER];
|
|
73
|
+
if (tokenMatches(token, Array.isArray(header) ? header[0] : header))
|
|
74
|
+
return true;
|
|
75
|
+
if (mutating)
|
|
76
|
+
return false;
|
|
77
|
+
return tokenMatches(token, parseCookies(req.headers.cookie)[TOKEN_COOKIE]);
|
|
78
|
+
};
|
|
79
|
+
const handle = async (req, res) => {
|
|
80
|
+
if (!hostAllowed(req.headers.host, port))
|
|
81
|
+
return fail(res, 403, "Unexpected Host header");
|
|
82
|
+
const origin = req.headers.origin;
|
|
83
|
+
if (!originAllowed(Array.isArray(origin) ? origin[0] : origin, port)) {
|
|
84
|
+
return fail(res, 403, "Unexpected Origin header");
|
|
85
|
+
}
|
|
86
|
+
const method = req.method ?? "GET";
|
|
87
|
+
if (method !== "GET" && method !== "POST") {
|
|
88
|
+
return fail(res, 405, "Method not allowed", { allow: "GET, POST" });
|
|
89
|
+
}
|
|
90
|
+
const url = new URL(req.url ?? "/", `http://127.0.0.1:${port}`);
|
|
91
|
+
if (method === "GET" && url.pathname === "/" && url.searchParams.has("token")) {
|
|
92
|
+
if (!tokenMatches(token, url.searchParams.get("token") ?? undefined)) {
|
|
93
|
+
return fail(res, 401, "Invalid token");
|
|
94
|
+
}
|
|
95
|
+
return send(res, 302, "", "text/plain; charset=utf-8", {
|
|
96
|
+
location: "/",
|
|
97
|
+
"set-cookie": `${TOKEN_COOKIE}=${token}; Path=/; HttpOnly; SameSite=Strict`,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
if (!authorised(req, method === "POST"))
|
|
101
|
+
return fail(res, 401, "A valid token is required");
|
|
102
|
+
const fixed = method === "GET" ? STATIC[url.pathname] : undefined;
|
|
103
|
+
if (fixed) {
|
|
104
|
+
const content = await loadAsset(fixed.asset);
|
|
105
|
+
return send(res, 200, content.replaceAll(TOKEN_PLACEHOLDER, token), fixed.type);
|
|
106
|
+
}
|
|
107
|
+
if (!url.pathname.startsWith("/api/"))
|
|
108
|
+
return fail(res, 404, "Not found");
|
|
109
|
+
let segments;
|
|
110
|
+
try {
|
|
111
|
+
segments = url.pathname
|
|
112
|
+
.slice("/api/".length)
|
|
113
|
+
.split("/")
|
|
114
|
+
.map((part) => decodeURIComponent(part));
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
return fail(res, 400, "Malformed path");
|
|
118
|
+
}
|
|
119
|
+
let body;
|
|
120
|
+
if (method === "POST") {
|
|
121
|
+
if (!String(req.headers["content-type"] ?? "")
|
|
122
|
+
.toLowerCase()
|
|
123
|
+
.startsWith("application/json")) {
|
|
124
|
+
return fail(res, 415, "Send application/json");
|
|
125
|
+
}
|
|
126
|
+
const raw = await readBody(req);
|
|
127
|
+
try {
|
|
128
|
+
body = raw ? JSON.parse(raw) : {};
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return fail(res, 400, "The body is not valid JSON");
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
const result = await handleApi(deps, { method, segments, query: url.searchParams, body });
|
|
135
|
+
sendJson(res, result.status, result.body);
|
|
136
|
+
};
|
|
137
|
+
const server = createServer((req, res) => {
|
|
138
|
+
handle(req, res).catch((error) => {
|
|
139
|
+
if (res.headersSent) {
|
|
140
|
+
res.destroy();
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
// Our own validation messages are safe to show; system errors (paths, codes) are not.
|
|
144
|
+
const system = error instanceof Error && typeof error.code === "string";
|
|
145
|
+
const status = system || !(error instanceof Error) ? 500 : statusFor(error);
|
|
146
|
+
const message = status === 500 ? "Internal error" : error.message;
|
|
147
|
+
if (error instanceof ApiError && error.status === 413) {
|
|
148
|
+
fail(res, 413, message, { connection: "close" });
|
|
149
|
+
res.once("finish", () => req.destroy());
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
fail(res, status, message);
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
server.keepAliveTimeout = 5000;
|
|
156
|
+
await new Promise((resolve, reject) => {
|
|
157
|
+
server.once("error", reject);
|
|
158
|
+
server.listen(0, "127.0.0.1", () => resolve());
|
|
159
|
+
});
|
|
160
|
+
port = server.address().port;
|
|
161
|
+
const baseUrl = `http://127.0.0.1:${port}`;
|
|
162
|
+
let closing;
|
|
163
|
+
let forget = () => undefined;
|
|
164
|
+
const close = () => {
|
|
165
|
+
closing ??= new Promise((resolve) => {
|
|
166
|
+
forget();
|
|
167
|
+
options.onClosed?.();
|
|
168
|
+
server.close(() => resolve());
|
|
169
|
+
server.closeAllConnections();
|
|
170
|
+
});
|
|
171
|
+
return closing;
|
|
172
|
+
};
|
|
173
|
+
forget = options.services.onTeardown(close);
|
|
174
|
+
return { url: `${baseUrl}/?token=${token}`, baseUrl, port, token, close };
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/dashboard/server.ts"],"names":[],"mappings":"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"}
|
package/dist/doctor.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Toolchain } from "./toolchains/profile.js";
|
|
2
|
+
export interface DoctorItem {
|
|
3
|
+
id: string;
|
|
4
|
+
label: string;
|
|
5
|
+
status: "ok" | "warn" | "missing";
|
|
6
|
+
detail: string;
|
|
7
|
+
}
|
|
8
|
+
export interface DoctorReport {
|
|
9
|
+
items: DoctorItem[];
|
|
10
|
+
/** False when something the swarm cannot run without is missing. */
|
|
11
|
+
ok: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface DoctorDeps {
|
|
14
|
+
nodeVersion: string;
|
|
15
|
+
workspace: string;
|
|
16
|
+
toolchain: Toolchain;
|
|
17
|
+
initialised: boolean;
|
|
18
|
+
exec(command: string, args: string[]): Promise<string>;
|
|
19
|
+
writable(path: string): Promise<boolean>;
|
|
20
|
+
}
|
|
21
|
+
export declare const defaultDoctorDeps: {
|
|
22
|
+
exec(command: string, args: string[]): Promise<string>;
|
|
23
|
+
writable(path: string): Promise<boolean>;
|
|
24
|
+
};
|
|
25
|
+
export declare function parseGitVersion(text: string): [number, number, number] | undefined;
|
|
26
|
+
export declare function runDoctor(deps: DoctorDeps): Promise<DoctorReport>;
|
|
27
|
+
export declare function formatDoctor(report: DoctorReport): string;
|
|
28
|
+
//# sourceMappingURL=doctor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../src/doctor.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAIzD,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,IAAI,GAAG,MAAM,GAAG,SAAS,CAAC;IAClC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,oEAAoE;IACpE,EAAE,EAAE,OAAO,CAAC;CACb;AAED,MAAM,WAAW,UAAU;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,SAAS,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;IACrB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACvD,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC1C;AAMD,eAAO,MAAM,iBAAiB;IACtB,IAAI,UAAU,MAAM,QAAQ,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IAItD,QAAQ,OAAO,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAQ/C,CAAC;AAEF,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAGlF;AAMD,wBAAsB,SAAS,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,YAAY,CAAC,CAwEvE;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAKzD"}
|