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 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. `--api-url <portal-url>` is the supported mode. |
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
- # attach the TUI to a deployment the URL is the only credential you need
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
- # host a portal (needs worker-side env: DATABASE_URL etc.)
19
- pilotswarm-web --plugin ./plugin
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
- # expose a deployment to an MCP client
22
- pilotswarm-mcp --api-url https://portal.example.com
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
- Library surfaces (used by the shipped UIs; importable for custom hosts):
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 your `.mcp.json` (project root or `~/.copilot/`):
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,QAmG1E"}
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' (content as "
30
- + "text — for text artifacts). Binary artifacts: use the download_url returned in meta.",
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
- try {
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":"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"}
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.11",
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.11",
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
- await this.artifactStore.uploadArtifact(sessionId, filename, content, contentType);
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: content.length,
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