agent-yes 1.288.0 → 1.289.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 (56) hide show
  1. package/default.config.yaml +106 -0
  2. package/dist/{SUPPORTED_CLIS-BHowMgMY.js → SUPPORTED_CLIS-BlgVWZrM.js} +2 -2
  3. package/dist/{SUPPORTED_CLIS-ChhJyq4h.js → SUPPORTED_CLIS-C65PuIs6.js} +3 -3
  4. package/dist/{agentShare-T3RskvDT.js → agentShare-Cm15-dfZ.js} +3 -3
  5. package/dist/{callback-CuohbCJA.js → callback-Bbplrsbv.js} +3 -3
  6. package/dist/{callback-DeaETuHt.js → callback-Dwnkeqpz.js} +3 -3
  7. package/dist/cli.js +11 -11
  8. package/dist/cmdPs-BYBZQ0Vc.js +331 -0
  9. package/dist/{cwdConflictWarn-DIPD6HH5.js → cwdConflictWarn-BgXN56Jc.js} +1 -1
  10. package/dist/{cwdPassthroughHint-C0tfZlgZ.js → cwdPassthroughHint-CDEGVteh.js} +1 -1
  11. package/dist/{expose-Ds9aUFsq.js → expose-BlddYYx9.js} +1 -1
  12. package/dist/{forkNested-Dt5XP8ZH.js → forkNested-CJ7lQQCe.js} +1 -1
  13. package/dist/{hist-DSYaJEd3.js → hist-DZxq7NMA.js} +1 -1
  14. package/dist/index.js +2 -2
  15. package/dist/{initMsg-CeF4QNOM.js → initMsg-DoltWTZj.js} +1 -1
  16. package/dist/{initMsg-DejbYYjX.js → initMsg-axyQAPLK.js} +1 -1
  17. package/dist/{nodeRuntime-_zeWDISM.js → nodeRuntime-CipjIwfO.js} +1 -1
  18. package/dist/{notifyDaemon-BORqPeAl.js → notifyDaemon-Q3FF-WEZ.js} +47 -39
  19. package/dist/{openBrowser-BNP8xByu.js → openBrowser-THOzOGNm.js} +1 -1
  20. package/dist/{parentLink-Bv8ihRcu.js → parentLink-bpDGuQi6.js} +1 -1
  21. package/dist/{parentLink-HWunrH1s.js → parentLink-fdiuB13i.js} +1 -1
  22. package/dist/{parentPingLoop-COePRYr9.js → parentPingLoop-Dc7co5D7.js} +4 -4
  23. package/dist/{cmdPs-Bm4JG7hi.js → procStats-CyD8h5nx.js} +2 -329
  24. package/dist/{remotes-b6reVXzo.js → remotes-BjsglpjL.js} +2 -2
  25. package/dist/{remotes-BlHTR9ow.js → remotes-CAqtL_Ib.js} +1 -1
  26. package/dist/{rustBinary-BmXzqj5z.js → rustBinary-B57oRftk.js} +2 -2
  27. package/dist/{rustBinary-D12k7iOD.js → rustBinary-Dq0AytZ7.js} +2 -2
  28. package/dist/{schedule-DLoUmyJh.js → schedule-pim0Izhs.js} +6 -6
  29. package/dist/{serve-DaDlOtss.js → serve-CMsvjqPU.js} +174 -27
  30. package/dist/{setup-Cqy8nRrq.js → setup-BmxYINCx.js} +4 -4
  31. package/dist/{spawnGate-B2hA_2NQ.js → spawnGate-B26UnzqP.js} +2 -2
  32. package/dist/{spawnGate-FLwTAPW6.js → spawnGate-C8MSRt7X.js} +1 -1
  33. package/dist/{subcommands-DKTa3E6O.js → subcommands-CJBV06rg.js} +2 -2
  34. package/dist/{subcommands-BfiOyfz7.js → subcommands-CWDqhKQe.js} +21 -21
  35. package/dist/{systemPathLink-Dl2gJqeg.js → systemPathLink-BVdk48Vz.js} +1 -1
  36. package/dist/{terminal-C0tnGnrZ.js → terminal-CcmBzB3G.js} +2 -2
  37. package/dist/{todoCli-MWrejmdM.js → todoCli-azqbjBtz.js} +1 -1
  38. package/dist/{tray-BhPVpP_r.js → tray-CIt9mCKZ.js} +1 -1
  39. package/dist/trayApp-BovAZs7m.js +5 -0
  40. package/dist/{trayApp-DvL9qW6V.js → trayApp-DuZBkGl_.js} +2 -2
  41. package/dist/{ts-DJLp2_bv.js → ts-CSp4mJ7Z.js} +23 -9
  42. package/dist/{versionChecker-CkO7wYyV.js → versionChecker-CWCN4vwm.js} +2 -2
  43. package/dist/{webrtcRemote-H9Hok0_R.js → webrtcRemote-Bevc5ogS.js} +1 -1
  44. package/dist/{widget-CJqpScCG.js → widget-D2cCC8nz.js} +3 -3
  45. package/dist/{workspaceConfig-D6QndOCz.js → workspaceConfig-DTvPtr5j.js} +1 -1
  46. package/dist/{ws-DGENGNrr.js → ws-B5Qb2c-1.js} +3 -3
  47. package/dist/{ws-DzCKvzWD.js → ws-CyO9r4jx.js} +3 -3
  48. package/package.json +1 -1
  49. package/ts/agentNice.spec.ts +42 -2
  50. package/ts/index.ts +5 -4
  51. package/ts/notifyDaemon.spec.ts +16 -0
  52. package/ts/notifyDaemon.ts +22 -0
  53. package/ts/serve.ts +14 -0
  54. package/ts/serveSampler.spec.ts +390 -0
  55. package/ts/serveSampler.ts +218 -0
  56. package/dist/trayApp-BrAMlCY7.js +0 -5
