pilotswarm 0.0.1 → 0.4.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/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 +81 -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 +1793 -0
- package/tui/src/platform.js +984 -0
- package/tui/src/plugin-config.js +239 -0
- package/tui/src/portal.js +7 -0
- package/tui/src/version.js +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 +6104 -0
- package/ui/core/src/formatting.js +1036 -0
- package/ui/core/src/history.js +932 -0
- package/ui/core/src/index.js +13 -0
- package/ui/core/src/layout.js +332 -0
- package/ui/core/src/reducer.js +1935 -0
- package/ui/core/src/selectors.js +5409 -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 +248 -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 +4390 -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-CBgQQk-j.css +1 -0
- package/web/dist/assets/index-DMefB7Wb.js +24 -0
- package/web/dist/assets/msal-CytV9RFv.js +7 -0
- package/web/dist/assets/pilotswarm-DyBlqVTY.js +87 -0
- package/web/dist/assets/react-BZwsW8pe.js +1 -0
- package/web/dist/index.html +16 -0
- package/web/runtime.js +454 -0
- package/web/server.js +276 -0
- package/index.js +0 -1
package/web/server.js
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import http from "node:http";
|
|
3
|
+
import https from "node:https";
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { WS_PATH } from "pilotswarm-sdk/api";
|
|
8
|
+
import { getPortalAssetFile, getPortalConfig } from "./config.js";
|
|
9
|
+
import { authenticateRequest, getAuthConfig } from "./auth.js";
|
|
10
|
+
import { getPublicAuthContext } from "./auth/authz/engine.js";
|
|
11
|
+
import { PortalRuntime } from "./runtime.js";
|
|
12
|
+
import { createApiRouter } from "./api/router.js";
|
|
13
|
+
import { attachWebSockets } from "./api/ws.js";
|
|
14
|
+
|
|
15
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
const DIST_DIR = path.join(__dirname, "dist");
|
|
17
|
+
const DIST_ASSETS_DIR = path.join(DIST_DIR, "assets");
|
|
18
|
+
|
|
19
|
+
function getPortalMode() {
|
|
20
|
+
const explicitMode = process.env.PORTAL_TUI_MODE || process.env.PORTAL_MODE;
|
|
21
|
+
if (explicitMode) return explicitMode;
|
|
22
|
+
return process.env.KUBERNETES_SERVICE_HOST ? "remote" : "local";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function createPortalServer({ app }) {
|
|
26
|
+
const certPath = process.env.TLS_CERT_PATH;
|
|
27
|
+
const keyPath = process.env.TLS_KEY_PATH;
|
|
28
|
+
if (certPath && keyPath && fs.existsSync(certPath) && fs.existsSync(keyPath)) {
|
|
29
|
+
return {
|
|
30
|
+
protocol: "https",
|
|
31
|
+
server: https.createServer({
|
|
32
|
+
cert: fs.readFileSync(certPath),
|
|
33
|
+
key: fs.readFileSync(keyPath),
|
|
34
|
+
}, app),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
protocol: "http",
|
|
39
|
+
server: http.createServer(app),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function createJsonRpcError(error, status = 500) {
|
|
44
|
+
return {
|
|
45
|
+
status,
|
|
46
|
+
body: {
|
|
47
|
+
ok: false,
|
|
48
|
+
error: error?.message || String(error),
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function sendSpaIndex(res) {
|
|
54
|
+
res.set("Cache-Control", "no-store, max-age=0");
|
|
55
|
+
res.sendFile(path.join(DIST_DIR, "index.html"));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export async function startServer(opts = {}) {
|
|
59
|
+
const { port = Number(process.env.PORT) || 3001, workers } = opts;
|
|
60
|
+
if (Number.isFinite(workers) && !process.env.WORKERS) {
|
|
61
|
+
process.env.WORKERS = String(workers);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Strip "__PS_UNSET__" sentinels written by deploy seed-secrets and
|
|
65
|
+
// by the portal-config render path so optional env vars (like
|
|
66
|
+
// ANTHROPIC_API_KEY or AZURE_OAI_KEY) appear unset to downstream code.
|
|
67
|
+
// Mirrors the worker's behavior in packages/sdk/examples/worker.js.
|
|
68
|
+
const SEED_SECRETS_UNSET_SENTINEL = "__PS_UNSET__";
|
|
69
|
+
for (const [k, v] of Object.entries(process.env)) {
|
|
70
|
+
if (v === SEED_SECRETS_UNSET_SENTINEL) delete process.env[k];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const portalConfig = getPortalConfig();
|
|
74
|
+
const mode = getPortalMode();
|
|
75
|
+
const useManagedIdentity = ["1", "true", "yes", "on"].includes(
|
|
76
|
+
String(process.env.PILOTSWARM_USE_MANAGED_IDENTITY || "").toLowerCase(),
|
|
77
|
+
);
|
|
78
|
+
const runtime = new PortalRuntime({
|
|
79
|
+
store: process.env.DATABASE_URL || "sqlite::memory:",
|
|
80
|
+
mode,
|
|
81
|
+
useManagedIdentity,
|
|
82
|
+
cmsFactsDatabaseUrl: process.env.PILOTSWARM_CMS_FACTS_DATABASE_URL || undefined,
|
|
83
|
+
aadDbUser: process.env.PILOTSWARM_DB_AAD_USER || undefined,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const app = express();
|
|
87
|
+
app.set("trust proxy", true);
|
|
88
|
+
app.use(express.json({ limit: "2mb" }));
|
|
89
|
+
|
|
90
|
+
const { server, protocol } = createPortalServer({ app });
|
|
91
|
+
|
|
92
|
+
async function requireAuth(req, res, next) {
|
|
93
|
+
const auth = await authenticateRequest(req);
|
|
94
|
+
if (!auth.ok) {
|
|
95
|
+
res.status(auth.status).json({ ok: false, error: auth.error || (auth.status === 403 ? "Forbidden" : "Unauthorized") });
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
req.auth = auth;
|
|
99
|
+
req.authClaims = auth.principal?.rawClaims || null;
|
|
100
|
+
next();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
app.get("/api/health", async (_req, res) => {
|
|
104
|
+
const started = runtime.started;
|
|
105
|
+
res.json({
|
|
106
|
+
ok: true,
|
|
107
|
+
started,
|
|
108
|
+
mode,
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
app.get("/api/portal-config", async (req, res) => {
|
|
113
|
+
try {
|
|
114
|
+
const auth = await getAuthConfig(req);
|
|
115
|
+
res.json({
|
|
116
|
+
ok: true,
|
|
117
|
+
portal: portalConfig,
|
|
118
|
+
auth,
|
|
119
|
+
});
|
|
120
|
+
} catch (error) {
|
|
121
|
+
const payload = createJsonRpcError(error, 500);
|
|
122
|
+
res.status(payload.status).json(payload.body);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
app.get("/api/auth-config", async (req, res) => {
|
|
127
|
+
try {
|
|
128
|
+
const auth = await getAuthConfig(req);
|
|
129
|
+
res.json(auth);
|
|
130
|
+
} catch (error) {
|
|
131
|
+
const payload = createJsonRpcError(error, 500);
|
|
132
|
+
res.status(payload.status).json(payload.body);
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
app.get("/api/auth/me", requireAuth, async (req, res) => {
|
|
137
|
+
res.json({
|
|
138
|
+
ok: true,
|
|
139
|
+
...getPublicAuthContext(req.auth),
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
app.get("/api/bootstrap", requireAuth, async (_req, res) => {
|
|
144
|
+
try {
|
|
145
|
+
const bootstrap = await runtime.getBootstrap();
|
|
146
|
+
res.json({
|
|
147
|
+
ok: true,
|
|
148
|
+
...bootstrap,
|
|
149
|
+
auth: getPublicAuthContext(_req.auth),
|
|
150
|
+
});
|
|
151
|
+
} catch (error) {
|
|
152
|
+
const payload = createJsonRpcError(error, 500);
|
|
153
|
+
res.status(payload.status).json(payload.body);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// The versioned Web API (the supported product surface). The legacy
|
|
158
|
+
// /api/rpc + /portal-ws routes below stay mounted through the same
|
|
159
|
+
// dispatcher during the deprecation window.
|
|
160
|
+
app.use("/api/v1", createApiRouter({ runtime, requireAuth }));
|
|
161
|
+
|
|
162
|
+
app.post("/api/rpc", requireAuth, async (req, res) => {
|
|
163
|
+
const method = String(req.body?.method || "").trim();
|
|
164
|
+
if (!method) {
|
|
165
|
+
res.status(400).json({ ok: false, error: "RPC method is required" });
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
try {
|
|
169
|
+
const result = await runtime.call(method, req.body?.params || {}, req.auth);
|
|
170
|
+
res.json({ ok: true, result });
|
|
171
|
+
} catch (error) {
|
|
172
|
+
const status = /Unsupported portal RPC method/i.test(String(error?.message || ""))
|
|
173
|
+
? 400
|
|
174
|
+
: 500;
|
|
175
|
+
const payload = createJsonRpcError(error, status);
|
|
176
|
+
res.status(payload.status).json(payload.body);
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
app.get("/api/sessions/:sessionId/artifacts/:filename/download", requireAuth, async (req, res) => {
|
|
181
|
+
try {
|
|
182
|
+
const sessionId = req.params.sessionId;
|
|
183
|
+
const filename = req.params.filename;
|
|
184
|
+
const artifact = await runtime.downloadArtifactBinary(sessionId, filename);
|
|
185
|
+
const contentType = String(artifact?.contentType || "application/octet-stream");
|
|
186
|
+
res.setHeader("content-type", contentType);
|
|
187
|
+
res.setHeader("content-disposition", `attachment; filename="${path.basename(filename)}"`);
|
|
188
|
+
res.send(artifact.body);
|
|
189
|
+
} catch (error) {
|
|
190
|
+
const payload = createJsonRpcError(error, 404);
|
|
191
|
+
res.status(payload.status).json(payload.body);
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
app.get("/api/sessions/:sessionId/artifacts/:filename/meta", requireAuth, async (req, res) => {
|
|
196
|
+
try {
|
|
197
|
+
const sessionId = req.params.sessionId;
|
|
198
|
+
const filename = req.params.filename;
|
|
199
|
+
const metadata = await runtime.getArtifactMetadata(sessionId, filename);
|
|
200
|
+
if (!metadata) {
|
|
201
|
+
res.status(404).json({ ok: false, error: "Artifact not found" });
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
res.json({ ok: true, ...metadata });
|
|
205
|
+
} catch (error) {
|
|
206
|
+
const payload = createJsonRpcError(error, 404);
|
|
207
|
+
res.status(payload.status).json(payload.body);
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
app.get("/api/portal-assets/:assetName", async (req, res) => {
|
|
212
|
+
const assetFile = getPortalAssetFile(req.params.assetName);
|
|
213
|
+
if (!assetFile || !fs.existsSync(assetFile)) {
|
|
214
|
+
res.status(404).end();
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
res.sendFile(assetFile, {
|
|
218
|
+
maxAge: "1h",
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
if (fs.existsSync(DIST_DIR)) {
|
|
223
|
+
app.use("/assets", express.static(DIST_ASSETS_DIR, {
|
|
224
|
+
immutable: true,
|
|
225
|
+
maxAge: "1y",
|
|
226
|
+
fallthrough: true,
|
|
227
|
+
}));
|
|
228
|
+
app.use("/assets", (_req, res) => {
|
|
229
|
+
res.status(404).type("text/plain").send("Asset not found");
|
|
230
|
+
});
|
|
231
|
+
app.use(express.static(DIST_DIR, { index: false }));
|
|
232
|
+
app.get(/^\/(?!api\/).*/, (_req, res) => {
|
|
233
|
+
sendSpaIndex(res);
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const socketServers = attachWebSockets(server, runtime, [
|
|
238
|
+
{ path: "/portal-ws", allowThemeMessages: true },
|
|
239
|
+
{ path: WS_PATH },
|
|
240
|
+
]);
|
|
241
|
+
|
|
242
|
+
async function shutdown() {
|
|
243
|
+
for (const socketServer of socketServers) {
|
|
244
|
+
for (const client of socketServer.clients) {
|
|
245
|
+
try {
|
|
246
|
+
client.close();
|
|
247
|
+
} catch {}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
await runtime.stop().catch(() => {});
|
|
251
|
+
server.close();
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
process.on("SIGINT", shutdown);
|
|
255
|
+
process.on("SIGTERM", shutdown);
|
|
256
|
+
|
|
257
|
+
await new Promise((resolve, reject) => {
|
|
258
|
+
server.once("error", reject);
|
|
259
|
+
server.listen(port, () => {
|
|
260
|
+
server.off("error", reject);
|
|
261
|
+
resolve();
|
|
262
|
+
});
|
|
263
|
+
});
|
|
264
|
+
console.log(`[portal] PilotSwarm Web at ${protocol}://localhost:${port}`);
|
|
265
|
+
|
|
266
|
+
// Test/embedder handle: stops the runtime and closes the server.
|
|
267
|
+
server.stopPortal = shutdown;
|
|
268
|
+
return server;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (process.argv[1]?.endsWith("server.js") || import.meta.url === `file://${process.argv[1]}`) {
|
|
272
|
+
startServer().catch((error) => {
|
|
273
|
+
console.error("[portal] Failed to start:", error);
|
|
274
|
+
process.exitCode = 1;
|
|
275
|
+
});
|
|
276
|
+
}
|
package/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
module.exports = {};
|