pilotswarm 0.0.1 → 0.4.1
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/README.md +37 -1
- package/mcp/README.md +484 -0
- package/mcp/dist/bin/pilotswarm-mcp.d.ts +3 -0
- package/mcp/dist/bin/pilotswarm-mcp.d.ts.map +1 -0
- package/mcp/dist/bin/pilotswarm-mcp.js +367 -0
- package/mcp/dist/bin/pilotswarm-mcp.js.map +1 -0
- package/mcp/dist/src/auth.d.ts +7 -0
- package/mcp/dist/src/auth.d.ts.map +1 -0
- package/mcp/dist/src/auth.js +99 -0
- package/mcp/dist/src/auth.js.map +1 -0
- package/mcp/dist/src/context.d.ts +48 -0
- package/mcp/dist/src/context.d.ts.map +1 -0
- package/mcp/dist/src/context.js +83 -0
- package/mcp/dist/src/context.js.map +1 -0
- package/mcp/dist/src/index.d.ts +4 -0
- package/mcp/dist/src/index.d.ts.map +1 -0
- package/mcp/dist/src/index.js +3 -0
- package/mcp/dist/src/index.js.map +1 -0
- package/mcp/dist/src/prompts/skills.d.ts +4 -0
- package/mcp/dist/src/prompts/skills.d.ts.map +1 -0
- package/mcp/dist/src/prompts/skills.js +11 -0
- package/mcp/dist/src/prompts/skills.js.map +1 -0
- package/mcp/dist/src/resources/agents.d.ts +4 -0
- package/mcp/dist/src/resources/agents.d.ts.map +1 -0
- package/mcp/dist/src/resources/agents.js +64 -0
- package/mcp/dist/src/resources/agents.js.map +1 -0
- package/mcp/dist/src/resources/facts.d.ts +4 -0
- package/mcp/dist/src/resources/facts.d.ts.map +1 -0
- package/mcp/dist/src/resources/facts.js +125 -0
- package/mcp/dist/src/resources/facts.js.map +1 -0
- package/mcp/dist/src/resources/models.d.ts +4 -0
- package/mcp/dist/src/resources/models.d.ts.map +1 -0
- package/mcp/dist/src/resources/models.js +43 -0
- package/mcp/dist/src/resources/models.js.map +1 -0
- package/mcp/dist/src/resources/sessions.d.ts +4 -0
- package/mcp/dist/src/resources/sessions.d.ts.map +1 -0
- package/mcp/dist/src/resources/sessions.js +190 -0
- package/mcp/dist/src/resources/sessions.js.map +1 -0
- package/mcp/dist/src/resources/subscriptions.d.ts +9 -0
- package/mcp/dist/src/resources/subscriptions.d.ts.map +1 -0
- package/mcp/dist/src/resources/subscriptions.js +157 -0
- package/mcp/dist/src/resources/subscriptions.js.map +1 -0
- package/mcp/dist/src/server.d.ts +4 -0
- package/mcp/dist/src/server.d.ts.map +1 -0
- package/mcp/dist/src/server.js +59 -0
- package/mcp/dist/src/server.js.map +1 -0
- package/mcp/dist/src/tools/agents.d.ts +4 -0
- package/mcp/dist/src/tools/agents.d.ts.map +1 -0
- package/mcp/dist/src/tools/agents.js +317 -0
- package/mcp/dist/src/tools/agents.js.map +1 -0
- package/mcp/dist/src/tools/facts.d.ts +4 -0
- package/mcp/dist/src/tools/facts.d.ts.map +1 -0
- package/mcp/dist/src/tools/facts.js +151 -0
- package/mcp/dist/src/tools/facts.js.map +1 -0
- package/mcp/dist/src/tools/models.d.ts +4 -0
- package/mcp/dist/src/tools/models.d.ts.map +1 -0
- package/mcp/dist/src/tools/models.js +256 -0
- package/mcp/dist/src/tools/models.js.map +1 -0
- package/mcp/dist/src/tools/sessions.d.ts +4 -0
- package/mcp/dist/src/tools/sessions.d.ts.map +1 -0
- package/mcp/dist/src/tools/sessions.js +606 -0
- package/mcp/dist/src/tools/sessions.js.map +1 -0
- package/mcp/dist/src/util/command.d.ts +52 -0
- package/mcp/dist/src/util/command.d.ts.map +1 -0
- package/mcp/dist/src/util/command.js +78 -0
- package/mcp/dist/src/util/command.js.map +1 -0
- package/package.json +82 -6
- package/tui/README.md +35 -0
- package/tui/bin/tui.js +30 -0
- package/tui/plugins/.mcp.json +7 -0
- package/tui/plugins/plugin.json +13 -0
- package/tui/plugins/session-policy.json +8 -0
- package/tui/src/app.js +850 -0
- package/tui/src/auth/cli.js +111 -0
- package/tui/src/auth/entra-auth.js +218 -0
- package/tui/src/bootstrap-env.js +176 -0
- package/tui/src/embedded-workers.js +79 -0
- package/tui/src/http-transport-host.js +106 -0
- package/tui/src/index.js +340 -0
- package/tui/src/node-sdk-transport.js +1794 -0
- package/tui/src/platform.js +984 -0
- package/tui/src/plugin-config.js +273 -0
- package/tui/src/portal.js +7 -0
- package/tui/src/version.js +7 -0
- package/tui/tui-splash-mobile.txt +7 -0
- package/tui/tui-splash.txt +11 -0
- package/ui/core/README.md +6 -0
- package/ui/core/src/commands.js +93 -0
- package/ui/core/src/context-usage.js +212 -0
- package/ui/core/src/controller.js +6201 -0
- package/ui/core/src/formatting.js +1036 -0
- package/ui/core/src/history.js +950 -0
- package/ui/core/src/index.js +13 -0
- package/ui/core/src/layout.js +332 -0
- package/ui/core/src/reducer.js +1952 -0
- package/ui/core/src/selectors.js +5419 -0
- package/ui/core/src/session-errors.js +14 -0
- package/ui/core/src/session-tree.js +151 -0
- package/ui/core/src/state.js +251 -0
- package/ui/core/src/store.js +23 -0
- package/ui/core/src/system-titles.js +24 -0
- package/ui/core/src/themes/catppuccin-mocha.js +56 -0
- package/ui/core/src/themes/cobalt2.js +56 -0
- package/ui/core/src/themes/dark-high-contrast.js +56 -0
- package/ui/core/src/themes/daylight.js +62 -0
- package/ui/core/src/themes/dracula.js +56 -0
- package/ui/core/src/themes/github-dark.js +56 -0
- package/ui/core/src/themes/github-light.js +59 -0
- package/ui/core/src/themes/gruvbox-dark.js +56 -0
- package/ui/core/src/themes/hacker-x-matrix.js +56 -0
- package/ui/core/src/themes/hacker-x-orion-prime.js +56 -0
- package/ui/core/src/themes/helpers.js +79 -0
- package/ui/core/src/themes/high-contrast-mono.js +59 -0
- package/ui/core/src/themes/index.js +52 -0
- package/ui/core/src/themes/light-high-contrast.js +62 -0
- package/ui/core/src/themes/noctis-obscuro.js +56 -0
- package/ui/core/src/themes/noctis.js +56 -0
- package/ui/core/src/themes/paper-ink.js +62 -0
- package/ui/core/src/themes/solarized-ops.js +59 -0
- package/ui/core/src/themes/terminal-green.js +59 -0
- package/ui/core/src/themes/tokyo-night.js +56 -0
- package/ui/react/README.md +5 -0
- package/ui/react/src/chat-status.js +39 -0
- package/ui/react/src/components.js +1989 -0
- package/ui/react/src/index.js +4 -0
- package/ui/react/src/platform.js +15 -0
- package/ui/react/src/use-controller-state.js +38 -0
- package/ui/react/src/web-app.js +4457 -0
- package/web/README.md +198 -0
- package/web/api/router.js +196 -0
- package/web/api/ws.js +152 -0
- package/web/auth/authz/engine.js +204 -0
- package/web/auth/config.js +115 -0
- package/web/auth/index.js +175 -0
- package/web/auth/normalize/entra.js +22 -0
- package/web/auth/providers/entra.js +76 -0
- package/web/auth/providers/none.js +24 -0
- package/web/auth.js +10 -0
- package/web/bin/serve.js +53 -0
- package/web/config.js +20 -0
- package/web/dist/app.js +469 -0
- package/web/dist/assets/index-DmGOcKR-.css +1 -0
- package/web/dist/assets/index-xJ8IzIZY.js +24 -0
- package/web/dist/assets/msal-CytV9RFv.js +7 -0
- package/web/dist/assets/pilotswarm-D9pEmenA.js +90 -0
- package/web/dist/assets/react-CEPDSRB6.js +1 -0
- package/web/dist/index.html +16 -0
- package/web/runtime.js +455 -0
- package/web/server.js +276 -0
- package/index.js +0 -1
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { parseArgs } from "node:util";
|
|
3
|
+
import { randomUUID, timingSafeEqual } from "node:crypto";
|
|
4
|
+
import { Buffer } from "node:buffer";
|
|
5
|
+
import { createContext } from "../src/context.js";
|
|
6
|
+
import { createMcpServer } from "../src/server.js";
|
|
7
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
8
|
+
const { values } = parseArgs({
|
|
9
|
+
options: {
|
|
10
|
+
transport: { type: "string", default: "stdio" },
|
|
11
|
+
port: { type: "string", default: "3100" },
|
|
12
|
+
host: { type: "string" },
|
|
13
|
+
"allowed-hosts": { type: "string" },
|
|
14
|
+
"max-sessions": { type: "string" },
|
|
15
|
+
"session-idle-timeout-ms": { type: "string" },
|
|
16
|
+
store: { type: "string" },
|
|
17
|
+
"api-url": { type: "string" },
|
|
18
|
+
plugin: { type: "string", multiple: true },
|
|
19
|
+
"model-providers": { type: "string" },
|
|
20
|
+
"log-level": { type: "string", default: "error" },
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
// Log-level wiring — gates the lifecycle messages emitted by the bin
|
|
24
|
+
// (startup banner, signal handlers, shutdown). Lower tiers also include the
|
|
25
|
+
// higher tiers; "silent" suppresses everything. Fatal `Error:` messages on
|
|
26
|
+
// stderr before process.exit() always print regardless of level.
|
|
27
|
+
const LOG_LEVELS = ["debug", "info", "warn", "error", "silent"];
|
|
28
|
+
const requestedLevel = (values["log-level"] ?? "error").toLowerCase();
|
|
29
|
+
const activeLevel = LOG_LEVELS.includes(requestedLevel)
|
|
30
|
+
? requestedLevel
|
|
31
|
+
: "error";
|
|
32
|
+
const activeLevelIdx = LOG_LEVELS.indexOf(activeLevel);
|
|
33
|
+
const log = {
|
|
34
|
+
debug: (msg) => { if (activeLevelIdx <= 0)
|
|
35
|
+
console.error(msg); },
|
|
36
|
+
info: (msg) => { if (activeLevelIdx <= 1)
|
|
37
|
+
console.error(msg); },
|
|
38
|
+
warn: (msg) => { if (activeLevelIdx <= 2)
|
|
39
|
+
console.error(msg); },
|
|
40
|
+
error: (msg) => { if (activeLevelIdx <= 3)
|
|
41
|
+
console.error(msg); },
|
|
42
|
+
};
|
|
43
|
+
// Web API mode (supported): no database credentials in this process. Direct
|
|
44
|
+
// --store mode remains for trusted/co-located placement and internal testing.
|
|
45
|
+
const apiUrl = (values["api-url"] ?? process.env.PILOTSWARM_API_URL)?.trim();
|
|
46
|
+
const store = values.store ?? process.env.DATABASE_URL;
|
|
47
|
+
if (apiUrl && values.store) {
|
|
48
|
+
console.error("Error: pass either --api-url (Web API mode) or --store (direct mode), not both.");
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
if (!apiUrl && !store) {
|
|
52
|
+
console.error("Error: --api-url <url> (Web API mode) or --store <url> / DATABASE_URL (direct mode) is required.");
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
const ctx = await createContext({
|
|
56
|
+
...(apiUrl ? { apiUrl } : { store }),
|
|
57
|
+
modelProvidersPath: values["model-providers"],
|
|
58
|
+
pluginDirs: values.plugin,
|
|
59
|
+
});
|
|
60
|
+
if (values.transport === "stdio") {
|
|
61
|
+
const server = createMcpServer(ctx);
|
|
62
|
+
const transport = new StdioServerTransport();
|
|
63
|
+
await server.connect(transport);
|
|
64
|
+
const shutdown = async (signal) => {
|
|
65
|
+
log.info(`[pilotswarm-mcp] ${signal} received — closing stdio transport`);
|
|
66
|
+
try {
|
|
67
|
+
await transport.close?.();
|
|
68
|
+
}
|
|
69
|
+
catch { /* ignore */ }
|
|
70
|
+
try {
|
|
71
|
+
await ctx.client.stop?.();
|
|
72
|
+
}
|
|
73
|
+
catch { /* ignore */ }
|
|
74
|
+
try {
|
|
75
|
+
await ctx.mgmt.stop?.();
|
|
76
|
+
}
|
|
77
|
+
catch { /* ignore */ }
|
|
78
|
+
try {
|
|
79
|
+
await ctx.facts.close?.();
|
|
80
|
+
}
|
|
81
|
+
catch { /* ignore */ }
|
|
82
|
+
process.exit(0);
|
|
83
|
+
};
|
|
84
|
+
process.on("SIGTERM", () => void shutdown("SIGTERM"));
|
|
85
|
+
process.on("SIGINT", () => void shutdown("SIGINT"));
|
|
86
|
+
}
|
|
87
|
+
else if (values.transport === "http") {
|
|
88
|
+
const mcpKey = process.env.PILOTSWARM_MCP_KEY;
|
|
89
|
+
if (!mcpKey) {
|
|
90
|
+
console.error("Error: PILOTSWARM_MCP_KEY env var required for HTTP transport.");
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
|
+
const expectedAuthBuf = Buffer.from(`Bearer ${mcpKey}`);
|
|
94
|
+
const { WebStandardStreamableHTTPServerTransport } = await import("@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js");
|
|
95
|
+
const { Hono } = await import("hono");
|
|
96
|
+
const { cors } = await import("hono/cors");
|
|
97
|
+
const { serve } = await import("@hono/node-server");
|
|
98
|
+
const port = parseInt(values.port ?? "3100", 10);
|
|
99
|
+
const host = values.host ??
|
|
100
|
+
process.env.PILOTSWARM_MCP_HOST ??
|
|
101
|
+
"127.0.0.1";
|
|
102
|
+
// allowedHosts — Host header allowlist to prevent DNS-rebinding attacks.
|
|
103
|
+
// Defaults to localhost-equivalents on the chosen port plus the host:port the
|
|
104
|
+
// server is bound to. Operators bridging through reverse proxies / public DNS
|
|
105
|
+
// must pass --allowed-hosts (or PILOTSWARM_MCP_ALLOWED_HOSTS) explicitly.
|
|
106
|
+
const allowedHostsSrc = values["allowed-hosts"] ??
|
|
107
|
+
process.env.PILOTSWARM_MCP_ALLOWED_HOSTS ??
|
|
108
|
+
"";
|
|
109
|
+
const allowedHosts = new Set(allowedHostsSrc
|
|
110
|
+
.split(",")
|
|
111
|
+
.map((s) => s.trim().toLowerCase())
|
|
112
|
+
.filter((s) => s.length > 0));
|
|
113
|
+
// Always permit the bound host:port + standard localhost aliases on the
|
|
114
|
+
// configured port so the default `--transport http` invocation works without
|
|
115
|
+
// additional flags.
|
|
116
|
+
allowedHosts.add(`${host}:${port}`.toLowerCase());
|
|
117
|
+
allowedHosts.add(`127.0.0.1:${port}`);
|
|
118
|
+
allowedHosts.add(`localhost:${port}`);
|
|
119
|
+
allowedHosts.add(`[::1]:${port}`);
|
|
120
|
+
// max-sessions — concurrent MCP session cap. New sessions beyond this cap are
|
|
121
|
+
// rejected with a 503 + JSON-RPC service-unavailable shape so memory cannot be
|
|
122
|
+
// exhausted by an unbounded number of long-lived SSE connections.
|
|
123
|
+
const maxSessionsRaw = values["max-sessions"] ??
|
|
124
|
+
process.env.PILOTSWARM_MCP_MAX_SESSIONS ??
|
|
125
|
+
"256";
|
|
126
|
+
const parsedMax = parseInt(maxSessionsRaw, 10);
|
|
127
|
+
const maxSessions = Number.isFinite(parsedMax) && parsedMax > 0 ? parsedMax : 256;
|
|
128
|
+
// session-idle-timeout-ms — close + free sessions whose last request was
|
|
129
|
+
// more than this many milliseconds ago. Required because one-shot HTTP
|
|
130
|
+
// clients (initialize + disconnect without DELETE /mcp) never trigger
|
|
131
|
+
// transport.onclose, which would otherwise leak max-sessions slots.
|
|
132
|
+
// Default: 5 minutes (300_000 ms). Set to 0 to disable the sweeper.
|
|
133
|
+
const idleTimeoutRaw = values["session-idle-timeout-ms"] ??
|
|
134
|
+
process.env.PILOTSWARM_MCP_SESSION_IDLE_MS ??
|
|
135
|
+
"300000";
|
|
136
|
+
const parsedIdle = parseInt(idleTimeoutRaw, 10);
|
|
137
|
+
const sessionIdleTimeoutMs = Number.isFinite(parsedIdle) && parsedIdle >= 0 ? parsedIdle : 300_000;
|
|
138
|
+
// Sweep cadence: every 30s, but never longer than the idle threshold itself
|
|
139
|
+
// (otherwise tiny test thresholds are missed entirely).
|
|
140
|
+
const sweepIntervalMs = sessionIdleTimeoutMs > 0
|
|
141
|
+
? Math.max(1_000, Math.min(30_000, Math.floor(sessionIdleTimeoutMs / 2) || 1_000))
|
|
142
|
+
: 0;
|
|
143
|
+
const app = new Hono();
|
|
144
|
+
const sessions = new Map();
|
|
145
|
+
// Atomic in-flight counter for the max-sessions cap — incremented BEFORE
|
|
146
|
+
// transport construction (so concurrent bursts cannot all observe
|
|
147
|
+
// sessions.size < maxSessions and slip past the gate) and decremented on
|
|
148
|
+
// construction failure or transport close. The counter is the source of
|
|
149
|
+
// truth for cap enforcement; `sessions` is just the lookup table.
|
|
150
|
+
let inFlightSessionCount = 0;
|
|
151
|
+
const acquireSlot = () => {
|
|
152
|
+
if (inFlightSessionCount >= maxSessions)
|
|
153
|
+
return false;
|
|
154
|
+
inFlightSessionCount++;
|
|
155
|
+
return true;
|
|
156
|
+
};
|
|
157
|
+
const releaseSlot = () => {
|
|
158
|
+
if (inFlightSessionCount > 0)
|
|
159
|
+
inFlightSessionCount--;
|
|
160
|
+
};
|
|
161
|
+
const touchSession = (id) => {
|
|
162
|
+
const entry = sessions.get(id);
|
|
163
|
+
if (entry)
|
|
164
|
+
entry.lastActivityAt = Date.now();
|
|
165
|
+
};
|
|
166
|
+
// Track in-flight HTTP request handlers so SIGTERM can wait for them to drain
|
|
167
|
+
// instead of severing connections mid-response.
|
|
168
|
+
const inFlight = new Set();
|
|
169
|
+
let shuttingDown = false;
|
|
170
|
+
app.use("*", async (c, next) => {
|
|
171
|
+
if (shuttingDown) {
|
|
172
|
+
return c.json({ error: "Server shutting down" }, 503);
|
|
173
|
+
}
|
|
174
|
+
// DNS-rebinding defense: validate Host header against the allowlist.
|
|
175
|
+
const hostHeader = (c.req.header("host") ?? "").toLowerCase();
|
|
176
|
+
if (!allowedHosts.has(hostHeader)) {
|
|
177
|
+
return c.json({ error: "Host not allowed" }, 403);
|
|
178
|
+
}
|
|
179
|
+
await next();
|
|
180
|
+
});
|
|
181
|
+
app.use("*", cors({
|
|
182
|
+
origin: "*",
|
|
183
|
+
allowMethods: ["GET", "POST", "DELETE", "OPTIONS"],
|
|
184
|
+
allowHeaders: ["Content-Type", "Authorization", "mcp-session-id", "Last-Event-ID", "mcp-protocol-version"],
|
|
185
|
+
exposeHeaders: ["mcp-session-id", "mcp-protocol-version"],
|
|
186
|
+
}));
|
|
187
|
+
// Bearer token auth middleware — constant-time comparison to avoid leaking
|
|
188
|
+
// the token via timing side-channels.
|
|
189
|
+
app.use("/mcp", async (c, next) => {
|
|
190
|
+
const auth = c.req.header("authorization") ?? "";
|
|
191
|
+
const authBuf = Buffer.from(auth);
|
|
192
|
+
const ok = authBuf.length === expectedAuthBuf.length &&
|
|
193
|
+
timingSafeEqual(authBuf, expectedAuthBuf);
|
|
194
|
+
if (!ok) {
|
|
195
|
+
return c.json({ error: "Unauthorized" }, 401);
|
|
196
|
+
}
|
|
197
|
+
await next();
|
|
198
|
+
});
|
|
199
|
+
app.post("/mcp", async (c) => {
|
|
200
|
+
const sessionId = c.req.header("mcp-session-id");
|
|
201
|
+
if (sessionId && sessions.has(sessionId)) {
|
|
202
|
+
touchSession(sessionId);
|
|
203
|
+
const p = sessions.get(sessionId).transport.handleRequest(c.req.raw);
|
|
204
|
+
inFlight.add(p);
|
|
205
|
+
try {
|
|
206
|
+
return await p;
|
|
207
|
+
}
|
|
208
|
+
finally {
|
|
209
|
+
inFlight.delete(p);
|
|
210
|
+
touchSession(sessionId);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
if (sessionId && !sessions.has(sessionId)) {
|
|
214
|
+
return c.json({ error: "Unknown session" }, 404);
|
|
215
|
+
}
|
|
216
|
+
// Atomic cap check — increment BEFORE constructing the transport so
|
|
217
|
+
// concurrent bursts cannot all observe spare capacity simultaneously.
|
|
218
|
+
if (!acquireSlot()) {
|
|
219
|
+
return c.json({
|
|
220
|
+
jsonrpc: "2.0",
|
|
221
|
+
error: {
|
|
222
|
+
code: -32000,
|
|
223
|
+
message: `Server at session capacity (${maxSessions}); try again later.`,
|
|
224
|
+
},
|
|
225
|
+
id: null,
|
|
226
|
+
}, 503);
|
|
227
|
+
}
|
|
228
|
+
// New session — create per-session server+transport pair. The slot is
|
|
229
|
+
// already reserved; releaseSlot is called exactly once via
|
|
230
|
+
// transport.onclose (which fires from DELETE /mcp, the idle sweeper,
|
|
231
|
+
// shutdown, or any underlying transport-level close).
|
|
232
|
+
let transport;
|
|
233
|
+
try {
|
|
234
|
+
transport = new WebStandardStreamableHTTPServerTransport({
|
|
235
|
+
sessionIdGenerator: () => randomUUID(),
|
|
236
|
+
onsessioninitialized: (id) => {
|
|
237
|
+
sessions.set(id, { transport, lastActivityAt: Date.now() });
|
|
238
|
+
},
|
|
239
|
+
});
|
|
240
|
+
let slotReleased = false;
|
|
241
|
+
transport.onclose = () => {
|
|
242
|
+
if (transport.sessionId)
|
|
243
|
+
sessions.delete(transport.sessionId);
|
|
244
|
+
if (!slotReleased) {
|
|
245
|
+
slotReleased = true;
|
|
246
|
+
releaseSlot();
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
const server = createMcpServer(ctx);
|
|
250
|
+
await server.connect(transport);
|
|
251
|
+
}
|
|
252
|
+
catch (err) {
|
|
253
|
+
releaseSlot();
|
|
254
|
+
throw err;
|
|
255
|
+
}
|
|
256
|
+
const p = transport.handleRequest(c.req.raw);
|
|
257
|
+
inFlight.add(p);
|
|
258
|
+
try {
|
|
259
|
+
return await p;
|
|
260
|
+
}
|
|
261
|
+
finally {
|
|
262
|
+
inFlight.delete(p);
|
|
263
|
+
if (transport.sessionId)
|
|
264
|
+
touchSession(transport.sessionId);
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
// SSE stream endpoint
|
|
268
|
+
app.get("/mcp", async (c) => {
|
|
269
|
+
const sessionId = c.req.header("mcp-session-id");
|
|
270
|
+
if (sessionId && sessions.has(sessionId)) {
|
|
271
|
+
touchSession(sessionId);
|
|
272
|
+
return sessions.get(sessionId).transport.handleRequest(c.req.raw);
|
|
273
|
+
}
|
|
274
|
+
return c.json({ error: "Invalid or missing session" }, 400);
|
|
275
|
+
});
|
|
276
|
+
// Session cleanup endpoint (per MCP spec)
|
|
277
|
+
app.delete("/mcp", async (c) => {
|
|
278
|
+
const sessionId = c.req.header("mcp-session-id");
|
|
279
|
+
if (!sessionId || !sessions.has(sessionId)) {
|
|
280
|
+
return c.json({ error: "Unknown session" }, 404);
|
|
281
|
+
}
|
|
282
|
+
const entry = sessions.get(sessionId);
|
|
283
|
+
await entry.transport.close();
|
|
284
|
+
sessions.delete(sessionId);
|
|
285
|
+
return c.json({ closed: true });
|
|
286
|
+
});
|
|
287
|
+
// Idle-session sweeper — reaps sessions whose last request was more than
|
|
288
|
+
// sessionIdleTimeoutMs ago. This is the primary defense against
|
|
289
|
+
// max-sessions slot leaks from one-shot HTTP clients that disconnect
|
|
290
|
+
// without sending DELETE /mcp (transport.onclose only fires when the
|
|
291
|
+
// SDK observes an explicit transport close, which one-shot HTTP requests
|
|
292
|
+
// do not trigger).
|
|
293
|
+
let sweeperTimer = null;
|
|
294
|
+
if (sessionIdleTimeoutMs > 0 && sweepIntervalMs > 0) {
|
|
295
|
+
sweeperTimer = setInterval(() => {
|
|
296
|
+
const cutoff = Date.now() - sessionIdleTimeoutMs;
|
|
297
|
+
// Snapshot keys to avoid mutating during iteration.
|
|
298
|
+
const expired = [];
|
|
299
|
+
for (const [id, entry] of sessions) {
|
|
300
|
+
if (entry.lastActivityAt < cutoff)
|
|
301
|
+
expired.push([id, entry]);
|
|
302
|
+
}
|
|
303
|
+
for (const [id, entry] of expired) {
|
|
304
|
+
log.debug(`[pilotswarm-mcp] sweeping idle session ${id}`);
|
|
305
|
+
sessions.delete(id);
|
|
306
|
+
// transport.close() will fire onclose, which calls releaseSlot.
|
|
307
|
+
Promise.resolve(entry.transport.close()).catch(() => { });
|
|
308
|
+
}
|
|
309
|
+
}, sweepIntervalMs);
|
|
310
|
+
sweeperTimer.unref?.();
|
|
311
|
+
}
|
|
312
|
+
const httpServer = serve({ fetch: app.fetch, port, hostname: host }, () => {
|
|
313
|
+
log.info(`PilotSwarm MCP server listening on http://${host}:${port}/mcp`);
|
|
314
|
+
});
|
|
315
|
+
const shutdown = async (signal) => {
|
|
316
|
+
if (shuttingDown)
|
|
317
|
+
return;
|
|
318
|
+
shuttingDown = true;
|
|
319
|
+
log.info(`[pilotswarm-mcp] ${signal} received — draining ${inFlight.size} in-flight request(s), closing ${sessions.size} session(s)`);
|
|
320
|
+
if (sweeperTimer) {
|
|
321
|
+
clearInterval(sweeperTimer);
|
|
322
|
+
sweeperTimer = null;
|
|
323
|
+
}
|
|
324
|
+
const drainTimeoutMs = 5_000;
|
|
325
|
+
await Promise.race([
|
|
326
|
+
Promise.allSettled([...inFlight]),
|
|
327
|
+
new Promise((r) => setTimeout(r, drainTimeoutMs)),
|
|
328
|
+
]);
|
|
329
|
+
for (const entry of sessions.values()) {
|
|
330
|
+
try {
|
|
331
|
+
await entry.transport.close();
|
|
332
|
+
}
|
|
333
|
+
catch { /* ignore */ }
|
|
334
|
+
}
|
|
335
|
+
sessions.clear();
|
|
336
|
+
await new Promise((resolve) => {
|
|
337
|
+
try {
|
|
338
|
+
httpServer.close(() => resolve());
|
|
339
|
+
}
|
|
340
|
+
catch {
|
|
341
|
+
resolve();
|
|
342
|
+
}
|
|
343
|
+
setTimeout(() => resolve(), drainTimeoutMs).unref?.();
|
|
344
|
+
});
|
|
345
|
+
try {
|
|
346
|
+
await ctx.client.stop?.();
|
|
347
|
+
}
|
|
348
|
+
catch { /* ignore */ }
|
|
349
|
+
try {
|
|
350
|
+
await ctx.mgmt.stop?.();
|
|
351
|
+
}
|
|
352
|
+
catch { /* ignore */ }
|
|
353
|
+
try {
|
|
354
|
+
await ctx.facts.close?.();
|
|
355
|
+
}
|
|
356
|
+
catch { /* ignore */ }
|
|
357
|
+
log.info(`[pilotswarm-mcp] shutdown complete`);
|
|
358
|
+
process.exit(0);
|
|
359
|
+
};
|
|
360
|
+
process.on("SIGTERM", () => void shutdown("SIGTERM"));
|
|
361
|
+
process.on("SIGINT", () => void shutdown("SIGINT"));
|
|
362
|
+
}
|
|
363
|
+
else {
|
|
364
|
+
console.error(`Unknown transport: ${values.transport}. Use "stdio" or "http".`);
|
|
365
|
+
process.exit(1);
|
|
366
|
+
}
|
|
367
|
+
//# sourceMappingURL=pilotswarm-mcp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pilotswarm-mcp.js","sourceRoot":"","sources":["../../bin/pilotswarm-mcp.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve a `getAccessToken` callback for a Web API deployment, or null when
|
|
3
|
+
* the deployment needs no auth. Throws with actionable guidance when an Entra
|
|
4
|
+
* deployment has no usable credential.
|
|
5
|
+
*/
|
|
6
|
+
export declare function createApiTokenProvider(apiUrl: string): Promise<(() => Promise<string | null>) | null>;
|
|
7
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/auth.ts"],"names":[],"mappings":"AAuEA;;;;GAIG;AACH,wBAAsB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CA8B3G"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { ApiClient } from "pilotswarm-sdk/api";
|
|
5
|
+
/**
|
|
6
|
+
* Bearer-token supplier for the MCP server's Web API (`--api-url`) mode.
|
|
7
|
+
*
|
|
8
|
+
* The MCP server is headless (launched by an MCP host), so it cannot run the
|
|
9
|
+
* interactive browser sign-in the TUI uses. Two credential paths, discovered
|
|
10
|
+
* from the deployment's public `GET /api/v1/auth/config`:
|
|
11
|
+
*
|
|
12
|
+
* - **no-auth deployments** → no token.
|
|
13
|
+
* - **Entra deployments** →
|
|
14
|
+
* 1. `PILOTSWARM_API_TOKEN` (static bearer — service principal / CI), else
|
|
15
|
+
* 2. the cached user token the TUI writes at
|
|
16
|
+
* `~/.config/pilotswarm/auth/<origin>.json` — run `pilotswarm auth
|
|
17
|
+
* login --api-url <url>` once, and the MCP server acquires silently
|
|
18
|
+
* from that cache (dev convenience).
|
|
19
|
+
*/
|
|
20
|
+
const AUTH_CACHE_DIR = path.join(process.env.XDG_CONFIG_HOME || path.join(os.homedir(), ".config"), "pilotswarm", "auth");
|
|
21
|
+
function cacheFileForOrigin(apiUrl) {
|
|
22
|
+
const origin = new URL(apiUrl).origin.replace(/[^\w.-]+/g, "_");
|
|
23
|
+
return path.join(AUTH_CACHE_DIR, `${origin}.json`);
|
|
24
|
+
}
|
|
25
|
+
async function silentTokenProviderFromCache(apiUrl, client) {
|
|
26
|
+
let msal;
|
|
27
|
+
try {
|
|
28
|
+
msal = await import("@azure/msal-node");
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
const cacheFile = cacheFileForOrigin(apiUrl);
|
|
34
|
+
const app = new msal.PublicClientApplication({
|
|
35
|
+
auth: { clientId: client.clientId, authority: client.authority },
|
|
36
|
+
cache: {
|
|
37
|
+
cachePlugin: {
|
|
38
|
+
beforeCacheAccess: async (ctx) => {
|
|
39
|
+
try {
|
|
40
|
+
ctx.tokenCache.deserialize(await fs.promises.readFile(cacheFile, "utf8"));
|
|
41
|
+
}
|
|
42
|
+
catch { }
|
|
43
|
+
},
|
|
44
|
+
afterCacheAccess: async (ctx) => {
|
|
45
|
+
if (!ctx.cacheHasChanged)
|
|
46
|
+
return;
|
|
47
|
+
await fs.promises.mkdir(AUTH_CACHE_DIR, { recursive: true, mode: 0o700 });
|
|
48
|
+
await fs.promises.writeFile(cacheFile, ctx.tokenCache.serialize(), { mode: 0o600 });
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
const scopes = [`${client.clientId}/.default`];
|
|
54
|
+
return async () => {
|
|
55
|
+
const accounts = await app.getTokenCache().getAllAccounts();
|
|
56
|
+
const account = accounts[0];
|
|
57
|
+
if (!account)
|
|
58
|
+
return null;
|
|
59
|
+
try {
|
|
60
|
+
const result = await app.acquireTokenSilent({ account, scopes });
|
|
61
|
+
return result?.accessToken || null;
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Resolve a `getAccessToken` callback for a Web API deployment, or null when
|
|
70
|
+
* the deployment needs no auth. Throws with actionable guidance when an Entra
|
|
71
|
+
* deployment has no usable credential.
|
|
72
|
+
*/
|
|
73
|
+
export async function createApiTokenProvider(apiUrl) {
|
|
74
|
+
const api = new ApiClient({ apiUrl });
|
|
75
|
+
const authConfig = await api.getAuthConfig();
|
|
76
|
+
if (!authConfig?.enabled || authConfig?.provider === "none") {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
if (authConfig.provider !== "entra") {
|
|
80
|
+
throw new Error(`Unsupported auth provider '${authConfig.provider}' reported by ${apiUrl}.`);
|
|
81
|
+
}
|
|
82
|
+
const staticToken = String(process.env.PILOTSWARM_API_TOKEN || "").trim();
|
|
83
|
+
if (staticToken) {
|
|
84
|
+
return async () => staticToken;
|
|
85
|
+
}
|
|
86
|
+
const client = authConfig.client;
|
|
87
|
+
if (client?.clientId && client?.authority) {
|
|
88
|
+
const provider = await silentTokenProviderFromCache(apiUrl, client);
|
|
89
|
+
if (provider) {
|
|
90
|
+
// Verify the cache actually yields a token; fail fast otherwise.
|
|
91
|
+
const token = await provider();
|
|
92
|
+
if (token)
|
|
93
|
+
return provider;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
throw new Error(`${apiUrl} requires Entra auth but no credential is available. Set PILOTSWARM_API_TOKEN `
|
|
97
|
+
+ `(service principal / CI), or run: pilotswarm auth login --api-url ${apiUrl}`);
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { PilotSwarmClient, PilotSwarmManagementClient, ModelProviderRegistry, loadAgentFiles, type FactStore } from "pilotswarm-sdk";
|
|
2
|
+
type AgentConfig = ReturnType<typeof loadAgentFiles>[number];
|
|
3
|
+
export interface ServerContext {
|
|
4
|
+
client: PilotSwarmClient;
|
|
5
|
+
mgmt: PilotSwarmManagementClient;
|
|
6
|
+
facts: FactStore;
|
|
7
|
+
/** True when running over the Web API (`--api-url`); false in direct mode. */
|
|
8
|
+
webMode: boolean;
|
|
9
|
+
models: ModelProviderRegistry | null;
|
|
10
|
+
skills: Array<{
|
|
11
|
+
name: string;
|
|
12
|
+
description: string;
|
|
13
|
+
prompt: string;
|
|
14
|
+
}>;
|
|
15
|
+
/**
|
|
16
|
+
* Agent definitions visible to this MCP server, loaded from
|
|
17
|
+
* `<pluginDir>/agents/*.agent.md` for each configured plugin dir.
|
|
18
|
+
* Used by the read-only `list_registered_agents` tool. Workers in
|
|
19
|
+
* different processes may have a different catalog — see the tool
|
|
20
|
+
* description for the divergence note.
|
|
21
|
+
*/
|
|
22
|
+
registeredAgents: AgentConfig[];
|
|
23
|
+
/**
|
|
24
|
+
* Set of agentIds for sessions where `isSystem === true`, derived from
|
|
25
|
+
* `mgmt.listSessions()` at startup. Used by resource registrations and
|
|
26
|
+
* subscription filters that need to enumerate system agents without
|
|
27
|
+
* hardcoding their names.
|
|
28
|
+
*/
|
|
29
|
+
systemAgentIds: Set<string>;
|
|
30
|
+
/**
|
|
31
|
+
* Re-query the management API to refresh `systemAgentIds`. Tool/resource
|
|
32
|
+
* handlers may call this when they suspect the list has drifted (e.g. a
|
|
33
|
+
* new system agent was registered after server startup). Cheap to call —
|
|
34
|
+
* shares the same listSessions() call already used elsewhere.
|
|
35
|
+
*/
|
|
36
|
+
refreshSystemAgentIds(): Promise<void>;
|
|
37
|
+
}
|
|
38
|
+
export interface CreateContextOptions {
|
|
39
|
+
/** Direct database URL (internal/trusted placement). Mutually exclusive with apiUrl. */
|
|
40
|
+
store?: string;
|
|
41
|
+
/** Web API base URL (supported remote mode). Mutually exclusive with store. */
|
|
42
|
+
apiUrl?: string;
|
|
43
|
+
modelProvidersPath?: string;
|
|
44
|
+
pluginDirs?: string[];
|
|
45
|
+
}
|
|
46
|
+
export declare function createContext(opts: CreateContextOptions): Promise<ServerContext>;
|
|
47
|
+
export {};
|
|
48
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,gBAAgB,EAChB,0BAA0B,EAI1B,qBAAqB,EAErB,cAAc,EACd,KAAK,SAAS,EACjB,MAAM,gBAAgB,CAAC;AAIxB,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAE7D,MAAM,WAAW,aAAa;IAC1B,MAAM,EAAE,gBAAgB,CAAC;IACzB,IAAI,EAAE,0BAA0B,CAAC;IACjC,KAAK,EAAE,SAAS,CAAC;IACjB,8EAA8E;IAC9E,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,qBAAqB,GAAG,IAAI,CAAC;IACrC,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACrE;;;;;;OAMG;IACH,gBAAgB,EAAE,WAAW,EAAE,CAAC;IAChC;;;;;OAKG;IACH,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC5B;;;;;OAKG;IACH,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1C;AAED,MAAM,WAAW,oBAAoB;IACjC,wFAAwF;IACxF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+EAA+E;IAC/E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,aAAa,CAAC,CA8EtF"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { PilotSwarmClient, PilotSwarmManagementClient, createFactStoreForUrl, createWebFactStore, loadModelProviders, loadSkills, loadAgentFiles, } from "pilotswarm-sdk";
|
|
2
|
+
import { ApiClient } from "pilotswarm-sdk/api";
|
|
3
|
+
import { createApiTokenProvider } from "./auth.js";
|
|
4
|
+
export async function createContext(opts) {
|
|
5
|
+
let client;
|
|
6
|
+
let mgmt;
|
|
7
|
+
let facts;
|
|
8
|
+
if (opts.apiUrl) {
|
|
9
|
+
// Web API mode (supported): no database credentials in this process.
|
|
10
|
+
const getAccessToken = await createApiTokenProvider(opts.apiUrl) ?? undefined;
|
|
11
|
+
client = new PilotSwarmClient({ apiUrl: opts.apiUrl, getAccessToken });
|
|
12
|
+
await client.start();
|
|
13
|
+
mgmt = new PilotSwarmManagementClient({ apiUrl: opts.apiUrl, getAccessToken });
|
|
14
|
+
await mgmt.start();
|
|
15
|
+
const api = new ApiClient({ apiUrl: opts.apiUrl, getAccessToken });
|
|
16
|
+
facts = await createWebFactStore(api);
|
|
17
|
+
}
|
|
18
|
+
else if (opts.store) {
|
|
19
|
+
// Direct mode (internal/testing): straight to the datastore.
|
|
20
|
+
client = new PilotSwarmClient({ store: opts.store });
|
|
21
|
+
await client.start();
|
|
22
|
+
mgmt = new PilotSwarmManagementClient({ store: opts.store });
|
|
23
|
+
await mgmt.start();
|
|
24
|
+
facts = await createFactStoreForUrl(opts.store);
|
|
25
|
+
await facts.initialize();
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
throw new Error("createContext requires either apiUrl (web mode) or store (direct mode).");
|
|
29
|
+
}
|
|
30
|
+
const models = loadModelProviders(opts.modelProvidersPath ?? undefined) ?? null;
|
|
31
|
+
let skills = [];
|
|
32
|
+
if (opts.pluginDirs) {
|
|
33
|
+
for (const dir of opts.pluginDirs) {
|
|
34
|
+
try {
|
|
35
|
+
const loaded = await loadSkills(dir + "/skills");
|
|
36
|
+
skills.push(...loaded.map(s => ({ name: s.name, description: s.description, prompt: s.prompt })));
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// Directory may not have skills — skip
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
let registeredAgents = [];
|
|
44
|
+
if (opts.pluginDirs) {
|
|
45
|
+
// Mirror the worker's loading semantics: name-keyed, last-write-wins
|
|
46
|
+
// (see packages/sdk/src/worker.ts ~line 720). Keying on `id ?? name`
|
|
47
|
+
// would let the MCP catalog diverge from any specific worker's
|
|
48
|
+
// runtime when two plugin dirs share a name but differ on id.
|
|
49
|
+
const byName = new Map();
|
|
50
|
+
for (const dir of opts.pluginDirs) {
|
|
51
|
+
try {
|
|
52
|
+
const loaded = loadAgentFiles(dir + "/agents");
|
|
53
|
+
for (const agent of loaded) {
|
|
54
|
+
if (!agent.name)
|
|
55
|
+
continue;
|
|
56
|
+
byName.set(agent.name, agent);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// Directory may not have agents — skip
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
registeredAgents = Array.from(byName.values());
|
|
64
|
+
}
|
|
65
|
+
const systemAgentIds = new Set();
|
|
66
|
+
async function refreshSystemAgentIds() {
|
|
67
|
+
try {
|
|
68
|
+
const sessions = await mgmt.listSessions();
|
|
69
|
+
systemAgentIds.clear();
|
|
70
|
+
for (const s of sessions) {
|
|
71
|
+
if (s?.isSystem && typeof s.agentId === "string" && s.agentId.length > 0) {
|
|
72
|
+
systemAgentIds.add(s.agentId);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// Best-effort — leave existing set untouched if mgmt is transiently unavailable.
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
await refreshSystemAgentIds();
|
|
81
|
+
return { client, mgmt, facts, webMode: Boolean(opts.apiUrl), models, skills, registeredAgents, systemAgentIds, refreshSystemAgentIds };
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/context.ts"],"names":[],"mappings":"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"}
|