svamp-cli 0.2.346 → 0.2.358

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 (33) hide show
  1. package/bin/skills/artifact/SKILL.md +19 -5
  2. package/bin/skills/crew/SKILL.md +42 -11
  3. package/dist/{acpBackend-BZ1MJhQV.mjs → acpBackend-UaBD4zL3.mjs} +3 -0
  4. package/dist/{agentCommands-B2Nl7Fof.mjs → agentCommands-C_KvvYFp.mjs} +5 -5
  5. package/dist/{claudeAuth-iAerdaMr.mjs → claudeAuth-DMDvPNhO.mjs} +55 -6
  6. package/dist/cli.mjs +164 -86
  7. package/dist/{commands-D69mnkYu.mjs → commands-B19lVZjm.mjs} +10 -3
  8. package/dist/{commands-BB1Kcyt-.mjs → commands-Bmoy6Rlw.mjs} +34 -4
  9. package/dist/{commands-CiRCwjkJ.mjs → commands-CFBKkfX8.mjs} +9 -9
  10. package/dist/{commands-D1pZq3d4.mjs → commands-CcdoQIkb.mjs} +126 -25
  11. package/dist/{commands-BOpoVBOM.mjs → commands-DH_YQrAP.mjs} +5 -21
  12. package/dist/{commands-H4PsTGn8.mjs → commands-DRxANaS5.mjs} +65 -19
  13. package/dist/{commands-C2aODeVc.mjs → commands-nkvTnUJj.mjs} +29 -13
  14. package/dist/{detectIsolation-CzoDCEnh.mjs → detectIsolation-CrV8IFDv.mjs} +36 -16
  15. package/dist/{fleet-DM_Q7w69.mjs → fleet-Ca-UjJNL.mjs} +40 -15
  16. package/dist/friendlyName-B-OEwt0t.mjs +341 -0
  17. package/dist/{headlessCli-C8HzQvis.mjs → headlessCli-BpTXz6FD.mjs} +24 -16
  18. package/dist/{hookSettings-DYMTUgVA.mjs → hookSettings-CvvUhwCK.mjs} +294 -55
  19. package/dist/{friendlyName-1UZcyDhu.mjs → inboxGuard-CDqmIypF.mjs} +32 -347
  20. package/dist/index.mjs +10 -9
  21. package/dist/{loopVerify-B47sSjxe.mjs → loopVerify-CxAKJB5a.mjs} +1 -1
  22. package/dist/{notifyCommands-D-IKIxiq.mjs → notifyCommands-CCUE18Q-.mjs} +1 -1
  23. package/dist/package-DTTWvJbp.mjs +64 -0
  24. package/dist/{rpc-Cf0grcru.mjs → rpc-CAlAHQAy.mjs} +8 -3
  25. package/dist/{rpc-BB6RF4-F.mjs → rpc-CacreLQ8.mjs} +13 -9
  26. package/dist/{run-ChdsgaSD.mjs → run-Bc81iurl.mjs} +27 -8
  27. package/dist/{run-DkjwF-zA.mjs → run-DKOND4dO.mjs} +222 -73
  28. package/dist/{scheduler-BY30ZrB9.mjs → scheduler-IyLWBF8T.mjs} +28 -10
  29. package/dist/{serveAuth-DbVLRCRR.mjs → serveAuth-4M0vwMyj.mjs} +53 -11
  30. package/dist/{serveCommands-CUHUiYnZ.mjs → serveCommands-B-Ym_QXc.mjs} +8 -8
  31. package/dist/{store-Bn0ByYev.mjs → store-D8dok6_d.mjs} +62 -14
  32. package/package.json +3 -3
  33. package/dist/package-DtuINqf5.mjs +0 -64
