agent-yes 1.112.0 → 1.113.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-2NKH3XTc.js";
1
+ import { t as CLIS_CONFIG } from "./ts-ChVJOKNr.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-CnoaGhEf.js.map
8
+ //# sourceMappingURL=SUPPORTED_CLIS-BpO7ZYx_.js.map
@@ -0,0 +1,8 @@
1
+ import "./ts-ChVJOKNr.js";
2
+ import "./logger-B9h0djqx.js";
3
+ import "./versionChecker-wxlWV_VT.js";
4
+ import "./pidStore-DBjlqzo8.js";
5
+ import "./globalPidIndex-yVd3mbsV.js";
6
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-BpO7ZYx_.js";
7
+
8
+ export { SUPPORTED_CLIS };
package/dist/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env bun
2
2
  import { n as logger } from "./logger-B9h0djqx.js";
3
- import { i as versionString, n as displayVersion, r as getInstalledPackage, t as checkAndAutoUpdate } from "./versionChecker-CJOmY2Jv.js";
3
+ import { i as versionString, n as displayVersion, r as getInstalledPackage, t as checkAndAutoUpdate } from "./versionChecker-wxlWV_VT.js";
4
4
  import { argv } from "process";
5
5
  import { execFileSync, spawn } from "child_process";
6
6
  import ms from "ms";
