claude-yes 1.133.0 → 1.134.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.
@@ -1,8 +1,8 @@
1
- import { t as CLIS_CONFIG } from "./ts-Bl0IyBFA.js";
1
+ import { t as CLIS_CONFIG } from "./ts-BU3Lc4kT.js";
2
2
 
3
3
  //#region ts/SUPPORTED_CLIS.ts
4
4
  const SUPPORTED_CLIS = Object.keys(CLIS_CONFIG);
5
5
 
6
6
  //#endregion
7
7
  export { SUPPORTED_CLIS as t };
8
- //# sourceMappingURL=SUPPORTED_CLIS-DrXi-ZoG.js.map
8
+ //# sourceMappingURL=SUPPORTED_CLIS-4rW_joc3.js.map
@@ -0,0 +1,8 @@
1
+ import "./ts-BU3Lc4kT.js";
2
+ import "./logger-CDIsZ-Pp.js";
3
+ import "./versionChecker-BesRo3cR.js";
4
+ import "./pidStore-fqXqTKkh.js";
5
+ import "./globalPidIndex-DlmmJlO8.js";
6
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-4rW_joc3.js";
7
+
8
+ export { SUPPORTED_CLIS };
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env bun
2
2
  import { t as invokedCliName } from "./invokedCli-zdFbz1ST.js";
3
3
  import { n as logger } from "./logger-CDIsZ-Pp.js";
4
- import { i as versionString, n as displayVersion, r as getInstalledPackage, t as checkAndAutoUpdate } from "./versionChecker-NEubo1N6.js";
4
+ import { i as versionString, n as displayVersion, r as getInstalledPackage, t as checkAndAutoUpdate } from "./versionChecker-BesRo3cR.js";
5
5
  import { argv } from "process";
6
6
  import { execFileSync, spawn } from "child_process";
7
7
  import ms from "ms";