@@ -1,20 +1,29 @@
1
1
  import { rmSync, mkdirSync, writeFileSync, renameSync, statSync, existsSync, readFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
  import os from 'node:os';
4
- import { randomUUID, randomBytes } from 'node:crypto';
4
+ import { randomUUID } from 'node:crypto';
5
5
 
6
6
  const SVAMP_HOME = process.env.SVAMP_HOME || join(os.homedir(), ".svamp");
7
- const num = (key, def) => {
8
- const v = Number(process.env[key]);
9
- return Number.isFinite(v) && v > 0 ? v : def;
10
- };
7
+ function __parseEnvNum(key, def, min = 0) {
8
+ const raw = process.env[key];
9
+ if (raw === void 0 || raw.trim() === "") return def;
10
+ const v = Number(raw);
11
+ if (!Number.isFinite(v) || v < min) {
12
+ const why = min > 0 && v === 0 ? `this is a measurement period, not a ceiling \u2014 0 measures nothing, which turns the guard OFF rather than tightening it` : `expected a number >= ${min}`;
13
+ console.error(`[svamp] ignoring ${key}=${JSON.stringify(raw)} \u2014 ${why}; using ${def}`);
14
+ return def;
15
+ }
16
+ return v;
17
+ }
18
+ const num = (key, def) => __parseEnvNum(key, def, 0);
19
+ const period = (key, def) => __parseEnvNum(key, def, 1);
11
20
  const MAX_AGENT_HOPS = num("SVAMP_INBOX_MAX_HOPS", 3);
12
21
  const URGENT_BURST = num("SVAMP_INBOX_URGENT_BURST", 5);
13
- const URGENT_REFILL_MS = num("SVAMP_INBOX_URGENT_REFILL_MS", 6e4);
14
- const BREAKER_WINDOW_MS = num("SVAMP_INBOX_BREAKER_WINDOW_MS", 6e4);
22
+ const URGENT_REFILL_MS = period("SVAMP_INBOX_URGENT_REFILL_MS", 6e4);
23
+ const BREAKER_WINDOW_MS = period("SVAMP_INBOX_BREAKER_WINDOW_MS", 6e4);
15
24
  const BREAKER_MAX_WAKES = num("SVAMP_INBOX_BREAKER_MAX_WAKES", 30);
16
25
  const BREAKER_COOLDOWN_MS = num("SVAMP_INBOX_BREAKER_COOLDOWN_MS", 12e4);
17
- const CTX_STALE_MS = num("SVAMP_INBOX_CTX_STALE_MS", 30 * 6e4);
26
+ const CTX_STALE_MS = period("SVAMP_INBOX_CTX_STALE_MS", 30 * 6e4);
18
27
  function ctxPath(sessionId) {
19
28
  return join(SVAMP_HOME, "inbound", `${sessionId}.json`);
20
29
  }
@@ -148,6 +157,20 @@ function computeOutboundHop(sessionId) {
148
157
  if (ctx) return { hopCount: (ctx.hopCount || 0) + 1, threadId: ctx.threadId, fromInboxTurn: true };
149
158
  return { hopCount: 1, fromInboxTurn: false };
150
159
  }
160
+ function resolveOutboundUrgency(args) {
161
+ if (args.explicit) return { urgency: args.explicit, downgraded: false };
162
+ const wanted = args.whenIdle ? "normal" : "urgent";
163
+ if (args.fromInboxTurn && wanted === "urgent") return { urgency: "normal", downgraded: true };
164
+ return { urgency: wanted, downgraded: false };
165
+ }
166
+ function parseUrgencyValue(raw, opts) {
167
+ if (raw === void 0) {
168
+ if (opts?.present) return { ok: false, message: "--urgency requires a value: urgent | normal" };
169
+ return { ok: true };
170
+ }
171
+ if (raw === "urgent" || raw === "normal") return { ok: true, value: raw };
172
+ return { ok: false, message: `--urgency: expected "urgent" or "normal", got ${JSON.stringify(raw)}` };
173
+ }
151
174
  const buckets = /* @__PURE__ */ new Map();
152
175
  const FULL_REFILL_MS = URGENT_BURST * URGENT_REFILL_MS;
153
176
  let lastBucketSweep = 0;
@@ -245,342 +268,4 @@ function applyInboxClear(inbox, opts) {
245
268
  return { kept, removed: inbox.length - kept.length };
246
269
  }
247
270
 
248
- const ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
249
- const LOWER_ALPHABET = "abcdefghijklmnopqrstuvwxyz";
250
- function shortId(length = 10, alphabet = ALPHABET) {
251
- const maxUnbiased = Math.floor(256 / alphabet.length) * alphabet.length;
252
- let out = "";
253
- while (out.length < length) {
254
- const buf = randomBytes(length - out.length);
255
- for (let i = 0; i < buf.length && out.length < length; i++) {
256
- const b = buf[i];
257
- if (b < maxUnbiased) out += alphabet[b % alphabet.length];
258
- }
259
- }
260
- return out;
261
- }
262
-
263
- const ADJECTIVES = [
264
- "able",
265
- "amber",
266
- "amused",
267
- "ancient",
268
- "arctic",
269
- "autumn",
270
- "azure",
271
- "blithe",
272
- "bold",
273
- "brave",
274
- "breezy",
275
- "bright",
276
- "brisk",
277
- "calm",
278
- "candid",
279
- "cheery",
280
- "chill",
281
- "clever",
282
- "cobalt",
283
- "cosmic",
284
- "cozy",
285
- "crimson",
286
- "crisp",
287
- "curious",
288
- "dapper",
289
- "daring",
290
- "dawn",
291
- "deft",
292
- "dewy",
293
- "eager",
294
- "early",
295
- "easy",
296
- "electric",
297
- "fancy",
298
- "feisty",
299
- "fleet",
300
- "fond",
301
- "frosty",
302
- "gallant",
303
- "gentle",
304
- "giddy",
305
- "glad",
306
- "gleaming",
307
- "golden",
308
- "graceful",
309
- "grand",
310
- "hardy",
311
- "hazel",
312
- "hearty",
313
- "honest",
314
- "humble",
315
- "jolly",
316
- "jovial",
317
- "keen",
318
- "kind",
319
- "lively",
320
- "loyal",
321
- "lucky",
322
- "lunar",
323
- "mellow",
324
- "merry",
325
- "mighty",
326
- "mint",
327
- "misty",
328
- "nimble",
329
- "noble",
330
- "opal",
331
- "patient",
332
- "peppy",
333
- "placid",
334
- "plucky",
335
- "polar",
336
- "prim",
337
- "proud",
338
- "quick",
339
- "quiet",
340
- "quirky",
341
- "radiant",
342
- "rapid",
343
- "ready",
344
- "regal",
345
- "rosy",
346
- "royal",
347
- "rugged",
348
- "sage",
349
- "sandy",
350
- "scarlet",
351
- "serene",
352
- "sharp",
353
- "shiny",
354
- "silent",
355
- "silver",
356
- "sleek",
357
- "smooth",
358
- "snappy",
359
- "snowy",
360
- "solar",
361
- "spry",
362
- "stellar",
363
- "sturdy",
364
- "sunny",
365
- "swift",
366
- "tender",
367
- "tidal",
368
- "tidy",
369
- "tranquil",
370
- "trusty",
371
- "upbeat",
372
- "valiant",
373
- "vivid",
374
- "warm",
375
- "whimsical",
376
- "wise",
377
- "witty",
378
- "zesty",
379
- "zippy"
380
- ];
381
- const ANIMALS = [
382
- "ant",
383
- "badger",
384
- "bat",
385
- "bear",
386
- "beaver",
387
- "bee",
388
- "bison",
389
- "boar",
390
- "bobcat",
391
- "buffalo",
392
- "camel",
393
- "caribou",
394
- "cat",
395
- "cheetah",
396
- "cobra",
397
- "condor",
398
- "cougar",
399
- "coyote",
400
- "crab",
401
- "crane",
402
- "cricket",
403
- "crow",
404
- "deer",
405
- "dingo",
406
- "dolphin",
407
- "donkey",
408
- "dove",
409
- "dragon",
410
- "duck",
411
- "eagle",
412
- "eel",
413
- "egret",
414
- "elk",
415
- "falcon",
416
- "ferret",
417
- "finch",
418
- "fox",
419
- "frog",
420
- "gecko",
421
- "gibbon",
422
- "goat",
423
- "goose",
424
- "gopher",
425
- "hare",
426
- "hawk",
427
- "hedgehog",
428
- "heron",
429
- "hippo",
430
- "horse",
431
- "ibex",
432
- "ibis",
433
- "iguana",
434
- "jackal",
435
- "jaguar",
436
- "jay",
437
- "kestrel",
438
- "koala",
439
- "krill",
440
- "lark",
441
- "lemur",
442
- "leopard",
443
- "lion",
444
- "llama",
445
- "lynx",
446
- "macaw",
447
- "magpie",
448
- "mantis",
449
- "marmot",
450
- "marten",
451
- "meerkat",
452
- "mink",
453
- "mole",
454
- "moose",
455
- "moth",
456
- "mouse",
457
- "newt",
458
- "ocelot",
459
- "octopus",
460
- "orca",
461
- "osprey",
462
- "otter",
463
- "owl",
464
- "ox",
465
- "panda",
466
- "panther",
467
- "parrot",
468
- "pelican",
469
- "penguin",
470
- "pheasant",
471
- "pigeon",
472
- "puffin",
473
- "puma",
474
- "quail",
475
- "rabbit",
476
- "raccoon",
477
- "ram",
478
- "raven",
479
- "robin",
480
- "salmon",
481
- "seal",
482
- "shark",
483
- "sheep",
484
- "shrew",
485
- "skunk",
486
- "sloth",
487
- "snail",
488
- "sparrow",
489
- "spider",
490
- "squid",
491
- "stag",
492
- "stoat",
493
- "stork",
494
- "swan",
495
- "tapir",
496
- "tiger",
497
- "toad",
498
- "trout",
499
- "turtle",
500
- "viper",
501
- "vole",
502
- "walrus",
503
- "weasel",
504
- "whale",
505
- "wolf",
506
- "wombat",
507
- "wren",
508
- "yak",
509
- "zebra"
510
- ];
511
- function pick(arr) {
512
- const n = arr.length;
513
- const max = Math.floor(256 / n) * n;
514
- let b;
515
- do {
516
- b = randomBytes(1)[0];
517
- } while (b >= max);
518
- return arr[b % n];
519
- }
520
- function generateFriendlyName(taken = []) {
521
- const used = taken instanceof Set ? taken : new Set(taken);
522
- for (let i = 0; i < 1e3; i++) {
523
- const name = `${pick(ADJECTIVES)}-${pick(ANIMALS)}`;
524
- if (!used.has(name)) return name;
525
- }
526
- const base = `${pick(ADJECTIVES)}-${pick(ANIMALS)}`;
527
- let n = 2;
528
- while (used.has(`${base}-${n}`)) n++;
529
- return `${base}-${n}`;
530
- }
531
- function sanitizeSegment(s) {
532
- return String(s || "").toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
533
- }
534
- function friendlyNameFromId(id) {
535
- const m = /^(.+)-[a-zA-Z0-9]{6,10}$/.exec(String(id || ""));
536
- return m ? m[1] : void 0;
537
- }
538
- function composeSessionId(friendlyName, takenIds = [], len = 6) {
539
- const taken = takenIds instanceof Set ? takenIds : new Set(takenIds);
540
- const A = LOWER_ALPHABET.length;
541
- const nthSuffix = (i, width2) => {
542
- let s = "";
543
- for (let k = 0; k < width2; k++) {
544
- s = LOWER_ALPHABET[i % A] + s;
545
- i = Math.floor(i / A);
546
- }
547
- return s;
548
- };
549
- let width = Math.max(1, len);
550
- for (; ; ) {
551
- const space = Math.pow(A, width);
552
- for (let i = 0; i < 64; i++) {
553
- const candidate = `${friendlyName}-${shortId(width, LOWER_ALPHABET)}`;
554
- if (!taken.has(candidate)) return candidate;
555
- }
556
- if (space <= 1e6) {
557
- for (let i = 0; i < space; i++) {
558
- const candidate = `${friendlyName}-${nthSuffix(i, width)}`;
559
- if (!taken.has(candidate)) return candidate;
560
- }
561
- }
562
- width++;
563
- }
564
- }
565
- function formatHandle(projectName, friendlyName) {
566
- if (!friendlyName) return void 0;
567
- const proj = sanitizeSegment(projectName || "");
568
- return proj ? `${proj}:${friendlyName}` : friendlyName;
569
- }
570
- function parseHandle(input) {
571
- const str = String(input || "").trim().replace(/^@/, "");
572
- if (!str) return null;
573
- const i = str.indexOf(":");
574
- if (i === -1) return { name: str.toLowerCase() };
575
- return { project: sanitizeSegment(str.slice(0, i)), name: str.slice(i + 1).toLowerCase() };
576
- }
577
- function handleMatchesMetadata(parsed, metadata) {
578
- const fn = String(metadata?.friendlyName || "").toLowerCase();
579
- if (!fn || fn !== parsed.name) return false;
580
- if (parsed.project) {
581
- return sanitizeSegment(metadata?.projectName || "") === parsed.project;
582
- }
583
- return true;
584
- }
585
-
586
- export { composeSessionId as a, applyInboxClear as b, computeOutboundHop as c, friendlyNameFromId as d, clearInboundContext as e, formatHandle as f, generateFriendlyName as g, handleMatchesMetadata as h, isHopCapParked as i, classifyInbound as j, consumeAwaitingReply as k, peekAwaitingReply as l, parseHandle as p, registerAwaitingReply as r, shortId as s, writeInboundContext as w };
271
+ export { MAX_AGENT_HOPS, __parseEnvNum, applyInboxClear, classifyInbound, clearInboundContext, computeOutboundHop, consumeAwaitingReply, isHopCapParked, parseUrgencyValue, peekAwaitingReply, readInboundContext, registerAwaitingReply, resolveOutboundUrgency, writeInboundContext };
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  export { c as connectToHypha, g as getHyphaServerUrl } from './hyphaClient-ybivOjZo.mjs';
2
- export { c as createSessionStore, r as registerMachineService } from './hookSettings-DYMTUgVA.mjs';
3
- export { d as daemonStatus, s as startDaemon, a as stopDaemon } from './run-DkjwF-zA.mjs';
2
+ export { c as createSessionStore, r as registerMachineService } from './hookSettings-CvvUhwCK.mjs';
3
+ export { d as daemonStatus, s as startDaemon, a as stopDaemon } from './run-DKOND4dO.mjs';
4
4
  import 'fs';
5
5
  import 'path';
6
6
  import 'node:crypto';
@@ -18,19 +18,20 @@ import 'http';
18
18
  import 'net';
19
19
  import 'os';
20
20
  import './instance-CeHz5C8v.mjs';
21
- import './serveAuth-DbVLRCRR.mjs';
21
+ import './serveAuth-4M0vwMyj.mjs';
22
22
  import 'node:os';
23
23
  import 'node:path';
24
24
  import './envFlag-COOuJ3AH.mjs';
25
- import './claudeAuth-iAerdaMr.mjs';
25
+ import './claudeAuth-DMDvPNhO.mjs';
26
26
  import './fileLock-BMPwCrB6.mjs';
27
27
  import './codexProvider-BtxYc5Rj.mjs';
28
- import './friendlyName-1UZcyDhu.mjs';
28
+ import './inboxGuard-CDqmIypF.mjs';
29
29
  import 'node:events';
30
30
  import './cron-hHGdb5kU.mjs';
31
31
  import 'url';
32
+ import './friendlyName-B-OEwt0t.mjs';
32
33
  import './sharingNotificationSync-qGIQ-3yf.mjs';
33
- import './acpBackend-BZ1MJhQV.mjs';
34
+ import './acpBackend-UaBD4zL3.mjs';
34
35
  import '@agentclientprotocol/sdk';
35
36
  import './DefaultTransport-vfp78fug.mjs';
36
37
  import './wrapCommand-CdNawSaa.mjs';
@@ -43,12 +44,12 @@ import './GeminiTransport-3OkhtFRO.mjs';
43
44
  import './KimiTransport-D8n009Tr.mjs';
44
45
  import './kimiProvider-DbxODebR.mjs';
45
46
  import './kimiSetup-NGsoYB5h.mjs';
46
- import './detectIsolation-CzoDCEnh.mjs';
47
+ import './detectIsolation-CrV8IFDv.mjs';
47
48
  import 'node:fs/promises';
48
49
  import './pinnedNono-5ZHb6328.mjs';
49
50
  import './bootStatus-DqtNgJch.mjs';
50
- import './loopVerify-B47sSjxe.mjs';
51
- import './store-Bn0ByYev.mjs';
51
+ import './loopVerify-CxAKJB5a.mjs';
52
+ import './store-D8dok6_d.mjs';
52
53
  import './api-BHjyC0Ph.mjs';
53
54
  import './store-fi0f-ff1.mjs';
54
55
  import 'yaml';
@@ -1,6 +1,6 @@
1
1
  import { n as normalizeAllowedUser } from './types-57J3Re0n.mjs';
2
2
  import { c as getSvampWebBaseUrl } from './instance-CeHz5C8v.mjs';
3
- import { l as listIssues, i as isPendingIssue, j as isVisibleTo } from './store-Bn0ByYev.mjs';
3
+ import { l as listIssues, j as isPendingIssue, k as isVisibleTo } from './store-D8dok6_d.mjs';
4
4
 
5
5
  function getShareBaseUrl() {
6
6
  return getSvampWebBaseUrl();
@@ -1,6 +1,6 @@
1
1
  import { c as connectToHypha } from './hyphaClient-ybivOjZo.mjs';
2
2
  import { S as SharingNotificationSync } from './sharingNotificationSync-qGIQ-3yf.mjs';
3
- import { p as parseJwtEmail } from './serveAuth-DbVLRCRR.mjs';
3
+ import { p as parseJwtEmail } from './serveAuth-4M0vwMyj.mjs';
4
4
  import { randomUUID } from 'node:crypto';
5
5
  import { existsSync, readFileSync } from 'node:fs';
6
6
  import { join } from 'node:path';
@@ -0,0 +1,64 @@
1
+ var name = "svamp-cli";
2
+ var version = "0.2.358";
3
+ var description = "Svamp CLI — AI workspace daemon on Hypha Cloud";
4
+ var author = "Amun AI AB";
5
+ var license = "SEE LICENSE IN LICENSE";
6
+ var type = "module";
7
+ var bin = {
8
+ svamp: "./bin/svamp.mjs"
9
+ };
10
+ var files = [
11
+ "dist",
12
+ "bin"
13
+ ];
14
+ var main = "./dist/index.mjs";
15
+ var exports$1 = {
16
+ ".": "./dist/index.mjs",
17
+ "./cli": "./dist/cli.mjs"
18
+ };
19
+ var scripts = {
20
+ build: "rm -rf dist bin/skills bin/commands && mkdir -p bin/skills && cp -r ../../skills/artifact bin/skills/artifact && cp -r ../../skills/crew bin/skills/crew && cp -r ../../commands bin/commands && tsc --noEmit && pkgroll",
21
+ typecheck: "tsc --noEmit",
22
+ test: "node test/with-timeout.mjs 420 npx tsx test/test-context-window.mjs && node test/with-timeout.mjs 420 npx tsx test/test-ratelimit-retry.mjs && node test/with-timeout.mjs 420 npx tsx test/test-completed-requests.mjs && node test/with-timeout.mjs 420 npx tsx test/test-reconnect-health.mjs &&node test/with-timeout.mjs 420 npx tsx test/test-fleet-version-parse.mjs &&node test/with-timeout.mjs 420 npx tsx test/test-instance-config.mjs && node test/with-timeout.mjs 420 npx tsx test/test-authorize.mjs && node test/with-timeout.mjs 420 npx tsx test/test-normalize-allowed-user.mjs && node test/with-timeout.mjs 420 npx tsx test/test-share-url.mjs && node test/with-timeout.mjs 420 npx tsx test/test-update-sharing-normalization.mjs && node test/with-timeout.mjs 420 npx tsx test/test-sharing-notify-sync.mjs && node test/with-timeout.mjs 420 npx tsx test/test-sharing-notify-resolve.mjs && node test/with-timeout.mjs 420 npx tsx test/test-staged-homes-sweep.mjs && node test/with-timeout.mjs 420 npx tsx test/test-session-helpers.mjs && node test/with-timeout.mjs 420 npx tsx test/test-cli-routing.mjs && node test/with-timeout.mjs 420 npx tsx test/test-security-context.mjs && node test/with-timeout.mjs 420 npx tsx test/test-isolation-decision.mjs && node test/with-timeout.mjs 420 npx tsx test/test-message-helpers.mjs && node test/with-timeout.mjs 420 npx tsx test/test-stdin-delivery.mjs && node test/with-timeout.mjs 420 npx tsx test/test-agent-config.mjs && node test/with-timeout.mjs 420 npx tsx test/test-wrap-command.mjs && node test/with-timeout.mjs 420 npx tsx test/test-credential-staging.mjs && node test/with-timeout.mjs 420 npx tsx test/test-claude-auth.mjs && node test/with-timeout.mjs 420 npx tsx test/test-backend-accounts.mjs && node test/with-timeout.mjs 420 npx tsx test/test-codex-force-model.mjs && node test/with-timeout.mjs 420 npx tsx test/test-backend-oauth.mjs && node test/with-timeout.mjs 420 npx tsx test/test-btw-proxy-env.mjs && node test/with-timeout.mjs 420 npx tsx test/test-output-formatters.mjs && node test/with-timeout.mjs 420 npx tsx test/test-inbox-guard.mjs && node test/with-timeout.mjs 420 npx tsx test/test-inbox-env-knobs.mjs && node test/with-timeout.mjs 420 npx tsx test/test-user-events.mjs && node test/with-timeout.mjs 420 npx tsx test/test-migrate-children.mjs && node test/with-timeout.mjs 420 npx tsx test/test-outpost-install.mjs && node test/with-timeout.mjs 420 npx tsx test/test-outpost-setup.mjs && node test/with-timeout.mjs 420 npx tsx test/test-outpost-pairing.mjs && node test/with-timeout.mjs 420 npx tsx test/test-outpost-coordinator.mjs && node test/with-timeout.mjs 420 npx tsx test/e2e-outpost-transport.mjs && node test/with-timeout.mjs 420 npx tsx test/test-inbox-reconcile.mjs && node test/with-timeout.mjs 420 npx tsx test/test-auto-topic.mjs && node test/with-timeout.mjs 420 npx tsx test/test-project-info.mjs && node test/with-timeout.mjs 420 npx tsx test/test-agent-types.mjs && node test/with-timeout.mjs 420 npx tsx test/test-transport.mjs && node test/with-timeout.mjs 420 npx tsx test/test-session-update-handlers.mjs && node test/with-timeout.mjs 420 npx tsx test/test-session-scanner.mjs && node test/with-timeout.mjs 420 npx tsx test/test-hypha-client.mjs && node test/with-timeout.mjs 420 npx tsx test/test-hook-settings.mjs && node test/with-timeout.mjs 420 npx tsx test/test-session-service-logic.mjs && node test/with-timeout.mjs 420 npx tsx test/test-daemon-persistence.mjs && node test/with-timeout.mjs 420 npx tsx test/test-detect-isolation.mjs && node test/with-timeout.mjs 420 npx tsx test/test-isolation-disable.mjs && node test/with-timeout.mjs 420 npx tsx test/test-machine-service-logic.mjs && node test/with-timeout.mjs 420 npx tsx test/test-machine-session-visibility.mjs && node test/with-timeout.mjs 420 npx tsx test/test-session-access-fallback.mjs && node test/with-timeout.mjs 420 npx tsx test/test-session-rpc-role-map.mjs && node test/with-timeout.mjs 420 npx tsx test/test-issue-workflow-op-roles.mjs && npx tsx test/test-issue-verify-cmd-authorship.mjs && node test/with-timeout.mjs 420 npx tsx test/test-interactive-helpers.mjs && node test/with-timeout.mjs 420 npx tsx test/test-pinned-codex.mjs && node test/with-timeout.mjs 420 npx tsx test/test-pinned-hypha-rpc.mjs && node test/with-timeout.mjs 420 npx tsx test/test-pinned-kimi.mjs && node test/with-timeout.mjs 420 npx tsx test/test-channel-receive-cap.mjs && node test/with-timeout.mjs 420 npx tsx test/test-dns-shadowing.mjs && node test/with-timeout.mjs 420 npx tsx test/test-codex-request-timeout.mjs && node test/with-timeout.mjs 420 npx tsx test/test-codex-app-server.mjs && node test/with-timeout.mjs 420 npx tsx test/test-kimi-provider.mjs && node test/with-timeout.mjs 420 npx tsx test/test-acp-backend.mjs && node test/with-timeout.mjs 420 npx tsx test/test-acp-bridge.mjs && node test/with-timeout.mjs 420 npx tsx test/test-listener-backoff.mjs && node test/with-timeout.mjs 420 npx tsx test/test-session-identity-env.mjs && node test/with-timeout.mjs 420 npx tsx test/test-hook-server.mjs && node test/with-timeout.mjs 420 npx tsx test/test-session-commands.mjs && node test/with-timeout.mjs 420 npx tsx test/test-interactive-console.mjs && node test/with-timeout.mjs 420 npx tsx test/test-session-messages.mjs && node test/with-timeout.mjs 420 npx tsx test/test-session-send-query.mjs && node test/with-timeout.mjs 420 npx tsx test/test-skills.mjs && node test/with-timeout.mjs 420 npx tsx test/test-agent-grouping.mjs && node test/with-timeout.mjs 420 npx tsx test/test-machine-list-directory.mjs && node test/with-timeout.mjs 420 npx tsx test/test-service-commands.mjs && node test/with-timeout.mjs 420 npx tsx test/test-supervisor.mjs && node test/with-timeout.mjs 420 npx tsx test/test-supervisor-lock.mjs && node test/with-timeout.mjs 420 node test/test-supervisor-restart.mjs && node test/with-timeout.mjs 420 npx tsx test/test-clear-detection.mjs && node test/with-timeout.mjs 420 npx tsx test/test-compact-detect.mjs && node test/with-timeout.mjs 420 npx tsx test/test-session-consolidation.mjs && node test/with-timeout.mjs 420 npx tsx test/test-inbox-store.mjs && npx tsx test/test-agent-permission-handler.mjs && node test/test-frps-auth-plugin-behaviour.mjs && node test/with-timeout.mjs 420 npx tsx test/test-inbox.mjs && npx tsx test/test-inbox-xml-escaping.mjs && node test/with-timeout.mjs 420 npx tsx test/test-inbox-cross-machine.mjs && node test/with-timeout.mjs 420 npx tsx test/test-issue-store.mjs && node test/with-timeout.mjs 420 npx tsx test/test-issue-store-lock.mjs && node test/with-timeout.mjs 420 npx tsx test/test-issue-close-gate.mjs && node test/with-timeout.mjs 420 npx tsx test/test-verify-gate-evidence.mjs && node test/with-timeout.mjs 420 npx tsx test/test-loop-verify.mjs && npx tsx test/test-loop-verify-error-guards.mjs && node test/with-timeout.mjs 420 npx tsx test/test-restore-decision.mjs && node test/with-timeout.mjs 420 npx tsx test/test-issue-rpc.mjs && npx tsx test/test-verify-gate-async.mjs && npx tsx test/test-process-env-redaction.mjs && npx tsx test/test-provisioner-pin-published.mjs && npx tsx test/test-pinned-nono.mjs && npx tsx test/test-nono-install-executability.mjs && node test/with-timeout.mjs 420 npx tsx test/test-issue-pause.mjs && node test/with-timeout.mjs 420 npx tsx test/test-workflow-store.mjs && node test/with-timeout.mjs 420 npx tsx test/test-workflow-rpc.mjs && node test/with-timeout.mjs 420 npx tsx test/test-workflow-scheduler.mjs && node test/with-timeout.mjs 420 npx tsx test/test-cron.mjs && node test/with-timeout.mjs 420 npx tsx test/test-workflow-runs.mjs && node test/with-timeout.mjs 420 npx tsx test/test-workflow-runs-lock.mjs && node test/with-timeout.mjs 420 npx tsx test/test-workflow-idle.mjs && node test/with-timeout.mjs 420 npx tsx test/test-workflow-reconcile.mjs && npx tsx test/test-workflow-reconcile-grace.mjs && node test/with-timeout.mjs 420 npx tsx test/test-serve-link-subdomain.mjs &&node test/with-timeout.mjs 420 npx tsx test/test-frpc-footprint.mjs && npx tsx test/test-serve-regenerate-url.mjs && node test/with-timeout.mjs 420 npx tsx test/test-short-id.mjs && node test/with-timeout.mjs 420 npx tsx test/test-transcript-edit.mjs && node test/with-timeout.mjs 420 npx tsx test/test-edit-history.mjs && node test/with-timeout.mjs 420 npx tsx test/test-friendly-name.mjs && node test/with-timeout.mjs 420 npx tsx test/test-session-rpc-dispatch.mjs && node test/with-timeout.mjs 420 npx tsx test/test-nightly-1615-1630.mjs && node test/with-timeout.mjs 420 npx tsx test/test-sandbox-cli.mjs && node test/with-timeout.mjs 420 npx tsx test/test-serve-manager.mjs && node test/with-timeout.mjs 420 npx tsx test/test-serve-listen-failure.mjs && node test/with-timeout.mjs 420 npx tsx test/test-session-files-gateway.mjs && node test/with-timeout.mjs 420 npx tsx test/test-serve-ws-upgrade.mjs && node test/with-timeout.mjs 420 npx tsx test/test-serve-auth.mjs && node test/with-timeout.mjs 420 npx tsx test/test-static-file-server.mjs && node test/with-timeout.mjs 420 npx tsx test/test-serve-stability.mjs && node test/with-timeout.mjs 420 npx tsx test/test-frpc-e2e.mjs --unit-only && node test/with-timeout.mjs 420 npx tsx test/test-frpc-status.mjs && node test/with-timeout.mjs 420 npx tsx test/test-frpc-orphan-reap.mjs && node test/with-timeout.mjs 420 node test/pinnedClaudeCode.test.mjs && node test/with-timeout.mjs 420 npx tsx test/test-claude-version-recheck.mjs && node test/with-timeout.mjs 420 node test/fleet.test.mjs && node test/with-timeout.mjs 420 npx tsx test/test-session-file.mjs && node test/with-timeout.mjs 420 npx tsx test/test-channel-rpc.mjs && node test/with-timeout.mjs 420 npx tsx test/test-wise-agent.mjs && node test/with-timeout.mjs 420 npx tsx test/test-channel-agent.mjs && node test/with-timeout.mjs 420 npx tsx test/test-channels-service.mjs && node test/with-timeout.mjs 420 npx tsx test/test-hotreload-seam.mjs && node test/with-timeout.mjs 420 npx tsx test/test-hot-reload.mjs && node test/with-timeout.mjs 420 npx tsx test/test-hot-reload-live.mjs && node test/with-timeout.mjs 420 npx tsx test/test-session-core.mjs && node test/with-timeout.mjs 420 npx tsx test/test-channel-async-reply.mjs && node test/with-timeout.mjs 420 npx tsx test/test-outbox-reload.mjs && node test/with-timeout.mjs 420 npx tsx test/test-channel-binding.mjs && node test/with-timeout.mjs 420 npx tsx test/test-channel-store-lock.mjs && npx tsx test/test-channel-lost-update.mjs && node test/with-timeout.mjs 420 npx tsx test/test-channel-identity.mjs && node test/with-timeout.mjs 420 npx tsx test/test-stateless-dispatch-limiter.mjs && node test/with-timeout.mjs 420 npx tsx test/test-shared-session-identity.mjs && node test/with-timeout.mjs 420 npx tsx test/test-session-authz-boundaries.mjs && node test/with-timeout.mjs 420 npx tsx test/test-wise-agent-auth.mjs && node test/with-timeout.mjs 420 npx tsx test/test-channel-http.mjs && node test/with-timeout.mjs 420 npx tsx test/test-channel-upload.mjs && node test/with-timeout.mjs 420 npx tsx test/test-channel-file-access.mjs && node test/with-timeout.mjs 420 npx tsx test/test-wise-voice.mjs && node test/with-timeout.mjs 420 npx tsx test/test-wise-headless.mjs && node test/with-timeout.mjs 420 npx tsx test/test-wise-machine.mjs && node test/with-timeout.mjs 420 npx tsx test/test-crew-merge.mjs && node test/with-timeout.mjs 420 npx tsx test/test-crew-merge-closes-issue.mjs && node test/with-timeout.mjs 420 npx tsx test/test-crew-verdict-routing.mjs && node test/with-timeout.mjs 420 npx tsx test/test-crew-standalone.mjs && node test/with-timeout.mjs 420 npx tsx test/test-session-links.mjs && node test/with-timeout.mjs 420 npx tsx test/test-issue-loop-pause-guard.mjs && node test/with-timeout.mjs 420 npx tsx test/test-resolve-project-root.mjs && node test/with-timeout.mjs 420 npx tsx test/test-artifact-guard.mjs && node test/with-timeout.mjs 420 npx tsx test/test-artifact-sync-pagination.mjs && node test/with-timeout.mjs 420 npx tsx test/test-graceful-restart.mjs && node test/with-timeout.mjs 420 npx tsx test/test-flush-exit.mjs && node test/with-timeout.mjs 420 npx tsx test/test-codex-skills.mjs && node test/with-timeout.mjs 420 npx tsx test/test-nightly-0807-fixes.mjs && node test/with-timeout.mjs 420 npx tsx test/test-nightly-0814-fixes.mjs && node test/with-timeout.mjs 420 npx tsx test/test-log-prune.mjs && node test/with-timeout.mjs 420 node test/test-message-count-cache.mjs && node test/with-timeout.mjs 420 node test/test-serve-link-gate.mjs && node test/with-timeout.mjs 420 node test/test-security-context-unenforceable.mjs && node test/with-timeout.mjs 420 npx tsx test/test-issue-id-resolution.mjs && node test/with-timeout.mjs 420 npx tsx test/test-issue-positional.mjs && node test/with-timeout.mjs 420 npx tsx test/test-issue-pending-tracker.mjs && npx tsx test/test-issue-status-validation.mjs && npx tsx test/test-issue-triage-reopen-guard.mjs && npx tsx test/test-issue-holder-display.mjs && npx tsx test/test-process-positional.mjs && npx tsx test/test-session-send-positional.mjs && npx tsx test/test-crew-root-and-max.mjs && npx tsx test/test-channel-sse-close.mjs && node test/with-timeout.mjs 420 npx tsx test/test-help-flag-never-acts.mjs && node test/with-timeout.mjs 420 npx tsx test/test-with-timeout.mjs && node test/with-timeout.mjs 420 npx tsx test/test-message-queue-cap.mjs && node test/with-timeout.mjs 420 npx tsx test/test-compaction-backstop.mjs && tsx test/test-launchpad-reconcile.mjs && tsx test/test-file-lock-failopen.mjs && node test/with-timeout.mjs 420 npx tsx test/test-log-format.mjs && npx tsx test/test-deploy-manifests.mjs && npx tsx test/test-kimi-default-model-1169.mjs && npx tsx test/test-loop-dormant-runtime-1170.mjs && npx tsx test/test-skill-install-atomic.mjs && node test/test-version-unpublished.mjs && node test/test-pin-holds.mjs && node test/test-hypha-token-renewal.mjs && node test/with-timeout.mjs 420 npx tsx test/test-boot-status.mjs && node test/with-timeout.mjs 420 npx tsx test/test-stream-log-policy.mjs && node test/with-timeout.mjs 420 npx tsx test/test-count-file-lines.mjs && node test/with-timeout.mjs 420 npx tsx test/test-read-jsonl-lines.mjs && node test/with-timeout.mjs 420 npx tsx test/test-messages-reverse-pager.mjs && node test/with-timeout.mjs 420 npx tsx test/test-spawn-index-scans.mjs && node test/with-timeout.mjs 420 npx tsx test/test-session-cwd-containment.mjs && node test/with-timeout.mjs 420 npx tsx test/test-register-listener.mjs && node test/with-timeout.mjs 420 npx tsx test/test-unreachable-machines.mjs",
23
+ "test:hypha": "node --no-warnings test/test-hypha-service.mjs",
24
+ dev: "tsx src/cli.ts",
25
+ "dev:daemon": "tsx src/cli.ts daemon start-sync",
26
+ "test:e2e": "node --no-warnings test/e2e-session-tests.mjs && npx tsx test/e2e-codex-midturn-steer.mjs && npx tsx test/e2e-serve-iframe-login.mjs && npx tsx test/e2e-proxy-account-capacity.mjs && npx tsx test/test-archive-preserves-claude-file.mjs && npx tsx test/test-daemon-e2e.mjs && npx tsx test/test-hypha-service.mjs && npx tsx test/test-permission-e2e.mjs && npx tsx test/test-serve-e2e.mjs && npx tsx test/test-session-archive-resume.mjs && npx tsx test/test-session-rpc-e2e.mjs && npx tsx test/test-skills-integration.mjs && npx tsx test/test-wm-refresh.mjs && npx tsx test/e2e-session-files-gateway.mjs && npx tsx test/e2e-dns-production-hosts.mjs && npx tsx test/e2e-cluster-manifest-drift.mjs && node test/with-timeout.mjs 600 npx tsx test/test-rev-lock-contention.mjs && npx tsx test/test-embed-headers.mjs && bash tests/test-sandbox-isolation.sh && bash tests/test-credential-staging.sh && bash tests/test-claude-sandbox-e2e.sh",
27
+ "test:frpc": "npx tsx test/test-frpc-e2e.mjs",
28
+ prepublishOnly: "node ../../scripts/bump-cli-version.mjs --check && yarn build && yarn test"
29
+ };
30
+ var dependencies = {
31
+ "@agentclientprotocol/sdk": "^0.14.1",
32
+ "@modelcontextprotocol/sdk": "^1.25.3",
33
+ "hypha-rpc": "0.21.46",
34
+ "node-pty": "1.2.0-beta.11",
35
+ ws: "^8.18.0",
36
+ yaml: "^2.8.2",
37
+ zod: "^3.24.4"
38
+ };
39
+ var devDependencies = {
40
+ "@types/node": ">=20",
41
+ "@types/ws": "^8.5.14",
42
+ pkgroll: "^2.14.2",
43
+ tsx: "^4.20.6",
44
+ typescript: "5.9.3"
45
+ };
46
+ var packageManager = "yarn@1.22.22";
47
+ var _package = {
48
+ name: name,
49
+ version: version,
50
+ description: description,
51
+ author: author,
52
+ license: license,
53
+ type: type,
54
+ bin: bin,
55
+ files: files,
56
+ main: main,
57
+ exports: exports$1,
58
+ scripts: scripts,
59
+ dependencies: dependencies,
60
+ devDependencies: devDependencies,
61
+ packageManager: packageManager
62
+ };
63
+
64
+ export { author, bin, _package as default, dependencies, description, devDependencies, exports$1 as exports, files, license, main, name, packageManager, scripts, type, version };
@@ -1,4 +1,4 @@
1
- import { r as resolveProjectRoot, u as updateIssue, p as parseScopeArg, I as ISSUE_SCOPES, g as getIssue, c as checkVerifyCommandAsync, a as parseStatusArg, b as ISSUE_STATUSES, d as resumeIssue, e as pauseIssue, f as addComment, h as addIssue, l as listIssues, i as isPendingIssue, s as searchIssues, j as isVisibleTo } from './store-Bn0ByYev.mjs';
1
+ import { r as resolveProjectRoot, a as applyTriageStatus, u as updateIssue, p as parseScopeArg, I as ISSUE_SCOPES, g as getIssue, c as checkVerifyCommandAsync, b as addComment, v as verifyGatePass, d as parseStatusArg, e as ISSUE_STATUSES, f as resumeIssue, h as pauseIssue, i as addIssue, l as listIssues, j as isPendingIssue, s as searchIssues, k as isVisibleTo } from './store-D8dok6_d.mjs';
2
2
  import 'node:fs';
3
3
  import 'node:path';
4
4
  import 'node:crypto';
@@ -66,7 +66,8 @@ async function issueRpc(cwd, params, deps = {}) {
66
66
  }
67
67
  case "add": {
68
68
  const owner = params.session || null;
69
- const scope = params.scope || (owner ? "session" : "project");
69
+ const scope = params.scope == null ? owner ? "session" : "project" : parseScopeArg(params.scope);
70
+ if (!scope) throw new Error(`issue add: invalid --scope "${String(params.scope)}" (expected one of: ${ISSUE_SCOPES.join(", ")})`);
70
71
  let title = params.title ? String(params.title) : "";
71
72
  let body = params.body ? String(params.body) : void 0;
72
73
  let original;
@@ -132,6 +133,7 @@ ${res.output || ""}
132
133
  (Fix the issue, or close with force to override.)`
133
134
  );
134
135
  }
136
+ if (res.cmd) addComment(root, String(params.id), verifyGatePass(res));
135
137
  }
136
138
  const patch = { status };
137
139
  if (status === "in_progress" && params.session) {
@@ -179,6 +181,7 @@ ${res.output || ""}
179
181
  (Use the 'archive' op for an obsolete/duplicate item, or pass force to override.)`
180
182
  );
181
183
  }
184
+ if (res.cmd) addComment(root, String(params.id), verifyGatePass(res));
182
185
  }