@@ -53,4 +53,4 @@ async function ensureNodeRuntime(which = Bun.which) {
53
53
 
54
54
  //#endregion
55
55
  export { liveEnv as n, ensureNodeRuntime as t };
56
- //# sourceMappingURL=nodeRuntime-_zeWDISM.js.map
56
+ //# sourceMappingURL=nodeRuntime-CipjIwfO.js.map
@@ -3,10 +3,10 @@ import "./globalPidIndex-DoaKEUkO.js";
3
3
  import "./messageLog-DdjBNqgt.js";
4
4
  import "./e2e-z1xpwSAg.js";
5
5
  import "./configShared-DKTgiz7K.js";
6
- import { D as listRecords, I as renderLogTailLines, b as isPidAlive, u as deriveLiveState } from "./subcommands-BfiOyfz7.js";
6
+ import { D as listRecords, I as renderLogTailLines, b as isPidAlive, u as deriveLiveState } from "./subcommands-CWDqhKQe.js";
7
7
  import { f as readInbox, g as daemonLockOwnerPath, h as daemonLockDir, i as gcInboxes, l as listInboxParents, m as shouldStealLock, n as appendEvent, s as hostId, u as liveWatchers, x as notifyDir } from "./notifyStore-2SWKGwZI.js";
8
8
  import "./webrtcLink-DLaJfTcB.js";
9
- import "./remotes-b6reVXzo.js";
9
+ import "./remotes-BjsglpjL.js";
10
10
  import { mkdir, readFile, rename, rm, stat, writeFile } from "fs/promises";
11
11
  import { randomUUID } from "node:crypto";
12
12
  import { execFile, execFileSync } from "node:child_process";
@@ -556,6 +556,7 @@ async function liveChildrenSnapshot() {
556
556
  return m;
557
557
  }
558
558
  const OWNER_TTL_MS = 3e4;
559
+ const LOCK_WAIT_BUDGET_MS = 5e3;
559
560
  let daemonStartedAt = 0;
560
561
  let daemonOsStart = null;
561
562
  let daemonToken = "";
@@ -597,46 +598,53 @@ async function writeOwner() {
597
598
  * mkdir→writeOwner window of another daemon coming up), so two concurrent starts
598
599
  * can't both "win". The liveness predicate is injectable for tests.
599
600
  */
600
- async function acquireDaemonLock(isAlive = isPidAlive) {
601
+ async function acquireDaemonLock(isAlive = isPidAlive, waitBudgetMs = LOCK_WAIT_BUDGET_MS) {
601
602
  await mkdir(notifyDir(), { recursive: true }).catch(() => {});
602
- for (;;) try {
603
- await mkdir(daemonLockDir(), { recursive: false });
604
- daemonStartedAt = Date.now();
605
- daemonToken = randomUUID();
606
- daemonOsStart = osProcessStartToken(process.pid);
607
- if (!await writeOwner()) {
608
- await rm(daemonLockDir(), {
609
- recursive: true,
610
- force: true
611
- }).catch(() => {});
612
- await new Promise((r) => setTimeout(r, 15));
613
- continue;
603
+ const deadline = Date.now() + waitBudgetMs;
604
+ for (;;) {
605
+ if (Date.now() >= deadline) {
606
+ logger.warn("[notifyd] gave up acquiring the daemon lock — unverifiable live holder");
607
+ return false;
614
608
  }
615
- return true;
616
- } catch (e) {
617
- if (e.code !== "EEXIST") throw e;
618
- const raw = await readFile(daemonLockOwnerPath(), "utf8").catch(() => "");
619
- const now = Date.now();
620
- let owner = null;
621
609
  try {
622
- owner = JSON.parse(raw);
623
- } catch {}
624
- const recycled = owner && typeof owner.pid === "number" && owner.pid > 0 ? osStartTokenMismatch(owner.os_start, osProcessStartToken(owner.pid)) : false;
625
- if (owner && typeof owner.pid === "number" && owner.pid > 0 && owner.pid !== process.pid && typeof owner.ts === "number" && now - owner.ts <= OWNER_TTL_MS && isAlive(owner.pid) && !recycled) return false;
626
- const lockAgeMs = now - await stat(daemonLockDir()).then((s) => s.mtimeMs).catch(() => now);
627
- if (recycled || shouldStealLock(raw, now, {
628
- staleMs: OWNER_TTL_MS,
629
- lockAgeMs,
630
- selfPid: process.pid,
631
- isAlive
632
- })) {
633
- await rm(daemonLockDir(), {
634
- recursive: true,
635
- force: true
636
- }).catch(() => {});
637
- continue;
610
+ await mkdir(daemonLockDir(), { recursive: false });
611
+ daemonStartedAt = Date.now();
612
+ daemonToken = randomUUID();
613
+ daemonOsStart = osProcessStartToken(process.pid);
614
+ if (!await writeOwner()) {
615
+ await rm(daemonLockDir(), {
616
+ recursive: true,
617
+ force: true
618
+ }).catch(() => {});
619
+ await new Promise((r) => setTimeout(r, 15));
620
+ continue;
621
+ }
622
+ return true;
623
+ } catch (e) {
624
+ if (e.code !== "EEXIST") throw e;
625
+ const raw = await readFile(daemonLockOwnerPath(), "utf8").catch(() => "");
626
+ const now = Date.now();
627
+ let owner = null;
628
+ try {
629
+ owner = JSON.parse(raw);
630
+ } catch {}
631
+ const recycled = owner && typeof owner.pid === "number" && owner.pid > 0 ? osStartTokenMismatch(owner.os_start, osProcessStartToken(owner.pid)) : false;
632
+ if (owner && typeof owner.pid === "number" && owner.pid > 0 && owner.pid !== process.pid && typeof owner.ts === "number" && now - owner.ts <= OWNER_TTL_MS && isAlive(owner.pid) && !recycled) return false;
633
+ const lockAgeMs = now - await stat(daemonLockDir()).then((s) => s.mtimeMs).catch(() => now);
634
+ if (recycled || shouldStealLock(raw, now, {
635
+ staleMs: OWNER_TTL_MS,
636
+ lockAgeMs,
637
+ selfPid: process.pid,
638
+ isAlive
639
+ })) {
640
+ await rm(daemonLockDir(), {
641
+ recursive: true,
642
+ force: true
643
+ }).catch(() => {});
644
+ continue;
645
+ }
646
+ await new Promise((r) => setTimeout(r, 15));
638
647
  }
639
- await new Promise((r) => setTimeout(r, 15));
640
648
  }
641
649
  }
642
650
  /**
@@ -863,4 +871,4 @@ async function ensureDaemon() {
863
871
 
864
872
  //#endregion
865
873
  export { daemonStatus, ensureDaemon, requestDaemonStop, runDaemon };
866
- //# sourceMappingURL=notifyDaemon-BORqPeAl.js.map
874
+ //# sourceMappingURL=notifyDaemon-Q3FF-WEZ.js.map
@@ -68,4 +68,4 @@ async function offerOpenInBrowser(url, prompt = "Open in browser now? [Y/n] ") {
68
68
 
69
69
  //#endregion
70
70
  export { offerOpenInBrowser };
71
- //# sourceMappingURL=openBrowser-BNP8xByu.js.map
71
+ //# sourceMappingURL=openBrowser-THOzOGNm.js.map
@@ -50,4 +50,4 @@ function spawnerFromRecords(records, parentPid) {
50
50
 
51
51
  //#endregion
52
52
  export { spawnerFromRecords as n, resolveSpawner as t };
53
- //# sourceMappingURL=parentLink-Bv8ihRcu.js.map
53
+ //# sourceMappingURL=parentLink-bpDGuQi6.js.map
@@ -1,5 +1,5 @@
1
1
  import "./logger-CDIsZ-Pp.js";
2
2
  import "./globalPidIndex-DoaKEUkO.js";
3
- import { n as spawnerFromRecords, t as resolveSpawner } from "./parentLink-Bv8ihRcu.js";
3
+ import { n as spawnerFromRecords, t as resolveSpawner } from "./parentLink-bpDGuQi6.js";
4
4
 
5
5
  export { resolveSpawner };
@@ -1,8 +1,8 @@
1
1
  import { n as logger } from "./logger-CDIsZ-Pp.js";
2
2
  import "./globalPidIndex-DoaKEUkO.js";
3
3
  import { t as classifyNeedsInput } from "./needsInput-BHopZGM8.js";
4
- import { n as replyTargetOf, r as shortenHome } from "./initMsg-DejbYYjX.js";
5
- import { t as resolveSpawner } from "./parentLink-Bv8ihRcu.js";
4
+ import { n as replyTargetOf, r as shortenHome } from "./initMsg-axyQAPLK.js";
5
+ import { t as resolveSpawner } from "./parentLink-bpDGuQi6.js";
6
6
  import { spawn } from "node:child_process";
7
7
 
8
8
  //#region ts/parentPing.ts
@@ -338,7 +338,7 @@ async function isParentWatching(parentWrapperPid, parentAgentPid, selfAgentPid,
338
338
  logger.debug("[parent-ping] watcher lookup failed:", error);
339
339
  }
340
340
  try {
341
- const { lastReadAt, READ_WINDOW_MS } = await import("./subcommands-DKTa3E6O.js");
341
+ const { lastReadAt, READ_WINDOW_MS } = await import("./subcommands-CJBV06rg.js");
342
342
  const last = await lastReadAt(`agent:${parentAgentPid}`, selfAgentPid);
343
343
  if (last !== null && now - last <= READ_WINDOW_MS) return true;
344
344
  } catch (error) {
@@ -466,4 +466,4 @@ function startParentPingLoop(opts) {
466
466
 
467
467
  //#endregion
468
468
  export { startParentPingLoop };
469
- //# sourceMappingURL=parentPingLoop-COePRYr9.js.map
469
+ //# sourceMappingURL=parentPingLoop-Dc7co5D7.js.map
@@ -1,17 +1,4 @@
1
- import "./logger-CDIsZ-Pp.js";
2
- import "./globalPidIndex-DoaKEUkO.js";
3
- import "./messageLog-DdjBNqgt.js";
4
- import "./e2e-z1xpwSAg.js";
5
- import "./configShared-DKTgiz7K.js";
6
- import { $ as buildAgentForest, D as listRecords, et as flattenForest, u as deriveLiveState } from "./subcommands-BfiOyfz7.js";
7
- import "./notifyStore-2SWKGwZI.js";
8
- import "./webrtcLink-DLaJfTcB.js";
9
- import "./remotes-b6reVXzo.js";
10
- import yargs from "yargs";
11
1
  import { execFile } from "node:child_process";
12
- import { readFileSync } from "node:fs";
13
- import path from "node:path";
14
- import { homedir } from "node:os";
15
2
  import { readFile, readdir } from "node:fs/promises";
16
3
  import { promisify } from "node:util";
17
4
 
@@ -372,319 +359,5 @@ function humanBytes(bytes) {
372
359
  }
373
360
 
374
361
  //#endregion
375
- //#region ts/cmdPs.ts
376
- /**
377
- * `ay ps` — what each agent COSTS, rolled up per process tree.
378
- *
379
- * The split from `ay ls`: `ls` answers "what is running and what is it doing"
380
- * (prompt, git, badges, forest); `ps` answers "what is this box spending on
381
- * each agent, and is any of it safe to reclaim". Same rows, different question.
382
- *
383
- * `ps` used to be a plain alias for `ls`. It is now its own command, because the
384
- * number you actually want is not in any `ps`/`htop` output: one agent is a
385
- * whole subtree of wrapper + CLI + pty hosts + subagents, and only ay knows
386
- * which pids collapse into which session.
387
- *
388
- * Deliberately local-only. Resource numbers are per-machine and there is nothing
389
- * meaningful to sum across remotes; `ay ls` remains the fleet-wide view.
390
- */
391
- /** Default CPU sampling window. Long enough to be meaningful, short enough to feel instant. */
392
- const DEFAULT_WINDOW_MS = 1e3;
393
- function shortenPath(p) {
394
- const home = homedir();
395
- return p.startsWith(home) ? "~" + p.slice(home.length) : p;
396
- }
397
- /** Trailing path segment that identifies a worktree — `/code/x/foo/tree/main` → `foo`. */
398
- function repoLabel(cwd) {
399
- const parts = cwd.split(/[\\/]+/).filter(Boolean);
400
- const treeAt = parts.lastIndexOf("tree");
401
- if (treeAt > 0) return parts[treeAt - 1];
402
- return parts[parts.length - 1] ?? shortenPath(cwd);
403
- }
404
- /** `15.23 12.52 9.86 (8 cpu, 1.9x) mem 13.0/15Gi swap 4.8/4.8Gi FULL zombies 1440` */
405
- function formatSystemLine(sys) {
406
- const bits = [];
407
- if (sys.load) {
408
- const [one, five, fifteen] = sys.load;
409
- const ratio = one / Math.max(1, sys.ncpu);
410
- bits.push(`load ${one.toFixed(2)} ${five.toFixed(2)} ${fifteen.toFixed(2)} (${sys.ncpu} cpu, ${ratio.toFixed(1)}x)`);
411
- }
412
- if (sys.memTotalBytes !== null) {
413
- const used = sys.memAvailableBytes !== null ? sys.memTotalBytes - sys.memAvailableBytes : null;
414
- bits.push(`mem ${humanBytes(used)}/${humanBytes(sys.memTotalBytes)}`);
415
- }
416
- if (sys.swapTotalBytes !== null && sys.swapTotalBytes > 0) {
417
- const usedSwap = sys.swapFreeBytes !== null ? sys.swapTotalBytes - sys.swapFreeBytes : null;
418
- const full = sys.swapFreeBytes !== null && sys.swapFreeBytes <= 0 ? " FULL" : "";
419
- bits.push(`swap ${humanBytes(usedSwap)}/${humanBytes(sys.swapTotalBytes)}${full}`);
420
- }
421
- if (sys.zombies > 0) bits.push(`zombies ${sys.zombies}`);
422
- return bits.join(" ");
423
- }
424
- /**
425
- * Roll each row up with its descendants, using the forest `depth` column: a
426
- * row's descendants are the following rows with a strictly greater depth, up to
427
- * the next row at or above its own level.
428
- *
429
- * Needed because sampleTrees attributes DEEPEST-FIRST and exclusively — a
430
- * parent's own numbers deliberately omit its subagents, so without this a
431
- * fan-out parent looks cheap while its real cost sits in rows below it.
432
- *
433
- * Returns null for a row with no descendants.
434
- */
435
- function subtreeTotals(rows) {
436
- return rows.map((r, i) => {
437
- const depth = r.depth ?? 0;
438
- const own = r.stats;
439
- if (!own) return null;
440
- let rss = own.rss;
441
- let cpuPercent = own.cpuPercent;
442
- let procs = own.procs;
443
- let found = false;
444
- for (let j = i + 1; j < rows.length; j++) {
445
- if ((rows[j]?.depth ?? 0) <= depth) break;
446
- const st = rows[j]?.stats;
447
- if (!st) continue;
448
- found = true;
449
- rss += st.rss;
450
- cpuPercent += st.cpuPercent;
451
- procs += st.procs;
452
- }
453
- return found ? {
454
- pid: own.pid,
455
- rss,
456
- cpuPercent,
457
- procs
458
- } : null;
459
- });
460
- }
461
- function pad(s, n) {
462
- return s.length >= n ? s : s + " ".repeat(n - s.length);
463
- }
464
- function padStart(s, n) {
465
- return s.length >= n ? s : " ".repeat(n - s.length) + s;
466
- }
467
- function renderTable(rows, unattributed, opts = {}) {
468
- const tree = opts.tree === true;
469
- const showProcs = opts.procs === true;
470
- const cells = rows.map((r) => ({
471
- pid: (tree ? r.prefix ?? "" : "") + String(r.pid),
472
- cli: r.cli,
473
- status: r.status,
474
- cpu: r.stats.cpuPercent.toFixed(1),
475
- rss: humanBytes(r.stats.rss),
476
- procs: String(r.stats.procs),
477
- repo: r.repo,
478
- cwd: shortenPath(r.cwd),
479
- scpu: r.subtree ? r.subtree.cpuPercent.toFixed(1) : "",
480
- srss: r.subtree ? humanBytes(r.subtree.rss) : "",
481
- sprocs: r.subtree ? String(r.subtree.procs) : "",
482
- self: r.self
483
- }));
484
- if (unattributed && unattributed.procs > 0) cells.push({
485
- pid: "-",
486
- cli: "unmanaged",
487
- status: "-",
488
- cpu: unattributed.cpuPercent.toFixed(1),
489
- rss: humanBytes(unattributed.rss),
490
- procs: String(unattributed.procs),
491
- repo: "-",
492
- cwd: "-",
493
- scpu: "",
494
- srss: "",
495
- sprocs: "",
496
- self: false
497
- });
498
- const w = {
499
- pid: Math.max(3, ...cells.map((c) => c.pid.length)),
500
- cli: Math.max(3, ...cells.map((c) => c.cli.length)),
501
- status: Math.max(6, ...cells.map((c) => c.status.length)),
502
- cpu: Math.max(5, ...cells.map((c) => c.cpu.length)),
503
- rss: Math.max(5, ...cells.map((c) => c.rss.length)),
504
- procs: Math.max(5, ...cells.map((c) => c.procs.length)),
505
- repo: Math.max(4, ...cells.map((c) => c.repo.length)),
506
- scpu: Math.max(5, ...cells.map((c) => c.scpu.length)),
507
- srss: Math.max(5, ...cells.map((c) => c.srss.length)),
508
- sprocs: Math.max(6, ...cells.map((c) => c.sprocs.length))
509
- };
510
- const line = (pid, cli, status, cpu, rss, procs, repo, cwd, scpu = "", srss = "", sprocs = "", tail = "") => `${pad(pid, w.pid)} ${pad(cli, w.cli)} ${pad(status, w.status)} ${padStart(cpu, w.cpu)} ${padStart(rss, w.rss)} ${padStart(procs, w.procs)} ` + (tree ? `${padStart(scpu, w.scpu)} ${padStart(srss, w.srss)} ${padStart(sprocs, w.sprocs)} ` : "") + `${pad(repo, w.repo)} ${cwd}${tail}`;
511
- const out = [line("PID", "CLI", "STATUS", "CPU%", "RSS", "PROCS", "REPO", "CWD", "ΣCPU%", "ΣRSS", "ΣPROCS")];
512
- cells.forEach((c, i) => {
513
- out.push(line(c.pid, c.cli, c.status, c.cpu, c.rss, c.procs, c.repo, c.cwd, c.scpu, c.srss, c.sprocs, c.self ? " ← this session" : ""));
514
- const kids = rows[i]?.procRows;
515
- if (!showProcs || !kids?.length) return;
516
- const indent = " ".repeat((tree ? (rows[i]?.prefix ?? "").length : 0) + 2);
517
- const pw = Math.max(...kids.map((k) => String(k.pid).length));
518
- const cw = Math.max(...kids.map((k) => k.comm.length));
519
- for (const k of kids) out.push(`${indent}· ${padStart(String(k.pid), pw)} ${pad(k.comm, cw)} ${padStart(k.cpuPercent.toFixed(1), 5)} ${padStart(humanBytes(k.rss), 7)}` + (k.state === "Z" ? " zombie" : ""));
520
- });
521
- return out.join("\n");
522
- }
523
- async function cmdPs(rest) {
524
- const y = yargs(rest).usage("Usage: ay ps [keyword] [options]\n\nPer-agent resource usage, rolled up across each agent's whole process tree\n(wrapper + CLI + pty hosts + subagents). Local-only; use `ay ls` for the\nfleet view and per-agent prompts.").option("all", {
525
- type: "boolean",
526
- default: false,
527
- description: "Include exited agents"
528
- }).option("json", {
529
- type: "boolean",
530
- default: false,
531
- description: "Output as JSON"
532
- }).option("sort", {
533
- type: "string",
534
- choices: [
535
- "rss",
536
- "cpu",
537
- "procs",
538
- "pid"
539
- ],
540
- default: "rss",
541
- description: "Sort rows by resource usage (default: rss, biggest first)"
542
- }).option("tree", {
543
- type: "boolean",
544
- default: false,
545
- description: "Forest order with ├─ rails, plus Σ columns rolling each agent up with its subagents"
546
- }).option("procs", {
547
- type: "boolean",
548
- default: false,
549
- description: "Expand each agent into the OS processes it owns (wrapper, CLI, shells)"
550
- }).option("interval", {
551
- type: "number",
552
- default: DEFAULT_WINDOW_MS / 1e3,
553
- description: "CPU sampling window in seconds — CPU% is a live delta, not a lifetime average"
554
- }).option("cwd", {
555
- type: "string",
556
- description: "Restrict to agents whose cwd starts with dir"
557
- }).option("help", {
558
- alias: "h",
559
- type: "boolean",
560
- default: false,
561
- description: "Show this help"
562
- }).example("ay ps", "biggest agents first, with box vitals").example("ay ps --sort cpu", "who is actually burning CPU right now").example("ay ps --tree", "parent>child forest; Σ columns include each agent's subagents").example("ay ps --procs", "expand each agent into its wrapper/CLI/shell processes").example("ay ps --json", "machine-readable rollup").help(false).version(false).exitProcess(false);
563
- const argv = await y.parseAsync();
564
- if (argv.help || argv.h) {
565
- process.stdout.write(await y.getHelp() + "\n");
566
- return 0;
567
- }
568
- const keyword = argv._[0] !== void 0 ? String(argv._[0]) : void 0;
569
- const records = await listRecords(keyword, {
570
- all: argv.all,
571
- active: false,
572
- json: false,
573
- latest: false,
574
- cwdScope: typeof argv.cwd === "string" ? path.resolve(argv.cwd) : null
575
- });
576
- if (records.length === 0) {
577
- process.stderr.write(keyword ? `no agents matched "${keyword}"\n` : "no running agents\n");
578
- return 0;
579
- }
580
- const flat = flattenForest(buildAgentForest(records));
581
- const ordered = flat.map((r) => r.record);
582
- const windowMs = Math.max(100, (Number.isFinite(argv.interval) ? argv.interval : 1) * 1e3);
583
- const { trees, unattributed, members } = await sampleTrees([...ordered].reverse().map((r) => r.pid), { windowMs });
584
- const states = new Map(await Promise.all(ordered.map(async (r) => [r.pid, (await deriveLiveState(r)).state])));
585
- const selfPid = process.pid;
586
- const rows = ordered.map((r, i) => ({
587
- pid: r.pid,
588
- cli: r.cli,
589
- status: states.get(r.pid) ?? r.status,
590
- cwd: r.cwd,
591
- repo: repoLabel(r.cwd),
592
- stats: trees.get(r.pid) ?? {
593
- pid: r.pid,
594
- rss: 0,
595
- cpuPercent: 0,
596
- procs: 0
597
- },
598
- self: trees.get(r.pid) !== void 0 && isSelfTree(r.pid, selfPid),
599
- prefix: flat[i]?.prefix ?? "",
600
- depth: flat[i]?.depth ?? 0
601
- }));
602
- if (argv.tree) {
603
- const totals = subtreeTotals(rows);
604
- rows.forEach((r, i) => {
605
- r.subtree = totals[i] ?? null;
606
- });
607
- }
608
- if (argv.procs) for (const r of rows) r.procRows = [...members?.get(r.pid) ?? []].sort((x, y) => y.rss - x.rss);
609
- if (!argv.tree) {
610
- const key = String(argv.sort);
611
- rows.sort((a, b) => {
612
- if (key === "cpu") return b.stats.cpuPercent - a.stats.cpuPercent;
613
- if (key === "procs") return b.stats.procs - a.stats.procs;
614
- if (key === "pid") return a.pid - b.pid;
615
- return b.stats.rss - a.stats.rss;
616
- });
617
- }
618
- const sys = await systemStats(await snapshotProcs());
619
- if (argv.json) {
620
- process.stdout.write(JSON.stringify({
621
- system: sys,
622
- agents: rows.map((r) => ({
623
- pid: r.pid,
624
- cli: r.cli,
625
- status: r.status,
626
- cwd: r.cwd,
627
- rssBytes: r.stats.rss,
628
- cpuPercent: Number(r.stats.cpuPercent.toFixed(2)),
629
- procs: r.stats.procs,
630
- self: r.self
631
- })),
632
- unattributed: {
633
- rssBytes: unattributed.rss,
634
- cpuPercent: Number(unattributed.cpuPercent.toFixed(2)),
635
- procs: unattributed.procs
636
- }
637
- }, null, 2) + "\n");
638
- return 0;
639
- }
640
- const header = formatSystemLine(sys);
641
- if (header) process.stdout.write(` ${header}\n\n`);
642
- process.stdout.write(renderTable(rows, unattributed, {
643
- tree: argv.tree === true,
644
- procs: argv.procs === true
645
- }) + "\n");
646
- return 0;
647
- }
648
- /**
649
- * Is `rootPid` the wrapper of the tree this very process lives in?
650
- *
651
- * Walks our own ancestry rather than the snapshot: cheap, and correct even when
652
- * the snapshot raced. Linux-only detail (reads /proc), returns false elsewhere —
653
- * mislabelling the self row is cosmetic.
654
- */
655
- function isSelfTree(rootPid, selfPid, readPpid = readPpidSync, platform = process.platform) {
656
- if (platform !== "linux") return false;
657
- let pid = selfPid;
658
- for (let hops = 0; hops < 64 && pid > 1; hops++) {
659
- if (pid === rootPid) return true;
660
- const ppid = readPpid(pid);
661
- if (ppid === null) return false;
662
- pid = ppid;
663
- }
664
- return false;
665
- }
666
- /**
667
- * ppid out of one `/proc/<pid>/stat` line.
668
- *
669
- * Split from the read so the parsing — the part that can actually be wrong — is
670
- * testable on hosts without /proc. Field 2 is `comm`, wrapped in parens and NOT
671
- * escaped: a process named `foo) bar (baz` is legal, so the fields after it can
672
- * only be found from the LAST `)`, never by splitting on whitespace.
673
- */
674
- function parsePpidFromStat(stat) {
675
- const close = stat.lastIndexOf(")");
676
- if (close < 0) return null;
677
- const ppid = Number(stat.slice(close + 1).trim().split(/\s+/)[1]);
678
- return Number.isFinite(ppid) ? ppid : null;
679
- }
680
- function readPpidSync(pid) {
681
- try {
682
- return parsePpidFromStat(readFileSync(`/proc/${pid}/stat`, "utf8"));
683
- } catch {
684
- return null;
685
- }
686
- }
687
-
688
- //#endregion
689
- export { cmdPs };
690
- //# sourceMappingURL=cmdPs-Bm4JG7hi.js.map
362
+ export { systemStats as i, sampleTrees as n, snapshotProcs as r, humanBytes as t };
363
+ //# sourceMappingURL=procStats-CyD8h5nx.js.map
@@ -93,7 +93,7 @@ async function resolveRemoteSpec(spec) {
93
93
  * The bridge lives for the rest of the process (torn down on `process.exit`).
94
94
  */
95
95
  async function resolveWebrtc(link, keyword, label) {
96
- const { startWebrtcBridge } = await import("./webrtcRemote-H9Hok0_R.js");
96
+ const { startWebrtcBridge } = await import("./webrtcRemote-Bevc5ogS.js");
97
97
  const bridge = await startWebrtcBridge(link);
98
98
  return {
99
99
  url: bridge.baseUrl,
@@ -181,4 +181,4 @@ async function cmdRemote(rest) {
181
181
 
182
182
  //#endregion
183
183
  export { resolveRemoteSpec as a, readRemotes as i, deleteRemoteAlias as n, writeRemoteAlias as o, parseDirectRemoteSpec as r, cmdRemote as t };
184
- //# sourceMappingURL=remotes-b6reVXzo.js.map
184
+ //# sourceMappingURL=remotes-BjsglpjL.js.map
@@ -1,5 +1,5 @@
1
1
  import "./e2e-z1xpwSAg.js";
2
2
  import "./webrtcLink-DLaJfTcB.js";
3
- import { a as resolveRemoteSpec, i as readRemotes, n as deleteRemoteAlias, o as writeRemoteAlias, r as parseDirectRemoteSpec, t as cmdRemote } from "./remotes-b6reVXzo.js";
3
+ import { a as resolveRemoteSpec, i as readRemotes, n as deleteRemoteAlias, o as writeRemoteAlias, r as parseDirectRemoteSpec, t as cmdRemote } from "./remotes-BjsglpjL.js";
4
4
 
5
5
  export { cmdRemote };
@@ -1,4 +1,4 @@
1
- import { i as getInstalledPackage, n as compareVersions } from "./versionChecker-CkO7wYyV.js";
1
+ import { i as getInstalledPackage, n as compareVersions } from "./versionChecker-CWCN4vwm.js";
2
2
  import { execFileSync } from "child_process";
3
3
  import { existsSync, mkdirSync, readdirSync, rmSync, statSync, unlinkSync } from "fs";
4
4
  import { chmod, copyFile } from "fs/promises";
@@ -310,4 +310,4 @@ async function getRustBinary(options = {}) {
310
310
 
311
311
  //#endregion
312
312
  export { gcOldBinaryDirs as a, getDownloadUrl as c, findTrayLauncher as i, getRustBinary as l, findRustBinary as n, getBinDir as o, findSpawnHiddenLauncher as r, getBinaryName as s, downloadBinary as t };
313
- //# sourceMappingURL=rustBinary-BmXzqj5z.js.map
313
+ //# sourceMappingURL=rustBinary-B57oRftk.js.map
@@ -1,4 +1,4 @@
1
- import "./versionChecker-CkO7wYyV.js";
2
- import { a as gcOldBinaryDirs, c as getDownloadUrl, i as findTrayLauncher, l as getRustBinary, n as findRustBinary, o as getBinDir, r as findSpawnHiddenLauncher, s as getBinaryName, t as downloadBinary } from "./rustBinary-BmXzqj5z.js";
1
+ import "./versionChecker-CWCN4vwm.js";
2
+ import { a as gcOldBinaryDirs, c as getDownloadUrl, i as findTrayLauncher, l as getRustBinary, n as findRustBinary, o as getBinDir, r as findSpawnHiddenLauncher, s as getBinaryName, t as downloadBinary } from "./rustBinary-B57oRftk.js";
3
3
 
4
4
  export { gcOldBinaryDirs };
@@ -1,15 +1,15 @@
1
1
  import "./logger-CDIsZ-Pp.js";
2
- import "./versionChecker-CkO7wYyV.js";
3
- import "./ts-DJLp2_bv.js";
2
+ import "./versionChecker-CWCN4vwm.js";
3
+ import "./ts-CSp4mJ7Z.js";
4
4
  import "./todoAutomation-CbVVS4re.js";
5
5
  import "./JsonlStore-BqF7AQ6E.js";
6
6
  import "./globalPidIndex-DoaKEUkO.js";
7
7
  import "./pidStore-B73xnAeT.js";
8
8
  import "./messageLog-DdjBNqgt.js";
9
9
  import "./todoStore-DF6vxHgV.js";
10
- import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-BHowMgMY.js";
11
- import { f as resolveSpawnCwd } from "./workspaceConfig-D6QndOCz.js";
12
- import { n as liveEnv, t as ensureNodeRuntime } from "./nodeRuntime-_zeWDISM.js";
10
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-BlgVWZrM.js";
11
+ import { f as resolveSpawnCwd } from "./workspaceConfig-DTvPtr5j.js";
12
+ import { n as liveEnv, t as ensureNodeRuntime } from "./nodeRuntime-CipjIwfO.js";
13
13
  import { createHash } from "node:crypto";
14
14
 
15
15
  //#region ts/oxmgrService.ts
@@ -154,4 +154,4 @@ async function cmdSchedule(rest) {
154
154
 
155
155
  //#endregion
156
156
  export { cmdSchedule };
157
- //# sourceMappingURL=schedule-DLoUmyJh.js.map
157
+ //# sourceMappingURL=schedule-pim0Izhs.js.map