agent-yes 1.127.2 → 1.129.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-BEGaVwg2.js +8 -0
- package/dist/{SUPPORTED_CLIS-CJzJB2Ov.js → SUPPORTED_CLIS-CRiTrZVs.js} +2 -2
- package/dist/cli.js +3 -3
- package/dist/index.js +2 -2
- package/dist/{schedule-BCS2rT7U.js → schedule-QakndFJf.js} +4 -4
- package/dist/{serve-vyKtlci4.js → serve-DpBmExxp.js} +38 -11
- package/dist/{setup-BGfP7vQL.js → setup-DCisWuHB.js} +2 -2
- package/dist/{share-YuM6-Q6A.js → share-Cvb0PBKg.js} +12 -5
- package/dist/subcommands-BLUX23IJ.js +7 -0
- package/dist/{subcommands-DlWWdnWv.js → subcommands-D1floyWA.js} +127 -24
- package/dist/{ts-CRNaIrcV.js → ts-D21pYxoi.js} +2 -2
- package/dist/{versionChecker-CY79Ce_C.js → versionChecker-VKD48fzV.js} +2 -2
- package/package.json +1 -1
- package/ts/serve.ts +75 -15
- package/ts/share.spec.ts +36 -0
- package/ts/share.ts +28 -4
- package/ts/subcommands.spec.ts +71 -0
- package/ts/subcommands.ts +0 -0
- package/dist/SUPPORTED_CLIS-BQ95wXgT.js +0 -8
- package/dist/subcommands-BPz3ie1B.js +0 -7
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import "./ts-D21pYxoi.js";
|
|
2
|
+
import "./logger-B9h0djqx.js";
|
|
3
|
+
import "./versionChecker-VKD48fzV.js";
|
|
4
|
+
import "./pidStore-CGKIhaJO.js";
|
|
5
|
+
import "./globalPidIndex-C7r2m6s7.js";
|
|
6
|
+
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-CRiTrZVs.js";
|
|
7
|
+
|
|
8
|
+
export { SUPPORTED_CLIS };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { t as CLIS_CONFIG } from "./ts-
|
|
1
|
+
import { t as CLIS_CONFIG } from "./ts-D21pYxoi.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-CRiTrZVs.js.map
|
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import { n as logger } from "./logger-B9h0djqx.js";
|
|
3
|
-
import { i as versionString, n as displayVersion, r as getInstalledPackage, t as checkAndAutoUpdate } from "./versionChecker-
|
|
3
|
+
import { i as versionString, n as displayVersion, r as getInstalledPackage, t as checkAndAutoUpdate } from "./versionChecker-VKD48fzV.js";
|
|
4
4
|
import { argv } from "process";
|
|
5
5
|
import { execFileSync, spawn } from "child_process";
|
|
6
6
|
import ms from "ms";
|
|
@@ -482,7 +482,7 @@ function buildRustArgs(argv, cliFromScript, supportedClis) {
|
|
|
482
482
|
{
|
|
483
483
|
const rawArg = process.argv[2];
|
|
484
484
|
const isHelpFlag = rawArg === "-h" || rawArg === "--help";
|
|
485
|
-
const { isSubcommand, runSubcommand, cmdHelp } = await import("./subcommands-
|
|
485
|
+
const { isSubcommand, runSubcommand, cmdHelp } = await import("./subcommands-BLUX23IJ.js");
|
|
486
486
|
if (isHelpFlag && process.argv.length === 3) {
|
|
487
487
|
cmdHelp();
|
|
488
488
|
process.exit(0);
|
|
@@ -515,7 +515,7 @@ if (config.useRust) {
|
|
|
515
515
|
}
|
|
516
516
|
}
|
|
517
517
|
if (rustBinary) {
|
|
518
|
-
const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-
|
|
518
|
+
const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-BEGaVwg2.js");
|
|
519
519
|
const rustArgs = buildRustArgs(process.argv, config.cli, SUPPORTED_CLIS);
|
|
520
520
|
if (config.verbose) {
|
|
521
521
|
console.log(`[rust] Using binary: ${rustBinary}`);
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { a as removeControlCharacters, i as AgentContext, n as agentYes, r as config, t as CLIS_CONFIG } from "./ts-
|
|
1
|
+
import { a as removeControlCharacters, i as AgentContext, n as agentYes, r as config, t as CLIS_CONFIG } from "./ts-D21pYxoi.js";
|
|
2
2
|
import "./logger-B9h0djqx.js";
|
|
3
|
-
import "./versionChecker-
|
|
3
|
+
import "./versionChecker-VKD48fzV.js";
|
|
4
4
|
import "./pidStore-CGKIhaJO.js";
|
|
5
5
|
import "./globalPidIndex-C7r2m6s7.js";
|
|
6
6
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import "./ts-
|
|
1
|
+
import "./ts-D21pYxoi.js";
|
|
2
2
|
import "./logger-B9h0djqx.js";
|
|
3
|
-
import "./versionChecker-
|
|
3
|
+
import "./versionChecker-VKD48fzV.js";
|
|
4
4
|
import "./pidStore-CGKIhaJO.js";
|
|
5
5
|
import "./globalPidIndex-C7r2m6s7.js";
|
|
6
|
-
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-
|
|
6
|
+
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-CRiTrZVs.js";
|
|
7
7
|
import { n as resolveSpawnCwd } from "./workspaceConfig-BJO4fzEn.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-QakndFJf.js.map
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import "./ts-
|
|
1
|
+
import "./ts-D21pYxoi.js";
|
|
2
2
|
import "./logger-B9h0djqx.js";
|
|
3
|
-
import { r as getInstalledPackage } from "./versionChecker-
|
|
3
|
+
import { r as getInstalledPackage } from "./versionChecker-VKD48fzV.js";
|
|
4
4
|
import "./pidStore-CGKIhaJO.js";
|
|
5
5
|
import { a as updateGlobalPidStatus } from "./globalPidIndex-C7r2m6s7.js";
|
|
6
6
|
import { t as pgidForWrapper } from "./reaper-BkjPN7mw.js";
|
|
7
7
|
import "./configShared-C5QaNPnz.js";
|
|
8
|
-
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-
|
|
8
|
+
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-CRiTrZVs.js";
|
|
9
9
|
import "./remotes-D8GvSbhf.js";
|
|
10
|
-
import {
|
|
10
|
+
import { d as listRecords, g as resolveOne, i as controlCodeFromName, m as renderRawLog, p as readNotes, s as extractTaskCounts, x as writeToIpc, y as snapshotStatus } from "./subcommands-D1floyWA.js";
|
|
11
11
|
import yargs from "yargs";
|
|
12
12
|
import { mkdir, open, readFile, stat, writeFile } from "fs/promises";
|
|
13
13
|
import { homedir, hostname, userInfo } from "os";
|
|
@@ -208,6 +208,18 @@ function portFromArgs(args) {
|
|
|
208
208
|
const m = /--port[=\s](\d+)/.exec(args.join(" "));
|
|
209
209
|
return m ? Number(m[1]) : DEFAULT_PORT;
|
|
210
210
|
}
|
|
211
|
+
function explicitWebrtcUrl(args) {
|
|
212
|
+
for (let i = 0; i < args.length; i++) {
|
|
213
|
+
const a = args[i];
|
|
214
|
+
for (const flag of ["--webrtc", "--share"]) {
|
|
215
|
+
if (a === flag && args[i + 1]?.startsWith("webrtc://")) return args[i + 1];
|
|
216
|
+
if (a.startsWith(`${flag}=`)) {
|
|
217
|
+
const v = a.slice(flag.length + 1);
|
|
218
|
+
if (v.startsWith("webrtc://")) return v;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
211
223
|
async function fetchDaemonVersion(port, token) {
|
|
212
224
|
try {
|
|
213
225
|
const r = await fetch(`http://127.0.0.1:${port}/api/version`, {
|
|
@@ -231,14 +243,30 @@ async function cmdServeDaemon(sub, args) {
|
|
|
231
243
|
const priorArgs = await readDaemonServeArgs(mgr);
|
|
232
244
|
const effArgs = args.length ? args : priorArgs ?? [];
|
|
233
245
|
const current = getInstalledPackage().version;
|
|
246
|
+
const webrtcDaemon = effArgs.some((a) => a.startsWith("--webrtc") || a.startsWith("--share"));
|
|
247
|
+
let shareLink = null;
|
|
248
|
+
let shareLinkMinted = false;
|
|
249
|
+
if (webrtcDaemon) try {
|
|
250
|
+
const { loadOrCreateShareRoom, shareLinkFromRoomUrl } = await import("./share-Cvb0PBKg.js");
|
|
251
|
+
const explicit = explicitWebrtcUrl(effArgs);
|
|
252
|
+
shareLink = shareLinkFromRoomUrl(explicit ?? await loadOrCreateShareRoom());
|
|
253
|
+
shareLinkMinted = !explicit;
|
|
254
|
+
} catch {}
|
|
255
|
+
const emitShareLink = () => {
|
|
256
|
+
if (!webrtcDaemon) return;
|
|
257
|
+
if (shareLink) process.stdout.write(`\nshared over WebRTC — open this link (the token is eaten from the URL on open):\n ${shareLink}\n` + (shareLinkMinted ? ` (persistent room — same link across restarts; delete ~/.agent-yes/.share-room to rotate)\n` : ``));
|
|
258
|
+
else process.stdout.write("\nthe WebRTC share link carries a secret, so the daemon does NOT log it —\nread it from ~/.agent-yes/.share-link (mode 0600). The room persists in\n~/.agent-yes/.share-room, so the link survives restarts.\n");
|
|
259
|
+
};
|
|
234
260
|
if (priorArgs !== null) {
|
|
261
|
+
const sameConfig = JSON.stringify(effArgs) === JSON.stringify(priorArgs);
|
|
235
262
|
const runningVer = await fetchDaemonVersion(portFromArgs(effArgs), token);
|
|
236
|
-
if (runningVer === current) {
|
|
263
|
+
if (runningVer === current && sameConfig) {
|
|
237
264
|
await ensureBootAutostart(mgr);
|
|
238
265
|
process.stdout.write(`'${DAEMON_NAME}' already running v${current} (up to date)\n`);
|
|
266
|
+
emitShareLink();
|
|
239
267
|
return 0;
|
|
240
268
|
}
|
|
241
|
-
process.stdout.write(`rolling '${DAEMON_NAME}' ${runningVer ? `v${runningVer}` : "(unknown)"} → v${current}…\n`);
|
|
269
|
+
process.stdout.write(runningVer === current ? `reconfiguring '${DAEMON_NAME}' (serve args changed)…\n` : `rolling '${DAEMON_NAME}' ${runningVer ? `v${runningVer}` : "(unknown)"} → v${current}…\n`);
|
|
242
270
|
await spawnExit([
|
|
243
271
|
mgr.bin,
|
|
244
272
|
"stop",
|
|
@@ -251,7 +279,7 @@ async function cmdServeDaemon(sub, args) {
|
|
|
251
279
|
]);
|
|
252
280
|
}
|
|
253
281
|
const serveArgv = ayServeArgv(effArgs);
|
|
254
|
-
const oxmgrHealth =
|
|
282
|
+
const oxmgrHealth = webrtcDaemon && mgr.id === "oxmgr" ? [
|
|
255
283
|
"--health-cmd",
|
|
256
284
|
ayServeArgv(["healthcheck"]).join(" "),
|
|
257
285
|
"--health-interval",
|
|
@@ -291,7 +319,6 @@ async function cmdServeDaemon(sub, args) {
|
|
|
291
319
|
if (code === 0) {
|
|
292
320
|
const onBoot = await ensureBootAutostart(mgr);
|
|
293
321
|
const port = portFromArgs(effArgs);
|
|
294
|
-
const webrtcish = effArgs.some((a) => a.startsWith("--webrtc") || a.startsWith("--share"));
|
|
295
322
|
const httpish = effArgs.some((a) => a.startsWith("--http") || a.startsWith("--share")) || !effArgs.some((a) => a.startsWith("--webrtc"));
|
|
296
323
|
process.stdout.write(`\n${priorArgs !== null ? `rolled '${DAEMON_NAME}' forward to` : `installed '${DAEMON_NAME}' as a daemon via ${mgr.id} —`} v${current}\n`);
|
|
297
324
|
if (mgr.id === "oxmgr") process.stdout.write(onBoot ? `start-on-boot: enabled (systemd --user + linger, starts at boot)\n` : `start-on-boot: not registered — needs a user systemd session; run \`oxmgr service install\` to enable\n`);
|
|
@@ -304,7 +331,7 @@ async function cmdServeDaemon(sub, args) {
|
|
|
304
331
|
}
|
|
305
332
|
process.stdout.write(` ay serve logs # view server logs\n`);
|
|
306
333
|
process.stdout.write(` ay serve uninstall # remove daemon\n`);
|
|
307
|
-
|
|
334
|
+
emitShareLink();
|
|
308
335
|
}
|
|
309
336
|
return code ?? 1;
|
|
310
337
|
}
|
|
@@ -1052,7 +1079,7 @@ Options:
|
|
|
1052
1079
|
const webrtcVal = argv.webrtc ?? argv.share;
|
|
1053
1080
|
const explicitUrl = typeof webrtcVal === "string" && webrtcVal.startsWith("webrtc://") ? webrtcVal : void 0;
|
|
1054
1081
|
try {
|
|
1055
|
-
const { startShare, loadOrCreateShareRoom } = await import("./share-
|
|
1082
|
+
const { startShare, loadOrCreateShareRoom } = await import("./share-Cvb0PBKg.js");
|
|
1056
1083
|
const linkFile = path.join(process.env.AGENT_YES_HOME ?? path.join(homedir(), ".agent-yes"), ".share-link");
|
|
1057
1084
|
const announce = async (room, link, rotated) => {
|
|
1058
1085
|
const lead = rotated ? "the room was rejected by signaling (stale generation) — rotated to a fresh link" : "shared over WebRTC — open this link (the token is eaten from the URL on open)";
|
|
@@ -1107,4 +1134,4 @@ Options:
|
|
|
1107
1134
|
|
|
1108
1135
|
//#endregion
|
|
1109
1136
|
export { cmdServe };
|
|
1110
|
-
//# sourceMappingURL=serve-
|
|
1137
|
+
//# sourceMappingURL=serve-DpBmExxp.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-DpBmExxp.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-DCisWuHB.js.map
|
|
@@ -244,6 +244,15 @@ function parseShareUrl(s) {
|
|
|
244
244
|
host: m[3]
|
|
245
245
|
};
|
|
246
246
|
}
|
|
247
|
+
function formatShareLink(room, S, host) {
|
|
248
|
+
return `${host === "s.agent-yes.com" ? "https://agent-yes.com/w" : "http://localhost:7778/w"}/#${room}:${MARKER}${S}${host === "s.agent-yes.com" ? "" : "@" + host}`;
|
|
249
|
+
}
|
|
250
|
+
function shareLinkFromRoomUrl(url) {
|
|
251
|
+
const { room, token, host } = parseShareUrl(url);
|
|
252
|
+
const { s, v2 } = parseSecret(token);
|
|
253
|
+
if (!v2) throw new Error("refusing to derive a link for an unencrypted room — delete ~/.agent-yes/.share-room to rotate to an encrypted link");
|
|
254
|
+
return formatShareLink(room, s, host);
|
|
255
|
+
}
|
|
247
256
|
async function linkFromBunCache() {
|
|
248
257
|
const { existsSync, symlinkSync, mkdirSync, readdirSync } = await import("fs");
|
|
249
258
|
const path = (await import("path")).default;
|
|
@@ -320,9 +329,7 @@ async function startShare(opts) {
|
|
|
320
329
|
if (!v2) throw new Error("refusing to host an unencrypted room — delete ~/.agent-yes/.share-room to rotate to an encrypted link");
|
|
321
330
|
let S = firstS;
|
|
322
331
|
const wsScheme = host.startsWith("localhost") || host.startsWith("127.") ? "ws" : "wss";
|
|
323
|
-
const
|
|
324
|
-
const suffix = host === "s.agent-yes.com" ? "" : "@" + host;
|
|
325
|
-
const mkLink = () => `${ui}/#${room}:${MARKER}${S}${suffix}`;
|
|
332
|
+
const mkLink = () => formatShareLink(room, S, host);
|
|
326
333
|
let authToken = await deriveAuthToken(S, room, host);
|
|
327
334
|
let link = mkLink();
|
|
328
335
|
const RTCPeerConnection = await importRTC();
|
|
@@ -698,5 +705,5 @@ async function startShare(opts) {
|
|
|
698
705
|
}
|
|
699
706
|
|
|
700
707
|
//#endregion
|
|
701
|
-
export { loadOrCreateShareRoom, startShare };
|
|
702
|
-
//# sourceMappingURL=share-
|
|
708
|
+
export { loadOrCreateShareRoom, shareLinkFromRoomUrl, startShare };
|
|
709
|
+
//# sourceMappingURL=share-Cvb0PBKg.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import "./logger-B9h0djqx.js";
|
|
2
|
+
import "./globalPidIndex-C7r2m6s7.js";
|
|
3
|
+
import "./configShared-C5QaNPnz.js";
|
|
4
|
+
import "./remotes-D8GvSbhf.js";
|
|
5
|
+
import { _ as resolveReadWindow, a as cursorAbs, b as stopTipForCli, c as finalizedLines, d as listRecords, f as matchKeyword, g as resolveOne, h as renderRawLogLines, i as controlCodeFromName, l as isPidAlive, m as renderRawLog, n as READ_PAGE_DEFAULT, o as extractNeedsInput, p as readNotes, r as cmdHelp, s as extractTaskCounts, t as GRACEFUL_EXIT_COMMANDS, u as isSubcommand, v as runSubcommand, x as writeToIpc, y as snapshotStatus } from "./subcommands-D1floyWA.js";
|
|
6
|
+
|
|
7
|
+
export { cmdHelp, isSubcommand, runSubcommand };
|
|
@@ -524,15 +524,15 @@ async function runSubcommand(argv) {
|
|
|
524
524
|
case "restart": return await cmdRestart(rest);
|
|
525
525
|
case "note": return await cmdNote(rest);
|
|
526
526
|
case "serve": {
|
|
527
|
-
const { cmdServe } = await import("./serve-
|
|
527
|
+
const { cmdServe } = await import("./serve-DpBmExxp.js");
|
|
528
528
|
return cmdServe(rest);
|
|
529
529
|
}
|
|
530
530
|
case "setup": {
|
|
531
|
-
const { cmdSetup } = await import("./setup-
|
|
531
|
+
const { cmdSetup } = await import("./setup-DCisWuHB.js");
|
|
532
532
|
return cmdSetup(rest);
|
|
533
533
|
}
|
|
534
534
|
case "schedule": {
|
|
535
|
-
const { cmdSchedule } = await import("./schedule-
|
|
535
|
+
const { cmdSchedule } = await import("./schedule-QakndFJf.js");
|
|
536
536
|
return cmdSchedule(rest);
|
|
537
537
|
}
|
|
538
538
|
case "remote": {
|
|
@@ -552,7 +552,7 @@ async function runSubcommand(argv) {
|
|
|
552
552
|
}
|
|
553
553
|
}
|
|
554
554
|
function cmdHelp() {
|
|
555
|
-
process.stdout.write("ay - agent-yes CLI\n\nManagement:\n ay ls [keyword] list running agents\n ay tail [-f] <keyword>
|
|
555
|
+
process.stdout.write("ay - agent-yes CLI\n\nManagement:\n ay ls [keyword] list running agents\n ay tail [-f] [-n N] <keyword> last N lines (96), -f to follow\n ay read <keyword> [page opts] paginate: --last/--head N, --range A:B,\n --before-line L [--limit N]\n ay cat <keyword> full log\n ay head <keyword> first N lines\n ay send <keyword> <msg> send a message\n ay attach <keyword> interactive attach (detach: Ctrl-\\)\n ay stop <keyword> graceful shutdown (/exit for claude/codex)\n ay status <keyword> agent status snapshot\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\nRemote:\n ay setup guided setup: pick a workspace, share to agent-yes.com\n 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 ls <token>@<host>:<port> connect inline (no alias needed)\n ay send <token>@<host>:<port>:<kw> <msg>\n\nRun an agent:\n ay [claude|codex|gemini|...] [options] -- [prompt]\n ay claude -- \"fix the bug in auth.ts\"\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");
|
|
556
556
|
return 0;
|
|
557
557
|
}
|
|
558
558
|
function matchKeyword(record, keyword) {
|
|
@@ -1153,7 +1153,7 @@ function truncate(s, n) {
|
|
|
1153
1153
|
return s.slice(0, n - 1) + "…";
|
|
1154
1154
|
}
|
|
1155
1155
|
async function cmdRead(rest, { mode }) {
|
|
1156
|
-
const argv = await yargs(rest).usage("Usage: ay read/cat/tail/head <keyword> [options]").option("follow", {
|
|
1156
|
+
const argv = await yargs(rest).usage("Usage: ay read/cat/tail/head <keyword> [options]\n\nPagination (static read; render the log once, window the rendered lines):\n --last N | --head N last / first N lines\n --range A:B lines A..B (1-indexed, inclusive)\n --before-line L [--limit N] the page of N lines ending just above line L").option("follow", {
|
|
1157
1157
|
alias: "f",
|
|
1158
1158
|
type: "boolean",
|
|
1159
1159
|
default: false,
|
|
@@ -1161,6 +1161,21 @@ async function cmdRead(rest, { mode }) {
|
|
|
1161
1161
|
}).option("n", {
|
|
1162
1162
|
type: "number",
|
|
1163
1163
|
description: "Number of lines (default: 96 for tail/head)"
|
|
1164
|
+
}).option("last", {
|
|
1165
|
+
type: "number",
|
|
1166
|
+
description: "Show the last N rendered lines"
|
|
1167
|
+
}).option("head", {
|
|
1168
|
+
type: "number",
|
|
1169
|
+
description: "Show the first N rendered lines"
|
|
1170
|
+
}).option("range", {
|
|
1171
|
+
type: "string",
|
|
1172
|
+
description: "Show rendered lines A:B (1-indexed, inclusive)"
|
|
1173
|
+
}).option("before-line", {
|
|
1174
|
+
type: "number",
|
|
1175
|
+
description: "Paginate: show the page of lines ending just above line L"
|
|
1176
|
+
}).option("limit", {
|
|
1177
|
+
type: "number",
|
|
1178
|
+
description: "Page size for --before-line (default 96)"
|
|
1164
1179
|
}).option("plain", {
|
|
1165
1180
|
type: "boolean",
|
|
1166
1181
|
default: false,
|
|
@@ -1214,22 +1229,41 @@ async function cmdRead(rest, { mode }) {
|
|
|
1214
1229
|
}
|
|
1215
1230
|
if (!stats.isFile()) throw new Error(`pid ${record.pid}: log path is not a file: ${logPath}`);
|
|
1216
1231
|
const buf = await readFile(logPath);
|
|
1217
|
-
const rendered = await renderRawLog(buf, {
|
|
1218
|
-
mode,
|
|
1219
|
-
n
|
|
1220
|
-
});
|
|
1221
1232
|
const noteLabel = (await readNotes()).get(record.pid);
|
|
1222
1233
|
const header = noteLabel ? `[pid ${record.pid} ${shortenPath(record.cwd)} * ${noteLabel}]` : `[pid ${record.pid} ${shortenPath(record.cwd)}]`;
|
|
1223
|
-
process.stderr.write(header + "\n");
|
|
1224
|
-
process.stdout.write(rendered);
|
|
1225
|
-
if (!rendered.endsWith("\n")) process.stdout.write("\n");
|
|
1226
1234
|
if (follow) {
|
|
1235
|
+
const rendered = await renderRawLog(buf, {
|
|
1236
|
+
mode,
|
|
1237
|
+
n
|
|
1238
|
+
});
|
|
1239
|
+
process.stderr.write(header + "\n");
|
|
1240
|
+
process.stdout.write(rendered);
|
|
1241
|
+
if (!rendered.endsWith("\n")) process.stdout.write("\n");
|
|
1227
1242
|
setInterval(() => void recordRead(reader.key, record.pid), 3e4).unref?.();
|
|
1228
1243
|
return plain ? followPlainLocal(logPath, buf) : followRawLocal(logPath, buf);
|
|
1229
1244
|
}
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1245
|
+
const allLines = await renderRawLogLines(buf);
|
|
1246
|
+
const total = allLines.length;
|
|
1247
|
+
const win = resolveReadWindow({
|
|
1248
|
+
total,
|
|
1249
|
+
mode,
|
|
1250
|
+
n: argv.n,
|
|
1251
|
+
last: argv.last,
|
|
1252
|
+
head: argv.head,
|
|
1253
|
+
range: argv.range,
|
|
1254
|
+
beforeLine: argv["before-line"],
|
|
1255
|
+
limit: argv.limit
|
|
1256
|
+
});
|
|
1257
|
+
const rendered = allLines.slice(win.start, win.end).join("\n");
|
|
1258
|
+
process.stderr.write(header + "\n");
|
|
1259
|
+
process.stdout.write(rendered);
|
|
1260
|
+
if (!rendered.endsWith("\n")) process.stdout.write("\n");
|
|
1261
|
+
const firstVisible = win.start + 1;
|
|
1262
|
+
const shown = win.end - win.start;
|
|
1263
|
+
const hints = [`\n`, ` ay ls # list all agents\n`];
|
|
1264
|
+
if (win.start > 0) hints.push(` ay read ${record.pid} --before-line ${firstVisible} --limit ${shown || READ_PAGE_DEFAULT} # older lines (page up)\n`);
|
|
1265
|
+
hints.push(` ay read ${record.pid} --range A:B # lines A..B of ${total}\n`, ` ay tail -f ${record.pid} # follow live output\n`, ` ay send ${record.pid} "next: ..." # send a prompt\n`);
|
|
1266
|
+
process.stderr.write(hints.join(""));
|
|
1233
1267
|
return 0;
|
|
1234
1268
|
}
|
|
1235
1269
|
/**
|
|
@@ -1394,9 +1428,23 @@ async function followPlainLocal(logPath, buf) {
|
|
|
1394
1428
|
* Same approach as koho's renderTerminalBuffer + agent-yes's XtermProxy.
|
|
1395
1429
|
*/
|
|
1396
1430
|
async function renderRawLog(buf, { mode, n }) {
|
|
1431
|
+
const lines = await renderRawLogLines(buf);
|
|
1432
|
+
if (mode === "cat") return lines.join("\n");
|
|
1433
|
+
if (mode === "tail") return lines.slice(Math.max(0, lines.length - n)).join("\n");
|
|
1434
|
+
return lines.slice(0, n).join("\n");
|
|
1435
|
+
}
|
|
1436
|
+
/**
|
|
1437
|
+
* Render the raw PTY byte stream to its full array of scrollback lines (trailing
|
|
1438
|
+
* blanks trimmed). This is the substrate `renderRawLog` slices by mode and that
|
|
1439
|
+
* pagination (`resolveReadWindow`) indexes into — slicing the FINAL rendered
|
|
1440
|
+
* state is sound, but rendering from an arbitrary mid-stream offset is not (PTY
|
|
1441
|
+
* cursor moves / clears / wraps), so we always render the whole buffer once and
|
|
1442
|
+
* window the resulting lines.
|
|
1443
|
+
*/
|
|
1444
|
+
async function renderRawLogLines(buf) {
|
|
1397
1445
|
const cols = 200;
|
|
1398
1446
|
const rows = 50;
|
|
1399
|
-
const scrollback =
|
|
1447
|
+
const scrollback = 5e4;
|
|
1400
1448
|
try {
|
|
1401
1449
|
const { Terminal } = await import("@xterm/headless");
|
|
1402
1450
|
const term = new Terminal({
|
|
@@ -1413,18 +1461,73 @@ async function renderRawLog(buf, { mode, n }) {
|
|
|
1413
1461
|
lines.push(line ? line.translateToString(false).trimEnd() : "");
|
|
1414
1462
|
}
|
|
1415
1463
|
while (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
|
|
1416
|
-
|
|
1417
|
-
if (mode === "tail") return lines.slice(Math.max(0, lines.length - n)).join("\n");
|
|
1418
|
-
return lines.slice(0, n).join("\n");
|
|
1464
|
+
return lines;
|
|
1419
1465
|
} catch {
|
|
1420
1466
|
let text = new TextDecoder().decode(buf);
|
|
1421
1467
|
text = text.replace(/\x1b\[[0-?]*[ -/]*[@-~]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[@-Z\\-_]/g, "").replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, "");
|
|
1422
1468
|
const lines = text.split("\n");
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
return lines.slice(0, n).join("\n");
|
|
1469
|
+
while (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
|
|
1470
|
+
return lines;
|
|
1426
1471
|
}
|
|
1427
1472
|
}
|
|
1473
|
+
const READ_PAGE_DEFAULT = 96;
|
|
1474
|
+
/**
|
|
1475
|
+
* Resolve which rendered lines to show. Precedence (first match wins):
|
|
1476
|
+
* 1. `range` "A:B" — explicit 1-indexed inclusive window
|
|
1477
|
+
* 2. `beforeLine` (+limit)— the page of `limit` lines ending just ABOVE line L
|
|
1478
|
+
* (the pagination cursor `ay read` prints in its footer)
|
|
1479
|
+
* 3. `head` / `last` — explicit first/last N rendered lines
|
|
1480
|
+
* 4. mode preset + `-n` — tail/head default to the last/first N (96); cat = all
|
|
1481
|
+
* Indices are clamped to `[0, total]`; an empty / non-matching `range` falls through.
|
|
1482
|
+
*/
|
|
1483
|
+
function resolveReadWindow(opts) {
|
|
1484
|
+
const total = Math.max(0, Math.floor(opts.total));
|
|
1485
|
+
const clamp = (v) => Math.max(0, Math.min(total, Math.floor(v)));
|
|
1486
|
+
const pos = (v) => v != null && Number.isFinite(v) && v > 0 ? Math.floor(v) : void 0;
|
|
1487
|
+
const range = opts.range?.trim();
|
|
1488
|
+
if (range) {
|
|
1489
|
+
const m = /^(\d+):(\d+)$/.exec(range);
|
|
1490
|
+
if (m) {
|
|
1491
|
+
const a = parseInt(m[1], 10);
|
|
1492
|
+
const b = parseInt(m[2], 10);
|
|
1493
|
+
return {
|
|
1494
|
+
start: clamp(Math.min(a, b) - 1),
|
|
1495
|
+
end: clamp(Math.max(a, b))
|
|
1496
|
+
};
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
if (opts.beforeLine != null && Number.isFinite(opts.beforeLine)) {
|
|
1500
|
+
const limit = pos(opts.limit) ?? READ_PAGE_DEFAULT;
|
|
1501
|
+
const end = clamp(opts.beforeLine - 1);
|
|
1502
|
+
return {
|
|
1503
|
+
start: clamp(end - limit),
|
|
1504
|
+
end
|
|
1505
|
+
};
|
|
1506
|
+
}
|
|
1507
|
+
const head = pos(opts.head);
|
|
1508
|
+
if (head != null) return {
|
|
1509
|
+
start: 0,
|
|
1510
|
+
end: clamp(head)
|
|
1511
|
+
};
|
|
1512
|
+
const last = pos(opts.last);
|
|
1513
|
+
if (last != null) return {
|
|
1514
|
+
start: clamp(total - last),
|
|
1515
|
+
end: total
|
|
1516
|
+
};
|
|
1517
|
+
const n = pos(opts.n);
|
|
1518
|
+
if (opts.mode === "head") return {
|
|
1519
|
+
start: 0,
|
|
1520
|
+
end: clamp(n ?? READ_PAGE_DEFAULT)
|
|
1521
|
+
};
|
|
1522
|
+
if (opts.mode === "tail") return {
|
|
1523
|
+
start: clamp(total - (n ?? READ_PAGE_DEFAULT)),
|
|
1524
|
+
end: total
|
|
1525
|
+
};
|
|
1526
|
+
return {
|
|
1527
|
+
start: 0,
|
|
1528
|
+
end: total
|
|
1529
|
+
};
|
|
1530
|
+
}
|
|
1428
1531
|
/**
|
|
1429
1532
|
* Extract a one-line activity summary from a raw log file.
|
|
1430
1533
|
* Reads only the last 32 KB for speed, renders via xterm for clean output.
|
|
@@ -2303,5 +2406,5 @@ async function cmdResultSet(rest) {
|
|
|
2303
2406
|
}
|
|
2304
2407
|
|
|
2305
2408
|
//#endregion
|
|
2306
|
-
export {
|
|
2307
|
-
//# sourceMappingURL=subcommands-
|
|
2409
|
+
export { resolveReadWindow as _, cursorAbs as a, stopTipForCli as b, finalizedLines as c, listRecords as d, matchKeyword as f, resolveOne as g, renderRawLogLines as h, controlCodeFromName as i, isPidAlive as l, renderRawLog as m, READ_PAGE_DEFAULT as n, extractNeedsInput as o, readNotes as p, cmdHelp as r, extractTaskCounts as s, GRACEFUL_EXIT_COMMANDS as t, isSubcommand as u, runSubcommand as v, writeToIpc as x, snapshotStatus as y };
|
|
2410
|
+
//# sourceMappingURL=subcommands-D1floyWA.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as logger, t as addTransport } from "./logger-B9h0djqx.js";
|
|
2
|
-
import { r as getInstalledPackage } from "./versionChecker-
|
|
2
|
+
import { r as getInstalledPackage } from "./versionChecker-VKD48fzV.js";
|
|
3
3
|
import { t as agentYesHome } from "./agentYesHome-BvaUOzCV.js";
|
|
4
4
|
import { i as shouldUseLock, r as releaseLock, t as acquireLock } from "./runningLock-CJxsoGdb.js";
|
|
5
5
|
import { t as PidStore } from "./pidStore-CGKIhaJO.js";
|
|
@@ -1787,4 +1787,4 @@ function sleep(ms) {
|
|
|
1787
1787
|
|
|
1788
1788
|
//#endregion
|
|
1789
1789
|
export { removeControlCharacters as a, AgentContext as i, agentYes as n, config as r, CLIS_CONFIG as t };
|
|
1790
|
-
//# sourceMappingURL=ts-
|
|
1790
|
+
//# sourceMappingURL=ts-D21pYxoi.js.map
|
|
@@ -7,7 +7,7 @@ import { fileURLToPath } from "url";
|
|
|
7
7
|
|
|
8
8
|
//#region package.json
|
|
9
9
|
var name = "agent-yes";
|
|
10
|
-
var version = "1.
|
|
10
|
+
var version = "1.129.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-VKD48fzV.js.map
|
package/package.json
CHANGED
package/ts/serve.ts
CHANGED
|
@@ -276,6 +276,24 @@ function portFromArgs(args: string[]): number {
|
|
|
276
276
|
return m ? Number(m[1]) : DEFAULT_PORT;
|
|
277
277
|
}
|
|
278
278
|
|
|
279
|
+
// An explicit webrtc:// URL passed to --webrtc/--share in the daemon's serve args,
|
|
280
|
+
// or undefined for a bare flag (which mints a persisted room instead). Mirrors how
|
|
281
|
+
// cmdServe resolves argv.webrtc/argv.share, but over the raw arg list install holds
|
|
282
|
+
// (oxmgr splits the command on whitespace → `--webrtc url`; pm2/`=` → `--webrtc=url`).
|
|
283
|
+
function explicitWebrtcUrl(args: string[]): string | undefined {
|
|
284
|
+
for (let i = 0; i < args.length; i++) {
|
|
285
|
+
const a = args[i]!;
|
|
286
|
+
for (const flag of ["--webrtc", "--share"]) {
|
|
287
|
+
if (a === flag && args[i + 1]?.startsWith("webrtc://")) return args[i + 1];
|
|
288
|
+
if (a.startsWith(`${flag}=`)) {
|
|
289
|
+
const v = a.slice(flag.length + 1);
|
|
290
|
+
if (v.startsWith("webrtc://")) return v;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return undefined;
|
|
295
|
+
}
|
|
296
|
+
|
|
279
297
|
// Ask the live daemon its version over the local HTTP API. null if it's not
|
|
280
298
|
// listening (webrtc-only) or too old to expose /api/version — both of which we
|
|
281
299
|
// treat as "outdated" so a re-install rolls it forward.
|
|
@@ -314,19 +332,68 @@ async function cmdServeDaemon(sub: string, args: string[]): Promise<number> {
|
|
|
314
332
|
const effArgs = args.length ? args : (priorArgs ?? []);
|
|
315
333
|
const current = getInstalledPackage().version;
|
|
316
334
|
|
|
335
|
+
// WebRTC daemon: resolve the share link up front so we can print it on every
|
|
336
|
+
// install path (fresh install, roll-forward, and the already-up-to-date no-op).
|
|
337
|
+
// The link is a pure transform of the room URL, so the foreground install
|
|
338
|
+
// command can show it even though the background daemon is what runs the bridge.
|
|
339
|
+
// Resolve (and persist, when auto-minting) the room BEFORE spawning, so the
|
|
340
|
+
// daemon reads the SAME ~/.agent-yes/.share-room and can't race us into minting
|
|
341
|
+
// a divergent one. We print the link directly — the install receipt already
|
|
342
|
+
// prints the bearer token, so the operator's terminal is the right trust scope
|
|
343
|
+
// for a secret-bearing link (unlike the daemon's persisted logs, which omit it).
|
|
344
|
+
const webrtcDaemon = effArgs.some((a) => a.startsWith("--webrtc") || a.startsWith("--share"));
|
|
345
|
+
let shareLink: string | null = null;
|
|
346
|
+
let shareLinkMinted = false; // auto-minted (persisted/rotatable) vs explicit URL
|
|
347
|
+
if (webrtcDaemon) {
|
|
348
|
+
try {
|
|
349
|
+
const { loadOrCreateShareRoom, shareLinkFromRoomUrl } = await import("./share.ts");
|
|
350
|
+
const explicit = explicitWebrtcUrl(effArgs);
|
|
351
|
+
shareLink = shareLinkFromRoomUrl(explicit ?? (await loadOrCreateShareRoom()));
|
|
352
|
+
shareLinkMinted = !explicit;
|
|
353
|
+
} catch {
|
|
354
|
+
/* best effort — fall back to the .share-link file hint in emitShareLink */
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
const emitShareLink = () => {
|
|
358
|
+
if (!webrtcDaemon) return;
|
|
359
|
+
if (shareLink)
|
|
360
|
+
process.stdout.write(
|
|
361
|
+
`\nshared over WebRTC — open this link (the token is eaten from the URL on open):\n` +
|
|
362
|
+
` ${shareLink}\n` +
|
|
363
|
+
(shareLinkMinted
|
|
364
|
+
? ` (persistent room — same link across restarts; delete ~/.agent-yes/.share-room to rotate)\n`
|
|
365
|
+
: ``),
|
|
366
|
+
);
|
|
367
|
+
else
|
|
368
|
+
process.stdout.write(
|
|
369
|
+
`\nthe WebRTC share link carries a secret, so the daemon does NOT log it —\n` +
|
|
370
|
+
`read it from ~/.agent-yes/.share-link (mode 0600). The room persists in\n` +
|
|
371
|
+
`~/.agent-yes/.share-room, so the link survives restarts.\n`,
|
|
372
|
+
);
|
|
373
|
+
};
|
|
374
|
+
|
|
317
375
|
if (priorArgs !== null) {
|
|
318
|
-
// A daemon already exists
|
|
376
|
+
// A daemon already exists. Treat this as a no-op only when it's BOTH current
|
|
377
|
+
// AND already running the requested config — otherwise a config change (e.g.
|
|
378
|
+
// `install --webrtc` over an --http daemon, which the version probe still
|
|
379
|
+
// reaches on the default port) would be silently ignored, and we'd print a
|
|
380
|
+
// share link for a WebRTC bridge that isn't actually running. A bare re-run
|
|
381
|
+
// passes no args, so effArgs === priorArgs and this stays a no-op as before.
|
|
382
|
+
const sameConfig = JSON.stringify(effArgs) === JSON.stringify(priorArgs);
|
|
319
383
|
const runningVer = await fetchDaemonVersion(portFromArgs(effArgs), token);
|
|
320
|
-
if (runningVer === current) {
|
|
384
|
+
if (runningVer === current && sameConfig) {
|
|
321
385
|
await ensureBootAutostart(mgr);
|
|
322
386
|
process.stdout.write(`'${DAEMON_NAME}' already running v${current} (up to date)\n`);
|
|
387
|
+
emitShareLink();
|
|
323
388
|
return 0;
|
|
324
389
|
}
|
|
325
|
-
// Outdated
|
|
326
|
-
//
|
|
327
|
-
//
|
|
390
|
+
// Outdated, unreachable, or reconfigured → graceful roll-forward. `stop` sends
|
|
391
|
+
// SIGTERM, which cmdServe handles cleanly (closing share peers so browsers
|
|
392
|
+
// reconnect fast), then we re-create with the new binary/args.
|
|
328
393
|
process.stdout.write(
|
|
329
|
-
|
|
394
|
+
runningVer === current
|
|
395
|
+
? `reconfiguring '${DAEMON_NAME}' (serve args changed)…\n`
|
|
396
|
+
: `rolling '${DAEMON_NAME}' ${runningVer ? `v${runningVer}` : "(unknown)"} → v${current}…\n`,
|
|
330
397
|
);
|
|
331
398
|
await spawnExit([mgr.bin, "stop", DAEMON_NAME]);
|
|
332
399
|
await spawnExit([mgr.bin, "delete", DAEMON_NAME]);
|
|
@@ -340,7 +407,7 @@ async function cmdServeDaemon(sub: string, args: string[]): Promise<number> {
|
|
|
340
407
|
// freeze the JS event loop (host answers nobody, no in-process timer can
|
|
341
408
|
// recover it), so an EXTERNAL probe of the serve heartbeat is the only thing
|
|
342
409
|
// that can detect+restart it. 15s stale + 3 misses at 10s ≈ 45s to auto-recover.
|
|
343
|
-
|
|
410
|
+
// (webrtcDaemon resolved above, where we also derive the share link.)
|
|
344
411
|
const oxmgrHealth =
|
|
345
412
|
webrtcDaemon && mgr.id === "oxmgr"
|
|
346
413
|
? [
|
|
@@ -389,7 +456,6 @@ async function cmdServeDaemon(sub: string, args: string[]): Promise<number> {
|
|
|
389
456
|
const onBoot = await ensureBootAutostart(mgr);
|
|
390
457
|
const port = portFromArgs(effArgs);
|
|
391
458
|
// Mirror cmdServe's mode resolution: webrtc-only daemons open no HTTP port.
|
|
392
|
-
const webrtcish = effArgs.some((a) => a.startsWith("--webrtc") || a.startsWith("--share"));
|
|
393
459
|
const httpish =
|
|
394
460
|
effArgs.some((a) => a.startsWith("--http") || a.startsWith("--share")) ||
|
|
395
461
|
!effArgs.some((a) => a.startsWith("--webrtc"));
|
|
@@ -421,13 +487,7 @@ async function cmdServeDaemon(sub: string, args: string[]): Promise<number> {
|
|
|
421
487
|
}
|
|
422
488
|
process.stdout.write(` ay serve logs # view server logs\n`);
|
|
423
489
|
process.stdout.write(` ay serve uninstall # remove daemon\n`);
|
|
424
|
-
|
|
425
|
-
process.stdout.write(
|
|
426
|
-
`\nthe WebRTC share link carries a secret, so the daemon does NOT log it —\n` +
|
|
427
|
-
`read it from ~/.agent-yes/.share-link (mode 0600). The room persists in\n` +
|
|
428
|
-
`~/.agent-yes/.share-room, so the link survives restarts.\n`,
|
|
429
|
-
);
|
|
430
|
-
}
|
|
490
|
+
emitShareLink();
|
|
431
491
|
}
|
|
432
492
|
return code ?? 1;
|
|
433
493
|
}
|
package/ts/share.spec.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { shareLinkFromRoomUrl } from "./share.ts";
|
|
3
|
+
import { MARKER } from "../lab/ui/e2e.js";
|
|
4
|
+
|
|
5
|
+
const S = "a".repeat(64); // a valid 64-hex room secret
|
|
6
|
+
const TOK = `${MARKER}${S}`; // encrypted-room token (v2)
|
|
7
|
+
|
|
8
|
+
// shareLinkFromRoomUrl turns a persisted/explicit webrtc://room:token@host room
|
|
9
|
+
// into the browser console link `ay serve install` prints — it MUST match the
|
|
10
|
+
// link startShare announces from the same room (both go through formatShareLink).
|
|
11
|
+
describe("shareLinkFromRoomUrl", () => {
|
|
12
|
+
it("derives the prod console link (no host suffix; secret rides in the fragment)", () => {
|
|
13
|
+
const link = shareLinkFromRoomUrl(`webrtc://r1a2b3c:${TOK}@s.agent-yes.com`);
|
|
14
|
+
expect(link).toBe(`https://agent-yes.com/w/#r1a2b3c:${MARKER}${S}`);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("derives a dev/self-hosted link carrying the signaling host in the fragment", () => {
|
|
18
|
+
const link = shareLinkFromRoomUrl(`webrtc://r1a2b3c:${TOK}@localhost:7778`);
|
|
19
|
+
expect(link).toBe(`http://localhost:7778/w/#r1a2b3c:${MARKER}${S}@localhost:7778`);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("round-trips room + token in the fragment the browser splits back out", () => {
|
|
23
|
+
const link = shareLinkFromRoomUrl(`webrtc://room0:${TOK}@s.agent-yes.com`);
|
|
24
|
+
expect(link.split("#")[1]).toBe(`room0:${TOK}`);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("refuses a legacy (unencrypted) room — operator must rotate to an encrypted link", () => {
|
|
28
|
+
expect(() => shareLinkFromRoomUrl(`webrtc://room0:${S}@s.agent-yes.com`)).toThrow(
|
|
29
|
+
/unencrypted/,
|
|
30
|
+
);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("rejects a malformed room url", () => {
|
|
34
|
+
expect(() => shareLinkFromRoomUrl("not-a-webrtc-url")).toThrow(/webrtc:\/\//);
|
|
35
|
+
});
|
|
36
|
+
});
|
package/ts/share.ts
CHANGED
|
@@ -160,6 +160,33 @@ function parseShareUrl(s: string): { room: string; token: string; host: string }
|
|
|
160
160
|
return { room: m[1]!, token: m[2]!, host: m[3]! };
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
+
// The browser console URL for a room — what the operator opens to reach the host.
|
|
164
|
+
// Pure function of (room, secret S, signaling host): S rides in the URL fragment
|
|
165
|
+
// (never sent to any server) and is eaten by the page on open. Single source of
|
|
166
|
+
// truth for the link format, shared by startShare's live announce and by
|
|
167
|
+
// shareLinkFromRoomUrl (so `ay serve install` prints the exact link the daemon serves).
|
|
168
|
+
function formatShareLink(room: string, S: string, host: string): string {
|
|
169
|
+
// The console web-app is served under /w/ (landing page lives at /). A non-prod
|
|
170
|
+
// signaling host targets the local dev UI and carries the host in the fragment.
|
|
171
|
+
const ui = host === "s.agent-yes.com" ? "https://agent-yes.com/w" : "http://localhost:7778/w";
|
|
172
|
+
const suffix = host === "s.agent-yes.com" ? "" : "@" + host;
|
|
173
|
+
return `${ui}/#${room}:${MARKER}${S}${suffix}`;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Derive the shareable console link from a persisted/explicit webrtc://room:token@host
|
|
177
|
+
// URL WITHOUT starting a bridge, so `ay serve install` can print the same link the
|
|
178
|
+
// background daemon will serve. Throws on a legacy (unencrypted) room, mirroring
|
|
179
|
+
// startShare's refusal to host one.
|
|
180
|
+
export function shareLinkFromRoomUrl(url: string): string {
|
|
181
|
+
const { room, token, host } = parseShareUrl(url);
|
|
182
|
+
const { s, v2 } = parseSecret(token);
|
|
183
|
+
if (!v2)
|
|
184
|
+
throw new Error(
|
|
185
|
+
"refusing to derive a link for an unencrypted room — delete ~/.agent-yes/.share-room to rotate to an encrypted link",
|
|
186
|
+
);
|
|
187
|
+
return formatShareLink(room, s, host);
|
|
188
|
+
}
|
|
189
|
+
|
|
163
190
|
// node-datachannel ships a native addon. Under Bun the module sometimes resolves
|
|
164
191
|
// from the global cache where the prebuilt .node isn't linked; this best-effort
|
|
165
192
|
// shim symlinks it in before we import. In a normal npm/bunx install the binary
|
|
@@ -280,10 +307,7 @@ export async function startShare(
|
|
|
280
307
|
let S = firstS;
|
|
281
308
|
|
|
282
309
|
const wsScheme = host.startsWith("localhost") || host.startsWith("127.") ? "ws" : "wss";
|
|
283
|
-
|
|
284
|
-
const ui = host === "s.agent-yes.com" ? "https://agent-yes.com/w" : "http://localhost:7778/w";
|
|
285
|
-
const suffix = host === "s.agent-yes.com" ? "" : "@" + host;
|
|
286
|
-
const mkLink = () => `${ui}/#${room}:${MARKER}${S}${suffix}`;
|
|
310
|
+
const mkLink = () => formatShareLink(room, S, host);
|
|
287
311
|
let authToken = await deriveAuthToken(S, room, host);
|
|
288
312
|
let link = mkLink();
|
|
289
313
|
|
package/ts/subcommands.spec.ts
CHANGED
|
@@ -1253,3 +1253,74 @@ describe("subcommands.listRecords merges per-cwd TS file with global", () => {
|
|
|
1253
1253
|
}
|
|
1254
1254
|
});
|
|
1255
1255
|
});
|
|
1256
|
+
|
|
1257
|
+
describe("subcommands.resolveReadWindow", () => {
|
|
1258
|
+
const total = 100;
|
|
1259
|
+
|
|
1260
|
+
it("defaults: tail = last 96, head = first 96, cat = all", async () => {
|
|
1261
|
+
const { resolveReadWindow } = await loadModule();
|
|
1262
|
+
expect(resolveReadWindow({ total: 200, mode: "tail" })).toEqual({ start: 104, end: 200 });
|
|
1263
|
+
expect(resolveReadWindow({ total: 200, mode: "head" })).toEqual({ start: 0, end: 96 });
|
|
1264
|
+
expect(resolveReadWindow({ total: 200, mode: "cat" })).toEqual({ start: 0, end: 200 });
|
|
1265
|
+
});
|
|
1266
|
+
|
|
1267
|
+
it("respects -n for tail/head; cat ignores -n (stays whole)", async () => {
|
|
1268
|
+
const { resolveReadWindow } = await loadModule();
|
|
1269
|
+
expect(resolveReadWindow({ total, mode: "tail", n: 10 })).toEqual({ start: 90, end: 100 });
|
|
1270
|
+
expect(resolveReadWindow({ total, mode: "head", n: 10 })).toEqual({ start: 0, end: 10 });
|
|
1271
|
+
expect(resolveReadWindow({ total, mode: "cat", n: 10 })).toEqual({ start: 0, end: 100 });
|
|
1272
|
+
});
|
|
1273
|
+
|
|
1274
|
+
it("--last / --head override the mode", async () => {
|
|
1275
|
+
const { resolveReadWindow } = await loadModule();
|
|
1276
|
+
expect(resolveReadWindow({ total, mode: "cat", last: 5 })).toEqual({ start: 95, end: 100 });
|
|
1277
|
+
expect(resolveReadWindow({ total, mode: "tail", head: 5 })).toEqual({ start: 0, end: 5 });
|
|
1278
|
+
});
|
|
1279
|
+
|
|
1280
|
+
it("--range A:B is 1-indexed inclusive and order-insensitive", async () => {
|
|
1281
|
+
const { resolveReadWindow } = await loadModule();
|
|
1282
|
+
expect(resolveReadWindow({ total, mode: "cat", range: "10:20" })).toEqual({
|
|
1283
|
+
start: 9,
|
|
1284
|
+
end: 20,
|
|
1285
|
+
});
|
|
1286
|
+
expect(resolveReadWindow({ total, mode: "cat", range: "20:10" })).toEqual({
|
|
1287
|
+
start: 9,
|
|
1288
|
+
end: 20,
|
|
1289
|
+
});
|
|
1290
|
+
});
|
|
1291
|
+
|
|
1292
|
+
it("--before-line L shows the page of `limit` lines ending just above L", async () => {
|
|
1293
|
+
const { resolveReadWindow } = await loadModule();
|
|
1294
|
+
// page-up cursor: lines strictly before line 51, limit 10 -> [41..50] (0-idx 40..50)
|
|
1295
|
+
expect(resolveReadWindow({ total, mode: "cat", beforeLine: 51, limit: 10 })).toEqual({
|
|
1296
|
+
start: 40,
|
|
1297
|
+
end: 50,
|
|
1298
|
+
});
|
|
1299
|
+
// round-trip: first-visible of the above is line 41; paging up again from 41
|
|
1300
|
+
expect(resolveReadWindow({ total, mode: "cat", beforeLine: 41, limit: 10 })).toEqual({
|
|
1301
|
+
start: 30,
|
|
1302
|
+
end: 40,
|
|
1303
|
+
});
|
|
1304
|
+
});
|
|
1305
|
+
|
|
1306
|
+
it("clamps out-of-range indices", async () => {
|
|
1307
|
+
const { resolveReadWindow } = await loadModule();
|
|
1308
|
+
expect(resolveReadWindow({ total: 5, mode: "tail", n: 999 })).toEqual({ start: 0, end: 5 });
|
|
1309
|
+
expect(resolveReadWindow({ total: 5, mode: "cat", range: "3:999" })).toEqual({
|
|
1310
|
+
start: 2,
|
|
1311
|
+
end: 5,
|
|
1312
|
+
});
|
|
1313
|
+
expect(resolveReadWindow({ total: 5, mode: "cat", beforeLine: 2, limit: 999 })).toEqual({
|
|
1314
|
+
start: 0,
|
|
1315
|
+
end: 1,
|
|
1316
|
+
});
|
|
1317
|
+
});
|
|
1318
|
+
|
|
1319
|
+
it("ignores a malformed --range and falls through to the mode default", async () => {
|
|
1320
|
+
const { resolveReadWindow } = await loadModule();
|
|
1321
|
+
expect(resolveReadWindow({ total, mode: "head", range: "not-a-range" })).toEqual({
|
|
1322
|
+
start: 0,
|
|
1323
|
+
end: 96,
|
|
1324
|
+
});
|
|
1325
|
+
});
|
|
1326
|
+
});
|
package/ts/subcommands.ts
CHANGED
|
Binary file
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import "./ts-CRNaIrcV.js";
|
|
2
|
-
import "./logger-B9h0djqx.js";
|
|
3
|
-
import "./versionChecker-CY79Ce_C.js";
|
|
4
|
-
import "./pidStore-CGKIhaJO.js";
|
|
5
|
-
import "./globalPidIndex-C7r2m6s7.js";
|
|
6
|
-
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-CJzJB2Ov.js";
|
|
7
|
-
|
|
8
|
-
export { SUPPORTED_CLIS };
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import "./logger-B9h0djqx.js";
|
|
2
|
-
import "./globalPidIndex-C7r2m6s7.js";
|
|
3
|
-
import "./configShared-C5QaNPnz.js";
|
|
4
|
-
import "./remotes-D8GvSbhf.js";
|
|
5
|
-
import { _ as stopTipForCli, a as extractNeedsInput, c as isPidAlive, d as matchKeyword, f as readNotes, g as snapshotStatus, h as runSubcommand, i as cursorAbs, l as isSubcommand, m as resolveOne, n as cmdHelp, o as extractTaskCounts, p as renderRawLog, r as controlCodeFromName, s as finalizedLines, t as GRACEFUL_EXIT_COMMANDS, u as listRecords, v as writeToIpc } from "./subcommands-DlWWdnWv.js";
|
|
6
|
-
|
|
7
|
-
export { cmdHelp, isSubcommand, runSubcommand };
|