183
186
  }
184
187
  const nextStatus = parseStatusArg(params.status);
@@ -201,7 +204,9 @@ ${res.output || ""}
201
204
  const v = buildVerify(params);
202
205
  if (v) patch.verify = v;
203
206
  if (params.body != null) patch.body = String(params.body);
204
- patch.status = "ready";
207
+ applyTriageStatus(root, String(params.id), patch, (m) => {
208
+ throw new Error(m);
209
+ });
205
210
  const updated = updateIssue(root, String(params.id), patch);
206
211
  if (!updated) throw new Error(`Issue not found: ${params.id}`);
207
212
  return updated;
@@ -1,6 +1,6 @@
1
- import { r as resolveProjectRoot } from './store-Bn0ByYev.mjs';
1
+ import { r as resolveProjectRoot } from './store-D8dok6_d.mjs';
2
2
  import { g as getWorkflow, s as setWorkflowEnabled, r as removeWorkflow, v as validateWorkflowName, a as saveWorkflow, b as rawWorkflow, l as listWorkflows } from './store-fi0f-ff1.mjs';
3
- import { g as getRun, l as listRuns, r as runWorkflow } from './run-DkjwF-zA.mjs';
3
+ import { g as getRun, l as listRuns, r as runWorkflow } from './run-DKOND4dO.mjs';
4
4
  import { v as validateCronExpr } from './cron-hHGdb5kU.mjs';
