agent-yes 1.300.3 → 1.300.4
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-CWJr4TJH.js → SUPPORTED_CLIS-BIg_X-jE.js} +2 -2
- package/dist/{SUPPORTED_CLIS-ChSO8FiS.js → SUPPORTED_CLIS-D7lOxOnw.js} +3 -3
- package/dist/{agentShare-NkW0b0mg.js → agentShare-BfHUZgk4.js} +4 -4
- package/dist/{callback-CjlaFXp1.js → callback-COR0F2eB.js} +3 -3
- package/dist/{callback-B8CUyag4.js → callback-yWhCmi4k.js} +3 -3
- package/dist/{channels-aYwLMbFV.js → channels-rhxQArqN.js} +2 -2
- package/dist/cli.js +5 -5
- package/dist/{cmdPs-C8DIl8V6.js → cmdPs-B3jTE4f3.js} +3 -3
- package/dist/index.js +2 -2
- package/dist/{notifyDaemon-DWIfzEE7.js → notifyDaemon-BIo7Nkk2.js} +3 -3
- package/dist/{parentPingLoop-Bh3KuFcW.js → parentPingLoop-9T4Pzp6c.js} +2 -2
- package/dist/{remotes-B4v0Nowo.js → remotes-B4CNXwDS.js} +2 -2
- package/dist/{remotes-DnMQWaqQ.js → remotes-CQTEY8Gt.js} +1 -1
- package/dist/{rustBinary-gI_r3g9K.js → rustBinary-CJXUPUWd.js} +48 -5
- package/dist/rustBinary-FhGG3Yfl.js +4 -0
- package/dist/{schedule-DryC--yx.js → schedule-DmijwnTA.js} +4 -4
- package/dist/{serve-DVoYVgBo.js → serve-F5tXYO0-.js} +99 -32
- package/dist/{serveRust-DDQiZmMf.js → serveRust-BAawe83i.js} +16 -2
- package/dist/{setup--1zRHaGW.js → setup-mCb1JbTX.js} +2 -2
- package/dist/{share-C9oGvjXT.js → share-CLataFnI.js} +18 -12
- package/dist/share-nmgSIjYP.js +4 -0
- package/dist/{shareCmd-BI3hRWGO.js → shareCmd-aOKimJeT.js} +25 -7
- package/dist/{subcommands-BviFOQZE.js → subcommands-DRq0MgSm.js} +2 -2
- package/dist/{subcommands-UWLkF5pq.js → subcommands-Ddo2TYZs.js} +404 -77
- package/dist/{terminal-DviH1E3C.js → terminal-DEXrWOa9.js} +2 -2
- package/dist/trayApp-0RVjo43f.js +5 -0
- package/dist/{trayApp-PJrwfyDs.js → trayApp-BUXx7cES.js} +2 -2
- package/dist/{ts--JmSY117.js → ts-CRbycTni.js} +5 -5
- package/dist/{versionChecker-CCGSbjio.js → versionChecker-DeQEhRGB.js} +2 -2
- package/dist/{webrtcRemote-Cd4vTrgv.js → webrtcRemote-Cy5kB8aR.js} +115 -14
- package/dist/{widget-DgBDIGRQ.js → widget-DneXqnL8.js} +3 -3
- package/dist/{ws-DnBg8-g-.js → ws-DPw1lVTi.js} +3 -3
- package/dist/{ws-dc7MP054.js → ws-Dr-0Ckq7.js} +2 -2
- package/package.json +1 -1
- package/ts/remoteCache.bun.spec.ts +191 -0
- package/ts/remoteCache.spec.ts +191 -0
- package/ts/remoteCache.ts +193 -0
- package/ts/rustBinary.bun.spec.ts +39 -2
- package/ts/rustBinary.spec.ts +53 -2
- package/ts/rustBinary.ts +61 -3
- package/ts/serve.spec.ts +103 -0
- package/ts/serve.ts +100 -15
- package/ts/serveRust.spec.ts +24 -1
- package/ts/serveRust.ts +19 -0
- package/ts/share.bun.spec.ts +54 -2
- package/ts/share.spec.ts +54 -2
- package/ts/share.ts +40 -15
- package/ts/shareCmd.ts +33 -2
- package/ts/strayServe.ts +1 -0
- package/ts/subcommands.spec.ts +36 -3
- package/ts/subcommands.ts +384 -98
- package/ts/webrtcRemote.ts +66 -20
- package/ts/webrtcSignal.bun.spec.ts +120 -0
- package/ts/webrtcSignal.spec.ts +120 -0
- package/ts/webrtcSignal.ts +143 -0
- package/dist/rustBinary-BNmAJEBb.js +0 -4
- package/dist/share-Bmnd_qT0.js +0 -4
- package/dist/trayApp-BDIb8fOj.js +0 -5
|
@@ -8,7 +8,7 @@ import { n as localHost, r as localUser, t as formatIdentity } from "./identity-
|
|
|
8
8
|
import { n as isWorkingScreen, r as parseMenu, t as classifyNeedsInput } from "./needsInput-BHopZGM8.js";
|
|
9
9
|
import { S as postmortemStartedAt, _ as filterSinceSeq, a as getCursor, b as maxSeq, f as readInbox, o as heartbeatWatcher, p as setCursor, r as clearWatcher, s as hostId, v as filterSinceTs, y as filterUnread } from "./notifyStore-2SWKGwZI.js";
|
|
10
10
|
import { n as isWebrtcSpec } from "./webrtcLink-DFRyWNMs.js";
|
|
11
|
-
import { l as readRemotes, u as resolveRemoteSpec } from "./remotes-
|
|
11
|
+
import { l as readRemotes, u as resolveRemoteSpec } from "./remotes-B4CNXwDS.js";
|
|
12
12
|
import { execFile } from "child_process";
|
|
13
13
|
import ms from "ms";
|
|
14
14
|
import yargs from "yargs";
|
|
@@ -17,7 +17,7 @@ import { appendFile, mkdir, open, readFile, readdir, rename, rm, stat, writeFile
|
|
|
17
17
|
import { homedir } from "os";
|
|
18
18
|
import path from "path";
|
|
19
19
|
import { lock } from "proper-lockfile";
|
|
20
|
-
import { execFileSync as execFileSync$1 } from "node:child_process";
|
|
20
|
+
import { execFileSync as execFileSync$1, spawn as spawn$1 } from "node:child_process";
|
|
21
21
|
import { createHash, randomBytes } from "crypto";
|
|
22
22
|
import { promisify } from "util";
|
|
23
23
|
|
|
@@ -762,6 +762,137 @@ async function readAncestryTable(read = readProcessTable) {
|
|
|
762
762
|
}
|
|
763
763
|
}
|
|
764
764
|
|
|
765
|
+
//#endregion
|
|
766
|
+
//#region ts/remoteCache.ts
|
|
767
|
+
/** Rows younger than this are served as-is without kicking a refresh. */
|
|
768
|
+
const REMOTE_CACHE_FRESH_MS = 3e4;
|
|
769
|
+
function remoteCachePath(alias) {
|
|
770
|
+
return path.join(agentYesHome(), "remotes", `${encodeURIComponent(alias)}.cache.json`);
|
|
771
|
+
}
|
|
772
|
+
/** The persisted snapshot, or null for missing/corrupt — a cold cache, never an error. */
|
|
773
|
+
async function readRemoteSnapshot(alias) {
|
|
774
|
+
try {
|
|
775
|
+
const parsed = JSON.parse(await readFile(remoteCachePath(alias), "utf-8"));
|
|
776
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
|
|
777
|
+
const s = parsed;
|
|
778
|
+
if (s.version !== 1 || !Array.isArray(s.records)) return null;
|
|
779
|
+
const fetchedAt = typeof s.fetchedAt === "number" && Number.isFinite(s.fetchedAt) ? s.fetchedAt : null;
|
|
780
|
+
const e = s.error;
|
|
781
|
+
const error = e && typeof e === "object" && typeof e.message === "string" && typeof e.since === "number" ? {
|
|
782
|
+
message: e.message,
|
|
783
|
+
since: e.since,
|
|
784
|
+
lastTriedAt: Number(e.lastTriedAt) || e.since
|
|
785
|
+
} : null;
|
|
786
|
+
return {
|
|
787
|
+
version: 1,
|
|
788
|
+
alias,
|
|
789
|
+
fetchedAt,
|
|
790
|
+
records: s.records,
|
|
791
|
+
error
|
|
792
|
+
};
|
|
793
|
+
} catch {
|
|
794
|
+
return null;
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
/** tmp + rename, like the health file: a concurrent reader never sees a torn file. */
|
|
798
|
+
async function writeRemoteSnapshot(snap) {
|
|
799
|
+
const dest = remoteCachePath(snap.alias);
|
|
800
|
+
await mkdir(path.dirname(dest), { recursive: true });
|
|
801
|
+
const tmp = `${dest}.${process.pid}.tmp`;
|
|
802
|
+
await writeFile(tmp, JSON.stringify(snap));
|
|
803
|
+
await rename(tmp, dest);
|
|
804
|
+
}
|
|
805
|
+
/**
|
|
806
|
+
* Fold one fetch into the snapshot. Success replaces the rows and clears the
|
|
807
|
+
* error; failure keeps the last good rows and extends the streak, preserving
|
|
808
|
+
* `since` so the operator sees "unreachable since 14:02", not "since now".
|
|
809
|
+
*/
|
|
810
|
+
function noteSnapshotResult(prev, alias, result, now) {
|
|
811
|
+
if (result.ok) return {
|
|
812
|
+
version: 1,
|
|
813
|
+
alias,
|
|
814
|
+
fetchedAt: now,
|
|
815
|
+
records: result.records,
|
|
816
|
+
error: null
|
|
817
|
+
};
|
|
818
|
+
return {
|
|
819
|
+
version: 1,
|
|
820
|
+
alias,
|
|
821
|
+
fetchedAt: prev?.fetchedAt ?? null,
|
|
822
|
+
records: prev?.records ?? [],
|
|
823
|
+
error: {
|
|
824
|
+
message: result.message,
|
|
825
|
+
since: prev?.error?.since ?? now,
|
|
826
|
+
lastTriedAt: now
|
|
827
|
+
}
|
|
828
|
+
};
|
|
829
|
+
}
|
|
830
|
+
/** Whether the rows are old enough to warrant a refresh. Fails open to "stale". */
|
|
831
|
+
function isStale(snap, now, freshMs = REMOTE_CACHE_FRESH_MS) {
|
|
832
|
+
if (!snap || snap.fetchedAt === null) return true;
|
|
833
|
+
const age = now - snap.fetchedAt;
|
|
834
|
+
return age < 0 || age >= freshMs;
|
|
835
|
+
}
|
|
836
|
+
/** "0s", "42s", "3m", "5h", "2d" — the table's AGE column vocabulary. */
|
|
837
|
+
function humanizeAge(ms) {
|
|
838
|
+
if (ms < 1e3) return "0s";
|
|
839
|
+
const s = Math.floor(ms / 1e3);
|
|
840
|
+
if (s < 60) return `${s}s`;
|
|
841
|
+
const m = Math.floor(s / 60);
|
|
842
|
+
if (m < 60) return `${m}m`;
|
|
843
|
+
const h = Math.floor(m / 60);
|
|
844
|
+
if (h < 24) return `${h}h`;
|
|
845
|
+
return `${Math.floor(h / 24)}d`;
|
|
846
|
+
}
|
|
847
|
+
/** Local wall-clock HH:MM, for "unreachable since 14:02". */
|
|
848
|
+
function hhmm(ts) {
|
|
849
|
+
const d = new Date(ts);
|
|
850
|
+
return `${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}`;
|
|
851
|
+
}
|
|
852
|
+
/**
|
|
853
|
+
* The per-remote tag printed beside its section: where the rows came from and
|
|
854
|
+
* how old they are. `refreshing` says a background refresh was just kicked.
|
|
855
|
+
*/
|
|
856
|
+
function cacheAgeLabel(snap, now, refreshing) {
|
|
857
|
+
const base = snap && snap.fetchedAt !== null ? `cached ${humanizeAge(now - snap.fetchedAt)} ago` : "no cache yet";
|
|
858
|
+
if (snap?.error) return `${base}, unreachable since ${hhmm(snap.error.since)}`;
|
|
859
|
+
return refreshing ? `${base}, refreshing…` : base;
|
|
860
|
+
}
|
|
861
|
+
/**
|
|
862
|
+
* `--waitFor=all|local|<alias>[,<alias>…]`. Unknown aliases are an error
|
|
863
|
+
* rather than silently waited-for-nothing: the flag exists so a caller can
|
|
864
|
+
* trust the rows it gets, and a typo must not become a false "fresh".
|
|
865
|
+
*/
|
|
866
|
+
function parseWaitFor(value, aliases) {
|
|
867
|
+
const v = value.trim();
|
|
868
|
+
if (!v) return { error: "--waitFor needs a value: all, local, or an alias list" };
|
|
869
|
+
if (v === "all") return {
|
|
870
|
+
local: true,
|
|
871
|
+
remotes: [...aliases]
|
|
872
|
+
};
|
|
873
|
+
if (v === "local") return {
|
|
874
|
+
local: true,
|
|
875
|
+
remotes: []
|
|
876
|
+
};
|
|
877
|
+
const want = v.split(",").map((s) => s.trim()).filter(Boolean);
|
|
878
|
+
const unknown = want.filter((a) => !aliases.includes(a));
|
|
879
|
+
if (unknown.length) return { error: `--waitFor: unknown remote ${unknown.join(", ")} (configured: ${aliases.join(", ") || "none"})` };
|
|
880
|
+
return {
|
|
881
|
+
local: false,
|
|
882
|
+
remotes: [...new Set(want)]
|
|
883
|
+
};
|
|
884
|
+
}
|
|
885
|
+
/** The `--json` shape for one remote: additive, so a parser can decide on staleness. */
|
|
886
|
+
function jsonRemote(snap, now) {
|
|
887
|
+
return {
|
|
888
|
+
fetchedAt: snap?.fetchedAt ?? null,
|
|
889
|
+
stale: isStale(snap, now),
|
|
890
|
+
ageMs: snap && snap.fetchedAt !== null ? now - snap.fetchedAt : null,
|
|
891
|
+
error: snap?.error ?? null,
|
|
892
|
+
records: snap?.records ?? []
|
|
893
|
+
};
|
|
894
|
+
}
|
|
895
|
+
|
|
765
896
|
//#endregion
|
|
766
897
|
//#region ts/remoteHealth.ts
|
|
767
898
|
/**
|
|
@@ -1737,6 +1868,7 @@ function mergeRecords(...buckets) {
|
|
|
1737
1868
|
}
|
|
1738
1869
|
const SUBCOMMANDS = new Set([
|
|
1739
1870
|
"ls",
|
|
1871
|
+
"ls-refresh",
|
|
1740
1872
|
"list",
|
|
1741
1873
|
"ps",
|
|
1742
1874
|
"status",
|
|
@@ -2011,7 +2143,7 @@ async function runSubcommand(argv) {
|
|
|
2011
2143
|
switch (sub) {
|
|
2012
2144
|
case "ls":
|
|
2013
2145
|
case "list": return await cmdLs(rest);
|
|
2014
|
-
case "ps": return await (await import("./cmdPs-
|
|
2146
|
+
case "ps": return await (await import("./cmdPs-B3jTE4f3.js")).cmdPs(rest);
|
|
2015
2147
|
case "status": return await cmdStatus(rest);
|
|
2016
2148
|
case "whoami": return await cmdWhoami(rest);
|
|
2017
2149
|
case "result": return await cmdResult(rest);
|
|
@@ -2025,6 +2157,7 @@ async function runSubcommand(argv) {
|
|
|
2025
2157
|
case "history": return await (await import("./hist-DZxq7NMA.js")).cmdHist(rest);
|
|
2026
2158
|
case "send": return await cmdSend(rest);
|
|
2027
2159
|
case "send-drain": return await cmdSendDrain(rest);
|
|
2160
|
+
case "ls-refresh": return await cmdLsRefresh(rest);
|
|
2028
2161
|
case "msgs": return await cmdMsgs(rest);
|
|
2029
2162
|
case "key": return await cmdKey(rest);
|
|
2030
2163
|
case "select": return await cmdSelect(rest);
|
|
@@ -2055,51 +2188,51 @@ async function runSubcommand(argv) {
|
|
|
2055
2188
|
}
|
|
2056
2189
|
case "ch":
|
|
2057
2190
|
case "channels": {
|
|
2058
|
-
const { cmdCh } = await import("./channels-
|
|
2191
|
+
const { cmdCh } = await import("./channels-rhxQArqN.js");
|
|
2059
2192
|
return cmdCh(rest);
|
|
2060
2193
|
}
|
|
2061
2194
|
case "term": {
|
|
2062
|
-
const { cmdTerm } = await import("./terminal-
|
|
2195
|
+
const { cmdTerm } = await import("./terminal-DEXrWOa9.js");
|
|
2063
2196
|
return cmdTerm(rest);
|
|
2064
2197
|
}
|
|
2065
2198
|
case "widget": {
|
|
2066
|
-
const { cmdWidget } = await import("./widget-
|
|
2199
|
+
const { cmdWidget } = await import("./widget-DneXqnL8.js");
|
|
2067
2200
|
return cmdWidget(rest);
|
|
2068
2201
|
}
|
|
2069
2202
|
case "mint": {
|
|
2070
|
-
const { cmdMint } = await import("./widget-
|
|
2203
|
+
const { cmdMint } = await import("./widget-DneXqnL8.js");
|
|
2071
2204
|
return cmdMint(rest);
|
|
2072
2205
|
}
|
|
2073
2206
|
case "serve": {
|
|
2074
|
-
const { cmdServe } = await import("./serve-
|
|
2207
|
+
const { cmdServe } = await import("./serve-F5tXYO0-.js");
|
|
2075
2208
|
return cmdServe(rest);
|
|
2076
2209
|
}
|
|
2077
2210
|
case "tray": {
|
|
2078
|
-
const { cmdTray } = await import("./trayApp-
|
|
2211
|
+
const { cmdTray } = await import("./trayApp-0RVjo43f.js");
|
|
2079
2212
|
return cmdTray(rest);
|
|
2080
2213
|
}
|
|
2081
2214
|
case "setup": {
|
|
2082
|
-
const { cmdSetup } = await import("./setup
|
|
2215
|
+
const { cmdSetup } = await import("./setup-mCb1JbTX.js");
|
|
2083
2216
|
return cmdSetup(rest);
|
|
2084
2217
|
}
|
|
2085
2218
|
case "ws": {
|
|
2086
|
-
const { cmdWs } = await import("./ws-
|
|
2219
|
+
const { cmdWs } = await import("./ws-DPw1lVTi.js");
|
|
2087
2220
|
return cmdWs(rest);
|
|
2088
2221
|
}
|
|
2089
2222
|
case "schedule": {
|
|
2090
|
-
const { cmdSchedule } = await import("./schedule-
|
|
2223
|
+
const { cmdSchedule } = await import("./schedule-DmijwnTA.js");
|
|
2091
2224
|
return cmdSchedule(rest);
|
|
2092
2225
|
}
|
|
2093
2226
|
case "remote": {
|
|
2094
|
-
const { cmdRemote } = await import("./remotes-
|
|
2227
|
+
const { cmdRemote } = await import("./remotes-CQTEY8Gt.js");
|
|
2095
2228
|
return cmdRemote(rest);
|
|
2096
2229
|
}
|
|
2097
2230
|
case "share": {
|
|
2098
|
-
const { cmdShare } = await import("./shareCmd-
|
|
2231
|
+
const { cmdShare } = await import("./shareCmd-aOKimJeT.js");
|
|
2099
2232
|
return cmdShare(rest);
|
|
2100
2233
|
}
|
|
2101
2234
|
case "connect": {
|
|
2102
|
-
const { cmdConnect } = await import("./remotes-
|
|
2235
|
+
const { cmdConnect } = await import("./remotes-CQTEY8Gt.js");
|
|
2103
2236
|
return cmdConnect(rest);
|
|
2104
2237
|
}
|
|
2105
2238
|
case "expose": {
|
|
@@ -2107,7 +2240,7 @@ async function runSubcommand(argv) {
|
|
|
2107
2240
|
return cmdExpose(rest);
|
|
2108
2241
|
}
|
|
2109
2242
|
case "callback": {
|
|
2110
|
-
const { cmdCallback } = await import("./callback-
|
|
2243
|
+
const { cmdCallback } = await import("./callback-yWhCmi4k.js");
|
|
2111
2244
|
return cmdCallback(rest);
|
|
2112
2245
|
}
|
|
2113
2246
|
case "reap":
|
|
@@ -2118,7 +2251,7 @@ async function runSubcommand(argv) {
|
|
|
2118
2251
|
return await cmdDsh(rest);
|
|
2119
2252
|
}
|
|
2120
2253
|
case "gc": {
|
|
2121
|
-
const { gcOldBinaryDirs } = await import("./rustBinary-
|
|
2254
|
+
const { gcOldBinaryDirs } = await import("./rustBinary-FhGG3Yfl.js");
|
|
2122
2255
|
const { gcLogs } = await import("./globalPidIndex-DC93JhHQ.js");
|
|
2123
2256
|
const bins = gcOldBinaryDirs();
|
|
2124
2257
|
const logs = await gcLogs();
|
|
@@ -2181,7 +2314,7 @@ async function cmdHelp(managerCommands = true) {
|
|
|
2181
2314
|
const shareAliasNote = managerCommands ? `` : ` (\`ay\` only — \`cy share\`/\`cy connect\` are prompts)\n`;
|
|
2182
2315
|
const self = process.env.AGENT_YES_PID ? await resolveSender() : null;
|
|
2183
2316
|
const agentSection = self ? await buildAgentContextSection(self) : "";
|
|
2184
|
-
process.stdout.write(agentSection + "ay - agent-yes CLI\n\nManagement:\n ay ls [keyword] list running agents\n ay ps [keyword] per-agent CPU/RSS, rolled up over each\n agent's whole process tree, + box vitals\n ay tail [-f] [-n N] <keyword> last N lines (96), -f to follow\n ay tail <keyword> --until TEXT block until TEXT is printed, then exit 0\n [--timeout 10m] [-q] [--regex] (1 = agent exited without it, 2 = no match,\n [--fail-on TEXT] [--count N] 3 = --fail-on hit first)\n ay read <keyword> [page opts] paginate: --last/--head N, --range A:B,\n --before-line L [--limit N]\n" + readAliasNote + " ay cat <keyword> full log\n ay head <keyword> first N lines\n ay hist [-n 6] [--all] [--json] past agent conversations (claude/codex\n transcripts, incl. exited sessions);\n this cwd unless --all\n ay send <keyword> <msg> send a message (keyword '.' = agent in this cwd)\n ay msgs [keyword] [--in|--out] inter-agent message log (sent + received)\n ay ch mk|join|send|read|tail <topic> local-first E2E channels: AI ↔ humans on a topic (ay ch help)\n ay term embed <pid> <script> to embed a live read-only agent terminal in a page (ay term help)\n ay widget ls | read selection|dom read an opted-in page widget's context (selection/DOM) (ay widget help)\n ay mint <target> --caps ... mint a scoped, short-TTL capability token (embeddable in a page)\n ay key <keyword> <key...> send raw keystrokes (down/up/enter/esc/…) — drives menus\n ay select <keyword> <N> pick option N of a needs_input selection menu\n ay attach <keyword> interactive attach (detach: Ctrl-\\)\n ay stop <keyword> graceful shutdown (/exit for claude/codex)\n ay exit <keyword> [reason] graceful shutdown, recording who/why (= 'ay send <kw> exit')\n ay restart <keyword> [--fresh] stop (if live) + relaunch resuming the session; --fresh replays the prompt\n ay status <keyword> agent status snapshot\n ay notify watch --unread get notified when sub-agents finish/stuck/crash (writes to inbox)\n ay whoami [--json] (inside an agent) your own registry identity + reply address\n ay result <keyword> [--wait] pull an agent's structured result envelope\n ay result set '<json>' (inside an agent) deposit your result envelope\n ay reap kill process groups leaked by dead agents\n ay gc reclaim old-version binary cache dirs + stale session logs\n ay dsh-legacy [args...] launch the DeepSeek Harness terminal client (dsh-tui)\n" + wsLines + "\nRemote:\n" + setupLine + " ay share [local|lan|tailscale|webrtc] share this machine: one URL for web console + CLI\n ay connect <share-url> [alias] save another machine's share URL as a remote\n" + shareAliasNote + " ay schedule <when> <cli> -- <msg> run an agent on a schedule (HH:MM or cron)\n ay serve [--port N] start HTTP API server (prints token)\n ay serve status show serve daemon/server status\n ay remote add <alias> http://<token>@<host>:<port>\n ay remote ls / rm <alias> manage saved remotes\n ay expose <port> share localhost:<port> at https://<id>.agent-yes.com (private link)\n ay callback --expires 7d mint an embeddable message-me widget for one agent\n ay ls <token>@<host>:<port> connect inline (no alias needed)\n ay send <token>@<host>:<port>:<kw> <msg>\n\nRun an agent (naming a CLI is what launches one — bare 'ay' shows this help):\n ay <claude|codex|...> [options] -- [prompt]\n ay claude -- \"fix the bug in auth.ts\"\n cy [options] -- [prompt] shortcut for 'ay claude' (bare 'cy' starts claude)\n ay claude --help full agent-runner options\n\nLabs (examples at https://github.com/snomiao/agent-yes/tree/main/lab):\n local-role-play/ designer + builder on one machine\n http-remote/ ay serve remote access demo\n p2p-pairing/ libp2p P2P (needs: cargo build --features swarm)\n");
|
|
2317
|
+
process.stdout.write(agentSection + "ay - agent-yes CLI\n\nManagement:\n ay ls [keyword] list running agents (remotes from cache, refreshed\n [--waitFor=all|local|<alias>] in the background; --waitFor blocks for fresh rows)\n ay ps [keyword] per-agent CPU/RSS, rolled up over each\n agent's whole process tree, + box vitals\n ay tail [-f] [-n N] <keyword> last N lines (96), -f to follow\n ay tail <keyword> --until TEXT block until TEXT is printed, then exit 0\n [--timeout 10m] [-q] [--regex] (1 = agent exited without it, 2 = no match,\n [--fail-on TEXT] [--count N] 3 = --fail-on hit first)\n ay read <keyword> [page opts] paginate: --last/--head N, --range A:B,\n --before-line L [--limit N]\n" + readAliasNote + " ay cat <keyword> full log\n ay head <keyword> first N lines\n ay hist [-n 6] [--all] [--json] past agent conversations (claude/codex\n transcripts, incl. exited sessions);\n this cwd unless --all\n ay send <keyword> <msg> send a message (keyword '.' = agent in this cwd)\n ay msgs [keyword] [--in|--out] inter-agent message log (sent + received)\n ay ch mk|join|send|read|tail <topic> local-first E2E channels: AI ↔ humans on a topic (ay ch help)\n ay term embed <pid> <script> to embed a live read-only agent terminal in a page (ay term help)\n ay widget ls | read selection|dom read an opted-in page widget's context (selection/DOM) (ay widget help)\n ay mint <target> --caps ... mint a scoped, short-TTL capability token (embeddable in a page)\n ay key <keyword> <key...> send raw keystrokes (down/up/enter/esc/…) — drives menus\n ay select <keyword> <N> pick option N of a needs_input selection menu\n ay attach <keyword> interactive attach (detach: Ctrl-\\)\n ay stop <keyword> graceful shutdown (/exit for claude/codex)\n ay exit <keyword> [reason] graceful shutdown, recording who/why (= 'ay send <kw> exit')\n ay restart <keyword> [--fresh] stop (if live) + relaunch resuming the session; --fresh replays the prompt\n ay status <keyword> agent status snapshot\n ay notify watch --unread get notified when sub-agents finish/stuck/crash (writes to inbox)\n ay whoami [--json] (inside an agent) your own registry identity + reply address\n ay result <keyword> [--wait] pull an agent's structured result envelope\n ay result set '<json>' (inside an agent) deposit your result envelope\n ay reap kill process groups leaked by dead agents\n ay gc reclaim old-version binary cache dirs + stale session logs\n ay dsh-legacy [args...] launch the DeepSeek Harness terminal client (dsh-tui)\n" + wsLines + "\nRemote:\n" + setupLine + " ay share [local|lan|tailscale|webrtc] share this machine: one URL for web console + CLI\n ay connect <share-url> [alias] save another machine's share URL as a remote\n" + shareAliasNote + " ay schedule <when> <cli> -- <msg> run an agent on a schedule (HH:MM or cron)\n ay serve [--port N] start HTTP API server (prints token)\n ay serve status show serve daemon/server status\n ay remote add <alias> http://<token>@<host>:<port>\n ay remote ls / rm <alias> manage saved remotes\n ay expose <port> share localhost:<port> at https://<id>.agent-yes.com (private link)\n ay callback --expires 7d mint an embeddable message-me widget for one agent\n ay ls <token>@<host>:<port> connect inline (no alias needed)\n ay send <token>@<host>:<port>:<kw> <msg>\n\nRun an agent (naming a CLI is what launches one — bare 'ay' shows this help):\n ay <claude|codex|...> [options] -- [prompt]\n ay claude -- \"fix the bug in auth.ts\"\n cy [options] -- [prompt] shortcut for 'ay claude' (bare 'cy' starts claude)\n ay claude --help full agent-runner options\n\nLabs (examples at https://github.com/snomiao/agent-yes/tree/main/lab):\n local-role-play/ designer + builder on one machine\n http-remote/ ay serve remote access demo\n p2p-pairing/ libp2p P2P (needs: cargo build --features swarm)\n");
|
|
2185
2318
|
return 0;
|
|
2186
2319
|
}
|
|
2187
2320
|
/** Looks like an agent_id (12 hex, or a longer injected one) rather than a word. */
|
|
@@ -2380,6 +2513,13 @@ async function runRemoteLs(remote, opts) {
|
|
|
2380
2513
|
return 1;
|
|
2381
2514
|
}
|
|
2382
2515
|
const records = await res.json();
|
|
2516
|
+
if (!remote.keyword && (await readRemotes()).has(remote.label)) {
|
|
2517
|
+
const now = Date.now();
|
|
2518
|
+
await writeRemoteSnapshot(noteSnapshotResult(await readRemoteSnapshot(remote.label), remote.label, {
|
|
2519
|
+
ok: true,
|
|
2520
|
+
records
|
|
2521
|
+
}, now)).catch(() => {});
|
|
2522
|
+
}
|
|
2383
2523
|
if (records.length === 0) {
|
|
2384
2524
|
process.stderr.write(remote.keyword ? `no agents matched "${remote.keyword}" on ${remote.label}\n` : `no running agents on ${remote.label}\n`);
|
|
2385
2525
|
return 0;
|
|
@@ -2717,7 +2857,7 @@ async function fetchRemoteRecordsRaw(url, token, opts, onReachable) {
|
|
|
2717
2857
|
let base = url;
|
|
2718
2858
|
let bearer = token;
|
|
2719
2859
|
if (isWebrtcSpec(url)) {
|
|
2720
|
-
const { startWebrtcBridge } = await import("./webrtcRemote-
|
|
2860
|
+
const { startWebrtcBridge } = await import("./webrtcRemote-Cy5kB8aR.js");
|
|
2721
2861
|
bridge = await startWebrtcBridge(url);
|
|
2722
2862
|
base = bridge.baseUrl;
|
|
2723
2863
|
bearer = bridge.token;
|
|
@@ -2729,13 +2869,93 @@ async function fetchRemoteRecordsRaw(url, token, opts, onReachable) {
|
|
|
2729
2869
|
onReachable?.(true);
|
|
2730
2870
|
if (!res.ok) return [];
|
|
2731
2871
|
return await res.json();
|
|
2732
|
-
} catch {
|
|
2733
|
-
onReachable?.(false);
|
|
2872
|
+
} catch (e) {
|
|
2873
|
+
onReachable?.(false, String(e?.message ?? e).split("\n")[0]);
|
|
2734
2874
|
return [];
|
|
2735
2875
|
} finally {
|
|
2736
2876
|
bridge?.close();
|
|
2737
2877
|
}
|
|
2738
2878
|
}
|
|
2879
|
+
/** Probe one remote and fold the outcome into its snapshot. Never throws. */
|
|
2880
|
+
async function refreshRemoteSnapshot(alias, cfg, opts, now) {
|
|
2881
|
+
let ok = false;
|
|
2882
|
+
let why = "did not answer";
|
|
2883
|
+
const records = await fetchRemoteRecordsRaw(cfg.url, cfg.token, opts, (r, w) => {
|
|
2884
|
+
ok = r;
|
|
2885
|
+
if (w) why = w;
|
|
2886
|
+
});
|
|
2887
|
+
const snap = noteSnapshotResult(await readRemoteSnapshot(alias), alias, ok ? {
|
|
2888
|
+
ok: true,
|
|
2889
|
+
records
|
|
2890
|
+
} : {
|
|
2891
|
+
ok: false,
|
|
2892
|
+
message: why
|
|
2893
|
+
}, now);
|
|
2894
|
+
await writeRemoteSnapshot(snap).catch(() => {});
|
|
2895
|
+
return {
|
|
2896
|
+
snap,
|
|
2897
|
+
ok
|
|
2898
|
+
};
|
|
2899
|
+
}
|
|
2900
|
+
/** Run `ay <args…>` detached from this process (same launcher as the send drainer). */
|
|
2901
|
+
function spawnDetachedAy(args) {
|
|
2902
|
+
const launcher = drainerLauncher(process.execPath, process.argv[1], Bun.which("ay"));
|
|
2903
|
+
if (!launcher) return null;
|
|
2904
|
+
const [cmd, ...pre] = launcher;
|
|
2905
|
+
try {
|
|
2906
|
+
const child = spawn$1(cmd, [...pre, ...args], {
|
|
2907
|
+
detached: true,
|
|
2908
|
+
stdio: "ignore"
|
|
2909
|
+
});
|
|
2910
|
+
child.on("error", () => {});
|
|
2911
|
+
child.unref();
|
|
2912
|
+
return child.pid ?? null;
|
|
2913
|
+
} catch {
|
|
2914
|
+
return null;
|
|
2915
|
+
}
|
|
2916
|
+
}
|
|
2917
|
+
/**
|
|
2918
|
+
* `ay ls-refresh <alias…> [--all] [--active] [--keyword k]` — hidden: the
|
|
2919
|
+
* background half of cached-first `ay ls`. Probes each alias once, writes its
|
|
2920
|
+
* snapshot and health, and exits. One refresher per alias at a time (pid
|
|
2921
|
+
* lock), so a burst of listings does not fan out into a burst of probes.
|
|
2922
|
+
*/
|
|
2923
|
+
async function cmdLsRefresh(rest) {
|
|
2924
|
+
const aliases = [];
|
|
2925
|
+
const opts = {
|
|
2926
|
+
all: false,
|
|
2927
|
+
active: false,
|
|
2928
|
+
keyword: void 0
|
|
2929
|
+
};
|
|
2930
|
+
for (let i = 0; i < rest.length; i++) {
|
|
2931
|
+
const a = rest[i];
|
|
2932
|
+
if (a === "--all") opts.all = true;
|
|
2933
|
+
else if (a === "--active") opts.active = true;
|
|
2934
|
+
else if (a === "--keyword") opts.keyword = rest[++i];
|
|
2935
|
+
else if (a.startsWith("--keyword=")) opts.keyword = a.slice(10);
|
|
2936
|
+
else if (!a.startsWith("-")) aliases.push(a);
|
|
2937
|
+
}
|
|
2938
|
+
const remotes = await readRemotes();
|
|
2939
|
+
const now = Date.now();
|
|
2940
|
+
await Promise.all(aliases.map(async (alias) => {
|
|
2941
|
+
const cfg = remotes.get(alias);
|
|
2942
|
+
if (!cfg) return;
|
|
2943
|
+
const lock = path.join(agentYesHome(), "remotes", `${encodeURIComponent(alias)}.refresh.lock`);
|
|
2944
|
+
const owner = Number((await readFile(lock, "utf-8").catch(() => "")).trim());
|
|
2945
|
+
if (owner > 0 && owner !== process.pid && pidAlive(owner)) return;
|
|
2946
|
+
await mkdir(path.dirname(lock), { recursive: true }).catch(() => {});
|
|
2947
|
+
await writeFile(lock, String(process.pid)).catch(() => {});
|
|
2948
|
+
try {
|
|
2949
|
+
const { ok } = await refreshRemoteSnapshot(alias, cfg, opts, now);
|
|
2950
|
+
const health = await readRemoteHealth();
|
|
2951
|
+
health[alias] = noteRemoteResult(health[alias], ok, now);
|
|
2952
|
+
await writeRemoteHealth(health).catch(() => {});
|
|
2953
|
+
} finally {
|
|
2954
|
+
await rm(lock, { force: true }).catch(() => {});
|
|
2955
|
+
}
|
|
2956
|
+
}));
|
|
2957
|
+
return 0;
|
|
2958
|
+
}
|
|
2739
2959
|
async function runAllRemotesLs(opts) {
|
|
2740
2960
|
const remotes = await readRemotes();
|
|
2741
2961
|
const localOpts = {
|
|
@@ -2747,30 +2967,72 @@ async function runAllRemotesLs(opts) {
|
|
|
2747
2967
|
};
|
|
2748
2968
|
const health = await readRemoteHealth();
|
|
2749
2969
|
const healthNow = Date.now();
|
|
2750
|
-
const
|
|
2751
|
-
const
|
|
2752
|
-
|
|
2970
|
+
const waiting = new Set(opts.waitFor?.remotes ?? []);
|
|
2971
|
+
const fetchOpts = {
|
|
2972
|
+
all: opts.all,
|
|
2973
|
+
active: opts.active,
|
|
2974
|
+
keyword: opts.keyword
|
|
2975
|
+
};
|
|
2976
|
+
const [localResult, ...waited] = await Promise.allSettled([listRecords(opts.keyword, localOpts).then((recs) => ({
|
|
2753
2977
|
host: "local",
|
|
2754
2978
|
records: recs
|
|
2755
|
-
})), ...
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2979
|
+
})), ...[...remotes.entries()].filter(([alias]) => waiting.has(alias)).map(async ([alias, cfg]) => {
|
|
2980
|
+
const budget = new Promise((resolve) => {
|
|
2981
|
+
setTimeout(() => resolve("timeout"), opts.timeoutMs).unref?.();
|
|
2982
|
+
});
|
|
2983
|
+
const res = await Promise.race([refreshRemoteSnapshot(alias, cfg, fetchOpts, healthNow), budget]);
|
|
2984
|
+
if (res === "timeout") return {
|
|
2985
|
+
alias,
|
|
2986
|
+
snap: await readRemoteSnapshot(alias),
|
|
2987
|
+
ok: false,
|
|
2988
|
+
failure: `timed out after ${Math.round(opts.timeoutMs / 1e3)}s`
|
|
2989
|
+
};
|
|
2990
|
+
const failure = res.ok ? null : res.snap.error ? `unreachable since ${hhmm(res.snap.error.since)} (${res.snap.error.message})` : "unreachable";
|
|
2991
|
+
return {
|
|
2992
|
+
alias,
|
|
2993
|
+
snap: res.snap,
|
|
2994
|
+
ok: res.ok,
|
|
2995
|
+
failure
|
|
2996
|
+
};
|
|
2764
2997
|
})]);
|
|
2765
|
-
{
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2998
|
+
const nextHealth = pruneRemoteHealth({ ...health }, remotes.keys());
|
|
2999
|
+
const waitedByAlias = /* @__PURE__ */ new Map();
|
|
3000
|
+
for (const r of waited) if (r.status === "fulfilled") waitedByAlias.set(r.value.alias, r.value);
|
|
3001
|
+
const sections = [];
|
|
3002
|
+
const background = [];
|
|
3003
|
+
for (const [alias] of remotes) {
|
|
3004
|
+
const w = waitedByAlias.get(alias);
|
|
3005
|
+
if (w) {
|
|
3006
|
+
nextHealth[alias] = noteRemoteResult(health[alias], w.ok, healthNow);
|
|
3007
|
+
sections.push({
|
|
3008
|
+
alias,
|
|
3009
|
+
snap: w.snap,
|
|
3010
|
+
live: w.ok,
|
|
3011
|
+
refreshing: false,
|
|
3012
|
+
failure: w.failure
|
|
3013
|
+
});
|
|
3014
|
+
continue;
|
|
2771
3015
|
}
|
|
2772
|
-
await
|
|
3016
|
+
const snap = await readRemoteSnapshot(alias);
|
|
3017
|
+
const refreshing = isStale(snap, healthNow) && !shouldSkipRemote(health[alias], healthNow);
|
|
3018
|
+
if (refreshing) background.push(alias);
|
|
3019
|
+
sections.push({
|
|
3020
|
+
alias,
|
|
3021
|
+
snap,
|
|
3022
|
+
live: false,
|
|
3023
|
+
refreshing,
|
|
3024
|
+
failure: null
|
|
3025
|
+
});
|
|
2773
3026
|
}
|
|
3027
|
+
await writeRemoteHealth(nextHealth).catch(() => {});
|
|
3028
|
+
if (background.length) spawnDetachedAy([
|
|
3029
|
+
"ls-refresh",
|
|
3030
|
+
...background,
|
|
3031
|
+
...opts.all ? ["--all"] : [],
|
|
3032
|
+
...opts.active ? ["--active"] : [],
|
|
3033
|
+
...opts.keyword ? ["--keyword", opts.keyword] : []
|
|
3034
|
+
]);
|
|
3035
|
+
const failed = sections.some((s) => waiting.has(s.alias) && s.failure);
|
|
2774
3036
|
const byHost = [];
|
|
2775
3037
|
const gitRootCache = /* @__PURE__ */ new Map();
|
|
2776
3038
|
const gitInfoCache = /* @__PURE__ */ new Map();
|
|
@@ -2803,25 +3065,45 @@ async function runAllRemotesLs(opts) {
|
|
|
2803
3065
|
records: enriched
|
|
2804
3066
|
});
|
|
2805
3067
|
}
|
|
2806
|
-
for (const
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
process.stderr.write(`${alias}: unreachable — skipped after ${h?.streak ?? 0} failed attempt(s), retrying in ${left} (ay ls ${alias} to force a probe now)\n`);
|
|
2811
|
-
}
|
|
2812
|
-
for (const res of remoteResults) if (res.status === "fulfilled") byHost.push({
|
|
2813
|
-
host: res.value.host,
|
|
2814
|
-
records: res.value.records
|
|
3068
|
+
for (const s of sections) byHost.push({
|
|
3069
|
+
host: s.alias,
|
|
3070
|
+
records: s.snap?.records ?? [],
|
|
3071
|
+
cached: !s.live
|
|
2815
3072
|
});
|
|
3073
|
+
if (opts.json) {
|
|
3074
|
+
const out = {
|
|
3075
|
+
local: byHost.find((h) => h.host === "local")?.records ?? [],
|
|
3076
|
+
remotes: Object.fromEntries(sections.map((s) => [s.alias, {
|
|
3077
|
+
...jsonRemote(s.snap, healthNow),
|
|
3078
|
+
live: s.live,
|
|
3079
|
+
refreshing: s.refreshing,
|
|
3080
|
+
failure: s.failure
|
|
3081
|
+
}]))
|
|
3082
|
+
};
|
|
3083
|
+
await writeStdoutFlushed(JSON.stringify(out, null, 2) + "\n");
|
|
3084
|
+
return failed ? 1 : 0;
|
|
3085
|
+
}
|
|
3086
|
+
if (sections.length) {
|
|
3087
|
+
const aliasW = Math.max(...sections.map((s) => s.alias.length));
|
|
3088
|
+
for (const s of sections) process.stdout.write(`${s.alias.padEnd(aliasW)} ${s.failure ?? (s.live ? "live" : cacheAgeLabel(s.snap, healthNow, s.refreshing))}\n`);
|
|
3089
|
+
process.stdout.write("\n");
|
|
3090
|
+
}
|
|
2816
3091
|
const rows = [];
|
|
2817
|
-
for (const { host, records } of byHost) for (const { record, prefix } of flattenForest(buildAgentForest(records))) rows.push({
|
|
3092
|
+
for (const { host, records, cached } of byHost) for (const { record, prefix } of flattenForest(buildAgentForest(records))) rows.push({
|
|
2818
3093
|
host,
|
|
2819
3094
|
rec: record,
|
|
2820
|
-
prefix
|
|
3095
|
+
prefix,
|
|
3096
|
+
cached: cached === true
|
|
2821
3097
|
});
|
|
3098
|
+
const anyCached = sections.some((s) => !s.live);
|
|
3099
|
+
const footer = () => {
|
|
3100
|
+
if (rows.some((r) => r.cached)) process.stderr.write(`* = from cache (AGE is real; STATUS may have changed since)\n`);
|
|
3101
|
+
if (anyCached) process.stderr.write(`ay ls --waitFor=all # block until fresh data from every local+remote machine\n`);
|
|
3102
|
+
};
|
|
2822
3103
|
if (rows.length === 0) {
|
|
2823
3104
|
process.stderr.write("no running agents\n");
|
|
2824
|
-
|
|
3105
|
+
footer();
|
|
3106
|
+
return failed ? 1 : 0;
|
|
2825
3107
|
}
|
|
2826
3108
|
const termWidth = process.stdout.columns ?? 120;
|
|
2827
3109
|
const now = Date.now();
|
|
@@ -2830,7 +3112,8 @@ async function runAllRemotesLs(opts) {
|
|
|
2830
3112
|
const hostW = Math.max(4, ...rows.map((r) => r.host.length));
|
|
2831
3113
|
const pidW = Math.max(3, ...rows.map((r) => String(r.rec.pid).length));
|
|
2832
3114
|
const cliW = Math.max(3, ...rows.map((r) => String(r.rec.cli).length));
|
|
2833
|
-
const
|
|
3115
|
+
const statusOf = (r) => String(r.rec.status) + (r.cached ? "*" : "");
|
|
3116
|
+
const statusW = Math.max(6, ...rows.map((r) => statusOf(r).length));
|
|
2834
3117
|
const ageW = Math.max(3, ...rows.map((r) => ageOf(r.rec).length));
|
|
2835
3118
|
const cwdW = Math.max(3, ...rows.map((r) => cwdLabel(r.rec).length));
|
|
2836
3119
|
const promptBudget = Math.max(20, termWidth - hostW - pidW - cliW - statusW - ageW - cwdW - 12 - 1);
|
|
@@ -2843,7 +3126,8 @@ async function runAllRemotesLs(opts) {
|
|
|
2843
3126
|
"CWD".padEnd(cwdW),
|
|
2844
3127
|
"PROMPT"
|
|
2845
3128
|
].join(" ") + "\n");
|
|
2846
|
-
for (const
|
|
3129
|
+
for (const row of rows) {
|
|
3130
|
+
const { host, rec, prefix } = row;
|
|
2847
3131
|
const flagStr = badgeLabels(rec.badges);
|
|
2848
3132
|
const branchStr = branchLabel(rec.git);
|
|
2849
3133
|
const gitStr = gitLabel(rec.git);
|
|
@@ -2854,13 +3138,14 @@ async function runAllRemotesLs(opts) {
|
|
|
2854
3138
|
host.padEnd(hostW),
|
|
2855
3139
|
String(rec.pid).padEnd(pidW),
|
|
2856
3140
|
String(rec.cli).padEnd(cliW),
|
|
2857
|
-
|
|
3141
|
+
statusOf(row).padEnd(statusW),
|
|
2858
3142
|
ageOf(rec).padEnd(ageW),
|
|
2859
3143
|
cwdLabel(rec).padEnd(cwdW),
|
|
2860
3144
|
label
|
|
2861
3145
|
].join(" ") + "\n");
|
|
2862
3146
|
}
|
|
2863
|
-
|
|
3147
|
+
footer();
|
|
3148
|
+
return failed ? 1 : 0;
|
|
2864
3149
|
}
|
|
2865
3150
|
/**
|
|
2866
3151
|
* Cheap live status from liveness + log quiescence only (no log-content read):
|
|
@@ -3196,12 +3481,19 @@ async function cmdLs(rest) {
|
|
|
3196
3481
|
type: "boolean",
|
|
3197
3482
|
default: false,
|
|
3198
3483
|
description: "Only this machine's agents — skip configured remotes (the pre-default behaviour)"
|
|
3484
|
+
}).option("waitFor", {
|
|
3485
|
+
type: "string",
|
|
3486
|
+
description: "Block until fresh rows from this set instead of printing cached remote rows: all | local | <alias>[,<alias>…]. Each remote gets --timeout; a failure is reported per remote"
|
|
3487
|
+
}).option("timeout", {
|
|
3488
|
+
type: "number",
|
|
3489
|
+
default: 30,
|
|
3490
|
+
description: "Seconds to wait per remote with --waitFor"
|
|
3199
3491
|
}).option("help", {
|
|
3200
3492
|
alias: "h",
|
|
3201
3493
|
type: "boolean",
|
|
3202
3494
|
default: false,
|
|
3203
3495
|
description: "Show this help"
|
|
3204
|
-
}).example("ay ls", "
|
|
3496
|
+
}).example("ay ls", "local live + remotes from their last snapshot (refreshed in the background)").example("ay ls --waitFor=all", "block until every machine answered (or timed out)").example("ay ls --json --all-remotes", "{local, remotes:{alias:{fetchedAt, stale, records…}}}").example("ay ls --local", "only this machine's agents").example("ay ls --all", "include exited agents").example("ay ls --json", "machine-readable output").example("ay ls --watch", "stream state transitions for a whole fan-out as NDJSON").example("ay ls myrepo", "filter by cwd/prompt keyword").help(false).version(false).exitProcess(false);
|
|
3205
3497
|
const argv = await y.parseAsync();
|
|
3206
3498
|
if (argv.help || argv.h) {
|
|
3207
3499
|
process.stdout.write(await y.getHelp() + "\n");
|
|
@@ -3254,12 +3546,24 @@ async function cmdLs(rest) {
|
|
|
3254
3546
|
});
|
|
3255
3547
|
return 0;
|
|
3256
3548
|
}
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3549
|
+
const remotesCfg = await readRemotes();
|
|
3550
|
+
let waitFor = null;
|
|
3551
|
+
if (typeof argv.waitFor === "string") {
|
|
3552
|
+
const parsed = parseWaitFor(argv.waitFor, [...remotesCfg.keys()]);
|
|
3553
|
+
if ("error" in parsed) {
|
|
3554
|
+
process.stderr.write(parsed.error + "\n");
|
|
3555
|
+
return 2;
|
|
3556
|
+
}
|
|
3557
|
+
waitFor = parsed;
|
|
3558
|
+
}
|
|
3559
|
+
if (!argv.local && !argv.latest && (!opts.json || argv["all-remotes"] || waitFor)) {
|
|
3560
|
+
if (argv["all-remotes"] || waitFor || remotesCfg.size > 0) return runAllRemotesLs({
|
|
3260
3561
|
all: argv.all,
|
|
3261
3562
|
active: argv.active,
|
|
3262
|
-
keyword
|
|
3563
|
+
keyword,
|
|
3564
|
+
waitFor,
|
|
3565
|
+
timeoutMs: Math.max(1, Number(argv.timeout) || 30) * 1e3,
|
|
3566
|
+
json: opts.json
|
|
3263
3567
|
});
|
|
3264
3568
|
}
|
|
3265
3569
|
const records = await listRecords(keyword, opts);
|
|
@@ -3371,16 +3675,6 @@ async function cmdLs(rest) {
|
|
|
3371
3675
|
}
|
|
3372
3676
|
return 0;
|
|
3373
3677
|
}
|
|
3374
|
-
function humanizeAge(ms) {
|
|
3375
|
-
if (ms < 1e3) return "0s";
|
|
3376
|
-
const s = Math.floor(ms / 1e3);
|
|
3377
|
-
if (s < 60) return `${s}s`;
|
|
3378
|
-
const m = Math.floor(s / 60);
|
|
3379
|
-
if (m < 60) return `${m}m`;
|
|
3380
|
-
const h = Math.floor(m / 60);
|
|
3381
|
-
if (h < 24) return `${h}h`;
|
|
3382
|
-
return `${Math.floor(h / 24)}d`;
|
|
3383
|
-
}
|
|
3384
3678
|
function shortenPath(p) {
|
|
3385
3679
|
const home = homedir();
|
|
3386
3680
|
return p.startsWith(home) ? "~" + p.slice(home.length) : p;
|
|
@@ -4625,11 +4919,24 @@ function inspectSubmission(screen, identity) {
|
|
|
4625
4919
|
composerVisible: false,
|
|
4626
4920
|
inComposer: false
|
|
4627
4921
|
};
|
|
4628
|
-
|
|
4629
|
-
if (prompt < 0 || !identity.trim()) return unknown;
|
|
4922
|
+
if (!identity.trim()) return unknown;
|
|
4630
4923
|
const compact = (text) => text.replace(/\s+/gu, "");
|
|
4631
4924
|
const needle = compact(identity);
|
|
4632
4925
|
const wrapped = identity.startsWith("<ay-msg ");
|
|
4926
|
+
const prompt = composerPromptRow(screen);
|
|
4927
|
+
if (prompt < 0) {
|
|
4928
|
+
if (wrapped) return unknown;
|
|
4929
|
+
let last = screen.length - 1;
|
|
4930
|
+
while (last >= 0 && !screen[last].trim()) last--;
|
|
4931
|
+
const echoed = screen.slice(0, Math.max(0, last)).filter((line) => compact(line).endsWith(needle)).length;
|
|
4932
|
+
return echoed ? {
|
|
4933
|
+
submission: "submitted",
|
|
4934
|
+
retry: false,
|
|
4935
|
+
transcriptMatches: echoed,
|
|
4936
|
+
composerVisible: false,
|
|
4937
|
+
inComposer: false
|
|
4938
|
+
} : unknown;
|
|
4939
|
+
}
|
|
4633
4940
|
const transcriptLines = screen.slice(0, prompt);
|
|
4634
4941
|
const transcript = compact(transcriptLines.join("\n"));
|
|
4635
4942
|
let transcriptMatches = wrapped ? transcript.split(needle).length - 1 : 0;
|
|
@@ -5698,7 +6005,7 @@ async function cmdAttach(rest) {
|
|
|
5698
6005
|
const winsizeDir = path.join(ayHome, "winsize");
|
|
5699
6006
|
await mkdir(winsizeDir, { recursive: true });
|
|
5700
6007
|
const winsizePath = path.join(winsizeDir, String(record.pid));
|
|
5701
|
-
const { resolveDaemonHttpBase, loadTokenReadOnly } = await import("./serve-
|
|
6008
|
+
const { resolveDaemonHttpBase, loadTokenReadOnly } = await import("./serve-F5tXYO0-.js");
|
|
5702
6009
|
const daemonBase = await resolveDaemonHttpBase().catch(() => null);
|
|
5703
6010
|
const daemonToken = daemonBase ? await loadTokenReadOnly().catch(() => null) ?? "" : "";
|
|
5704
6011
|
const capViewer = `attach:${process.pid}`;
|
|
@@ -5744,12 +6051,32 @@ async function cmdAttach(rest) {
|
|
|
5744
6051
|
await writeWinsizeDirect(cols, rows);
|
|
5745
6052
|
};
|
|
5746
6053
|
for (let i = 0; i < 15 && (!process.stdout.columns || !process.stdout.rows); i++) await new Promise((r) => setTimeout(r, 100));
|
|
6054
|
+
if (process.platform === "win32" && !capMode) {
|
|
6055
|
+
const cols = process.stdout.columns ?? 0;
|
|
6056
|
+
const rows = process.stdout.rows ?? 0;
|
|
6057
|
+
if (cols > 0 && rows > 1) {
|
|
6058
|
+
await writeWinsizeDirect(cols, rows - 1);
|
|
6059
|
+
await new Promise((r) => setTimeout(r, 400));
|
|
6060
|
+
}
|
|
6061
|
+
}
|
|
5747
6062
|
await sendResize();
|
|
5748
6063
|
const capHeartbeat = capMode ? setInterval(() => {
|
|
5749
6064
|
if (capMode) sendResize();
|
|
5750
6065
|
}, 5e3) : null;
|
|
6066
|
+
const directHeartbeat = !capMode ? setInterval(() => {
|
|
6067
|
+
const cols = process.stdout.columns ?? 0;
|
|
6068
|
+
const rows = process.stdout.rows ?? 0;
|
|
6069
|
+
if (cols > 0 && rows > 0) writeFile(winsizePath, `${cols} ${rows} ${Date.now()}\n`).catch(() => {});
|
|
6070
|
+
}, 1e4) : null;
|
|
5751
6071
|
const withdrawAttachCap = async () => {
|
|
5752
6072
|
if (capHeartbeat) clearInterval(capHeartbeat);
|
|
6073
|
+
if (directHeartbeat) clearInterval(directHeartbeat);
|
|
6074
|
+
if (!capMode) {
|
|
6075
|
+
await rm(winsizePath, { force: true }).catch(() => {});
|
|
6076
|
+
try {
|
|
6077
|
+
process.kill(record.pid, "SIGWINCH");
|
|
6078
|
+
} catch {}
|
|
6079
|
+
}
|
|
5753
6080
|
if (!daemonBase) return;
|
|
5754
6081
|
try {
|
|
5755
6082
|
await fetch(`${daemonBase}/api/presence?token=${encodeURIComponent(daemonToken)}`, {
|
|
@@ -6427,7 +6754,7 @@ async function cmdNotify(rest) {
|
|
|
6427
6754
|
}
|
|
6428
6755
|
const ensure = async () => {
|
|
6429
6756
|
if (!argv["ensure-daemon"]) return;
|
|
6430
|
-
const { ensureDaemon } = await import("./notifyDaemon-
|
|
6757
|
+
const { ensureDaemon } = await import("./notifyDaemon-BIo7Nkk2.js");
|
|
6431
6758
|
await ensureDaemon().catch(() => null);
|
|
6432
6759
|
};
|
|
6433
6760
|
await heartbeatWatcher(parent, selfStartedAt);
|
|
@@ -6501,7 +6828,7 @@ async function cmdNotifyCursor(args) {
|
|
|
6501
6828
|
}
|
|
6502
6829
|
async function cmdNotifyd(rest) {
|
|
6503
6830
|
const sub = rest[0] ?? "status";
|
|
6504
|
-
const daemon = await import("./notifyDaemon-
|
|
6831
|
+
const daemon = await import("./notifyDaemon-BIo7Nkk2.js");
|
|
6505
6832
|
switch (sub) {
|
|
6506
6833
|
case "run": return daemon.runDaemon();
|
|
6507
6834
|
case "once": return daemon.runDaemon({ once: true });
|
|
@@ -6528,4 +6855,4 @@ async function cmdNotifyd(rest) {
|
|
|
6528
6855
|
|
|
6529
6856
|
//#endregion
|
|
6530
6857
|
export { renderLogTailLines as $, isAgentStuck as A, lastStdinAt as B, extractBadges as C, finalizedLines as D, extractTaskCounts as E, isSubcommand as F, probeStdinReachable as G, matchKeyword as H, isUnknownManagerToken as I, readLogForRender as J, readAgentPtysize as K, isUnreachableWriteErrno as L, isExitRequest as M, isPidAlive as N, inspectSubmission as O, isSlashCommand as P, recentReadEdges as Q, isUserTyping as R, envelopeCostFor as S, extractNeedsInput as T, menuSelectKeys as U, listRecords as V, ownedCollapsedPaste as W, readPtysize as X, readNotes as Y, recentMessageEdges as Z, cwdLabel as _, shouldFramePaste as _t, READ_WINDOW_MS as a, restartHintLines as at, drainerLauncher as b, flattenForest as bt, SEND_EXIT_UNREACHABLE as c, snapshotStatus as ct, buildEnvelope as d, submitAndConfirm as dt, renderRawLog as et, cliDefaults as f, waitForLogQuiet as ft, cursorAbs as g, framePaste as gt, controlCodeFromName as h, withIpcLock as ht, READ_PAGE_DEFAULT as i, resolveResumeArgs as it, isBareManagerInvocation as j, isAgentIdShaped as k, TYPING_WINDOW_MS as l, stdinActivityPath as lt, confirmStdinUnreachable as m, writeToIpc as mt, GRACEFUL_EXIT_COMMANDS as n, resolveOne as nt, SEND_BODY_MAX_CHARS as o, runSubcommand as ot, cmdHelp as p, writeKeysPaced as pt, readComposerState as q, MAX_RENDER_BYTES as r, resolveReadWindow as rt, SEND_EXIT_QUEUED as s, sendPayloadCapError as st, COMPOSER_WINDOWS as t, renderRawLogLines as tt, backoffWhileTyping as u, stopTipForCli as ut, deriveLiveState as v, TYPING_BADGE as vt, extractMenu as w, envelopeAttribution as x, deriveLiveStatus as y, buildAgentForest as yt, lastReadAt as z };
|
|
6531
|
-
//# sourceMappingURL=subcommands-
|
|
6858
|
+
//# sourceMappingURL=subcommands-Ddo2TYZs.js.map
|