pilotswarm 0.5.11 → 0.5.13
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 +224 -7
- package/mcp/README.md +1 -1
- package/mcp/dist/src/tools/artifacts.d.ts.map +1 -1
- package/mcp/dist/src/tools/artifacts.js +66 -10
- package/mcp/dist/src/tools/artifacts.js.map +1 -1
- package/package.json +2 -2
- package/tui/src/node-sdk-transport.js +43 -4
- package/web/dist/assets/{index-DySoKTKO.js → index-bQ2QInMX.js} +1 -1
- package/web/dist/index.html +1 -1
- package/web/runtime.js +11 -0
package/README.md
CHANGED
|
@@ -7,22 +7,236 @@ surface of a [PilotSwarm](https://github.com/affandar/pilotswarm) deployment:
|
|
|
7
7
|
|---|---|
|
|
8
8
|
| `pilotswarm` | Terminal UI. `pilotswarm remote --api-url <portal-url>` attaches to any deployment (auto Entra sign-in); `pilotswarm local` runs an embedded dev stack. `pilotswarm-cli` is an alias. |
|
|
9
9
|
| `pilotswarm-web` | The portal server: hosts the browser UI **and** the deployment's Web API (`/api/v1` + `/api/v1/ws`) — the single integration surface every client rides. |
|
|
10
|
-
| `pilotswarm-mcp` | MCP server exposing PilotSwarm sessions/facts/models to Claude Desktop, Cursor, or any MCP client.
|
|
10
|
+
| `pilotswarm-mcp` | MCP server exposing PilotSwarm sessions/facts/models to Claude Code, Claude Desktop, Copilot CLI, VS Code, Cursor, or any MCP client. |
|
|
11
|
+
|
|
12
|
+
Requires **Node 24+**. Everything below assumes you already have a deployment's
|
|
13
|
+
portal URL — that URL is the only thing you need to configure.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Quickstart
|
|
18
|
+
|
|
19
|
+
### 1. Install
|
|
11
20
|
|
|
12
21
|
```bash
|
|
13
22
|
npm install -g pilotswarm
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
(Or skip the install and prefix every command with `npx -p pilotswarm`.)
|
|
26
|
+
|
|
27
|
+
### 2. Sign in to a deployment
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pilotswarm auth login --api-url https://portal.example.com
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
This opens your browser for Entra sign-in and caches the token under
|
|
34
|
+
`~/.config/pilotswarm/auth/<origin>.json`. **Every surface in this package —
|
|
35
|
+
the TUI and the MCP server — reads that same cache**, so you sign in once per
|
|
36
|
+
deployment, not once per client.
|
|
14
37
|
|
|
15
|
-
|
|
38
|
+
If the deployment has auth disabled, the command simply tells you so and there
|
|
39
|
+
is nothing to do.
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pilotswarm auth status --api-url https://portal.example.com # who am I, is the token still good
|
|
43
|
+
pilotswarm auth logout --api-url https://portal.example.com # drop cached tokens for that origin
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
On a headless box (no browser), add `--device-code` to use the device-code flow
|
|
47
|
+
instead — but note many corporate tenants block device code via Conditional
|
|
48
|
+
Access.
|
|
49
|
+
|
|
50
|
+
### 3. Run the TUI
|
|
51
|
+
|
|
52
|
+
```bash
|
|
16
53
|
pilotswarm remote --api-url https://portal.example.com
|
|
54
|
+
```
|
|
17
55
|
|
|
18
|
-
|
|
19
|
-
pilotswarm
|
|
56
|
+
The `remote` positional is required — `--api-url` without it is an error,
|
|
57
|
+
because bare `pilotswarm` means *local* mode. You can drop the flag entirely by
|
|
58
|
+
setting `PILOTSWARM_API_URL`, or by putting it in a `.env.remote` file next to
|
|
59
|
+
you (remote mode auto-loads `.env.remote`; local mode auto-loads `.env`):
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
export PILOTSWARM_API_URL=https://portal.example.com
|
|
63
|
+
pilotswarm remote
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Step 2 is optional in practice: the TUI runs the same sign-in lazily on start
|
|
67
|
+
if no cached token is found. Running `auth login` first just gets the browser
|
|
68
|
+
dance out of the way before the full-screen UI takes over your terminal, and it
|
|
69
|
+
is what makes the MCP servers below work without any further setup.
|
|
70
|
+
|
|
71
|
+
Useful TUI flags: `-m, --model <name>` (initial model), `-p, --plugin <dir>`
|
|
72
|
+
(plugin directory), `-e, --env <file>` (env file), `-h, --help`.
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Connect your AI assistant (MCP)
|
|
77
|
+
|
|
78
|
+
`pilotswarm-mcp` is a local, stdio MCP server: your assistant spawns it as a
|
|
79
|
+
child process, and it calls the deployment's Web API **as you**, reusing the
|
|
80
|
+
token cached by `pilotswarm auth login`. Nothing extra is deployed, and no
|
|
81
|
+
secret goes into the config file — just the URL.
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
MCP host (Claude Code / Claude Desktop / Copilot CLI / VS Code)
|
|
85
|
+
└─ spawns → pilotswarm-mcp (local, stdio)
|
|
86
|
+
└─ HTTPS → https://portal.example.com/api/v1
|
|
87
|
+
(your identity; your role decides your tool surface)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Your Entra app role gates the surface: admin-only tools (embedder start/stop,
|
|
91
|
+
`facts_admin`, `restart_system_session`, graph-namespace writes) register only
|
|
92
|
+
for admin credentials.
|
|
93
|
+
|
|
94
|
+
### Claude Code
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
claude mcp add pilotswarm -- npx -y -p pilotswarm pilotswarm-mcp \
|
|
98
|
+
--api-url https://portal.example.com
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Or check a `.mcp.json` into the project root so your whole team gets it:
|
|
102
|
+
|
|
103
|
+
```json
|
|
104
|
+
{
|
|
105
|
+
"mcpServers": {
|
|
106
|
+
"pilotswarm": {
|
|
107
|
+
"type": "stdio",
|
|
108
|
+
"command": "npx",
|
|
109
|
+
"args": ["-y", "-p", "pilotswarm", "pilotswarm-mcp",
|
|
110
|
+
"--api-url", "https://portal.example.com"]
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Claude Desktop
|
|
117
|
+
|
|
118
|
+
Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS)
|
|
119
|
+
or `%APPDATA%\Claude\claude_desktop_config.json` (Windows), then restart the
|
|
120
|
+
app:
|
|
20
121
|
|
|
21
|
-
|
|
22
|
-
|
|
122
|
+
```json
|
|
123
|
+
{
|
|
124
|
+
"mcpServers": {
|
|
125
|
+
"pilotswarm": {
|
|
126
|
+
"command": "npx",
|
|
127
|
+
"args": ["-y", "-p", "pilotswarm", "pilotswarm-mcp",
|
|
128
|
+
"--api-url", "https://portal.example.com"]
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
23
132
|
```
|
|
24
133
|
|
|
25
|
-
|
|
134
|
+
### GitHub Copilot CLI
|
|
135
|
+
|
|
136
|
+
Write `.copilot/mcp-config.json` in the repo (or `~/.copilot/mcp-config.json`
|
|
137
|
+
to have it everywhere):
|
|
138
|
+
|
|
139
|
+
```json
|
|
140
|
+
{
|
|
141
|
+
"mcpServers": {
|
|
142
|
+
"pilotswarm": {
|
|
143
|
+
"type": "stdio",
|
|
144
|
+
"command": "npx",
|
|
145
|
+
"args": ["-y", "-p", "pilotswarm", "pilotswarm-mcp",
|
|
146
|
+
"--api-url", "https://portal.example.com"]
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### VS Code (Copilot)
|
|
153
|
+
|
|
154
|
+
Add `.vscode/mcp.json` to the workspace — note the key is `servers`, not
|
|
155
|
+
`mcpServers`. The server then shows up in agent mode's tool picker:
|
|
156
|
+
|
|
157
|
+
```json
|
|
158
|
+
{
|
|
159
|
+
"servers": {
|
|
160
|
+
"pilotswarm": {
|
|
161
|
+
"type": "stdio",
|
|
162
|
+
"command": "npx",
|
|
163
|
+
"args": ["-y", "-p", "pilotswarm", "pilotswarm-mcp",
|
|
164
|
+
"--api-url", "https://portal.example.com"]
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Cursor
|
|
171
|
+
|
|
172
|
+
Same shape as Claude Desktop, in `~/.cursor/mcp.json` (or **Settings → MCP**).
|
|
173
|
+
|
|
174
|
+
### Check that it worked
|
|
175
|
+
|
|
176
|
+
Ask your assistant to call `get_capabilities`. You should get back
|
|
177
|
+
`mode: "web"`, your `admin` flag, and the deployment's facts/graph flags. Then
|
|
178
|
+
`list_sessions` for a fleet view, and `create_session` → `send_and_wait` to
|
|
179
|
+
drive one.
|
|
180
|
+
|
|
181
|
+
If it fails to start, run the same command by hand — the error is much easier
|
|
182
|
+
to read outside the MCP host:
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
npx -y -p pilotswarm pilotswarm-mcp --api-url https://portal.example.com --log-level info
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
The usual cause is a missing or expired token: re-run
|
|
189
|
+
`pilotswarm auth login --api-url <url>`.
|
|
190
|
+
|
|
191
|
+
### Non-interactive credentials (CI, service principals, containers)
|
|
192
|
+
|
|
193
|
+
There is no browser to sign in with, so hand the server a bearer token
|
|
194
|
+
directly:
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
PILOTSWARM_API_TOKEN=<token> npx -y -p pilotswarm pilotswarm-mcp \
|
|
198
|
+
--api-url https://portal.example.com
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Remote / shared MCP over HTTP
|
|
202
|
+
|
|
203
|
+
For a shared endpoint rather than a per-user child process, run the server with
|
|
204
|
+
the HTTP transport and a bearer key clients must present:
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
PILOTSWARM_MCP_KEY=your-secret-key npx -y -p pilotswarm pilotswarm-mcp \
|
|
208
|
+
--transport http --port 3100 --api-url https://portal.example.com
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Clients then point at `http://your-host:3100/mcp` with an
|
|
212
|
+
`Authorization: Bearer ${PILOTSWARM_MCP_KEY}` header. Read the
|
|
213
|
+
[security model](https://github.com/affandar/pilotswarm/blob/main/packages/app/mcp/README.md#security-model)
|
|
214
|
+
before exposing this beyond localhost: the key is shared, and every client
|
|
215
|
+
behind it has the full scope of the deployment.
|
|
216
|
+
|
|
217
|
+
Full tool catalog, resources, and every flag:
|
|
218
|
+
[MCP server README](https://github.com/affandar/pilotswarm/blob/main/packages/app/mcp/README.md).
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## Other surfaces
|
|
223
|
+
|
|
224
|
+
**Run everything locally** (embedded workers, no deployment, no auth) — the fast
|
|
225
|
+
way to try PilotSwarm or develop a plugin:
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
pilotswarm local # bare `pilotswarm` does the same
|
|
229
|
+
pilotswarm local -p ./plugin -n 4
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
**Host a portal** — serves the browser UI and the Web API that every client
|
|
233
|
+
above rides on. Needs worker-side env (`DATABASE_URL`, model providers, …):
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
pilotswarm-web --plugin ./plugin
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
**Library surfaces** (used by the shipped UIs; importable for custom hosts):
|
|
26
240
|
|
|
27
241
|
- `pilotswarm/ui-core` — framework-free UI controller/state/selectors
|
|
28
242
|
- `pilotswarm/ui-react` — the shared React composition (Ink + DOM)
|
|
@@ -33,7 +247,10 @@ Building an app or service instead of a UI? You want
|
|
|
33
247
|
[`pilotswarm-sdk`](https://www.npmjs.com/package/pilotswarm-sdk) — including
|
|
34
248
|
its zero-dependency wire client at `pilotswarm-sdk/api`.
|
|
35
249
|
|
|
250
|
+
---
|
|
251
|
+
|
|
36
252
|
Docs: [Quick Start](https://github.com/affandar/pilotswarm/blob/main/docs/quickstart/docker.md) ·
|
|
37
253
|
[User Guide](https://github.com/affandar/pilotswarm/blob/main/docs/user-guide/README.md) ·
|
|
254
|
+
[MCP Setup](https://github.com/affandar/pilotswarm/blob/main/docs/user-guide/mcp-local-setup.md) ·
|
|
38
255
|
[Web API Reference](https://github.com/affandar/pilotswarm/blob/main/docs/api/reference.md) ·
|
|
39
256
|
[Architecture / Layering](https://github.com/affandar/pilotswarm/blob/main/docs/architecture/layering.md)
|
package/mcp/README.md
CHANGED
|
@@ -39,7 +39,7 @@ Each client below shows both **Stdio** (local, recommended) and **HTTP** (remote
|
|
|
39
39
|
|
|
40
40
|
### GitHub Copilot CLI
|
|
41
41
|
|
|
42
|
-
Add to
|
|
42
|
+
Add to `.copilot/mcp-config.json` (repo-scoped) or `~/.copilot/mcp-config.json` (global):
|
|
43
43
|
|
|
44
44
|
**Stdio (local):**
|
|
45
45
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"artifacts.d.ts","sourceRoot":"","sources":["../../../src/tools/artifacts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAGnD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,aAAa,
|
|
1
|
+
{"version":3,"file":"artifacts.d.ts","sourceRoot":"","sources":["../../../src/tools/artifacts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAGnD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,aAAa,QAyK1E"}
|
|
@@ -26,23 +26,31 @@ export function registerArtifactTools(server, ctx) {
|
|
|
26
26
|
}));
|
|
27
27
|
server.registerTool("get_artifact", {
|
|
28
28
|
title: "Get Artifact",
|
|
29
|
-
description: "Fetch a session artifact. include controls what comes back: 'meta' (metadata), 'text'
|
|
30
|
-
+ "text — for text artifacts)
|
|
29
|
+
description: "Fetch a session artifact. include controls what comes back: 'meta' (metadata with sha256), 'text' "
|
|
30
|
+
+ "(content as text — for text artifacts), 'base64' (content base64-encoded — for binary artifacts, "
|
|
31
|
+
+ "bounded by max_bytes). Large binaries: use the download_url returned with meta.",
|
|
31
32
|
inputSchema: {
|
|
32
33
|
session_id: sessionIdShape().describe("The session owning the artifact"),
|
|
33
34
|
filename: z.string().min(1).describe("Artifact filename"),
|
|
34
|
-
include: z.array(z.enum(["meta", "text"])).optional().describe("What to fetch (default ['meta'])"),
|
|
35
|
+
include: z.array(z.enum(["meta", "text", "base64"])).optional().describe("What to fetch (default ['meta'])"),
|
|
36
|
+
max_bytes: z.number().optional().describe("Byte cap for include 'base64' (default 256KB, max 1MB)"),
|
|
35
37
|
},
|
|
36
|
-
}, withToolErrors(async ({ session_id, filename, include }) => {
|
|
38
|
+
}, withToolErrors(async ({ session_id, filename, include, max_bytes }) => {
|
|
37
39
|
const wants = new Set(include ?? ["meta"]);
|
|
38
40
|
const result = { session_id, filename };
|
|
41
|
+
let meta;
|
|
42
|
+
try {
|
|
43
|
+
meta = await api.call("getArtifactMetadata", { sessionId: session_id, filename });
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
return errorResult(err instanceof Error ? err.message : String(err), { session_id, filename });
|
|
47
|
+
}
|
|
48
|
+
if (!meta) {
|
|
49
|
+
// Loud not-found: no fabricated download_url for files that don't exist.
|
|
50
|
+
return errorResult(`Artifact not found: ${filename} in session ${session_id}`, { session_id, filename });
|
|
51
|
+
}
|
|
39
52
|
if (wants.has("meta")) {
|
|
40
|
-
|
|
41
|
-
result.meta = await api.call("getArtifactMetadata", { sessionId: session_id, filename });
|
|
42
|
-
}
|
|
43
|
-
catch (err) {
|
|
44
|
-
return errorResult(err instanceof Error ? err.message : String(err), { session_id, filename });
|
|
45
|
-
}
|
|
53
|
+
result.meta = meta;
|
|
46
54
|
// Authenticated binary download route (bespoke, streams).
|
|
47
55
|
result.download_url = `/api/v1/sessions/${session_id}/artifacts/${encodeURIComponent(filename)}/download`;
|
|
48
56
|
}
|
|
@@ -55,6 +63,17 @@ export function registerArtifactTools(server, ctx) {
|
|
|
55
63
|
result.text_error = err instanceof Error ? err.message : String(err);
|
|
56
64
|
}
|
|
57
65
|
}
|
|
66
|
+
if (wants.has("base64")) {
|
|
67
|
+
try {
|
|
68
|
+
const read = await api.call("readArtifactBase64", { sessionId: session_id, filename, maxBytes: max_bytes });
|
|
69
|
+
result.base64 = read?.base64;
|
|
70
|
+
result.truncated = read?.truncated;
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
result.base64 = null;
|
|
74
|
+
result.base64_error = err instanceof Error ? err.message : String(err);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
58
77
|
return jsonResult(result);
|
|
59
78
|
}));
|
|
60
79
|
server.registerTool("upload_artifact", {
|
|
@@ -78,6 +97,43 @@ export function registerArtifactTools(server, ctx) {
|
|
|
78
97
|
});
|
|
79
98
|
return jsonResult({ uploaded: true, meta });
|
|
80
99
|
}));
|
|
100
|
+
server.registerTool("copy_artifact", {
|
|
101
|
+
title: "Copy Artifact",
|
|
102
|
+
description: "Server-side copy of an artifact between sessions — bytes never leave the store. "
|
|
103
|
+
+ "Optionally verify the copy against an expected SHA-256.",
|
|
104
|
+
inputSchema: {
|
|
105
|
+
from_session_id: sessionIdShape().describe("Session that owns the source artifact"),
|
|
106
|
+
from_filename: z.string().min(1).describe("Source artifact filename"),
|
|
107
|
+
to_session_id: sessionIdShape().describe("Destination session"),
|
|
108
|
+
to_filename: z.string().optional().describe("Destination filename (defaults to the source name)"),
|
|
109
|
+
expected_sha256: z.string().optional().describe("Fail (and delete the copy) if the copied bytes hash differently"),
|
|
110
|
+
},
|
|
111
|
+
}, withToolErrors(async ({ from_session_id, from_filename, to_session_id, to_filename, expected_sha256 }) => {
|
|
112
|
+
const meta = await api.call("copyArtifact", {
|
|
113
|
+
fromSessionId: from_session_id,
|
|
114
|
+
fromFilename: from_filename,
|
|
115
|
+
toSessionId: to_session_id,
|
|
116
|
+
toFilename: to_filename,
|
|
117
|
+
});
|
|
118
|
+
if (expected_sha256 && meta?.sha256 !== expected_sha256) {
|
|
119
|
+
await api.call("deleteArtifact", { sessionId: to_session_id, filename: meta?.filename ?? to_filename ?? from_filename });
|
|
120
|
+
return errorResult(`SHA_MISMATCH: copied bytes hash ${meta?.sha256}, expected ${expected_sha256}. The copy was deleted.`, { from_session_id, from_filename });
|
|
121
|
+
}
|
|
122
|
+
return jsonResult({ copied: true, meta });
|
|
123
|
+
}));
|
|
124
|
+
server.registerTool("pin_artifact", {
|
|
125
|
+
title: "Pin Artifact",
|
|
126
|
+
description: "Pin (or unpin) an artifact so it survives session cleanup — use for deliverables that must "
|
|
127
|
+
+ "outlive their producing session.",
|
|
128
|
+
inputSchema: {
|
|
129
|
+
session_id: sessionIdShape().describe("The session owning the artifact"),
|
|
130
|
+
filename: z.string().min(1).describe("Artifact filename"),
|
|
131
|
+
pinned: z.boolean().optional().describe("true to pin (default), false to unpin"),
|
|
132
|
+
},
|
|
133
|
+
}, withToolErrors(async ({ session_id, filename, pinned }) => {
|
|
134
|
+
const meta = await api.call("setArtifactPinned", { sessionId: session_id, filename, pinned: pinned !== false });
|
|
135
|
+
return jsonResult({ pinned: pinned !== false, meta });
|
|
136
|
+
}));
|
|
81
137
|
server.registerTool("delete_artifact", {
|
|
82
138
|
title: "Delete Artifact",
|
|
83
139
|
description: "Delete an artifact from a session.",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"artifacts.js","sourceRoot":"","sources":["../../../src/tools/artifacts.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAE7E;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CAAC,MAAiB,EAAE,GAAkB;IACvE,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;IACpB,IAAI,CAAC,GAAG;QAAE,OAAO;IAEjB,MAAM,CAAC,YAAY,CACf,gBAAgB,EAChB;QACI,KAAK,EAAE,gBAAgB;QACvB,WAAW,EAAE,6EAA6E;QAC1F,WAAW,EAAE;YACT,UAAU,EAAE,cAAc,EAAE,CAAC,QAAQ,CAAC,qCAAqC,CAAC;SAC/E;KACJ,EACD,cAAc,CAAC,KAAK,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE;QACpC,MAAM,SAAS,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,eAAe,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC,CAAC;QAC7E,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,SAAS,IAAI,EAAE,CAAC,CAAC;QACjF,OAAO,UAAU,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/D,CAAC,CAAC,CACL,CAAC;IAEF,MAAM,CAAC,YAAY,CACf,cAAc,EACd;QACI,KAAK,EAAE,cAAc;QACrB,WAAW,EACP,oGAAoG;cAClG,
|
|
1
|
+
{"version":3,"file":"artifacts.js","sourceRoot":"","sources":["../../../src/tools/artifacts.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pilotswarm",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.13",
|
|
4
4
|
"description": "PilotSwarm application package: terminal UI, browser portal + Web API server, and MCP server — one install, three bins.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
"hono": "^4.12.10",
|
|
78
78
|
"ink": "^6.8.0",
|
|
79
79
|
"jose": "^6.2.2",
|
|
80
|
-
"pilotswarm-sdk": "^0.5.
|
|
80
|
+
"pilotswarm-sdk": "^0.5.13",
|
|
81
81
|
"react": "^19.2.4",
|
|
82
82
|
"react-dom": "^19.2.4",
|
|
83
83
|
"ws": "^8.18.2"
|
|
@@ -1152,10 +1152,43 @@ export class NodeSdkTransport {
|
|
|
1152
1152
|
|
|
1153
1153
|
async getArtifactMetadata(sessionId, filename) {
|
|
1154
1154
|
if (!this.artifactStore || !sessionId || !filename) return null;
|
|
1155
|
+
if (typeof this.artifactStore.statArtifact === "function") {
|
|
1156
|
+
return this.artifactStore.statArtifact(sessionId, filename);
|
|
1157
|
+
}
|
|
1155
1158
|
const artifacts = await this.artifactStore.listArtifacts(sessionId);
|
|
1156
1159
|
return (artifacts || []).find((artifact) => artifact?.filename === filename) || null;
|
|
1157
1160
|
}
|
|
1158
1161
|
|
|
1162
|
+
async copyArtifact(fromSessionId, fromFilename, toSessionId, toFilename) {
|
|
1163
|
+
if (!this.artifactStore) {
|
|
1164
|
+
throw new Error("Artifact store is not available for this transport.");
|
|
1165
|
+
}
|
|
1166
|
+
return this.artifactStore.copyArtifact(fromSessionId, fromFilename, toSessionId, toFilename);
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
async setArtifactPinned(sessionId, filename, pinned) {
|
|
1170
|
+
if (!this.artifactStore) {
|
|
1171
|
+
throw new Error("Artifact store is not available for this transport.");
|
|
1172
|
+
}
|
|
1173
|
+
return this.artifactStore.setArtifactPinned(sessionId, filename, pinned === true);
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
/** Base64 inline read for the MCP surface — JSON-safe, size-guarded. */
|
|
1177
|
+
async readArtifactBase64(sessionId, filename, maxBytes) {
|
|
1178
|
+
if (!this.artifactStore) {
|
|
1179
|
+
throw new Error("Artifact store is not available for this transport.");
|
|
1180
|
+
}
|
|
1181
|
+
const cap = Math.min(Math.max(1, Math.floor(maxBytes || 262144)), 1048576);
|
|
1182
|
+
const result = await this.artifactStore.downloadArtifact(sessionId, filename);
|
|
1183
|
+
const slice = result.body.subarray(0, cap);
|
|
1184
|
+
const { body: _body, ...meta } = result;
|
|
1185
|
+
return {
|
|
1186
|
+
...meta,
|
|
1187
|
+
base64: slice.toString("base64"),
|
|
1188
|
+
truncated: slice.length < result.body.length,
|
|
1189
|
+
};
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1159
1192
|
async deleteArtifact(sessionId, filename) {
|
|
1160
1193
|
if (!this.artifactStore) {
|
|
1161
1194
|
throw new Error("Artifact store is not available for this transport.");
|
|
@@ -1195,16 +1228,22 @@ export class NodeSdkTransport {
|
|
|
1195
1228
|
}
|
|
1196
1229
|
|
|
1197
1230
|
const filename = path.basename(resolvedPath);
|
|
1198
|
-
const content = await fs.promises.readFile(resolvedPath);
|
|
1199
1231
|
const contentType = guessArtifactContentType(filename);
|
|
1200
|
-
|
|
1232
|
+
let meta;
|
|
1233
|
+
if (typeof this.artifactStore.uploadArtifactFromFile === "function") {
|
|
1234
|
+
meta = await this.artifactStore.uploadArtifactFromFile(sessionId, filename, resolvedPath, contentType, { source: "user" });
|
|
1235
|
+
} else {
|
|
1236
|
+
const content = await fs.promises.readFile(resolvedPath);
|
|
1237
|
+
meta = await this.artifactStore.uploadArtifact(sessionId, filename, content, contentType);
|
|
1238
|
+
}
|
|
1201
1239
|
|
|
1202
1240
|
return {
|
|
1203
1241
|
sessionId,
|
|
1204
1242
|
filename,
|
|
1205
1243
|
resolvedPath,
|
|
1206
|
-
sizeBytes:
|
|
1244
|
+
sizeBytes: meta?.sizeBytes ?? stat.size,
|
|
1207
1245
|
contentType,
|
|
1246
|
+
...(meta?.sha256 ? { sha256: meta.sha256 } : {}),
|
|
1208
1247
|
};
|
|
1209
1248
|
}
|
|
1210
1249
|
|
|
@@ -1837,7 +1876,7 @@ function createArtifactStore() {
|
|
|
1837
1876
|
const reason = err?.message || String(err);
|
|
1838
1877
|
throw new Error(
|
|
1839
1878
|
`Azure Blob Storage is configured but cannot be initialized (reason: ${reason}). ` +
|
|
1840
|
-
`Either fix the configuration or unset AZURE_STORAGE_CONNECTION_STRING / PILOTSWARM_USE_MANAGED_IDENTITY to fall back to the local filesystem artifact store.`,
|
|
1879
|
+
`Either fix the configuration or unset AZURE_STORAGE_CONNECTION_STRING / PILOTSWARM_BLOB_USE_MANAGED_IDENTITY / PILOTSWARM_USE_MANAGED_IDENTITY / AZURE_STORAGE_ACCOUNT_URL to fall back to the local filesystem artifact store.`,
|
|
1841
1880
|
);
|
|
1842
1881
|
}
|
|
1843
1882
|
|