5
5
  import 'node:fs';
6
6
  import 'node:path';
@@ -15,7 +15,7 @@ import 'path';
15
15
  import 'url';
16
16
  import 'child_process';
17
17
  import 'crypto';
18
- import './hookSettings-DYMTUgVA.mjs';
18
+ import './hookSettings-CvvUhwCK.mjs';
19
19
  import './types-57J3Re0n.mjs';
20
20
  import './securityContext-Cm5UTCuD.mjs';
21
21
  import './machineTools-DygM1jXW.mjs';
@@ -24,16 +24,17 @@ import 'util';
24
24
  import 'http';
25
25
  import 'net';
26
26
  import './instance-CeHz5C8v.mjs';
27
- import './serveAuth-DbVLRCRR.mjs';
27
+ import './serveAuth-4M0vwMyj.mjs';
28
28
  import 'node:os';
29
29
  import './envFlag-COOuJ3AH.mjs';
30
- import './claudeAuth-iAerdaMr.mjs';
30
+ import './claudeAuth-DMDvPNhO.mjs';
31
31
  import './codexProvider-BtxYc5Rj.mjs';
32
- import './friendlyName-1UZcyDhu.mjs';
32
+ import './inboxGuard-CDqmIypF.mjs';
33
33
  import 'node:events';
