@robzilla1738/agentswarm 0.2.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.
Files changed (64) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +142 -0
  3. package/bin/swarm.js +10 -0
  4. package/dist/agent.js +211 -0
  5. package/dist/cli.js +667 -0
  6. package/dist/config.js +289 -0
  7. package/dist/control.js +96 -0
  8. package/dist/deepseek.js +321 -0
  9. package/dist/executor.js +988 -0
  10. package/dist/hub.js +553 -0
  11. package/dist/journal.js +152 -0
  12. package/dist/prompts.js +232 -0
  13. package/dist/providers.js +151 -0
  14. package/dist/run.js +309 -0
  15. package/dist/sandbox.js +505 -0
  16. package/dist/state.js +230 -0
  17. package/dist/terminal.js +298 -0
  18. package/dist/tools.js +491 -0
  19. package/dist/types.js +26 -0
  20. package/dist/util.js +209 -0
  21. package/dist/webtools.js +205 -0
  22. package/package.json +63 -0
  23. package/ui/out/404/index.html +1 -0
  24. package/ui/out/404.html +1 -0
  25. package/ui/out/_next/static/chunks/255-2aa030c9ba2867e3.js +1 -0
  26. package/ui/out/_next/static/chunks/383-289a866b246b41cc.js +1 -0
  27. package/ui/out/_next/static/chunks/4bd1b696-c023c6e3521b1417.js +1 -0
  28. package/ui/out/_next/static/chunks/619-ba102abea3e3d0e4.js +1 -0
  29. package/ui/out/_next/static/chunks/677-b37981ba0eca75b2.js +1 -0
  30. package/ui/out/_next/static/chunks/app/_not-found/page-2d0982e372f7be41.js +1 -0
  31. package/ui/out/_next/static/chunks/app/layout-37ad32c5fdb26f29.js +1 -0
  32. package/ui/out/_next/static/chunks/app/page-0c9f35bd4aa8e370.js +1 -0
  33. package/ui/out/_next/static/chunks/app/run/page-13dc41a57e34da71.js +1 -0
  34. package/ui/out/_next/static/chunks/app/settings/page-a1763be7f6de888c.js +1 -0
  35. package/ui/out/_next/static/chunks/framework-2c534e0e662575a2.js +1 -0
  36. package/ui/out/_next/static/chunks/main-app-889ed884f8bc78e3.js +1 -0
  37. package/ui/out/_next/static/chunks/main-eb90ae3b35d2fd16.js +1 -0
  38. package/ui/out/_next/static/chunks/pages/_app-7d307437aca18ad4.js +1 -0
  39. package/ui/out/_next/static/chunks/pages/_error-cb2a52f75f2162e2.js +1 -0
  40. package/ui/out/_next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
  41. package/ui/out/_next/static/chunks/webpack-38639c05c96dbeca.js +1 -0
  42. package/ui/out/_next/static/css/82edaa7a5942f894.css +3 -0
  43. package/ui/out/_next/static/eiQeDU9uBHNsBj0CFkp8M/_buildManifest.js +1 -0
  44. package/ui/out/_next/static/eiQeDU9uBHNsBj0CFkp8M/_ssgManifest.js +1 -0
  45. package/ui/out/_next/static/media/0aa834ed78bf6d07-s.woff2 +0 -0
  46. package/ui/out/_next/static/media/438aa629764e75f3-s.woff2 +0 -0
  47. package/ui/out/_next/static/media/4c9affa5bc8f420e-s.p.woff2 +0 -0
  48. package/ui/out/_next/static/media/51251f8b9793cdb3-s.woff2 +0 -0
  49. package/ui/out/_next/static/media/67957d42bae0796d-s.woff2 +0 -0
  50. package/ui/out/_next/static/media/875ae681bfde4580-s.woff2 +0 -0
  51. package/ui/out/_next/static/media/886030b0b59bc5a7-s.woff2 +0 -0
  52. package/ui/out/_next/static/media/939c4f875ee75fbb-s.woff2 +0 -0
  53. package/ui/out/_next/static/media/bb3ef058b751a6ad-s.p.woff2 +0 -0
  54. package/ui/out/_next/static/media/cc978ac5ee68c2b6-s.woff2 +0 -0
  55. package/ui/out/_next/static/media/e857b654a2caa584-s.woff2 +0 -0
  56. package/ui/out/_next/static/media/f911b923c6adde36-s.woff2 +0 -0
  57. package/ui/out/icon.png +0 -0
  58. package/ui/out/index.html +1 -0
  59. package/ui/out/index.txt +22 -0
  60. package/ui/out/run/index.html +1 -0
  61. package/ui/out/run/index.txt +22 -0
  62. package/ui/out/settings/index.html +1 -0
  63. package/ui/out/settings/index.txt +22 -0
  64. package/ui/out/swarm-mark.png +0 -0