@@ -482,7 +482,7 @@ function buildRustArgs(argv, cliFromScript, supportedClis) {
482
482
  {
483
483
  const rawArg = process.argv[2];
484
484
  const isHelpFlag = rawArg === "-h" || rawArg === "--help";
485
- const { isSubcommand, runSubcommand, cmdHelp } = await import("./subcommands-DchgOXt6.js");
485
+ const { isSubcommand, runSubcommand, cmdHelp } = await import("./subcommands-DU5RXU0g.js");
486
486
  if (isHelpFlag && process.argv.length === 3) {
487
487
  cmdHelp();
488
488
  process.exit(0);
@@ -515,7 +515,7 @@ if (config.useRust) {
515
515
  }
516
516
  }
517
517
  if (rustBinary) {
518
- const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-C2WnTHmw.js");
518
+ const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-DtJakX4G.js");
519
519
  const rustArgs = buildRustArgs(process.argv, config.cli, SUPPORTED_CLIS);
520
520
  if (config.verbose) {
521
521
  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-2NKH3XTc.js";
1
+ import { a as removeControlCharacters, i as AgentContext, n as agentYes, r as config, t as CLIS_CONFIG } from "./ts-ChVJOKNr.js";
2
2
  import "./logger-B9h0djqx.js";
3
- import "./versionChecker-CJOmY2Jv.js";
3
+ import "./versionChecker-wxlWV_VT.js";
4
4
  import "./pidStore-DBjlqzo8.js";
5
5
  import "./globalPidIndex-yVd3mbsV.js";
6
6
 
@@ -1,11 +1,11 @@
1
- import "./ts-2NKH3XTc.js";
1
+ import "./ts-ChVJOKNr.js";
2
2
  import "./logger-B9h0djqx.js";
3
- import "./versionChecker-CJOmY2Jv.js";
3
+ import "./versionChecker-wxlWV_VT.js";
4
4
  import "./pidStore-DBjlqzo8.js";
5
5
  import "./globalPidIndex-yVd3mbsV.js";
6
- import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-CnoaGhEf.js";
6
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-BpO7ZYx_.js";
7
7
  import "./remotes-C3xPRtfg.js";
8
- import { c as listRecords, d as renderRawLog, f as resolveOne, g as writeToIpc, m as snapshotStatus, r as controlCodeFromName, u as readNotes } from "./subcommands-4T65cZAF.js";
8
+ import { c as listRecords, d as renderRawLog, f as resolveOne, g as writeToIpc, m as snapshotStatus, r as controlCodeFromName, u as readNotes } from "./subcommands-Za5uwCq6.js";
9
9
  import yargs from "yargs";
10
10
  import { mkdir, open, readFile, writeFile } from "fs/promises";
11
11
  import { homedir, hostname, userInfo } from "os";
@@ -241,6 +241,79 @@ Options:
241
241
  return new Response(e.message, { status: 500 });
242
242
  }
243
243
  }
244
+ if (req.method === "GET" && p === "/api/ls/subscribe") {
245
+ const keyword = url.searchParams.get("keyword") ?? void 0;
246
+ const opts = defaultOpts({
247
+ all: url.searchParams.get("all") === "1",
248
+ active: url.searchParams.get("active") === "1"
249
+ });
250
+ const enc = new TextEncoder();
251
+ const stream = new ReadableStream({ async start(ctrl) {
252
+ let closed = false;
253
+ const send = (obj) => {
254
+ try {
255
+ ctrl.enqueue(enc.encode(`data: ${JSON.stringify(obj)}\n\n`));
256
+ } catch {}
257
+ };
258
+ const sent = /* @__PURE__ */ new Map();
259
+ const compute = async () => {
260
+ const records = await listRecords(keyword, opts);
261
+ return Promise.all(records.map(async (r) => ({
262
+ ...r,
263
+ title: await logTitle(r.log_file)
264
+ })));
265
+ };
266
+ const tick = async (first) => {
267
+ if (closed) return;
268
+ const list = await compute().catch(() => null);
269
+ if (!list) return;
270
+ const upsert = [];
271
+ const seen = /* @__PURE__ */ new Set();
272
+ for (const r of list) {
273
+ seen.add(r.pid);
274
+ const j = JSON.stringify(r);
275
+ if (sent.get(r.pid) !== j) {
276
+ upsert.push(r);
277
+ sent.set(r.pid, j);
278
+ }
279
+ }
280
+ const remove = [];
281
+ for (const pid of sent.keys()) if (!seen.has(pid)) {
282
+ remove.push(pid);
283
+ sent.delete(pid);
284
+ }
285
+ if (first) send({
286
+ full: true,
287
+ upsert: list,
288
+ remove: []
289
+ });
290
+ else if (upsert.length || remove.length) send({
291
+ upsert,
292
+ remove
293
+ });
294
+ };
295
+ await tick(true);
296
+ const timer = setInterval(() => void tick(false), 1e3);
297
+ const heartbeat = setInterval(() => {
298
+ try {
299
+ ctrl.enqueue(enc.encode(": ping\n\n"));
300
+ } catch {}
301
+ }, 15e3);
302
+ req.signal.addEventListener("abort", () => {
303
+ closed = true;
304
+ clearInterval(timer);
305
+ clearInterval(heartbeat);
306
+ try {
307
+ ctrl.close();
308
+ } catch {}
309
+ });
310
+ } });
311
+ return new Response(stream, { headers: {
312
+ "Content-Type": "text/event-stream",
313
+ "Cache-Control": "no-cache",
314
+ Connection: "keep-alive"
315
+ } });
316
+ }
244
317
  if (req.method === "GET" && p === "/api/whoami") {
245
318
  let user = "";
246
319
  try {
@@ -560,4 +633,4 @@ Options:
560
633
 
561
634
  //#endregion
562
635
  export { cmdServe };
563
- //# sourceMappingURL=serve-BmzgHa0i.js.map
636
+ //# sourceMappingURL=serve-YPd9gtRO.js.map
@@ -1,6 +1,6 @@
1
1
  import "./logger-B9h0djqx.js";
2
2
  import "./globalPidIndex-yVd3mbsV.js";
3
3
  import "./remotes-C3xPRtfg.js";
4
- import { a as finalizedLines, c as listRecords, d as renderRawLog, f as resolveOne, g as writeToIpc, h as stopTipForCli, i as cursorAbs, l as matchKeyword, m as snapshotStatus, n as cmdHelp, o as isPidAlive, p as runSubcommand, r as controlCodeFromName, s as isSubcommand, t as GRACEFUL_EXIT_COMMANDS, u as readNotes } from "./subcommands-4T65cZAF.js";
4
+ import { a as finalizedLines, c as listRecords, d as renderRawLog, f as resolveOne, g as writeToIpc, h as stopTipForCli, i as cursorAbs, l as matchKeyword, m as snapshotStatus, n as cmdHelp, o as isPidAlive, p as runSubcommand, r as controlCodeFromName, s as isSubcommand, t as GRACEFUL_EXIT_COMMANDS, u as readNotes } from "./subcommands-Za5uwCq6.js";
5
5
 
6
6
  export { cmdHelp, isSubcommand, runSubcommand };
@@ -163,7 +163,7 @@ async function runSubcommand(argv) {
163
163
  case "restart": return await cmdRestart(rest);
164
164
  case "note": return await cmdNote(rest);
165
165
  case "serve": {
166
- const { cmdServe } = await import("./serve-BmzgHa0i.js");
166
+ const { cmdServe } = await import("./serve-YPd9gtRO.js");
167
167
  return cmdServe(rest);
168
168
  }
169
169
  case "setup": {
@@ -1595,4 +1595,4 @@ async function cmdStatus(rest) {
1595
1595
 
1596
1596
  //#endregion
1597
1597
  export { finalizedLines as a, listRecords as c, renderRawLog as d, resolveOne as f, writeToIpc as g, stopTipForCli as h, cursorAbs as i, matchKeyword as l, snapshotStatus as m, cmdHelp as n, isPidAlive as o, runSubcommand as p, controlCodeFromName as r, isSubcommand as s, GRACEFUL_EXIT_COMMANDS as t, readNotes as u };
1598
- //# sourceMappingURL=subcommands-4T65cZAF.js.map
1598
+ //# sourceMappingURL=subcommands-Za5uwCq6.js.map
@@ -1,5 +1,5 @@
1
1
  import { n as logger, t as addTransport } from "./logger-B9h0djqx.js";
2
- import { r as getInstalledPackage } from "./versionChecker-CJOmY2Jv.js";
2
+ import { r as getInstalledPackage } from "./versionChecker-wxlWV_VT.js";
3
3
  import { n as agentYesHome, t as PidStore } from "./pidStore-DBjlqzo8.js";
4
4
  import { i as shouldUseLock, r as releaseLock, t as acquireLock } from "./runningLock-CJxsoGdb.js";
5
5
  import { i as readGlobalPids } from "./globalPidIndex-yVd3mbsV.js";
@@ -1714,4 +1714,4 @@ function sleep(ms) {
1714
1714
 
1715
1715
  //#endregion
1716
1716
  export { removeControlCharacters as a, AgentContext as i, agentYes as n, config as r, CLIS_CONFIG as t };
1717
- //# sourceMappingURL=ts-2NKH3XTc.js.map
1717
+ //# sourceMappingURL=ts-ChVJOKNr.js.map
@@ -7,7 +7,7 @@ import { fileURLToPath } from "url";
7
7
 
8
8
  //#region package.json
9
9
  var name = "agent-yes";
10
- var version = "1.112.0";
10
+ var version = "1.113.0";
11
11
 
12
12
  //#endregion
13
13
  //#region ts/versionChecker.ts
@@ -221,4 +221,4 @@ async function displayVersion() {
221
221
 
222
222
  //#endregion
223
223
  export { versionString as i, displayVersion as n, getInstalledPackage as r, checkAndAutoUpdate as t };
224
- //# sourceMappingURL=versionChecker-CJOmY2Jv.js.map
224
+ //# sourceMappingURL=versionChecker-wxlWV_VT.js.map
package/lab/ui/index.html CHANGED
@@ -1227,7 +1227,7 @@
1227
1227
  const hasToken = !!localStorage.getItem("ay.localToken");
1228
1228
  const enabled = isLocalhost || hasToken || Object.keys(loadRooms()).length === 0;
1229
1229
  if (enabled && !sources.has(LOCAL)) {
1230
- sources.set(LOCAL, {
1230
+ const s = {
1231
1231
  id: LOCAL,
1232
1232
  host: "local",
1233
1233
  kind: "local",
@@ -1237,8 +1237,11 @@
1237
1237
  tried: true, // no connect phase — polled directly
1238
1238
  devices: new Set(),
1239
1239
  serverCount: 0,
1240
- });
1241
- } else if (!enabled) {
1240
+ };
1241
+ sources.set(LOCAL, s);
1242
+ subscribeSource(s);
1243
+ } else if (!enabled && sources.has(LOCAL)) {
1244
+ unsubscribeSource(sources.get(LOCAL));
1242
1245
  sources.delete(LOCAL);
1243
1246
  }
1244
1247
  }
@@ -1246,30 +1249,87 @@
1246
1249
  // Pull one source's agent list, tagging each row with its origin + a
1247
1250
  // composite key (pids can collide across rooms). Updates the source's live
1248
1251
  // flag, device set, and server count for the rooms panel.
1252
+ // Stamp a source's raw /api/ls records with their origin + composite key
1253
+ // (pids collide across rooms) and recompute the source's device set + server
1254
+ // count for the rooms panel. Pure transform of the source's current records.
1255
+ function stampAgents(s) {
1256
+ s.devices = new Set();
1257
+ const out = [...(s.byPid?.values() || [])].map((e) => {
1258
+ // codehost stamps _host per agent; agent-yes share rooms fall back to
1259
+ // the room's device label (from /api/whoami). Local stays unlabelled
1260
+ // (no deviceLabel) so a single-machine view keeps its clean path-only
1261
+ // identity instead of a blank "@:" prefix.
1262
+ const host = e._host || s.deviceLabel || "";
1263
+ if (host) s.devices.add(host);
1264
+ return { ...e, _room: s.id, _key: s.id + "#" + e.pid, _host: host };
1265
+ });
1266
+ s.serverCount =
1267
+ s.kind === "ch" ? s.client?.hosts().length || 0 : s.devices.size || (s.live ? 1 : 0);
1268
+ s.agents = out;
1269
+ }
1270
+
1271
+ // Replace a source's whole record set (full /api/ls fetch or a stream
1272
+ // snapshot). Resets byPid so later stream deltas diff against it cleanly.
1273
+ function applyFull(s, arr) {
1274
+ s.byPid = new Map((Array.isArray(arr) ? arr : []).map((r) => [r.pid, r]));
1275
+ s.live = true;
1276
+ stampAgents(s);
1277
+ }
1278
+
1279
+ // Apply one stream event: a full snapshot, or an incremental
1280
+ // { upsert:[changed], remove:[gone pids] } delta from /api/ls/subscribe.
1281
+ function applyDelta(s, ev) {
1282
+ if (ev.full) return applyFull(s, ev.upsert);
1283
+ if (!s.byPid) s.byPid = new Map();
1284
+ for (const r of ev.upsert || []) s.byPid.set(r.pid, r);
1285
+ for (const pid of ev.remove || []) s.byPid.delete(pid);
1286
+ s.live = true;
1287
+ stampAgents(s);
1288
+ }
1289
+
1290
+ // Fallback poll for a source that isn't streaming (older host with no
1291
+ // /api/ls/subscribe, or before its first delta lands).
1249
1292
  async function listSource(s) {
1250
1293
  try {
1251
- const arr = await s.tx.fetchJSON("/api/ls?all=1");
1252
- s.live = true;
1253
- s.devices = new Set();
1254
- const out = (Array.isArray(arr) ? arr : []).map((e) => {
1255
- // codehost stamps _host per agent; agent-yes share rooms fall back to
1256
- // the room's device label (from /api/whoami). Local stays unlabelled
1257
- // (no deviceLabel) so a single-machine view keeps its clean path-only
1258
- // identity instead of a blank "@:" prefix.
1259
- const host = e._host || s.deviceLabel || "";
1260
- if (host) s.devices.add(host);
1261
- return { ...e, _room: s.id, _key: s.id + "#" + e.pid, _host: host };
1262
- });
1263
- s.serverCount =
1264
- s.kind === "ch" ? s.client?.hosts().length || 0 : s.devices.size || (s.live ? 1 : 0);
1265
- return out;
1294
+ applyFull(s, await s.tx.fetchJSON("/api/ls?all=1"));
1295
+ return s.agents;
1266
1296
  } catch {
1267
1297
  s.live = false;
1268
1298
  s.serverCount = 0;
1299
+ s.byPid = new Map();
1300
+ s.agents = [];
1269
1301
  return [];
1270
1302
  }
1271
1303
  }
1272
1304
 
1305
+ // Subscribe to a source's throttled delta stream instead of re-polling its
1306
+ // whole list. The first event flips `streaming` on, so the reconcile poll
1307
+ // skips this source — no duplicate full-list bytes on the wire. Older hosts
1308
+ // without the endpoint never send an event, so the poll keeps covering them.
1309
+ function subscribeSource(s) {
1310
+ if (!s.tx || s.unsub) return;
1311
+ s.unsub = s.tx.subscribe(
1312
+ "/api/ls/subscribe?all=1",
1313
+ (ev) => {
1314
+ if (!ev || typeof ev !== "object") return;
1315
+ s.streaming = true;
1316
+ applyDelta(s, ev);
1317
+ mergeRender();
1318
+ },
1319
+ null,
1320
+ () => {
1321
+ s.streaming = false;
1322
+ },
1323
+ );
1324
+ }
1325
+ function unsubscribeSource(s) {
1326
+ try {
1327
+ s.unsub?.();
1328
+ } catch {}
1329
+ s.unsub = null;
1330
+ s.streaming = false;
1331
+ }
1332
+
1273
1333
  // Compact list: one line per agent (dot + cli + title), persisted per device.
1274
1334
  let compactList = localStorage.getItem("ay.compactList") === "1";
1275
1335
 
@@ -1354,10 +1414,23 @@
1354
1414
  // composite key (room#pid) or a bare pid (?pid= deep links, legacy ay.sel).
1355
1415
  const matchSel = (e, token) => e._key === token || String(e.pid) === String(token);
1356
1416
 
1417
+ // Reconcile poll: refresh only the sources that aren't streaming (older
1418
+ // hosts, or ones whose stream hasn't delivered its first snapshot yet) and
1419
+ // re-render. Streaming sources keep themselves current via subscribeSource,
1420
+ // so this stays a no-op on the wire for a modern fleet — it still re-renders
1421
+ // every tick, which keeps the relative "age" column fresh.
1357
1422
  async function loadList() {
1358
1423
  const srcs = [...sources.values()];
1359
- const lists = await Promise.all(srcs.map(listSource));
1360
- entries = lists.flat();
1424
+ await Promise.all(srcs.filter((s) => !s.streaming).map(listSource));
1425
+ mergeRender();
1426
+ }
1427
+
1428
+ // Merge every source's stamped agents into the flat `entries` list, update
1429
+ // the connection badge + rooms panel, and re-render. Called after every poll
1430
+ // and every stream delta.
1431
+ function mergeRender() {
1432
+ const srcs = [...sources.values()];
1433
+ entries = srcs.flatMap((s) => s.agents || []);
1361
1434
  // Badge: total agents + how many rooms are live. Red only when nothing
1362
1435
  // at all is reachable (no source answered).
1363
1436
  const roomSrcs = srcs.filter((s) => s.id !== LOCAL);
@@ -1450,7 +1523,20 @@
1450
1523
  // feed it, so we just surface the latest title as the header name. Falls
1451
1524
  // back to the cli name when the agent never sets one.
1452
1525
  term.onTitleChange((t) => {
1453
- if (sel === e._key && t && t.trim()) $("rname").textContent = t.trim();
1526
+ const title = t && t.trim();
1527
+ if (sel !== e._key || !title) return;
1528
+ $("rname").textContent = title;
1529
+ // Keep the left panel in lockstep with the terminal header. The row
1530
+ // title otherwise only refreshes on the 3s /api/ls poll, so it visibly
1531
+ // lagged the live terminal. Patch the current entry (entries is
1532
+ // replaced wholesale each poll, so look it up by _key, not via the
1533
+ // captured `e`) and re-render — but only when the title actually
1534
+ // changed, so a TUI re-emitting the same OSC doesn't churn the list.
1535
+ const ent = entries.find((x) => x._key === e._key);
1536
+ if (ent && ent.title !== title) {
1537
+ ent.title = title;
1538
+ renderList();
1539
+ }
1454
1540
  });
1455
1541
  // Adapt: drive the agent's PTY to the browser terminal size (POST
1456
1542
  // /api/resize → winsize + SIGWINCH) so its TUI reflows to match what we
@@ -1623,6 +1709,7 @@
1623
1709
  function removeSource(room) {
1624
1710
  const s = sources.get(room);
1625
1711
  if (!s) return;
1712
+ unsubscribeSource(s);
1626
1713
  try {
1627
1714
  s.client?.close?.();
1628
1715
  s.client?.pc?.close?.();
@@ -1690,6 +1777,7 @@
1690
1777
  }
1691
1778
  s.tried = true;
1692
1779
  renderRoomsIfOpen();
1780
+ if (s.live) subscribeSource(s);
1693
1781
  loadList();
1694
1782
  }
1695
1783
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-yes",
3
- "version": "1.112.0",
3
+ "version": "1.113.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
@@ -320,6 +320,93 @@ export async function cmdServe(rest: string[]): Promise<number> {
320
320
  }
321
321
  }
322
322
 
323
+ // GET /api/ls/subscribe — SSE: throttled live deltas of the agent list.
324
+ // The console used to re-poll /api/ls every 3s; this streams the SAME records
325
+ // (incl. each agent's OSC title) but only what CHANGED since the last tick, so
326
+ // an idle fleet costs ~nothing on the wire. The first event is a full snapshot
327
+ // ({ full:true, upsert:[all] }); each later event carries { upsert:[changed
328
+ // records], remove:[gone pids] }. listRecords is a couple of JSONL reads and
329
+ // logTitle is cached by (size,mtime), so the 1s tick stays cheap.
330
+ if (req.method === "GET" && p === "/api/ls/subscribe") {
331
+ const keyword = url.searchParams.get("keyword") ?? undefined;
332
+ const opts = defaultOpts({
333
+ all: url.searchParams.get("all") === "1",
334
+ active: url.searchParams.get("active") === "1",
335
+ });
336
+ const enc = new TextEncoder();
337
+ const stream = new ReadableStream({
338
+ async start(ctrl) {
339
+ let closed = false;
340
+ const send = (obj: unknown) => {
341
+ try {
342
+ ctrl.enqueue(enc.encode(`data: ${JSON.stringify(obj)}\n\n`));
343
+ } catch {
344
+ /* stream already closed */
345
+ }
346
+ };
347
+ // pid -> JSON of the last record we sent, for cheap change detection.
348
+ const sent = new Map<number, string>();
349
+ const compute = async () => {
350
+ const records = await listRecords(keyword, opts);
351
+ return Promise.all(
352
+ records.map(async (r) => ({ ...r, title: await logTitle(r.log_file) })),
353
+ );
354
+ };
355
+ const tick = async (first: boolean) => {
356
+ if (closed) return;
357
+ // Transient read error → skip this tick, retry on the next.
358
+ const list = await compute().catch(() => null);
359
+ if (!list) return;
360
+ const upsert: typeof list = [];
361
+ const seen = new Set<number>();
362
+ for (const r of list) {
363
+ seen.add(r.pid);
364
+ const j = JSON.stringify(r);
365
+ if (sent.get(r.pid) !== j) {
366
+ upsert.push(r);
367
+ sent.set(r.pid, j);
368
+ }
369
+ }
370
+ const remove: number[] = [];
371
+ for (const pid of sent.keys())
372
+ if (!seen.has(pid)) {
373
+ remove.push(pid);
374
+ sent.delete(pid);
375
+ }
376
+ if (first) send({ full: true, upsert: list, remove: [] });
377
+ else if (upsert.length || remove.length) send({ upsert, remove });
378
+ };
379
+
380
+ await tick(true);
381
+ const timer = setInterval(() => void tick(false), 1000);
382
+ const heartbeat = setInterval(() => {
383
+ try {
384
+ ctrl.enqueue(enc.encode(": ping\n\n"));
385
+ } catch {
386
+ /* closed */
387
+ }
388
+ }, 15_000);
389
+ req.signal.addEventListener("abort", () => {
390
+ closed = true;
391
+ clearInterval(timer);
392
+ clearInterval(heartbeat);
393
+ try {
394
+ ctrl.close();
395
+ } catch {
396
+ /* already closed */
397
+ }
398
+ });
399
+ },
400
+ });
401
+ return new Response(stream, {
402
+ headers: {
403
+ "Content-Type": "text/event-stream",
404
+ "Cache-Control": "no-cache",
405
+ Connection: "keep-alive",
406
+ },
407
+ });
408
+ }
409
+
323
410
  // GET /api/whoami — this host's device label (user@host), so a remote
324
411
  // console can tag each agent with the machine it came from. Unlike codehost,
325
412
  // `ay serve --share` carries no per-agent device id; the viewer fetches this
@@ -1,8 +0,0 @@
1
- import "./ts-2NKH3XTc.js";
2
- import "./logger-B9h0djqx.js";
3
- import "./versionChecker-CJOmY2Jv.js";
4
- import "./pidStore-DBjlqzo8.js";
5
- import "./globalPidIndex-yVd3mbsV.js";
6
- import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-CnoaGhEf.js";
7
-
8
- export { SUPPORTED_CLIS };