claude-yes 1.170.1 → 1.172.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.
- package/dist/{SUPPORTED_CLIS-Bz-oXKG8.js → SUPPORTED_CLIS-KMkjT-Ny.js} +2 -2
- package/dist/SUPPORTED_CLIS-oB5KxI57.js +8 -0
- package/dist/cli.js +4 -4
- package/dist/index.js +2 -2
- package/dist/{notifyDaemon-BB08sE7n.js → notifyDaemon-tmytSfnS.js} +2 -2
- package/dist/{rustBinary-CYS7148m.js → rustBinary-Bne6M0ik.js} +2 -2
- package/dist/{schedule-D4KvBh5w.js → schedule-BrpCaCuz.js} +4 -4
- package/dist/{serve-BN0mUK9m.js → serve-DvO4pj5p.js} +25 -6
- package/dist/{setup-CUrjwXNE.js → setup-DB2gMtqk.js} +2 -2
- package/dist/{subcommands-DOnqoHyP.js → subcommands-DZtrr8ps.js} +1 -1
- package/dist/{subcommands-CbcBfInM.js → subcommands-ZjESAcio.js} +6 -6
- package/dist/{ts-kJRKPdFi.js → ts-DiMpa4ri.js} +2 -2
- package/dist/{versionChecker-eVbxzEL3.js → versionChecker-D6Qc6O3F.js} +2 -2
- package/lab/ui/console-logic.js +58 -7
- package/lab/ui/index.html +173 -3
- package/package.json +1 -1
- package/ts/serve.ts +54 -0
- package/dist/SUPPORTED_CLIS-Dby-8X2S.js +0 -8
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { t as CLIS_CONFIG } from "./ts-
|
|
1
|
+
import { t as CLIS_CONFIG } from "./ts-DiMpa4ri.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-
|
|
8
|
+
//# sourceMappingURL=SUPPORTED_CLIS-KMkjT-Ny.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import "./ts-DiMpa4ri.js";
|
|
2
|
+
import "./logger-CDIsZ-Pp.js";
|
|
3
|
+
import "./versionChecker-D6Qc6O3F.js";
|
|
4
|
+
import "./pidStore-BIvsBQ8X.js";
|
|
5
|
+
import "./globalPidIndex-CoNr7tS8.js";
|
|
6
|
+
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-KMkjT-Ny.js";
|
|
7
|
+
|
|
8
|
+
export { SUPPORTED_CLIS };
|
package/dist/cli.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import { t as invokedCliName } from "./invokedCli-uqM2YYA7.js";
|
|
3
3
|
import { n as logger } from "./logger-CDIsZ-Pp.js";
|
|
4
|
-
import { i as versionString, n as displayVersion, t as checkAndAutoUpdate } from "./versionChecker-
|
|
5
|
-
import { n as getRustBinary } from "./rustBinary-
|
|
4
|
+
import { i as versionString, n as displayVersion, t as checkAndAutoUpdate } from "./versionChecker-D6Qc6O3F.js";
|
|
5
|
+
import { n as getRustBinary } from "./rustBinary-Bne6M0ik.js";
|
|
6
6
|
import { argv } from "process";
|
|
7
7
|
import { spawn } from "child_process";
|
|
8
8
|
import ms from "ms";
|
|
@@ -285,7 +285,7 @@ function buildRustArgs(argv, cliFromScript, supportedClis) {
|
|
|
285
285
|
const rawArg = process.argv[2];
|
|
286
286
|
const managerCommands = !invokedCliName(process.argv);
|
|
287
287
|
const isHelpFlag = rawArg === "-h" || rawArg === "--help";
|
|
288
|
-
const { isSubcommand, runSubcommand, cmdHelp } = await import("./subcommands-
|
|
288
|
+
const { isSubcommand, runSubcommand, cmdHelp } = await import("./subcommands-DZtrr8ps.js");
|
|
289
289
|
if (isHelpFlag && process.argv.length === 3) {
|
|
290
290
|
await cmdHelp(managerCommands);
|
|
291
291
|
process.exit(0);
|
|
@@ -327,7 +327,7 @@ if (config.useRust) {
|
|
|
327
327
|
}
|
|
328
328
|
}
|
|
329
329
|
if (rustBinary) {
|
|
330
|
-
const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-
|
|
330
|
+
const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-oB5KxI57.js");
|
|
331
331
|
const rustArgs = buildRustArgs(process.argv, config.cli, SUPPORTED_CLIS);
|
|
332
332
|
if (config.verbose) {
|
|
333
333
|
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-
|
|
1
|
+
import { a as removeControlCharacters, i as AgentContext, n as agentYes, r as config, t as CLIS_CONFIG } from "./ts-DiMpa4ri.js";
|
|
2
2
|
import "./logger-CDIsZ-Pp.js";
|
|
3
|
-
import "./versionChecker-
|
|
3
|
+
import "./versionChecker-D6Qc6O3F.js";
|
|
4
4
|
import "./pidStore-BIvsBQ8X.js";
|
|
5
5
|
import "./globalPidIndex-CoNr7tS8.js";
|
|
6
6
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { n as logger } from "./logger-CDIsZ-Pp.js";
|
|
2
2
|
import "./globalPidIndex-CoNr7tS8.js";
|
|
3
3
|
import "./configShared-0MnIQ652.js";
|
|
4
|
-
import { D as renderLogTailLines, G as hostId, J as readInbox, K as listInboxParents, Q as notifyDir, S as listRecords, U as appendEvent, W as gcInboxes, X as daemonLockDir, Y as shouldStealLock, Z as daemonLockOwnerPath, _ as isPidAlive, c as deriveLiveState, q as liveWatchers } from "./subcommands-
|
|
4
|
+
import { D as renderLogTailLines, G as hostId, J as readInbox, K as listInboxParents, Q as notifyDir, S as listRecords, U as appendEvent, W as gcInboxes, X as daemonLockDir, Y as shouldStealLock, Z as daemonLockOwnerPath, _ as isPidAlive, c as deriveLiveState, q as liveWatchers } from "./subcommands-ZjESAcio.js";
|
|
5
5
|
import "./e2e-jb0Hp43q.js";
|
|
6
6
|
import "./webrtcLink-B7REGtK2.js";
|
|
7
7
|
import "./remotes-CC-4GuJb.js";
|
|
@@ -588,4 +588,4 @@ async function ensureDaemon() {
|
|
|
588
588
|
|
|
589
589
|
//#endregion
|
|
590
590
|
export { daemonStatus, ensureDaemon, requestDaemonStop, runDaemon };
|
|
591
|
-
//# sourceMappingURL=notifyDaemon-
|
|
591
|
+
//# sourceMappingURL=notifyDaemon-tmytSfnS.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r as getInstalledPackage } from "./versionChecker-
|
|
1
|
+
import { r as getInstalledPackage } from "./versionChecker-D6Qc6O3F.js";
|
|
2
2
|
import { execFileSync } from "child_process";
|
|
3
3
|
import { existsSync, mkdirSync, unlinkSync } from "fs";
|
|
4
4
|
import { chmod, copyFile } from "fs/promises";
|
|
@@ -225,4 +225,4 @@ async function getRustBinary(options = {}) {
|
|
|
225
225
|
|
|
226
226
|
//#endregion
|
|
227
227
|
export { getRustBinary as n, findSpawnHiddenLauncher as t };
|
|
228
|
-
//# sourceMappingURL=rustBinary-
|
|
228
|
+
//# sourceMappingURL=rustBinary-Bne6M0ik.js.map
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import "./ts-
|
|
1
|
+
import "./ts-DiMpa4ri.js";
|
|
2
2
|
import "./logger-CDIsZ-Pp.js";
|
|
3
|
-
import "./versionChecker-
|
|
3
|
+
import "./versionChecker-D6Qc6O3F.js";
|
|
4
4
|
import "./pidStore-BIvsBQ8X.js";
|
|
5
5
|
import "./globalPidIndex-CoNr7tS8.js";
|
|
6
|
-
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-
|
|
6
|
+
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-KMkjT-Ny.js";
|
|
7
7
|
import { d as resolveSpawnCwd } from "./workspaceConfig-BgqK-31W.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-
|
|
144
|
+
//# sourceMappingURL=schedule-BrpCaCuz.js.map
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import "./ts-
|
|
1
|
+
import "./ts-DiMpa4ri.js";
|
|
2
2
|
import "./logger-CDIsZ-Pp.js";
|
|
3
|
-
import { r as getInstalledPackage } from "./versionChecker-
|
|
4
|
-
import { t as findSpawnHiddenLauncher } from "./rustBinary-
|
|
3
|
+
import { r as getInstalledPackage } from "./versionChecker-D6Qc6O3F.js";
|
|
4
|
+
import { t as findSpawnHiddenLauncher } from "./rustBinary-Bne6M0ik.js";
|
|
5
5
|
import "./pidStore-BIvsBQ8X.js";
|
|
6
6
|
import { a as updateGlobalPidStatus } from "./globalPidIndex-CoNr7tS8.js";
|
|
7
7
|
import { t as pgidForWrapper } from "./reaper-CWF2_ATd.js";
|
|
8
8
|
import "./configShared-0MnIQ652.js";
|
|
9
|
-
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-
|
|
10
|
-
import { A as resolveOne, E as readPtysize, F as snapshotStatus, H as TYPING_BADGE, O as renderRawLog, S as listRecords, T as readNotes, V as writeToIpc, b as isUserTyping, f as extractNeedsInput, l as deriveLiveStatus, o as controlCodeFromName, p as extractTaskCounts, u as extractBadges } from "./subcommands-
|
|
9
|
+
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-KMkjT-Ny.js";
|
|
10
|
+
import { A as resolveOne, E as readPtysize, F as snapshotStatus, H as TYPING_BADGE, O as renderRawLog, S as listRecords, T as readNotes, V as writeToIpc, b as isUserTyping, f as extractNeedsInput, l as deriveLiveStatus, o as controlCodeFromName, p as extractTaskCounts, u as extractBadges } from "./subcommands-ZjESAcio.js";
|
|
11
11
|
import "./e2e-jb0Hp43q.js";
|
|
12
12
|
import "./webrtcLink-B7REGtK2.js";
|
|
13
13
|
import "./remotes-CC-4GuJb.js";
|
|
@@ -845,13 +845,31 @@ Options:
|
|
|
845
845
|
if (!root) return null;
|
|
846
846
|
return ensureRepoWatch(root).val;
|
|
847
847
|
};
|
|
848
|
+
const meaningfulStdinAt = /* @__PURE__ */ new Map();
|
|
849
|
+
const anyDaemonWriteAt = /* @__PURE__ */ new Map();
|
|
850
|
+
const isTerminalReply = (s) => /^\x1b\[(\d+;\d+R|\?[\d;]*c|>[\d;]*c|\d*n)$/.test(s);
|
|
851
|
+
const noteStdinWrite = async (pid, fifo, meaningful) => {
|
|
852
|
+
const mt = await stat(fifo).then((s) => s.mtimeMs).catch(() => null);
|
|
853
|
+
if (mt == null) return;
|
|
854
|
+
anyDaemonWriteAt.set(pid, mt);
|
|
855
|
+
if (meaningful) meaningfulStdinAt.set(pid, mt);
|
|
856
|
+
};
|
|
857
|
+
const resolveLastStdinAt = async (r) => {
|
|
858
|
+
const meaningful = meaningfulStdinAt.get(r.pid) ?? null;
|
|
859
|
+
if (!r.fifo_file) return meaningful;
|
|
860
|
+
const mtime = await stat(r.fifo_file).then((s) => s.mtimeMs).catch(() => null);
|
|
861
|
+
if (mtime == null) return meaningful;
|
|
862
|
+
return mtime > (anyDaemonWriteAt.get(r.pid) ?? 0) ? mtime : meaningful;
|
|
863
|
+
};
|
|
848
864
|
const withMeta = async (r) => {
|
|
849
865
|
const status = await deriveLiveStatus(r);
|
|
850
866
|
const question = status !== "exited" && !r.unresponsive ? await logNeedsInput(r.log_file, r.cli) : null;
|
|
851
867
|
const lastActiveAt = r.log_file ? await stat(r.log_file).then((s) => s.mtimeMs).catch(() => r.started_at) : r.started_at;
|
|
868
|
+
const lastStdinAt = status !== "exited" ? await resolveLastStdinAt(r) : null;
|
|
852
869
|
return {
|
|
853
870
|
...r,
|
|
854
871
|
last_active_at: lastActiveAt,
|
|
872
|
+
last_stdin_at: lastStdinAt,
|
|
855
873
|
status: status === "exited" ? status : r.unresponsive ? "stuck" : question ? "needs_input" : status,
|
|
856
874
|
question,
|
|
857
875
|
title: await logTitle(r.log_file),
|
|
@@ -1115,6 +1133,7 @@ Options:
|
|
|
1115
1133
|
await new Promise((r) => setTimeout(r, 200));
|
|
1116
1134
|
await writeToIpc(record.fifo_file, trailing);
|
|
1117
1135
|
} else await writeToIpc(record.fifo_file, msg + trailing);
|
|
1136
|
+
await noteStdinWrite(record.pid, record.fifo_file, !isTerminalReply(msg));
|
|
1118
1137
|
return Response.json({
|
|
1119
1138
|
ok: true,
|
|
1120
1139
|
pid: record.pid
|
|
@@ -1558,4 +1577,4 @@ Options:
|
|
|
1558
1577
|
|
|
1559
1578
|
//#endregion
|
|
1560
1579
|
export { cmdServe };
|
|
1561
|
-
//# sourceMappingURL=serve-
|
|
1580
|
+
//# sourceMappingURL=serve-DvO4pj5p.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-
|
|
35
|
+
const { cmdServe } = await import("./serve-DvO4pj5p.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-
|
|
45
|
+
//# sourceMappingURL=setup-DB2gMtqk.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "./logger-CDIsZ-Pp.js";
|
|
2
2
|
import "./globalPidIndex-CoNr7tS8.js";
|
|
3
3
|
import "./configShared-0MnIQ652.js";
|
|
4
|
-
import { A as resolveOne, B as writeKeysPaced, C as matchKeyword, D as renderLogTailLines, E as readPtysize, F as snapshotStatus, I as stdinActivityPath, L as stopTipForCli, M as resolveResumeArgs, N as restartHintLines, O as renderRawLog, P as runSubcommand, R as submitAndConfirm, S as listRecords, T as readNotes, V as writeToIpc, _ as isPidAlive, a as cmdHelp, b as isUserTyping, c as deriveLiveState, d as extractMenu, f as extractNeedsInput, g as isExitRequest, h as isAgentStuck, i as backoffWhileTyping, j as resolveReadWindow, k as renderRawLogLines, l as deriveLiveStatus, m as finalizedLines, n as READ_PAGE_DEFAULT, o as controlCodeFromName, p as extractTaskCounts, r as TYPING_WINDOW_MS, s as cursorAbs, t as GRACEFUL_EXIT_COMMANDS, u as extractBadges, v as isSlashCommand, w as menuSelectKeys, x as lastStdinAt, y as isSubcommand, z as waitForLogQuiet } from "./subcommands-
|
|
4
|
+
import { A as resolveOne, B as writeKeysPaced, C as matchKeyword, D as renderLogTailLines, E as readPtysize, F as snapshotStatus, I as stdinActivityPath, L as stopTipForCli, M as resolveResumeArgs, N as restartHintLines, O as renderRawLog, P as runSubcommand, R as submitAndConfirm, S as listRecords, T as readNotes, V as writeToIpc, _ as isPidAlive, a as cmdHelp, b as isUserTyping, c as deriveLiveState, d as extractMenu, f as extractNeedsInput, g as isExitRequest, h as isAgentStuck, i as backoffWhileTyping, j as resolveReadWindow, k as renderRawLogLines, l as deriveLiveStatus, m as finalizedLines, n as READ_PAGE_DEFAULT, o as controlCodeFromName, p as extractTaskCounts, r as TYPING_WINDOW_MS, s as cursorAbs, t as GRACEFUL_EXIT_COMMANDS, u as extractBadges, v as isSlashCommand, w as menuSelectKeys, x as lastStdinAt, y as isSubcommand, z as waitForLogQuiet } from "./subcommands-ZjESAcio.js";
|
|
5
5
|
import "./e2e-jb0Hp43q.js";
|
|
6
6
|
import "./webrtcLink-B7REGtK2.js";
|
|
7
7
|
import "./remotes-CC-4GuJb.js";
|
|
@@ -1126,15 +1126,15 @@ async function runSubcommand(argv) {
|
|
|
1126
1126
|
case "restart": return await cmdRestart(rest);
|
|
1127
1127
|
case "note": return await cmdNote(rest);
|
|
1128
1128
|
case "serve": {
|
|
1129
|
-
const { cmdServe } = await import("./serve-
|
|
1129
|
+
const { cmdServe } = await import("./serve-DvO4pj5p.js");
|
|
1130
1130
|
return cmdServe(rest);
|
|
1131
1131
|
}
|
|
1132
1132
|
case "setup": {
|
|
1133
|
-
const { cmdSetup } = await import("./setup-
|
|
1133
|
+
const { cmdSetup } = await import("./setup-DB2gMtqk.js");
|
|
1134
1134
|
return cmdSetup(rest);
|
|
1135
1135
|
}
|
|
1136
1136
|
case "schedule": {
|
|
1137
|
-
const { cmdSchedule } = await import("./schedule-
|
|
1137
|
+
const { cmdSchedule } = await import("./schedule-BrpCaCuz.js");
|
|
1138
1138
|
return cmdSchedule(rest);
|
|
1139
1139
|
}
|
|
1140
1140
|
case "remote": {
|
|
@@ -3963,7 +3963,7 @@ async function cmdNotify(rest) {
|
|
|
3963
3963
|
}
|
|
3964
3964
|
const ensure = async () => {
|
|
3965
3965
|
if (!argv["ensure-daemon"]) return;
|
|
3966
|
-
const { ensureDaemon } = await import("./notifyDaemon-
|
|
3966
|
+
const { ensureDaemon } = await import("./notifyDaemon-tmytSfnS.js");
|
|
3967
3967
|
await ensureDaemon().catch(() => null);
|
|
3968
3968
|
};
|
|
3969
3969
|
await heartbeatWatcher(parent, selfStartedAt);
|
|
@@ -4037,7 +4037,7 @@ async function cmdNotifyCursor(args) {
|
|
|
4037
4037
|
}
|
|
4038
4038
|
async function cmdNotifyd(rest) {
|
|
4039
4039
|
const sub = rest[0] ?? "status";
|
|
4040
|
-
const daemon = await import("./notifyDaemon-
|
|
4040
|
+
const daemon = await import("./notifyDaemon-tmytSfnS.js");
|
|
4041
4041
|
switch (sub) {
|
|
4042
4042
|
case "run": return daemon.runDaemon();
|
|
4043
4043
|
case "once": return daemon.runDaemon({ once: true });
|
|
@@ -4064,4 +4064,4 @@ async function cmdNotifyd(rest) {
|
|
|
4064
4064
|
|
|
4065
4065
|
//#endregion
|
|
4066
4066
|
export { resolveOne as A, writeKeysPaced as B, matchKeyword as C, renderLogTailLines as D, readPtysize as E, snapshotStatus as F, hostId as G, TYPING_BADGE as H, stdinActivityPath as I, readInbox as J, listInboxParents as K, stopTipForCli as L, resolveResumeArgs as M, restartHintLines as N, renderRawLog as O, runSubcommand as P, notifyDir as Q, submitAndConfirm as R, listRecords as S, readNotes as T, appendEvent as U, writeToIpc as V, gcInboxes as W, daemonLockDir as X, shouldStealLock as Y, daemonLockOwnerPath as Z, isPidAlive as _, cmdHelp as a, isUserTyping as b, deriveLiveState as c, extractMenu as d, extractNeedsInput as f, isExitRequest as g, isAgentStuck as h, backoffWhileTyping as i, resolveReadWindow as j, renderRawLogLines as k, deriveLiveStatus as l, finalizedLines as m, READ_PAGE_DEFAULT as n, controlCodeFromName as o, extractTaskCounts as p, liveWatchers as q, TYPING_WINDOW_MS as r, cursorAbs as s, GRACEFUL_EXIT_COMMANDS as t, extractBadges as u, isSlashCommand as v, menuSelectKeys as w, lastStdinAt as x, isSubcommand as y, waitForLogQuiet as z };
|
|
4067
|
-
//# sourceMappingURL=subcommands-
|
|
4067
|
+
//# sourceMappingURL=subcommands-ZjESAcio.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-
|
|
2
|
+
import { r as getInstalledPackage } from "./versionChecker-D6Qc6O3F.js";
|
|
3
3
|
import { t as agentYesHome } from "./agentYesHome-CtHb5b71.js";
|
|
4
4
|
import { i as shouldUseLock, r as releaseLock, t as acquireLock } from "./runningLock-CNMl13dC.js";
|
|
5
5
|
import { t as PidStore } from "./pidStore-BIvsBQ8X.js";
|
|
@@ -1824,4 +1824,4 @@ function sleep(ms) {
|
|
|
1824
1824
|
|
|
1825
1825
|
//#endregion
|
|
1826
1826
|
export { removeControlCharacters as a, AgentContext as i, agentYes as n, config as r, CLIS_CONFIG as t };
|
|
1827
|
-
//# sourceMappingURL=ts-
|
|
1827
|
+
//# sourceMappingURL=ts-DiMpa4ri.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.
|
|
10
|
+
var version = "1.172.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-
|
|
218
|
+
//# sourceMappingURL=versionChecker-D6Qc6O3F.js.map
|
package/lab/ui/console-logic.js
CHANGED
|
@@ -348,7 +348,7 @@ function agentForestNodes(list) {
|
|
|
348
348
|
// flat entry list BEFORE layeredRows builds the room/peer/agent tree (which keeps
|
|
349
349
|
// the given order for siblings and first-seen order for room/peer groups). So
|
|
350
350
|
// sorting reorders roots and siblings without breaking the nesting.
|
|
351
|
-
export const SORT_MODES = ["state", "active", "created", "identity"];
|
|
351
|
+
export const SORT_MODES = ["state", "active", "stdin", "created", "identity"];
|
|
352
352
|
|
|
353
353
|
// Attention-first state ranking: someone scanning the fleet wants the agents that
|
|
354
354
|
// need them (needs_input) up top, then the wedged ones (stuck), then live work,
|
|
@@ -383,21 +383,26 @@ function lastActive(e) {
|
|
|
383
383
|
|
|
384
384
|
// Return a NEW array sorted for display per `mode` (default "state"):
|
|
385
385
|
// - "state": attention-first state, then git busyness, then newest.
|
|
386
|
-
// - "active": most recently active first (last_active_at desc).
|
|
386
|
+
// - "active": most recently active first (last_active_at, i.e. stdout, desc).
|
|
387
|
+
// - "stdin": most recently FED first (last_stdin_at desc) — which agent was
|
|
388
|
+
// just driven/typed-into/`ay send`-ed. Agents never fed sort last.
|
|
387
389
|
// - "created": newest first (started_at desc).
|
|
388
390
|
// - "identity": user@host:owner/repo/branch (alphabetical).
|
|
389
391
|
// Every comparator falls back to newest-first so order is total & deterministic.
|
|
390
392
|
export function sortEntries(entries, mode = "state") {
|
|
391
393
|
const byNewest = (a, b) => (b.started_at || 0) - (a.started_at || 0);
|
|
392
394
|
const byActive = (a, b) => lastActive(b) - lastActive(a) || byNewest(a, b);
|
|
395
|
+
const byStdin = (a, b) => (b.last_stdin_at ?? 0) - (a.last_stdin_at ?? 0) || byNewest(a, b);
|
|
393
396
|
const cmp =
|
|
394
397
|
mode === "active"
|
|
395
398
|
? byActive
|
|
396
|
-
: mode === "
|
|
397
|
-
?
|
|
398
|
-
: mode === "
|
|
399
|
-
?
|
|
400
|
-
:
|
|
399
|
+
: mode === "stdin"
|
|
400
|
+
? byStdin
|
|
401
|
+
: mode === "created"
|
|
402
|
+
? byNewest
|
|
403
|
+
: mode === "identity"
|
|
404
|
+
? (a, b) => fullIdent(a).localeCompare(fullIdent(b)) || byNewest(a, b)
|
|
405
|
+
: (a, b) => stateRank(a) - stateRank(b) || gitWeight(b) - gitWeight(a) || byNewest(a, b);
|
|
401
406
|
return entries.slice().sort(cmp);
|
|
402
407
|
}
|
|
403
408
|
|
|
@@ -549,6 +554,52 @@ export function nextIndex(len, i, dir) {
|
|
|
549
554
|
// coordinate math lives here so it's unit-testable without a browser.
|
|
550
555
|
// ---------------------------------------------------------------------------
|
|
551
556
|
|
|
557
|
+
// ---- collaborative presence: peer focus + stdin flash ----------------------
|
|
558
|
+
// The left panel colour-codes who's on which agent: your own selection is blue
|
|
559
|
+
// (the .sel bar), every OTHER human peer's focus is yellow (.peerfocus), and a
|
|
560
|
+
// row pulses when its stdin was just written — independent of focus, since an
|
|
561
|
+
// `ay send` feeds an agent no one is looking at. These pure helpers own the
|
|
562
|
+
// bookkeeping so index.html only measures the DOM.
|
|
563
|
+
|
|
564
|
+
// Detect agents whose stdin just advanced. `seen` maps _key -> the last
|
|
565
|
+
// last_stdin_at we observed; given the current entries, return the _keys whose
|
|
566
|
+
// last_stdin_at is NEWER than what we'd seen (someone typed / `ay send` pushed),
|
|
567
|
+
// and fold the new values into `seen`. A key seen for the FIRST time never
|
|
568
|
+
// flashes (else every agent would pulse on initial load) — only a later bump
|
|
569
|
+
// does. Keys no longer present are pruned so `seen` can't grow without bound.
|
|
570
|
+
export function advanceStdinFlashes(entries, seen) {
|
|
571
|
+
const fresh = [];
|
|
572
|
+
const alive = new Set();
|
|
573
|
+
for (const e of entries) {
|
|
574
|
+
const key = e._key;
|
|
575
|
+
if (!key) continue;
|
|
576
|
+
alive.add(key);
|
|
577
|
+
const at = e.last_stdin_at;
|
|
578
|
+
if (typeof at !== "number") continue;
|
|
579
|
+
const prev = seen.get(key);
|
|
580
|
+
if (prev !== undefined && at > prev) fresh.push(key);
|
|
581
|
+
if (prev === undefined || at > prev) seen.set(key, at);
|
|
582
|
+
}
|
|
583
|
+
for (const key of [...seen.keys()]) if (!alive.has(key)) seen.delete(key);
|
|
584
|
+
return fresh;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// Summarize fleet presence for the peers badge + per-row chips. `records` is one
|
|
588
|
+
// entry per OTHER viewer currently watching an agent: { key, viewer } (key = the
|
|
589
|
+
// composite _key of the agent they're on). Returns { total, byKey }: `total`
|
|
590
|
+
// distinct viewers (the "N peers" badge), `byKey` a Map of _key -> how many
|
|
591
|
+
// peers watch that agent (the yellow per-row chip + .peerfocus trigger).
|
|
592
|
+
export function focusSummary(records) {
|
|
593
|
+
const viewers = new Set();
|
|
594
|
+
const byKey = new Map();
|
|
595
|
+
for (const r of records) {
|
|
596
|
+
if (!r || !r.key || !r.viewer) continue;
|
|
597
|
+
viewers.add(r.viewer);
|
|
598
|
+
byKey.set(r.key, (byKey.get(r.key) || 0) + 1);
|
|
599
|
+
}
|
|
600
|
+
return { total: viewers.size, byKey };
|
|
601
|
+
}
|
|
602
|
+
|
|
552
603
|
// Stable per-viewer hue (0..359) for colour-coding peers' selections.
|
|
553
604
|
export function hashHue(s) {
|
|
554
605
|
let h = 0;
|
package/lab/ui/index.html
CHANGED
|
@@ -37,6 +37,9 @@
|
|
|
37
37
|
--purple: #bc8cff;
|
|
38
38
|
--pink: #f778ba;
|
|
39
39
|
--red: #f85149;
|
|
40
|
+
/* Peer-focus yellow: a human collaborator (not you) is on this agent.
|
|
41
|
+
Distinct from your own blue (--accent) and the idle-dot amber. */
|
|
42
|
+
--peer: #e3b341;
|
|
40
43
|
--mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
|
|
41
44
|
/* Safe-area insets — non-zero only in a standalone PWA on a notched /
|
|
42
45
|
gesture-bar device (else 0, so these are harmless on desktop). Consumed
|
|
@@ -70,6 +73,7 @@
|
|
|
70
73
|
--purple: #8250df;
|
|
71
74
|
--pink: #bf3989;
|
|
72
75
|
--red: #cf222e;
|
|
76
|
+
--peer: #bf8700;
|
|
73
77
|
}
|
|
74
78
|
}
|
|
75
79
|
* {
|
|
@@ -420,6 +424,21 @@
|
|
|
420
424
|
font-size: 11.5px;
|
|
421
425
|
margin-top: 8px;
|
|
422
426
|
}
|
|
427
|
+
.metaleft {
|
|
428
|
+
display: inline-flex;
|
|
429
|
+
align-items: center;
|
|
430
|
+
gap: 8px;
|
|
431
|
+
}
|
|
432
|
+
/* Peers badge beside the agent counter — how many other humans are in the
|
|
433
|
+
fleet right now. Yellow ("here") when a peer shares the agent you have
|
|
434
|
+
open, muted otherwise. Hidden entirely when you're alone. */
|
|
435
|
+
.peers {
|
|
436
|
+
color: var(--muted);
|
|
437
|
+
cursor: default;
|
|
438
|
+
}
|
|
439
|
+
.peers.here {
|
|
440
|
+
color: var(--peer);
|
|
441
|
+
}
|
|
423
442
|
.connbadge {
|
|
424
443
|
cursor: pointer;
|
|
425
444
|
user-select: none;
|
|
@@ -818,6 +837,39 @@
|
|
|
818
837
|
background: var(--panel);
|
|
819
838
|
box-shadow: inset 3px 0 0 var(--accent);
|
|
820
839
|
}
|
|
840
|
+
/* A human peer (not you) is watching this agent: yellow, mirrored on the
|
|
841
|
+
RIGHT edge so it can coexist with your own blue selection bar on the
|
|
842
|
+
left — a row you BOTH have open shows blue-left + yellow-right. */
|
|
843
|
+
.row.peerfocus {
|
|
844
|
+
box-shadow: inset -3px 0 0 var(--peer);
|
|
845
|
+
background: color-mix(in srgb, var(--peer) 7%, transparent);
|
|
846
|
+
}
|
|
847
|
+
.row.sel.peerfocus {
|
|
848
|
+
background: var(--panel);
|
|
849
|
+
box-shadow: inset 3px 0 0 var(--accent), inset -3px 0 0 var(--peer);
|
|
850
|
+
}
|
|
851
|
+
/* stdin just landed here (someone typed, or `ay send` pushed a command) —
|
|
852
|
+
pulse the row regardless of who (if anyone) is focused on it. Runs while
|
|
853
|
+
the .stdinflash class is present; index.html removes it after the window. */
|
|
854
|
+
.row.stdinflash {
|
|
855
|
+
animation: stdinpulse 0.6s ease-in-out infinite;
|
|
856
|
+
}
|
|
857
|
+
@keyframes stdinpulse {
|
|
858
|
+
0%,
|
|
859
|
+
100% {
|
|
860
|
+
background-color: transparent;
|
|
861
|
+
}
|
|
862
|
+
50% {
|
|
863
|
+
background-color: color-mix(in srgb, var(--peer) 24%, transparent);
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
/* Per-row chip: how many peers are watching THIS agent (yellow, mono). */
|
|
867
|
+
.peerchip {
|
|
868
|
+
font-family: var(--mono);
|
|
869
|
+
font-size: 11px;
|
|
870
|
+
color: var(--peer);
|
|
871
|
+
flex: none;
|
|
872
|
+
}
|
|
821
873
|
.r1 {
|
|
822
874
|
display: flex;
|
|
823
875
|
align-items: center;
|
|
@@ -1392,7 +1444,10 @@
|
|
|
1392
1444
|
/>
|
|
1393
1445
|
</div>
|
|
1394
1446
|
<div class="meta">
|
|
1395
|
-
<span
|
|
1447
|
+
<span class="metaleft">
|
|
1448
|
+
<span id="count"></span>
|
|
1449
|
+
<span id="peers" class="peers" hidden></span>
|
|
1450
|
+
</span>
|
|
1396
1451
|
<span class="metaright">
|
|
1397
1452
|
<button id="foldbtn" class="viewbtn" title="fold subagent trees">⊞ subs</button>
|
|
1398
1453
|
<button id="sortbtn" class="viewbtn" title="cycle sort order">⇅ state</button>
|
|
@@ -1584,6 +1639,8 @@
|
|
|
1584
1639
|
SORT_MODES,
|
|
1585
1640
|
taskLabel,
|
|
1586
1641
|
badgesFor,
|
|
1642
|
+
advanceStdinFlashes,
|
|
1643
|
+
focusSummary,
|
|
1587
1644
|
hashHue,
|
|
1588
1645
|
selFromBottom,
|
|
1589
1646
|
parseSel,
|
|
@@ -1610,6 +1667,17 @@
|
|
|
1610
1667
|
|
|
1611
1668
|
let entries = [];
|
|
1612
1669
|
let sel = null; // selected agent's composite key (room#pid)
|
|
1670
|
+
// Collaborative presence, fleet-wide: focusByKey maps an agent's _key ->
|
|
1671
|
+
// how many OTHER human peers are watching it (yellow bars + per-row chip);
|
|
1672
|
+
// peerTotal is the distinct-peer count for the header badge. stdinSeen +
|
|
1673
|
+
// flashUntil drive the stdin pulse: stdinSeen remembers each agent's last
|
|
1674
|
+
// last_stdin_at so we can spot a bump, flashUntil holds the deadline the
|
|
1675
|
+
// row keeps pulsing to. See advanceStdinFlashes/focusSummary (console-logic).
|
|
1676
|
+
let focusByKey = new Map();
|
|
1677
|
+
let peerTotal = 0;
|
|
1678
|
+
const stdinSeen = new Map();
|
|
1679
|
+
const flashUntil = new Map();
|
|
1680
|
+
const STDIN_FLASH_MS = 1200;
|
|
1613
1681
|
let es = null; // live-tail subscription closer
|
|
1614
1682
|
let term = null; // xterm.js Terminal rendering the raw PTY stream
|
|
1615
1683
|
let fit = null;
|
|
@@ -2713,10 +2781,65 @@
|
|
|
2713
2781
|
}
|
|
2714
2782
|
renderPeerSelections();
|
|
2715
2783
|
}
|
|
2784
|
+
// Fleet-wide presence: unlike pollPresence (only the SELECTED agent's host,
|
|
2785
|
+
// for the terminal overlay), this reads EVERY live source's blackboard so
|
|
2786
|
+
// the left panel can colour any agent a peer is watching. Each viewer
|
|
2787
|
+
// self-reports the pid it's on; we map that back to the source's composite
|
|
2788
|
+
// _key. Best-effort — a source that 404s (older host) contributes nothing.
|
|
2789
|
+
async function pollFleetPresence() {
|
|
2790
|
+
const recs = [];
|
|
2791
|
+
await Promise.all(
|
|
2792
|
+
[...sources.values()].map(async (s) => {
|
|
2793
|
+
if (!s.tx || !s.live) return;
|
|
2794
|
+
let all;
|
|
2795
|
+
try {
|
|
2796
|
+
all = await s.tx.fetchJSON("/api/presence");
|
|
2797
|
+
} catch {
|
|
2798
|
+
return;
|
|
2799
|
+
}
|
|
2800
|
+
if (!Array.isArray(all)) return;
|
|
2801
|
+
const keyOfPid = new Map((s.agents || []).map((e) => [String(e.pid), e._key]));
|
|
2802
|
+
for (const v of all) {
|
|
2803
|
+
if (!v || v.viewer === myViewerId) continue;
|
|
2804
|
+
const key = keyOfPid.get(String(v.agent));
|
|
2805
|
+
// Prefix the viewer id with its source so the same id on two hosts
|
|
2806
|
+
// still counts as two distinct people in the fleet total.
|
|
2807
|
+
if (key) recs.push({ key, viewer: s.id + ":" + v.viewer });
|
|
2808
|
+
}
|
|
2809
|
+
}),
|
|
2810
|
+
);
|
|
2811
|
+
const sum = focusSummary(recs);
|
|
2812
|
+
focusByKey = sum.byKey;
|
|
2813
|
+
peerTotal = sum.total;
|
|
2814
|
+
paintPeersBadge();
|
|
2815
|
+
renderList();
|
|
2816
|
+
}
|
|
2817
|
+
// "N peers" badge beside the agent counter. Yellow when a peer shares the
|
|
2818
|
+
// agent you have open ("same agent"); its tooltip breaks the count down by
|
|
2819
|
+
// agent so you can see who's where. Hidden when you're the only one here.
|
|
2820
|
+
function paintPeersBadge() {
|
|
2821
|
+
const el = $("peers");
|
|
2822
|
+
if (!el) return;
|
|
2823
|
+
if (!peerTotal) {
|
|
2824
|
+
el.hidden = true;
|
|
2825
|
+
el.removeAttribute("title");
|
|
2826
|
+
return;
|
|
2827
|
+
}
|
|
2828
|
+
el.hidden = false;
|
|
2829
|
+
el.textContent = `👤 ${peerTotal} peer${peerTotal === 1 ? "" : "s"}`;
|
|
2830
|
+
el.classList.toggle("here", !!(sel && focusByKey.get(sel)));
|
|
2831
|
+
const lines = [...focusByKey.entries()].map(([k, n]) => {
|
|
2832
|
+
const e = entries.find((x) => x._key === k);
|
|
2833
|
+
const name = e ? e.title || cliLabel(e) || ident(e) || `pid ${e.pid}` : k;
|
|
2834
|
+
return ` ${n} on ${name}`;
|
|
2835
|
+
});
|
|
2836
|
+
el.title = "peers watching agents in this fleet:\n" + lines.join("\n");
|
|
2837
|
+
}
|
|
2716
2838
|
// 3s heartbeat (< the host's 12s TTL): refresh our presence + read others',
|
|
2717
2839
|
// and follow the canonical grid size (resize+scale, never reflow) so all
|
|
2718
2840
|
// viewers stay on one shared canvas and selections line up.
|
|
2719
2841
|
setInterval(() => {
|
|
2842
|
+
pollFleetPresence(); // fleet-wide peer focus (independent of what's selected)
|
|
2720
2843
|
if (!sel) return;
|
|
2721
2844
|
sendPresence();
|
|
2722
2845
|
pollPresence();
|
|
@@ -2727,6 +2850,23 @@
|
|
|
2727
2850
|
.then(followAgentSize)
|
|
2728
2851
|
.catch(() => {});
|
|
2729
2852
|
}, 3000);
|
|
2853
|
+
// Fast pulse loop: drive the stdin flash on the STABLE list DOM (toggle a
|
|
2854
|
+
// class, no re-render) so a pulse begins/ends promptly regardless of the
|
|
2855
|
+
// list's own render cadence. Cheap — a no-op until something is flashing.
|
|
2856
|
+
setInterval(() => {
|
|
2857
|
+
if (!flashUntil.size) return;
|
|
2858
|
+
const now = Date.now();
|
|
2859
|
+
for (const [k, until] of [...flashUntil.entries()]) if (until <= now) flashUntil.delete(k);
|
|
2860
|
+
const listEl = $("list");
|
|
2861
|
+
if (!listEl) return;
|
|
2862
|
+
for (const el of listEl.querySelectorAll(".row[data-key]")) {
|
|
2863
|
+
const key = el.getAttribute("data-key");
|
|
2864
|
+
// Mirror rowFlags: never pulse the agent you have open (you see its input
|
|
2865
|
+
// live; also stops your own terminal's protocol replies blinking the row).
|
|
2866
|
+
const on = key !== sel && (flashUntil.get(key) || 0) > now;
|
|
2867
|
+
el.classList.toggle("stdinflash", on);
|
|
2868
|
+
}
|
|
2869
|
+
}, 200);
|
|
2730
2870
|
|
|
2731
2871
|
// Peer-selection overlay. The coordinate math (parseSel / selSegments /
|
|
2732
2872
|
// hashHue) lives in console-logic.js (unit-tested); here we just measure the
|
|
@@ -3311,6 +3451,26 @@
|
|
|
3311
3451
|
return `<div class="ghead g-${r.kind}">${r.branch ? `<span class="tbranch">${esc(r.branch)}</span>` : ""}<span class="gicon">${icon}</span><span class="glabel">${esc(r.label || "")}</span>${ctag}<span class="gkind">${r.kind}</span></div>`;
|
|
3312
3452
|
}
|
|
3313
3453
|
|
|
3454
|
+
// Extra row classes for collaborative presence: a yellow bar when a human
|
|
3455
|
+
// peer is watching this agent, plus the stdin pulse while its input just
|
|
3456
|
+
// advanced. Empty in the solo case, so a single-user list is unchanged.
|
|
3457
|
+
// The pulse is suppressed on the agent YOU have open (e._key === sel): you
|
|
3458
|
+
// already see its input land live in the terminal, so a flash there is
|
|
3459
|
+
// redundant — and it's the only row your own terminal's protocol replies
|
|
3460
|
+
// could ever nudge, so this also keeps a resize from blinking your own row.
|
|
3461
|
+
function rowFlags(e) {
|
|
3462
|
+
const peers = focusByKey.get(e._key) || 0;
|
|
3463
|
+
const flash = e._key !== sel && (flashUntil.get(e._key) || 0) > Date.now();
|
|
3464
|
+
return (peers ? " peerfocus" : "") + (flash ? " stdinflash" : "");
|
|
3465
|
+
}
|
|
3466
|
+
// A yellow chip showing how many peers are watching this agent ("" if none).
|
|
3467
|
+
function peerChipHtml(e) {
|
|
3468
|
+
const n = focusByKey.get(e._key) || 0;
|
|
3469
|
+
return n
|
|
3470
|
+
? `<span class="peerchip" title="${n} peer${n === 1 ? "" : "s"} watching this agent">👤${n}</span>`
|
|
3471
|
+
: "";
|
|
3472
|
+
}
|
|
3473
|
+
|
|
3314
3474
|
function renderList() {
|
|
3315
3475
|
const toks = $("q").value.trim().split(/\s+/).filter(Boolean);
|
|
3316
3476
|
// Build the full signalling-server > rooms > peers > agents > subagents
|
|
@@ -3325,6 +3485,7 @@
|
|
|
3325
3485
|
? fullRows.filter((r) => !(r.kind === "agent" && r.parentEntry))
|
|
3326
3486
|
: fullRows;
|
|
3327
3487
|
$("count").textContent = `${agentRows.length} / ${entries.length} agents`;
|
|
3488
|
+
paintPeersBadge(); // keep the peers badge in step with sel / the entry list
|
|
3328
3489
|
$("foldbtn").textContent = foldSubs ? "⊞ subs" : "⊟ subs";
|
|
3329
3490
|
$("foldbtn").classList.toggle("on", !foldSubs);
|
|
3330
3491
|
$("viewbtn").classList.toggle("on", compactList);
|
|
@@ -3347,7 +3508,7 @@
|
|
|
3347
3508
|
const t = e.title || e.prompt || "";
|
|
3348
3509
|
const id = compactIdent(e, ctx, 3, r.parentEntry);
|
|
3349
3510
|
const cli = cliLabel(e);
|
|
3350
|
-
return `<div class="row crow ${e._key === sel ? "sel" : ""}" data-key="${esc(e._key)}">
|
|
3511
|
+
return `<div class="row crow ${e._key === sel ? "sel" : ""}${rowFlags(e)}" data-key="${esc(e._key)}">
|
|
3351
3512
|
${r.branch ? `<span class="tbranch">${esc(r.branch)}</span>` : ""}
|
|
3352
3513
|
<span class="dot ${esc(e.status)}"></span>
|
|
3353
3514
|
${hasIdent(id) ? `<span class="cident" title="${esc(fullIdent(e))}">${esc(id)}</span>` : ""}
|
|
@@ -3357,6 +3518,7 @@
|
|
|
3357
3518
|
${taskChipHtml(e)}
|
|
3358
3519
|
${badgeChipsHtml(e)}
|
|
3359
3520
|
${gitChipHtml(e)}
|
|
3521
|
+
${peerChipHtml(e)}
|
|
3360
3522
|
<span class="age">${age(e)}</span></div>`;
|
|
3361
3523
|
})
|
|
3362
3524
|
.join("") || `<div class="empty">no match</div>`;
|
|
@@ -3376,7 +3538,7 @@
|
|
|
3376
3538
|
`<span class="rtag" data-k="${k}"><span style="opacity:.55">${k}:</span>${esc(v)}</span>`,
|
|
3377
3539
|
)
|
|
3378
3540
|
.join("");
|
|
3379
|
-
return `<div class="row ${e._key === sel ? "sel" : ""}" data-key="${esc(e._key)}">
|
|
3541
|
+
return `<div class="row ${e._key === sel ? "sel" : ""}${rowFlags(e)}" data-key="${esc(e._key)}">
|
|
3380
3542
|
<div class="r1">${r.branch ? `<span class="tbranch">${esc(r.branch)}</span>` : ""}<span class="dot ${esc(e.status)}"></span>
|
|
3381
3543
|
<span class="name">${esc(cliLabel(e) || ident(e) || "agent")}</span>
|
|
3382
3544
|
<span class="badge">pid ${e.pid}</span>
|
|
@@ -3384,6 +3546,7 @@
|
|
|
3384
3546
|
${taskChipHtml(e)}
|
|
3385
3547
|
${badgeChipsHtml(e)}
|
|
3386
3548
|
${gitChipHtml(e)}
|
|
3549
|
+
${peerChipHtml(e)}
|
|
3387
3550
|
<span class="age">${age(e)}</span></div>
|
|
3388
3551
|
${e.title ? `<div class="rowtitle" title="${esc(e.title)}">${esc(e.title)}</div>` : ""}
|
|
3389
3552
|
${
|
|
@@ -3445,6 +3608,13 @@
|
|
|
3445
3608
|
function mergeRender() {
|
|
3446
3609
|
const srcs = [...sources.values()];
|
|
3447
3610
|
entries = srcs.flatMap((s) => s.agents || []);
|
|
3611
|
+
// Spot agents whose stdin just advanced (last_stdin_at bumped since we
|
|
3612
|
+
// last saw them — someone typed, or `ay send` pushed input) and arm the
|
|
3613
|
+
// pulse. Runs before renderList so the flashing rows render pulsing; the
|
|
3614
|
+
// fast loop above stops them when the window lapses. Streamed deltas carry
|
|
3615
|
+
// the new mtime, so a silent `ay send` still trips the flash within ~1s.
|
|
3616
|
+
const until = Date.now() + STDIN_FLASH_MS;
|
|
3617
|
+
for (const k of advanceStdinFlashes(entries, stdinSeen)) flashUntil.set(k, until);
|
|
3448
3618
|
// Badge: total agents + how many rooms are live. Red only when nothing
|
|
3449
3619
|
// at all is reachable (no source answered).
|
|
3450
3620
|
const roomSrcs = srcs.filter((s) => s.id !== LOCAL);
|
package/package.json
CHANGED
package/ts/serve.ts
CHANGED
|
@@ -1232,6 +1232,47 @@ export async function cmdServe(rest: string[]): Promise<number> {
|
|
|
1232
1232
|
return ensureRepoWatch(root).val; // cached — the request path never spawns `git status`
|
|
1233
1233
|
};
|
|
1234
1234
|
|
|
1235
|
+
// Denoised "last meaningful stdin" tracking — drives the console's stdin flash
|
|
1236
|
+
// and the `stdin` sort order. The FIFO's mtime alone is too noisy: xterm forwards
|
|
1237
|
+
// the agent TUI's terminal-protocol auto-replies (cursor-position / device-
|
|
1238
|
+
// attributes reports) to stdin, so a mere resize/redraw would bump it and the row
|
|
1239
|
+
// would false-flash. So we stamp what WE write, split two ways: `meaningfulStdinAt`
|
|
1240
|
+
// skips those auto-replies; `anyDaemonWriteAt` records every write we make. A FIFO
|
|
1241
|
+
// mtime NEWER than any write we made can only be a local `ay send` (which writes
|
|
1242
|
+
// the FIFO directly, bypassing us) — always meaningful — so it still counts. See
|
|
1243
|
+
// resolveLastStdinAt below and the /api/send handler that stamps these.
|
|
1244
|
+
const meaningfulStdinAt = new Map<number, number>();
|
|
1245
|
+
const anyDaemonWriteAt = new Map<number, number>();
|
|
1246
|
+
// A payload that is PURELY a terminal auto-reply (Cursor Position Report, Device
|
|
1247
|
+
// Attributes, Device Status Report) — protocol chatter a TUI emits on redraw/resize,
|
|
1248
|
+
// not a keystroke. Anchored so a chunk that also carries real input never matches;
|
|
1249
|
+
// real typing (incl. arrow keys like `ESC[A`) never looks like one of these.
|
|
1250
|
+
const isTerminalReply = (s: string) => /^\x1b\[(\d+;\d+R|\?[\d;]*c|>[\d;]*c|\d*n)$/.test(s);
|
|
1251
|
+
// Stamp both maps after a daemon FIFO write, keyed off the FIFO's post-write mtime
|
|
1252
|
+
// (so `anyDaemonWriteAt` is exactly our write's mtime — an external write bumps it
|
|
1253
|
+
// strictly higher, which is how resolveLastStdinAt tells them apart with no clock skew).
|
|
1254
|
+
const noteStdinWrite = async (pid: number, fifo: string, meaningful: boolean) => {
|
|
1255
|
+
const mt = await stat(fifo)
|
|
1256
|
+
.then((s) => s.mtimeMs)
|
|
1257
|
+
.catch(() => null);
|
|
1258
|
+
if (mt == null) return;
|
|
1259
|
+
anyDaemonWriteAt.set(pid, mt);
|
|
1260
|
+
if (meaningful) meaningfulStdinAt.set(pid, mt);
|
|
1261
|
+
};
|
|
1262
|
+
// last_stdin_at for a record: newest of (a) the last meaningful write we made and
|
|
1263
|
+
// (b) a FIFO write we did NOT make (a local `ay send`, always meaningful). A mtime
|
|
1264
|
+
// at/below our last write means the newest write was ours → use the meaningful stamp
|
|
1265
|
+
// (which excludes auto-replies); a strictly newer mtime is an external real write.
|
|
1266
|
+
const resolveLastStdinAt = async (r: { pid: number; fifo_file?: string | null }) => {
|
|
1267
|
+
const meaningful = meaningfulStdinAt.get(r.pid) ?? null;
|
|
1268
|
+
if (!r.fifo_file) return meaningful;
|
|
1269
|
+
const mtime = await stat(r.fifo_file)
|
|
1270
|
+
.then((s) => s.mtimeMs)
|
|
1271
|
+
.catch(() => null);
|
|
1272
|
+
if (mtime == null) return meaningful;
|
|
1273
|
+
return mtime > (anyDaemonWriteAt.get(r.pid) ?? 0) ? mtime : meaningful;
|
|
1274
|
+
};
|
|
1275
|
+
|
|
1235
1276
|
// One agent record decorated for the console: the latest OSC title + a git
|
|
1236
1277
|
// snapshot (skipped for exited agents — their repo state is no longer live).
|
|
1237
1278
|
const withMeta = async (r: Awaited<ReturnType<typeof listRecords>>[number]) => {
|
|
@@ -1255,9 +1296,17 @@ export async function cmdServe(rest: string[]): Promise<number> {
|
|
|
1255
1296
|
.then((s) => s.mtimeMs)
|
|
1256
1297
|
.catch(() => r.started_at)
|
|
1257
1298
|
: r.started_at;
|
|
1299
|
+
// Last-stdin time: when this agent was last fed MEANINGFUL input — a real
|
|
1300
|
+
// keystroke, the console composer, or an `ay send` (local or remote) — but NOT
|
|
1301
|
+
// the TUI's terminal-protocol auto-replies, which would otherwise make a resize
|
|
1302
|
+
// or redraw look like input. The console flashes a row when this advances (so a
|
|
1303
|
+
// collaborator sees input land even when they weren't the one sending it) and can
|
|
1304
|
+
// sort by it. See resolveLastStdinAt / noteStdinWrite. Null for exited agents.
|
|
1305
|
+
const lastStdinAt = status !== "exited" ? await resolveLastStdinAt(r) : null;
|
|
1258
1306
|
return {
|
|
1259
1307
|
...r,
|
|
1260
1308
|
last_active_at: lastActiveAt,
|
|
1309
|
+
last_stdin_at: lastStdinAt,
|
|
1261
1310
|
// Precedence: exited stays exited; the Rust supervisor's unresponsive flag is
|
|
1262
1311
|
// an authoritative wedge signal (`stuck`); then a blocked menu (`needs_input`);
|
|
1263
1312
|
// else the base live status — so the console's dot matches `ay ls`. (A dead
|
|
@@ -1653,6 +1702,11 @@ export async function cmdServe(rest: string[]): Promise<number> {
|
|
|
1653
1702
|
} else {
|
|
1654
1703
|
await writeToIpc(record.fifo_file, msg + trailing);
|
|
1655
1704
|
}
|
|
1705
|
+
// Record this write for the stdin flash / sort. A payload that is purely a
|
|
1706
|
+
// terminal auto-reply (xterm answering the TUI's cursor/DA query, forwarded
|
|
1707
|
+
// over this same wire) is protocol noise, not input — stamp anyDaemonWriteAt
|
|
1708
|
+
// but not the "meaningful" time, so a resize/redraw can't trip the flash.
|
|
1709
|
+
await noteStdinWrite(record.pid, record.fifo_file, !isTerminalReply(msg));
|
|
1656
1710
|
return Response.json({ ok: true, pid: record.pid });
|
|
1657
1711
|
} catch (e) {
|
|
1658
1712
|
return new Response((e as Error).message, { status: 404 });
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import "./ts-kJRKPdFi.js";
|
|
2
|
-
import "./logger-CDIsZ-Pp.js";
|
|
3
|
-
import "./versionChecker-eVbxzEL3.js";
|
|
4
|
-
import "./pidStore-BIvsBQ8X.js";
|
|
5
|
-
import "./globalPidIndex-CoNr7tS8.js";
|
|
6
|
-
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-Bz-oXKG8.js";
|
|
7
|
-
|
|
8
|
-
export { SUPPORTED_CLIS };
|