34
+ import './friendlyName-B-OEwt0t.mjs';
34
35
  import './hyphaClient-ybivOjZo.mjs';
35
36
  import './sharingNotificationSync-qGIQ-3yf.mjs';
36
- import './acpBackend-BZ1MJhQV.mjs';
37
+ import './acpBackend-UaBD4zL3.mjs';
37
38
  import '@agentclientprotocol/sdk';
38
39
  import './DefaultTransport-vfp78fug.mjs';
39
40
  import './wrapCommand-CdNawSaa.mjs';
@@ -46,11 +47,11 @@ import './GeminiTransport-3OkhtFRO.mjs';
46
47
  import './KimiTransport-D8n009Tr.mjs';
47
48
  import './kimiProvider-DbxODebR.mjs';
48
49
  import './kimiSetup-NGsoYB5h.mjs';
49
- import './detectIsolation-CzoDCEnh.mjs';
50
+ import './detectIsolation-CrV8IFDv.mjs';
50
51
  import 'node:fs/promises';
51
52
  import './pinnedNono-5ZHb6328.mjs';
52
53
  import './bootStatus-DqtNgJch.mjs';
53
- import './loopVerify-B47sSjxe.mjs';
54
+ import './loopVerify-CxAKJB5a.mjs';
54
55
  import './api-BHjyC0Ph.mjs';
55
56
 
56
57
  async function workflowRpc(cwd, params) {
@@ -79,6 +80,9 @@ async function workflowRpc(cwd, params) {
79
80
  jobs: { run: { steps: runs.map((r) => ({ run: r })) } },
80
81
  session: params.session || null
81
82
  };
83
+ if (!params.force && !params.replace && getWorkflow(root, String(params.name))) {
84
+ throw new Error(`workflow add: "${params.name}" already exists \u2014 pass force to replace it, or remove it first (replacing discards its steps, schedule and owning session)`);
85
+ }
82
86
  saveWorkflow(root, wf);
83
87
  return wf;
84
88
  }