@@ -480,7 +480,7 @@ function buildRustArgs(argv, cliFromScript, supportedClis) {
480
480
  const rawArg = process.argv[2];
481
481
  const managerCommands = !invokedCliName(process.argv);
482
482
  const isHelpFlag = rawArg === "-h" || rawArg === "--help";
483
- const { isSubcommand, runSubcommand, cmdHelp } = await import("./subcommands-BF417PZx.js");
483
+ const { isSubcommand, runSubcommand, cmdHelp } = await import("./subcommands-CBJ9mwmE.js");
484
484
  if (isHelpFlag && process.argv.length === 3) {
485
485
  cmdHelp(managerCommands);
486
486
  process.exit(0);
@@ -513,7 +513,7 @@ if (config.useRust) {
513
513
  }
514
514
  }
515
515
  if (rustBinary) {
516
- const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-K_KExe0m.js");
516
+ const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-DBUDhKCV.js");
517
517
  const rustArgs = buildRustArgs(process.argv, config.cli, SUPPORTED_CLIS);
518
518
  if (config.verbose) {
519
519
  console.log(`[rust] Using binary: ${rustBinary}`);
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
- import { a as removeControlCharacters, i as AgentContext, n as agentYes, r as config, t as CLIS_CONFIG } from "./ts-Bl0IyBFA.js";
1
+ import { a as removeControlCharacters, i as AgentContext, n as agentYes, r as config, t as CLIS_CONFIG } from "./ts-BU3Lc4kT.js";
2
2
  import "./logger-CDIsZ-Pp.js";
3
- import "./versionChecker-NEubo1N6.js";
3
+ import "./versionChecker-BesRo3cR.js";
4
4
  import "./pidStore-fqXqTKkh.js";
5
5
  import "./globalPidIndex-DlmmJlO8.js";
6
6
 
@@ -1,9 +1,9 @@
1
- import "./ts-Bl0IyBFA.js";
1
+ import "./ts-BU3Lc4kT.js";
2
2
  import "./logger-CDIsZ-Pp.js";
3
- import "./versionChecker-NEubo1N6.js";
3
+ import "./versionChecker-BesRo3cR.js";
4
4
  import "./pidStore-fqXqTKkh.js";
5
5
  import "./globalPidIndex-DlmmJlO8.js";
6
- import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-DrXi-ZoG.js";
6
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-4rW_joc3.js";
7
7
  import { n as resolveSpawnCwd } from "./workspaceConfig-BCOqRBEW.js";
8
8
  import { createHash } from "node:crypto";
9
9
 
@@ -141,4 +141,4 @@ async function cmdSchedule(rest) {
141
141
 
142
142
  //#endregion
143
143
  export { cmdSchedule };
144
- //# sourceMappingURL=schedule-CxWiM9Gh.js.map
144
+ //# sourceMappingURL=schedule-DJXNTh4s.js.map
@@ -1,13 +1,13 @@
1
- import "./ts-Bl0IyBFA.js";
1
+ import "./ts-BU3Lc4kT.js";
2
2
  import "./logger-CDIsZ-Pp.js";
3
- import { r as getInstalledPackage } from "./versionChecker-NEubo1N6.js";
3
+ import { r as getInstalledPackage } from "./versionChecker-BesRo3cR.js";
4
4
  import "./pidStore-fqXqTKkh.js";
5
5
  import { a as updateGlobalPidStatus } from "./globalPidIndex-DlmmJlO8.js";
6
6
  import { t as pgidForWrapper } from "./reaper-C-eWAxIj.js";
7
7
  import "./configShared-C1C04bbq.js";
8
- import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-DrXi-ZoG.js";
8
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-4rW_joc3.js";
9
9
  import "./remotes-PKKjfTI1.js";
10
- import { d as listRecords, g as resolveOne, i as controlCodeFromName, m as renderRawLog, p as readNotes, s as extractTaskCounts, x as writeToIpc, y as snapshotStatus } from "./subcommands-DkjG64ez.js";
10
+ import { d as listRecords, g as resolveOne, i as controlCodeFromName, m as renderRawLog, p as readNotes, s as extractTaskCounts, x as writeToIpc, y as snapshotStatus } from "./subcommands-DroLVgke.js";
11
11
  import yargs from "yargs";
12
12
  import { mkdir, open, readFile, stat, writeFile } from "fs/promises";
13
13
  import { homedir, hostname, userInfo } from "os";
@@ -657,6 +657,8 @@ Options:
657
657
  git: r.status === "exited" ? null : await gitStatus(r.cwd),
658
658
  tasks: r.status === "exited" ? null : await logTasks(r.log_file)
659
659
  });
660
+ const presence = /* @__PURE__ */ new Map();
661
+ const PRESENCE_TTL_MS = 12e3;
660
662
  const apiFetch = async (req) => {
661
663
  if (!checkAuth(req, token)) return new Response("Unauthorized", { status: 401 });
662
664
  const url = new URL(req.url);
@@ -985,6 +987,33 @@ Options:
985
987
  return new Response(e.message, { status: 404 });
986
988
  }
987
989
  }
990
+ if (req.method === "POST" && p === "/api/presence") {
991
+ let b;
992
+ try {
993
+ b = await req.json();
994
+ } catch {
995
+ return new Response("invalid JSON body", { status: 400 });
996
+ }
997
+ const viewer = String(b.viewer ?? "").slice(0, 64);
998
+ if (!viewer) return new Response("missing viewer", { status: 400 });
999
+ if (b.agent == null) presence.delete(viewer);
1000
+ else presence.set(viewer, {
1001
+ viewer,
1002
+ agent: String(b.agent),
1003
+ cols: Math.max(0, Math.floor(Number(b.cols) || 0)),
1004
+ rows: Math.max(0, Math.floor(Number(b.rows) || 0)),
1005
+ sel: typeof b.sel === "string" ? b.sel.slice(0, 200) : null,
1006
+ ts: Date.now()
1007
+ });
1008
+ return new Response(null, { status: 204 });
1009
+ }
1010
+ if (req.method === "GET" && p === "/api/presence") {
1011
+ const now = Date.now();
1012
+ const live = [];
1013
+ for (const [k, v] of presence) if (now - v.ts > PRESENCE_TTL_MS) presence.delete(k);
1014
+ else live.push(v);
1015
+ return Response.json(live);
1016
+ }
988
1017
  if (req.method === "POST" && p === "/api/spawn") {
989
1018
  let body;
990
1019
  try {
@@ -1142,4 +1171,4 @@ Options:
1142
1171
 
1143
1172
  //#endregion
1144
1173
  export { cmdServe };
1145
- //# sourceMappingURL=serve-BVd4fj_7.js.map
1174
+ //# sourceMappingURL=serve-Cy1uIqfR.js.map
@@ -32,7 +32,7 @@ async function cmdSetup(rest) {
32
32
  if (!existsSync(abs)) process.stderr.write(` note: that directory doesn't exist yet — create it, or agents spawned there will fail\n`);
33
33
  if (noShare) return 0;
34
34
  process.stdout.write(`\nsharing this machine to agent-yes.com…\n`);
35
- const { cmdServe } = await import("./serve-BVd4fj_7.js");
35
+ const { cmdServe } = await import("./serve-Cy1uIqfR.js");
36
36
  return cmdServe([
37
37
  "install",
38
38
  "--share",
@@ -42,4 +42,4 @@ async function cmdSetup(rest) {
42
42
 
43
43
  //#endregion
44
44
  export { cmdSetup };
45
- //# sourceMappingURL=setup-C8qDU1KQ.js.map
45
+ //# sourceMappingURL=setup-ILLEDXVy.js.map
@@ -2,6 +2,6 @@ import "./logger-CDIsZ-Pp.js";
2
2
  import "./globalPidIndex-DlmmJlO8.js";
3
3
  import "./configShared-C1C04bbq.js";
4
4
  import "./remotes-PKKjfTI1.js";
5
- import { _ as resolveReadWindow, a as cursorAbs, b as stopTipForCli, c as finalizedLines, d as listRecords, f as matchKeyword, g as resolveOne, h as renderRawLogLines, i as controlCodeFromName, l as isPidAlive, m as renderRawLog, n as READ_PAGE_DEFAULT, o as extractNeedsInput, p as readNotes, r as cmdHelp, s as extractTaskCounts, t as GRACEFUL_EXIT_COMMANDS, u as isSubcommand, v as runSubcommand, x as writeToIpc, y as snapshotStatus } from "./subcommands-DkjG64ez.js";
5
+ import { _ as resolveReadWindow, a as cursorAbs, b as stopTipForCli, c as finalizedLines, d as listRecords, f as matchKeyword, g as resolveOne, h as renderRawLogLines, i as controlCodeFromName, l as isPidAlive, m as renderRawLog, n as READ_PAGE_DEFAULT, o as extractNeedsInput, p as readNotes, r as cmdHelp, s as extractTaskCounts, t as GRACEFUL_EXIT_COMMANDS, u as isSubcommand, v as runSubcommand, x as writeToIpc, y as snapshotStatus } from "./subcommands-DroLVgke.js";
6
6
 
7
7
  export { cmdHelp, isSubcommand, runSubcommand };
@@ -533,15 +533,15 @@ async function runSubcommand(argv) {
533
533
  case "restart": return await cmdRestart(rest);
534
534
  case "note": return await cmdNote(rest);
535
535
  case "serve": {
536
- const { cmdServe } = await import("./serve-BVd4fj_7.js");
536
+ const { cmdServe } = await import("./serve-Cy1uIqfR.js");
537
537
  return cmdServe(rest);
538
538
  }
539
539
  case "setup": {
540
- const { cmdSetup } = await import("./setup-C8qDU1KQ.js");
540
+ const { cmdSetup } = await import("./setup-ILLEDXVy.js");
541
541
  return cmdSetup(rest);
542
542
  }
543
543
  case "schedule": {
544
- const { cmdSchedule } = await import("./schedule-CxWiM9Gh.js");
544
+ const { cmdSchedule } = await import("./schedule-DJXNTh4s.js");
545
545
  return cmdSchedule(rest);
546
546
  }
547
547
  case "remote": {
@@ -2428,4 +2428,4 @@ async function cmdResultSet(rest) {
2428
2428
 
2429
2429
  //#endregion
2430
2430
  export { resolveReadWindow as _, cursorAbs as a, stopTipForCli as b, finalizedLines as c, listRecords as d, matchKeyword as f, resolveOne as g, renderRawLogLines as h, controlCodeFromName as i, isPidAlive as l, renderRawLog as m, READ_PAGE_DEFAULT as n, extractNeedsInput as o, readNotes as p, cmdHelp as r, extractTaskCounts as s, GRACEFUL_EXIT_COMMANDS as t, isSubcommand as u, runSubcommand as v, writeToIpc as x, snapshotStatus as y };
2431
- //# sourceMappingURL=subcommands-DkjG64ez.js.map
2431
+ //# sourceMappingURL=subcommands-DroLVgke.js.map
@@ -1,5 +1,5 @@
1
1
  import { n as logger, t as addTransport } from "./logger-CDIsZ-Pp.js";
2
- import { r as getInstalledPackage } from "./versionChecker-NEubo1N6.js";
2
+ import { r as getInstalledPackage } from "./versionChecker-BesRo3cR.js";
3
3
  import { t as agentYesHome } from "./agentYesHome-_eJa3DaX.js";
4
4
  import { i as shouldUseLock, r as releaseLock, t as acquireLock } from "./runningLock-V4qvXgAw.js";
5
5
  import { t as PidStore } from "./pidStore-fqXqTKkh.js";
@@ -1788,4 +1788,4 @@ function sleep(ms) {
1788
1788
 
1789
1789
  //#endregion
1790
1790
  export { removeControlCharacters as a, AgentContext as i, agentYes as n, config as r, CLIS_CONFIG as t };
1791
- //# sourceMappingURL=ts-Bl0IyBFA.js.map
1791
+ //# sourceMappingURL=ts-BU3Lc4kT.js.map
@@ -7,7 +7,7 @@ import { fileURLToPath } from "url";
7
7
 
8
8
  //#region package.json
9
9
  var name = "claude-yes";
10
- var version = "1.133.0";
10
+ var version = "1.134.0";
11
11
 
12
12
  //#endregion
13
13
  //#region ts/versionChecker.ts
@@ -215,4 +215,4 @@ async function displayVersion() {
215
215
 
216
216
  //#endregion
217
217
  export { versionString as i, displayVersion as n, getInstalledPackage as r, checkAndAutoUpdate as t };
218
- //# sourceMappingURL=versionChecker-NEubo1N6.js.map
218
+ //# sourceMappingURL=versionChecker-BesRo3cR.js.map
package/lab/ui/index.html CHANGED
@@ -1387,12 +1387,11 @@
1387
1387
  let es = null; // live-tail subscription closer
1388
1388
  let term = null; // xterm.js Terminal rendering the raw PTY stream
1389
1389
  let fit = null;
1390
- // Re-fit the open terminal and re-assert its size to the agent's PTY. Set per
1391
- // selection in select(); invoked on open AND whenever the tab is re-activated.
1392
- // A backgrounded tab can miss window resizes, and another viewer may have
1393
- // resized the shared PTY while we were away either leaves our stream
1394
- // rendering at the wrong width/height until something nudges it. null when no
1395
- // agent is open.
1390
+ // One-shot on OPEN (select): fit the terminal to our pane, then adopt — push
1391
+ // our size to the agent's PTY so its TUI reflows to match what we render.
1392
+ // Set per selection in select(); null when no agent is open. NOT re-run on
1393
+ // tab re-activation (that eager re-push fought a shared PTY and reflowed the
1394
+ // stream on every focus); the visibilitychange hook does a local re-fit only.
1396
1395
  let resyncTerm = null;
1397
1396
  // Terminal font size (px) — adjustable from the ⋯ menu, persisted across
1398
1397
  // reloads, applied live to the open terminal and used by every new one.
@@ -2387,12 +2386,27 @@
2387
2386
  const traffic =
2388
2387
  `<div class="ctip-traf"><span class="ctip-k">i/o</span>${sparkSvg(traf.hist, 96, 22) || "—"}</div>` +
2389
2388
  `<div><span class="ctip-k"></span>↓ ${fmtBytes(last.o)}/s&nbsp;&nbsp;↑ ${fmtBytes(last.i)}/s</div>`;
2389
+ const viewers = presenceHtml();
2390
2390
  return (
2391
2391
  `<div class="ctip-sec">${vp}</div>` +
2392
2392
  `<div class="ctip-sec">${capHtml}</div>` +
2393
- `<div class="ctip-sec">${traffic}</div>`
2393
+ `<div class="ctip-sec">${traffic}</div>` +
2394
+ (viewers ? `<div class="ctip-sec">${viewers}</div>` : "")
2394
2395
  );
2395
2396
  }
2397
+ // Other viewers watching THIS agent (we exclude ourselves) + their viewports.
2398
+ function presenceHtml() {
2399
+ const others = presencePeers;
2400
+ if (!others.length) return "";
2401
+ const head = `<div><span class="ctip-k">viewers</span>👁 ${others.length + 1}&nbsp;<span style="color:var(--muted)">(you + ${others.length})</span></div>`;
2402
+ const list = others
2403
+ .map(
2404
+ (v) =>
2405
+ `<div><span class="ctip-k"></span>${esc((v.cols || "?") + "×" + (v.rows || "?"))}${v.sel ? " · sel" : ""}</div>`,
2406
+ )
2407
+ .join("");
2408
+ return head + list;
2409
+ }
2396
2410
  // Always-on: sample the I/O ring every second and, while an agent is open,
2397
2411
  // repaint the badge so its hover tooltip (sparkline + caps + sizes) stays live.
2398
2412
  setInterval(() => {
@@ -2402,6 +2416,71 @@
2402
2416
  if (sel) paintHeaderBadge();
2403
2417
  }, 1000);
2404
2418
 
2419
+ // ---- multi-peer presence: report which agent we're watching + our viewport
2420
+ // to the host, and read back the OTHER viewers, so the badge can show how many
2421
+ // peers look at the same agent and at what size. Cosmetic; the host's
2422
+ // /api/presence blackboard TTLs entries (hosts on an older `ay serve` just 404
2423
+ // → we degrade to no viewers). Keyed by a per-tab viewer id so switching agents
2424
+ // moves our single entry rather than stacking up.
2425
+ const myViewerId = (() => {
2426
+ try {
2427
+ let v = sessionStorage.getItem("ay.viewer");
2428
+ if (!v) {
2429
+ v = Math.random().toString(36).slice(2, 10);
2430
+ sessionStorage.setItem("ay.viewer", v);
2431
+ }
2432
+ return v;
2433
+ } catch {
2434
+ return Math.random().toString(36).slice(2, 10);
2435
+ }
2436
+ })();
2437
+ let presencePeers = []; // others watching the SELECTED agent: [{cols,rows,sel}]
2438
+ function presenceTarget() {
2439
+ const e = sel ? entries.find((x) => x._key === sel) : null;
2440
+ const src = e ? srcFor(e) : null;
2441
+ return e && src && src.tx ? { e, tx: src.tx } : null;
2442
+ }
2443
+ function sendPresence() {
2444
+ const cur = presenceTarget();
2445
+ if (!cur || !term) return;
2446
+ let selR = null;
2447
+ try {
2448
+ const s = term.getSelectionPosition && term.getSelectionPosition();
2449
+ if (s) selR = `${s.startRow},${s.startColumn}-${s.endRow},${s.endColumn}`;
2450
+ } catch {}
2451
+ cur.tx
2452
+ .post("/api/presence", {
2453
+ viewer: myViewerId,
2454
+ agent: cur.e.pid,
2455
+ cols: term.cols,
2456
+ rows: term.rows,
2457
+ sel: selR,
2458
+ })
2459
+ .catch(() => {});
2460
+ }
2461
+ async function pollPresence() {
2462
+ const cur = presenceTarget();
2463
+ if (!cur) {
2464
+ presencePeers = [];
2465
+ return;
2466
+ }
2467
+ try {
2468
+ const all = await cur.tx.fetchJSON("/api/presence");
2469
+ presencePeers = (Array.isArray(all) ? all : []).filter(
2470
+ (v) => String(v.agent) === String(cur.e.pid) && v.viewer !== myViewerId,
2471
+ );
2472
+ } catch {
2473
+ presencePeers = [];
2474
+ }
2475
+ }
2476
+ // 3s heartbeat (< the host's 12s TTL): refresh our presence + read others'.
2477
+ setInterval(() => {
2478
+ if (sel) {
2479
+ sendPresence();
2480
+ pollPresence();
2481
+ }
2482
+ }, 3000);
2483
+
2405
2484
  // Wire the ⋯ overflow menu and restore the saved HUD preference.
2406
2485
  // Recovery actions for the SELECTED agent, both confirmed first (destructive):
2407
2486
  // Stop — graceful: send the CLI's exit command (claude/codex /exit, gemini
@@ -2956,6 +3035,8 @@
2956
3035
  // and the remembered agent winsize so the badge tooltip doesn't show stale data.
2957
3036
  trafReset();
2958
3037
  agentSize = null;
3038
+ presencePeers = []; // clear other-viewer list until the next poll for this agent
3039
+ sendPresence(); // announce we're now watching this agent (host TTLs the prior one)
2959
3040
  paintHeaderBadge();
2960
3041
  // Remember the selection so a refresh re-opens this agent (see boot/autoPid).
2961
3042
  try {
@@ -3883,11 +3964,19 @@
3883
3964
  if (document.visibilityState === "visible") {
3884
3965
  lastActivity = Date.now();
3885
3966
  loadList();
3886
- // Re-assert our viewport size to the open agent: while we were away the
3887
- // window may have resized (a hidden tab can miss it) or another viewer
3888
- // may have resized the shared PTY, so the stream could be rendering at
3889
- // the wrong width/height. fit + push-if-different brings it back.
3890
- resyncTerm?.();
3967
+ // Re-FIT our pane locally in case the window resized while this tab was
3968
+ // hidden (a backgrounded tab can miss window 'resize'). A genuine size
3969
+ // change then propagates to the agent via term.onResize pushSize.
3970
+ // We deliberately do NOT force-re-assert our size to the agent on every
3971
+ // focus: for a shared PTY (a user-spawned agent attached to a local
3972
+ // terminal of a different size) that re-push fights the local terminal
3973
+ // and reflows the stream on every tab switch — the glitch the eager
3974
+ // per-focus resync introduced. The one-shot adopt still runs on select.
3975
+ if (term && fit) {
3976
+ try {
3977
+ fit.fit();
3978
+ } catch {}
3979
+ }
3891
3980
  }
3892
3981
  });
3893
3982
  watchVersion();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-yes",
3
- "version": "1.133.0",
3
+ "version": "1.134.0",
4
4
  "description": "A wrapper tool that automates interactions with various AI CLI tools by automatically handling common prompts and responses.",
5
5
  "keywords": [
6
6
  "ai",
package/ts/serve.ts CHANGED
@@ -942,6 +942,15 @@ export async function cmdServe(rest: string[]): Promise<number> {
942
942
  tasks: r.status === "exited" ? null : await logTasks(r.log_file),
943
943
  });
944
944
 
945
+ // Multi-peer presence blackboard: viewerId -> what that viewer is watching +
946
+ // its viewport/selection. Purely cosmetic ("who else is looking at this agent"),
947
+ // never a security surface — viewers self-report. Pruned by TTL on read.
948
+ const presence = new Map<
949
+ string,
950
+ { viewer: string; agent: string; cols: number; rows: number; sel: string | null; ts: number }
951
+ >();
952
+ const PRESENCE_TTL_MS = 12_000;
953
+
945
954
  // The whole API as a plain handler: served over HTTP by Bun.serve (--http)
946
955
  // and called in-process by the WebRTC bridge (--webrtc) — the latter needs
947
956
  // no TCP port at all.
@@ -1375,6 +1384,46 @@ export async function cmdServe(rest: string[]): Promise<number> {
1375
1384
  }
1376
1385
  }
1377
1386
 
1387
+ // POST /api/presence body {viewer, agent, cols, rows, sel?} — a viewer
1388
+ // self-reports which agent it's watching + its viewport (agent=null clears).
1389
+ if (req.method === "POST" && p === "/api/presence") {
1390
+ let b: {
1391
+ viewer?: string;
1392
+ agent?: string | number | null;
1393
+ cols?: number;
1394
+ rows?: number;
1395
+ sel?: string;
1396
+ };
1397
+ try {
1398
+ b = (await req.json()) as typeof b;
1399
+ } catch {
1400
+ return new Response("invalid JSON body", { status: 400 });
1401
+ }
1402
+ const viewer = String(b.viewer ?? "").slice(0, 64);
1403
+ if (!viewer) return new Response("missing viewer", { status: 400 });
1404
+ if (b.agent == null) presence.delete(viewer);
1405
+ else
1406
+ presence.set(viewer, {
1407
+ viewer,
1408
+ agent: String(b.agent),
1409
+ cols: Math.max(0, Math.floor(Number(b.cols) || 0)),
1410
+ rows: Math.max(0, Math.floor(Number(b.rows) || 0)),
1411
+ sel: typeof b.sel === "string" ? b.sel.slice(0, 200) : null,
1412
+ ts: Date.now(),
1413
+ });
1414
+ return new Response(null, { status: 204 });
1415
+ }
1416
+ // GET /api/presence — all live viewers (TTL-pruned), for "who's watching".
1417
+ if (req.method === "GET" && p === "/api/presence") {
1418
+ const now = Date.now();
1419
+ const live: unknown[] = [];
1420
+ for (const [k, v] of presence) {
1421
+ if (now - v.ts > PRESENCE_TTL_MS) presence.delete(k);
1422
+ else live.push(v);
1423
+ }
1424
+ return Response.json(live);
1425
+ }
1426
+
1378
1427
  // POST /api/spawn body {cli, cwd, prompt} — launch a new agent
1379
1428
  if (req.method === "POST" && p === "/api/spawn") {
1380
1429
  let body: { cli?: string; cwd?: string; prompt?: string };
@@ -1,8 +0,0 @@
1
- import "./ts-Bl0IyBFA.js";
2
- import "./logger-CDIsZ-Pp.js";
3
- import "./versionChecker-NEubo1N6.js";
4
- import "./pidStore-fqXqTKkh.js";
5
- import "./globalPidIndex-DlmmJlO8.js";
6
- import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-DrXi-ZoG.js";
7
-
8
- export { SUPPORTED_CLIS };