package/dist/cli.js ADDED
@@ -0,0 +1,667 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.main = main;
37
+ const child_process_1 = require("child_process");
38
+ const fs = __importStar(require("fs"));
39
+ const path = __importStar(require("path"));
40
+ const config_1 = require("./config");
41
+ const control_1 = require("./control");
42
+ const deepseek_1 = require("./deepseek");
43
+ const deepseek_2 = require("./deepseek");
44
+ const providers_1 = require("./providers");
45
+ const hub_1 = require("./hub");
46
+ const journal_1 = require("./journal");
47
+ const run_1 = require("./run");
48
+ const executor_1 = require("./executor");
49
+ const sandbox_1 = require("./sandbox");
50
+ const terminal_1 = require("./terminal");
51
+ const util_1 = require("./util");
52
+ const BIN_PATH = path.join(__dirname, "..", "bin", "swarm.js");
53
+ /** Flags that never take a value — they must not swallow the next positional
54
+ * (`swarm run --fg "mission"` would otherwise eat the mission). */
55
+ const BOOL_FLAGS = new Set(["fg", "open", "resume"]);
56
+ function parseArgs(argv) {
57
+ const _ = [];
58
+ const flags = {};
59
+ for (let i = 0; i < argv.length; i++) {
60
+ const a = argv[i];
61
+ if (a.startsWith("--")) {
62
+ const key = a.slice(2);
63
+ const next = argv[i + 1];
64
+ if (key.startsWith("no-")) {
65
+ flags[key.slice(3)] = false;
66
+ }
67
+ else if (!BOOL_FLAGS.has(key) && next !== undefined && !next.startsWith("--")) {
68
+ flags[key] = next;
69
+ i++;
70
+ }
71
+ else {
72
+ flags[key] = true;
73
+ }
74
+ }
75
+ else {
76
+ _.push(a);
77
+ }
78
+ }
79
+ return { _, flags };
80
+ }
81
+ async function main() {
82
+ const argv = process.argv.slice(2);
83
+ const { _, flags } = parseArgs(argv);
84
+ const cmd = _[0];
85
+ try {
86
+ switch (cmd) {
87
+ case undefined:
88
+ case "help":
89
+ case "-h":
90
+ case "--help":
91
+ printHelp();
92
+ break;
93
+ case "run":
94
+ await cmdRun(_.slice(1).join(" "), flags);
95
+ break;
96
+ case "_exec":
97
+ await cmdExec(_[1], Boolean(flags.resume));
98
+ break;
99
+ case "resume":
100
+ await cmdResume(_[1], flags);
101
+ break;
102
+ case "sandbox":
103
+ await cmdSandbox(_[1]);
104
+ break;
105
+ case "serve":
106
+ await cmdServe(flags);
107
+ break;
108
+ case "watch":
109
+ await cmdWatch(_[1]);
110
+ break;
111
+ case "ls":
112
+ case "list":
113
+ cmdList();
114
+ break;
115
+ case "report":
116
+ cmdReport(_[1], flags);
117
+ break;
118
+ case "note":
119
+ cmdNote(_[1], _.slice(2).join(" "));
120
+ break;
121
+ case "cancel":
122
+ cmdCancel(_[1]);
123
+ break;
124
+ case "config":
125
+ await cmdConfig(_.slice(1), flags);
126
+ break;
127
+ case "models":
128
+ await cmdModels();
129
+ break;
130
+ case "demo":
131
+ await cmdDemo(flags);
132
+ break;
133
+ default:
134
+ console.error(util_1.ansi.red(`unknown command: ${cmd}`));
135
+ printHelp();
136
+ process.exit(1);
137
+ }
138
+ }
139
+ catch (e) {
140
+ console.error(util_1.ansi.red("error: ") + (0, util_1.errMsg)(e));
141
+ process.exit(1);
142
+ }
143
+ }
144
+ // ---------------------------------------------------------------- run
145
+ function optionOverrides(flags, cfg) {
146
+ const o = {};
147
+ if (flags.workers)
148
+ o.maxWorkers = Number(flags.workers);
149
+ if (flags.steps)
150
+ o.maxStepsPerTask = Number(flags.steps);
151
+ if (flags.tasks)
152
+ o.maxTasks = Number(flags.tasks);
153
+ if (flags.budget)
154
+ o.maxTokens = Number(flags.budget);
155
+ if (typeof flags.model === "string")
156
+ o.model = flags.model;
157
+ if (typeof flags.conductor === "string")
158
+ o.conductorModel = flags.conductor;
159
+ if (typeof flags.verify === "string" && ["off", "normal", "strict"].includes(flags.verify)) {
160
+ o.verification = flags.verify;
161
+ }
162
+ if (flags.thinking === false)
163
+ o.thinking = false;
164
+ if (typeof flags.effort === "string") {
165
+ if (!["low", "medium", "high", "max"].includes(flags.effort)) {
166
+ throw new Error("--effort must be one of: low | medium | high | max");
167
+ }
168
+ o.reasoningEffort = flags.effort;
169
+ }
170
+ if (flags.safe === false)
171
+ o.safeMode = false;
172
+ if (typeof flags.sandbox === "string") {
173
+ const v = flags.sandbox;
174
+ if (v !== "auto" && !sandbox_1.SANDBOX_KINDS.includes(v)) {
175
+ throw new Error(`--sandbox must be one of: ${sandbox_1.SANDBOX_KINDS.join(" | ")} | auto`);
176
+ }
177
+ o.sandboxRuntime = v === "auto" ? (0, sandbox_1.resolveSandboxKind)({ ...cfg, sandboxRuntime: "auto" }) : v;
178
+ }
179
+ return o;
180
+ }
181
+ async function cmdRun(mission, flags) {
182
+ if (!mission.trim()) {
183
+ console.error(util_1.ansi.red('Provide a mission: swarm run "build X / research Y"'));
184
+ process.exit(1);
185
+ }
186
+ const cfg = (0, config_1.loadConfig)();
187
+ // Validate flags before any network round-trip so typos fail instantly.
188
+ const overrides = optionOverrides(flags, cfg);
189
+ if (!cfg.apiKey && providers_1.PROVIDERS[cfg.provider].keyRequired) {
190
+ console.error(util_1.ansi.red(`No ${providers_1.PROVIDERS[cfg.provider].label} API key set. `) + "Run: swarm config set apiKey <...>");
191
+ process.exit(1);
192
+ }
193
+ process.stdout.write(util_1.ansi.gray("validating API key… "));
194
+ const auth = await (0, deepseek_2.validateAuth)(cfg);
195
+ if (auth.status === "invalid") {
196
+ console.error(util_1.ansi.red(`\n✗ ${providers_1.PROVIDERS[cfg.provider].label} key rejected: `) + (auth.message || "invalid key"));
197
+ console.error(util_1.ansi.gray(" Set a valid key: ") + "swarm config set apiKey <...>");
198
+ process.exit(1);
199
+ }
200
+ process.stdout.write(auth.status === "ok" ? util_1.ansi.green("ok\n") : util_1.ansi.gray("skipped\n"));
201
+ const sandbox = flags.sandbox !== false && !flags.cwd;
202
+ if (typeof flags.sandbox === "string" && flags.cwd) {
203
+ console.log(util_1.ansi.yellow("note: ") + "--cwd runs execute directly on the host — --sandbox is ignored");
204
+ }
205
+ const cwd = typeof flags.cwd === "string" ? flags.cwd : process.cwd();
206
+ const meta = (0, run_1.createRun)({
207
+ mission: mission.trim(),
208
+ cwd,
209
+ sandbox,
210
+ options: (0, run_1.optionsFromConfig)(cfg, overrides),
211
+ });
212
+ if (flags.fg) {
213
+ await execForeground(cfg, meta, true);
214
+ return;
215
+ }
216
+ // Default: launch detached, attach a live dashboard by tailing the journal.
217
+ (0, run_1.launchDetached)(meta.id, BIN_PATH);
218
+ console.log(`${util_1.ansi.cyan("🐝 swarm launched")} ${util_1.ansi.gray(meta.id)} ${util_1.ansi.gray("· workdir:")} ${meta.cwd}`);
219
+ await new Promise((r) => setTimeout(r, 400));
220
+ let detaching = false;
221
+ const onSig = () => {
222
+ detaching = true;
223
+ };
224
+ process.on("SIGINT", onSig);
225
+ await watchRunUntilSignal(meta.id, cfg.pricing, () => detaching);
226
+ process.off("SIGINT", onSig);
227
+ if (detaching && (0, run_1.isRunLive)(meta.id)) {
228
+ console.log("\n" +
229
+ util_1.ansi.yellow("detached") +
230
+ ` — run continues in the background.\n reattach: swarm watch ${meta.id}\n steer: swarm note ${meta.id} "..."\n stop: swarm cancel ${meta.id}`);
231
+ }
232
+ else {
233
+ printFinalLine(meta.id);
234
+ }
235
+ }
236
+ /** `swarm sandbox [test]` — show the resolved runtime; boot + echo + teardown. */
237
+ async function cmdSandbox(sub) {
238
+ const cfg = (0, config_1.loadConfig)();
239
+ const resolved = (0, sandbox_1.resolveSandboxKind)(cfg);
240
+ console.log(`configured: ${cfg.sandboxRuntime} → resolved: ${util_1.ansi.bold(resolved)}`);
241
+ console.log(util_1.ansi.gray(`docker daemon: ${(0, sandbox_1.dockerAvailable)() ? "up" : "not reachable"} · e2b key: ${cfg.e2bApiKey ? "set" : "—"} · modal: ${cfg.modalTokenId ? "set" : "—"} · vercel: ${cfg.vercelToken ? "set" : "—"}`));
242
+ if (resolved === "host") {
243
+ console.log(util_1.ansi.gray("host = the run's isolated workspace on this machine (the default; nothing to install)."));
244
+ console.log(util_1.ansi.gray("for container/cloud isolation: swarm config set sandboxRuntime docker|e2b|modal|vercel|auto"));
245
+ }
246
+ if (sub === "test" || (sub && sandbox_1.SANDBOX_KINDS.includes(sub))) {
247
+ const kind = sandbox_1.SANDBOX_KINDS.includes(sub) ? sub : resolved;
248
+ process.stdout.write(`testing ${kind}… `);
249
+ const r = await (0, sandbox_1.testSandbox)(cfg, kind);
250
+ console.log(r.ok ? util_1.ansi.green("✓ ok ") + util_1.ansi.gray(r.detail) : util_1.ansi.red("✗ ") + r.detail);
251
+ process.exit(r.ok ? 0 : 1);
252
+ }
253
+ }
254
+ async function cmdExec(id, resume = false) {
255
+ if (!id)
256
+ throw new Error("_exec requires a run id");
257
+ const meta = (0, run_1.loadMeta)(id);
258
+ if (!meta)
259
+ throw new Error(`run not found: ${id}`);
260
+ const cfg = (0, config_1.loadConfig)();
261
+ await execForeground(cfg, meta, false, resume);
262
+ process.exit(0);
263
+ }
264
+ /** Resume an interrupted run: settled tasks keep their results, in-flight tasks re-run. */
265
+ async function cmdResume(id, flags) {
266
+ if (!id)
267
+ throw new Error("usage: swarm resume <run-id>");
268
+ const info = (0, run_1.resumeInfo)(id);
269
+ if (!info.resumable) {
270
+ console.error(util_1.ansi.red("✗ cannot resume: ") + (info.reason || "unknown"));
271
+ process.exit(1);
272
+ }
273
+ const cfg = (0, config_1.loadConfig)();
274
+ const meta = (0, run_1.loadMeta)(id);
275
+ console.log(`${util_1.ansi.cyan("🐝 resuming")} ${util_1.ansi.gray(id)} ${util_1.ansi.gray("· workdir:")} ${meta.cwd}`);
276
+ if (flags.fg) {
277
+ await execForeground(cfg, meta, true, true);
278
+ return;
279
+ }
280
+ (0, run_1.launchDetached)(id, BIN_PATH, true);
281
+ await new Promise((r) => setTimeout(r, 400));
282
+ let detaching = false;
283
+ const onSig = () => {
284
+ detaching = true;
285
+ };
286
+ process.on("SIGINT", onSig);
287
+ await watchRunUntilSignal(id, cfg.pricing, () => detaching);
288
+ process.off("SIGINT", onSig);
289
+ if (detaching && (0, run_1.isRunLive)(id)) {
290
+ console.log("\n" + util_1.ansi.yellow("detached") + ` — run continues in the background. Reattach: swarm watch ${id}`);
291
+ }
292
+ else {
293
+ printFinalLine(id);
294
+ }
295
+ }
296
+ async function execForeground(cfg, meta, render, resume = false) {
297
+ // Reduce the journal BEFORE opening it for appends — the seed must reflect
298
+ // exactly what the dead engine left behind.
299
+ const seed = resume ? (0, run_1.loadRunState)(meta.id, cfg.pricing) : null;
300
+ const journal = new journal_1.Journal((0, config_1.runDir)(meta.id));
301
+ const renderer = render ? new terminal_1.TerminalRenderer(cfg.pricing) : null;
302
+ if (renderer) {
303
+ journal.onEvent = (ev) => renderer.ingest(ev);
304
+ renderer.start();
305
+ }
306
+ (0, run_1.writePid)(meta.id);
307
+ const executor = new executor_1.Executor(cfg, meta, journal);
308
+ if (resume && seed) {
309
+ const resets = seed
310
+ .taskList()
311
+ .filter((t) => t.status === "running" || t.status === "verifying")
312
+ .map((t) => t.id);
313
+ journal.append("run.resumed", { resets });
314
+ executor.seedFromState(seed, resets);
315
+ }
316
+ else {
317
+ journal.append("run.created", { meta });
318
+ }
319
+ const onSig = () => {
320
+ if (renderer) {
321
+ renderer.stop();
322
+ console.log(util_1.ansi.yellow("\ncancelling…"));
323
+ }
324
+ executor.cancel();
325
+ };
326
+ process.on("SIGINT", onSig);
327
+ // A crash without a terminal status would leave the run "running" forever
328
+ // in every viewer. Record the failure, flush, and exit non-zero.
329
+ const onFatal = (e) => {
330
+ try {
331
+ journal.append("run.status", { status: "failed", reason: `engine crashed: ${(0, util_1.errMsg)(e)}` });
332
+ }
333
+ catch {
334
+ /* nothing left to do */
335
+ }
336
+ journal.flush().finally(() => {
337
+ (0, run_1.clearPid)(meta.id);
338
+ if (renderer)
339
+ renderer.stop();
340
+ process.exit(1);
341
+ });
342
+ };
343
+ process.on("uncaughtException", onFatal);
344
+ process.on("unhandledRejection", onFatal);
345
+ try {
346
+ await executor.run();
347
+ }
348
+ finally {
349
+ process.off("SIGINT", onSig);
350
+ process.off("uncaughtException", onFatal);
351
+ process.off("unhandledRejection", onFatal);
352
+ (0, run_1.clearPid)(meta.id);
353
+ await journal.flush();
354
+ if (renderer)
355
+ renderer.stop();
356
+ }
357
+ if (render)
358
+ printFinalLine(meta.id);
359
+ }
360
+ async function watchRunUntilSignal(id, pricing, shouldStop) {
361
+ const { eventsFile, readNewEvents } = await Promise.resolve().then(() => __importStar(require("./journal")));
362
+ const renderer = new terminal_1.TerminalRenderer(pricing);
363
+ const file = eventsFile((0, config_1.runDir)(id));
364
+ const tail = { offset: 0, carry: "" };
365
+ renderer.start();
366
+ return new Promise((resolve) => {
367
+ const tick = () => {
368
+ if (shouldStop()) {
369
+ renderer.stop();
370
+ return resolve();
371
+ }
372
+ try {
373
+ for (const ev of readNewEvents(file, tail))
374
+ renderer.ingest(ev);
375
+ }
376
+ catch {
377
+ /* not ready */
378
+ }
379
+ const st = renderer.getState().status;
380
+ if (["done", "failed", "cancelled"].includes(st)) {
381
+ setTimeout(() => {
382
+ try {
383
+ for (const ev of readNewEvents(file, tail))
384
+ renderer.ingest(ev);
385
+ }
386
+ catch { /* ignore */ }
387
+ renderer.stop();
388
+ resolve();
389
+ }, 400);
390
+ return;
391
+ }
392
+ setTimeout(tick, 250);
393
+ };
394
+ tick();
395
+ });
396
+ }
397
+ // ---------------------------------------------------------------- serve
398
+ async function cmdServe(flags) {
399
+ const cfg = (0, config_1.loadConfig)();
400
+ const port = Number(flags.port) || (flags.port === "0" ? 0 : cfg.hubPort);
401
+ const uiDir = findUiDir();
402
+ const server = (0, hub_1.startHub)({ port, uiDir, binPath: BIN_PATH });
403
+ // Report the port actually bound (matters for --port 0 / collisions).
404
+ server.on("listening", () => {
405
+ const addr = server.address();
406
+ const bound = typeof addr === "object" && addr ? addr.port : port;
407
+ const url = `http://localhost:${bound}`;
408
+ console.log(`${util_1.ansi.cyan("🐝 agentswarm hub")} ${util_1.ansi.gray("·")} ${util_1.ansi.bold(url)}`);
409
+ console.log(util_1.ansi.gray(` api: ${url}/api`));
410
+ console.log(util_1.ansi.gray(` ui: ${uiDir ? "built ✓ (served here)" : "not built — run: npm run setup (or: npm run build:ui)"}`));
411
+ console.log(util_1.ansi.gray(` api key: ${cfg.apiKey ? (0, config_1.maskKey)(cfg.apiKey) + " ✓" : util_1.ansi.red("not set — open Settings or: swarm config set apiKey <sk-...>")}`));
412
+ console.log(util_1.ansi.gray(" Ctrl-C to stop the hub (background runs keep going).\n"));
413
+ if (flags.open)
414
+ openBrowser(url);
415
+ });
416
+ server.on("error", (e) => {
417
+ console.error(util_1.ansi.red(`hub failed: ${(0, util_1.errMsg)(e)}`));
418
+ process.exit(1);
419
+ });
420
+ await new Promise(() => { }); // run forever
421
+ }
422
+ function findUiDir() {
423
+ const candidates = [
424
+ path.join(__dirname, "..", "ui", "out"),
425
+ path.join(process.cwd(), "ui", "out"),
426
+ ];
427
+ for (const c of candidates) {
428
+ if (fs.existsSync(path.join(c, "index.html")))
429
+ return c;
430
+ }
431
+ return null;
432
+ }
433
+ function openBrowser(url) {
434
+ const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
435
+ try {
436
+ (0, child_process_1.spawn)(cmd, [url], { detached: true, stdio: "ignore" }).unref();
437
+ }
438
+ catch {
439
+ /* ignore */
440
+ }
441
+ }
442
+ // ---------------------------------------------------------------- watch / list / report
443
+ async function cmdWatch(id) {
444
+ if (!id)
445
+ throw new Error("usage: swarm watch <id>");
446
+ id = resolveId(id);
447
+ if (!(0, run_1.loadMeta)(id))
448
+ throw new Error(`run not found: ${id}`);
449
+ const cfg = (0, config_1.loadConfig)();
450
+ await (0, terminal_1.watchRun)(id, cfg.pricing);
451
+ printFinalLine(id);
452
+ }
453
+ function cmdList() {
454
+ const cfg = (0, config_1.loadConfig)();
455
+ const runs = (0, run_1.listRuns)(cfg.pricing);
456
+ if (!runs.length) {
457
+ console.log(util_1.ansi.gray("no runs yet. start one: ") + 'swarm run "your mission"');
458
+ return;
459
+ }
460
+ console.log(util_1.ansi.bold("runs") + util_1.ansi.gray(` (~/.agentswarm/runs)`));
461
+ for (const r of runs) {
462
+ const live = r.pid ? util_1.ansi.cyan(" ●live") : "";
463
+ const st = r.status === "done" ? util_1.ansi.green(r.status) : r.status === "failed" ? util_1.ansi.red(r.status) : util_1.ansi.yellow(r.status);
464
+ const tok = (0, util_1.fmtTokens)(r.usage.promptTokens + r.usage.completionTokens);
465
+ console.log(` ${util_1.ansi.gray(r.id)} ${st.padEnd(14)} ${util_1.ansi.gray(`${r.tasks.done}/${r.tasks.total} tasks`)} ${util_1.ansi.gray(tok + " tok")} ${util_1.ansi.green((0, util_1.fmtMoney)(r.cost))}${live}`);
466
+ console.log(` ${clipLine(r.mission, 90)}`);
467
+ }
468
+ }
469
+ function cmdReport(id, flags) {
470
+ if (!id)
471
+ throw new Error("usage: swarm report <id>");
472
+ id = resolveId(id);
473
+ const file = path.join((0, config_1.runDir)(id), "artifacts", "final-report.md");
474
+ if (!fs.existsSync(file)) {
475
+ console.error(util_1.ansi.yellow("no final report yet for ") + id);
476
+ process.exit(1);
477
+ }
478
+ if (flags.open) {
479
+ openBrowser("file://" + file);
480
+ console.log(file);
481
+ return;
482
+ }
483
+ process.stdout.write(fs.readFileSync(file, "utf8") + "\n");
484
+ const arts = path.join((0, config_1.runDir)(id), "artifacts");
485
+ console.log(util_1.ansi.gray(`\nartifacts: ${arts}`));
486
+ }
487
+ function cmdNote(id, text) {
488
+ if (!id || !text)
489
+ throw new Error('usage: swarm note <id> "message"');
490
+ id = resolveId(id);
491
+ if (!(0, run_1.loadMeta)(id))
492
+ throw new Error(`run not found: ${id}`);
493
+ (0, control_1.appendControl)((0, config_1.runDir)(id), { kind: "note", text });
494
+ console.log(util_1.ansi.green("✓ ") + ((0, run_1.isRunLive)(id) ? "note delivered to the conductor" : "note queued (run is not live)"));
495
+ }
496
+ function cmdCancel(id) {
497
+ if (!id)
498
+ throw new Error("usage: swarm cancel <id>");
499
+ id = resolveId(id);
500
+ if (!(0, run_1.loadMeta)(id))
501
+ throw new Error(`run not found: ${id}`);
502
+ (0, control_1.appendControl)((0, config_1.runDir)(id), { kind: "cancel" });
503
+ console.log(util_1.ansi.yellow("⛔ cancel requested for ") + id);
504
+ }
505
+ // ---------------------------------------------------------------- config / models
506
+ async function cmdConfig(rest, flags) {
507
+ const sub = rest[0] || "list";
508
+ if (sub === "list" || sub === "get") {
509
+ const cfg = (0, config_1.loadConfig)();
510
+ if (sub === "get" && rest[1]) {
511
+ const key = rest[1];
512
+ const v = key === "apiKey" || key === "tinyfishApiKey" ? (0, config_1.maskKey)(String(cfg[key])) : cfg[key];
513
+ console.log(typeof v === "object" ? JSON.stringify(v, null, 2) : String(v));
514
+ return;
515
+ }
516
+ console.log(util_1.ansi.bold("config") + util_1.ansi.gray(` (${(0, config_1.configPath)()})`));
517
+ for (const k of config_1.SETTABLE_KEYS) {
518
+ let v = cfg[k];
519
+ if (k === "apiKey" || k === "tinyfishApiKey")
520
+ v = v ? (0, config_1.maskKey)(String(v)) : util_1.ansi.red("(not set)");
521
+ console.log(` ${k.padEnd(18)} ${util_1.ansi.gray(String(v))}`);
522
+ }
523
+ return;
524
+ }
525
+ if (sub === "set") {
526
+ const key = rest[1];
527
+ const value = rest.slice(2).join(" ");
528
+ if (!key || value === "")
529
+ throw new Error("usage: swarm config set <key> <value>");
530
+ if (!config_1.SETTABLE_KEYS.includes(key)) {
531
+ throw new Error(`unknown/settable keys: ${config_1.SETTABLE_KEYS.join(", ")}`);
532
+ }
533
+ const coerced = (0, config_1.coerceConfigValue)(key, value);
534
+ if (key === "apiKey") {
535
+ const k = String(coerced);
536
+ if (k.includes("...") || k.includes("…") || k.length < 20) {
537
+ throw new Error(`that doesn't look like a real API key (got ${k.length} chars). ` +
538
+ `Paste the full key from your provider's console.`);
539
+ }
540
+ }
541
+ (0, config_1.saveConfig)({ [key]: coerced });
542
+ console.log(util_1.ansi.green("✓ ") + `set ${key}`);
543
+ if (key === "apiKey")
544
+ console.log(util_1.ansi.gray(" verify it works: ") + "swarm models");
545
+ return;
546
+ }
547
+ if (sub === "path") {
548
+ console.log((0, config_1.configPath)());
549
+ return;
550
+ }
551
+ throw new Error("usage: swarm config [list|get <key>|set <key> <value>|path]");
552
+ }
553
+ async function cmdModels() {
554
+ const cfg = (0, config_1.loadConfig)();
555
+ if (!cfg.apiKey) {
556
+ console.log(util_1.ansi.gray("known (priced) models:"));
557
+ for (const m of Object.keys(cfg.pricing))
558
+ console.log(" " + m);
559
+ console.log(util_1.ansi.gray("\nset an API key to list live models: swarm config set apiKey <sk-...>"));
560
+ return;
561
+ }
562
+ try {
563
+ const models = await (0, deepseek_1.listModels)(cfg);
564
+ console.log(util_1.ansi.bold("available models"));
565
+ for (const m of models) {
566
+ const priced = cfg.pricing[m] ? util_1.ansi.green(" priced") : "";
567
+ console.log(" " + m + priced);
568
+ }
569
+ }
570
+ catch (e) {
571
+ console.error(util_1.ansi.red("could not list models: ") + (0, util_1.errMsg)(e));
572
+ }
573
+ }
574
+ // ---------------------------------------------------------------- demo
575
+ async function cmdDemo(flags) {
576
+ const cfg = (0, config_1.loadConfig)();
577
+ if (!cfg.apiKey && providers_1.PROVIDERS[cfg.provider].keyRequired) {
578
+ console.error(util_1.ansi.red("Demo needs an API key for the active provider. ") + "Run: swarm config set apiKey <...>");
579
+ process.exit(1);
580
+ }
581
+ const mission = typeof flags.mission === "string"
582
+ ? flags.mission
583
+ : "Research the current state of open-weight agent-swarm systems (Kimi K2.6, others), then produce a concise comparison report with a recommendation for a developer wanting long-horizon autonomy on a budget. Save the report as comparison.md.";
584
+ const meta = (0, run_1.createRun)({
585
+ mission,
586
+ cwd: process.cwd(),
587
+ sandbox: true,
588
+ options: (0, run_1.optionsFromConfig)(cfg, { maxWorkers: 4, maxTasks: 12, ...optionOverrides(flags, cfg) }),
589
+ });
590
+ console.log(util_1.ansi.cyan("running demo mission in an isolated workspace…\n"));
591
+ await execForeground(cfg, meta, true);
592
+ }
593
+ // ---------------------------------------------------------------- shared
594
+ function resolveId(idOrPrefix) {
595
+ if ((0, run_1.loadMeta)(idOrPrefix))
596
+ return idOrPrefix;
597
+ // allow short prefixes
598
+ try {
599
+ const dir = path.join((0, config_1.runDir)(idOrPrefix), "..");
600
+ const ids = fs.readdirSync(dir).filter((d) => d.startsWith(idOrPrefix));
601
+ if (ids.length === 1)
602
+ return ids[0];
603
+ }
604
+ catch {
605
+ /* ignore */
606
+ }
607
+ return idOrPrefix;
608
+ }
609
+ function printFinalLine(id) {
610
+ const meta = (0, run_1.loadMeta)(id);
611
+ if (!meta)
612
+ return;
613
+ const reportFile = path.join((0, config_1.runDir)(id), "artifacts", "final-report.md");
614
+ console.log("");
615
+ if (fs.existsSync(reportFile)) {
616
+ console.log(util_1.ansi.green("✓ final report: ") + reportFile);
617
+ console.log(util_1.ansi.gray(" view: ") + `swarm report ${id}`);
618
+ }
619
+ else {
620
+ console.log(util_1.ansi.gray(`run ${id} ended without a final report (see: swarm watch ${id})`));
621
+ }
622
+ }
623
+ function clipLine(s, n) {
624
+ const t = s.replace(/\s+/g, " ").trim();
625
+ return util_1.ansi.gray(t.length > n ? t.slice(0, n - 1) + "…" : t);
626
+ }
627
+ function printHelp() {
628
+ const b = util_1.ansi.bold;
629
+ console.log(`${b("agentswarm")} — a local agent swarm for long-horizon work (DeepSeek, OpenAI, Anthropic, Grok, MiniMax, OpenRouter, Ollama, LM Studio)
630
+
631
+ ${b("USAGE")}
632
+ swarm run "<mission>" [options] decompose & execute a mission with a parallel swarm
633
+ swarm serve [--port 7777] [--open] start the mission-control web UI + API
634
+ swarm watch <id> attach a live dashboard to a run
635
+ swarm resume <id> [--fg] resume an interrupted run (done tasks keep their results)
636
+ swarm ls list runs
637
+ swarm report <id> [--open] print (or open) a run's final report
638
+ swarm note <id> "<text>" steer a live run (the conductor reads it)
639
+ swarm cancel <id> stop a run gracefully (still synthesizes)
640
+ swarm config [list|get|set ...] manage config (~/.agentswarm/config.json)
641
+ swarm sandbox [test|<runtime>] show / smoke-test the shell runtime (host, docker, e2b, modal, vercel)
642
+ swarm models list models from the active provider
643
+ swarm demo run a self-contained demo mission
644
+
645
+ ${b("RUN OPTIONS")}
646
+ --workers N max parallel agents (default ${(0, config_1.loadConfig)().maxWorkers})
647
+ --steps N max tool steps per task (default ${(0, config_1.loadConfig)().maxStepsPerTask})
648
+ --tasks N max total tasks (default ${(0, config_1.loadConfig)().maxTasks})
649
+ --budget N token budget for the whole run (default ${(0, util_1.fmtTokens)((0, config_1.loadConfig)().maxTokensPerRun)})
650
+ --model X worker model (default ${(0, config_1.loadConfig)().model})
651
+ --conductor X conductor model (default ${(0, config_1.loadConfig)().conductorModel})
652
+ --verify off|normal|strict adversarial verification (default ${(0, config_1.loadConfig)().verification})
653
+ --effort low|medium|high|max reasoning effort (default ${(0, config_1.loadConfig)().reasoningEffort})
654
+ --no-thinking disable thinking mode
655
+ --no-safe disable command/path safety guards (careful)
656
+ --sandbox X shell runtime for this run: host | docker | e2b | modal | vercel | auto
657
+ (default ${(0, config_1.loadConfig)().sandboxRuntime}; host = isolated workspace, no install needed)
658
+ --cwd <path> run against a real directory (default: isolated workspace)
659
+ --fg run in the foreground in this process (Ctrl-C cancels)
660
+
661
+ ${b("FIRST RUN")}
662
+ swarm config set apiKey <key> # key for the active provider (default: DeepSeek)
663
+ swarm config set provider <id> # deepseek | openai | anthropic | xai | minimax | openrouter | ollama | lmstudio | custom
664
+ pip install searchkit # optional: local, citable web search for agents
665
+ swarm serve --open # open the web UI
666
+ `);
667
+ }