agent-yes 1.205.0 → 1.207.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/README.md +2 -0
- package/dist/{SUPPORTED_CLIS-C8-qh98a.js → SUPPORTED_CLIS-B1CDJwYn.js} +2 -2
- package/dist/SUPPORTED_CLIS-vH8hvcZr.js +9 -0
- package/dist/{agentShare-99IGGOkv.js → agentShare-DdEbHtAn.js} +3 -3
- package/dist/cli.js +4 -4
- package/dist/index.js +2 -2
- package/dist/{notifyDaemon-DK1HOMjj.js → notifyDaemon-DivHVzZx.js} +2 -2
- package/dist/{rustBinary-Tgn948GR.js → rustBinary-CuBIr-zX.js} +2 -2
- package/dist/{schedule-CBoM67Vy.js → schedule-Bg_CDt2j.js} +4 -4
- package/dist/{serve-IvOlaL-7.js → serve-B-ZCy67S.js} +258 -41
- package/dist/{setup-BaKdS-3i.js → setup-D6UBzk4-.js} +2 -2
- package/dist/{share-Ciw1mWVN.js → share-_QYjPN7q.js} +1 -1
- package/dist/{share-BfIU8t_h.js → share-vchIyVd8.js} +98 -5
- package/dist/{subcommands-Bs7wJRBB.js → subcommands-6AQ8egRn.js} +7 -7
- package/dist/{subcommands-DyDHZ5YI.js → subcommands-CeRH9-_h.js} +1 -1
- package/dist/{ts-Dm6jjLwC.js → ts-DZWZvbLM.js} +2 -2
- package/dist/{versionChecker-BomfUpSo.js → versionChecker-C2CV9VOX.js} +2 -2
- package/dist/{ws-CSkP2mkD.js → ws-CGNmxCPD.js} +2 -2
- package/lab/ui/console-logic.js +23 -1
- package/lab/ui/index.html +188 -29
- package/package.json +1 -1
- package/ts/serve.spec.ts +13 -1
- package/ts/serve.ts +263 -34
- package/ts/serveLock.spec.ts +15 -14
- package/ts/share.ts +80 -1
- package/ts/sizeNego.spec.ts +61 -0
- package/ts/sizeNego.ts +59 -0
- package/ts/statusText.spec.ts +17 -0
- package/ts/statusText.ts +19 -0
- package/ts/ws.spec.ts +8 -10
- package/dist/SUPPORTED_CLIS-D3XTFfS7.js +0 -9
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import "./ts-
|
|
1
|
+
import "./ts-DZWZvbLM.js";
|
|
2
2
|
import "./logger-CDIsZ-Pp.js";
|
|
3
|
-
import { r as getInstalledPackage } from "./versionChecker-
|
|
4
|
-
import { t as findSpawnHiddenLauncher } from "./rustBinary-
|
|
3
|
+
import { r as getInstalledPackage } from "./versionChecker-C2CV9VOX.js";
|
|
4
|
+
import { t as findSpawnHiddenLauncher } from "./rustBinary-CuBIr-zX.js";
|
|
5
5
|
import { t as agentYesHome$1 } from "./agentYesHome-CtHb5b71.js";
|
|
6
6
|
import "./pidStore-BIvsBQ8X.js";
|
|
7
7
|
import { a as updateGlobalPidStatus } from "./globalPidIndex-CoNr7tS8.js";
|
|
8
8
|
import { r as recordInbox } from "./messageLog-CxrKJj77.js";
|
|
9
9
|
import { t as pgidForWrapper } from "./reaper-BUHCyxdF.js";
|
|
10
10
|
import "./configShared-aKTg-sa5.js";
|
|
11
|
-
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-
|
|
12
|
-
import { A as renderRawLog, D as recentMessageEdges, E as readPtysize, L as snapshotStatus, M as resolveOne, O as recentReadEdges, S as listRecords, T as readNotes, U as writeToIpc, b as isUserTyping, f as extractNeedsInput, k as renderLogTailLines, l as deriveLiveStatus, o as controlCodeFromName, p as extractTaskCounts, q as isTransientLockMkdirError, tt as TYPING_BADGE, u as extractBadges } from "./subcommands-
|
|
11
|
+
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-B1CDJwYn.js";
|
|
12
|
+
import { A as renderRawLog, D as recentMessageEdges, E as readPtysize, L as snapshotStatus, M as resolveOne, O as recentReadEdges, S as listRecords, T as readNotes, U as writeToIpc, b as isUserTyping, f as extractNeedsInput, k as renderLogTailLines, l as deriveLiveStatus, o as controlCodeFromName, p as extractTaskCounts, q as isTransientLockMkdirError, tt as TYPING_BADGE, u as extractBadges } from "./subcommands-6AQ8egRn.js";
|
|
13
13
|
import "./e2e-jb0Hp43q.js";
|
|
14
14
|
import "./webrtcLink-B7REGtK2.js";
|
|
15
15
|
import "./remotes-Cim0dBU7.js";
|
|
@@ -43,6 +43,26 @@ import { existsSync, renameSync, watch, writeFileSync } from "node:fs";
|
|
|
43
43
|
*/
|
|
44
44
|
const isTerminalReply = (s) => /^(?:\x1b\[(?:\??\d+(?:;\d+)*R|\?[\d;]*c|>[\d;]*c|\d*n))+$/.test(s);
|
|
45
45
|
|
|
46
|
+
//#endregion
|
|
47
|
+
//#region ts/statusText.ts
|
|
48
|
+
/**
|
|
49
|
+
* Extract a short "what is the agent doing right now?" line from the rendered
|
|
50
|
+
* terminal screen. Claude Code paints this as a spinner/status line, e.g.
|
|
51
|
+
* "✶ Verifying calendar meetings with real data… (6m 30s · ↓ 19.5k tokens)".
|
|
52
|
+
*/
|
|
53
|
+
const SPINNER_PREFIX = /^[\u2800-\u28ff✶✻✢✳✽✦✧✩✷✸✹✺✼·•●◐◓◒◑]\s+/u;
|
|
54
|
+
const CONTROL = /[\x00-\x1f\x7f-\x9f]/g;
|
|
55
|
+
function parseStatusText(lines) {
|
|
56
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
57
|
+
const line = lines[i]?.replace(CONTROL, "").trim();
|
|
58
|
+
if (!line || line.length < 3) continue;
|
|
59
|
+
if (!SPINNER_PREFIX.test(line)) continue;
|
|
60
|
+
if (/^(?:[•·]\s*)?(?:esc|ctrl|enter|return|shift|tab)\b/i.test(line)) continue;
|
|
61
|
+
return line.slice(0, 220).trim();
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
|
|
46
66
|
//#endregion
|
|
47
67
|
//#region ts/serveLock.ts
|
|
48
68
|
/**
|
|
@@ -187,6 +207,48 @@ async function acquireWebrtcHostLock(opts) {
|
|
|
187
207
|
}
|
|
188
208
|
}
|
|
189
209
|
|
|
210
|
+
//#endregion
|
|
211
|
+
//#region ts/sizeNego.ts
|
|
212
|
+
/** Reject junk caps (a viewer mid-layout can report 0×0 or absurd numbers). */
|
|
213
|
+
const CAP_MIN_COLS = 20;
|
|
214
|
+
const CAP_MIN_ROWS = 5;
|
|
215
|
+
const CAP_MAX_COLS = 500;
|
|
216
|
+
const CAP_MAX_ROWS = 200;
|
|
217
|
+
/** Never negotiate below this — TUIs (claude, codex) break down when the grid
|
|
218
|
+
* gets absurdly narrow, so a tiny watch-sized viewer can't wedge the agent. */
|
|
219
|
+
const NEGO_FLOOR_COLS = 40;
|
|
220
|
+
const NEGO_FLOOR_ROWS = 10;
|
|
221
|
+
/** Parse a presence-reported cap into a sane SizeCap, or null if unusable. */
|
|
222
|
+
function sanitizeCap(cap) {
|
|
223
|
+
if (typeof cap !== "object" || cap === null) return null;
|
|
224
|
+
const c = Math.floor(Number(cap.cols) || 0);
|
|
225
|
+
const r = Math.floor(Number(cap.rows) || 0);
|
|
226
|
+
if (c < CAP_MIN_COLS || c > CAP_MAX_COLS) return null;
|
|
227
|
+
if (r < CAP_MIN_ROWS || r > CAP_MAX_ROWS) return null;
|
|
228
|
+
return {
|
|
229
|
+
cols: c,
|
|
230
|
+
rows: r
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Elementwise minimum over the live viewers' capacities, clamped to the floor.
|
|
235
|
+
* Returns null when no viewer reports a capacity — meaning "withdraw the
|
|
236
|
+
* negotiated size, let the real tty size rule again".
|
|
237
|
+
*/
|
|
238
|
+
function negotiateSize(caps) {
|
|
239
|
+
let cols = Infinity;
|
|
240
|
+
let rows = Infinity;
|
|
241
|
+
for (const cap of caps) {
|
|
242
|
+
if (cap.cols < cols) cols = cap.cols;
|
|
243
|
+
if (cap.rows < rows) rows = cap.rows;
|
|
244
|
+
}
|
|
245
|
+
if (!Number.isFinite(cols) || !Number.isFinite(rows)) return null;
|
|
246
|
+
return {
|
|
247
|
+
cols: Math.max(NEGO_FLOOR_COLS, cols),
|
|
248
|
+
rows: Math.max(NEGO_FLOOR_ROWS, rows)
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
190
252
|
//#endregion
|
|
191
253
|
//#region ts/strayServe.ts
|
|
192
254
|
const MANAGEMENT_COMMAND_RE = /\b(?:install|uninstall|status|logs|restart)\b/;
|
|
@@ -236,7 +298,49 @@ async function listStrayServeProcesses() {
|
|
|
236
298
|
|
|
237
299
|
//#endregion
|
|
238
300
|
//#region ts/serve.ts
|
|
239
|
-
const DEFAULT_PORT =
|
|
301
|
+
const DEFAULT_PORT = 0;
|
|
302
|
+
const PORTLESS_APP_NAME = "agent-yes";
|
|
303
|
+
const PORTLESS_CHILD_ENV = "AGENT_YES_PORTLESS_CHILD";
|
|
304
|
+
function portlessConsoleUrl(token) {
|
|
305
|
+
return portlessConsoleUrlForOrigin(process.env.PORTLESS_URL?.replace(/\/$/, "") || `https://${PORTLESS_APP_NAME}.localhost`, token);
|
|
306
|
+
}
|
|
307
|
+
function portlessConsoleUrlForOrigin(origin, token) {
|
|
308
|
+
return `${origin}${token ? `/#k=${encodeURIComponent(token)}` : "/"}`;
|
|
309
|
+
}
|
|
310
|
+
async function resolvedPortlessConsoleUrl(token) {
|
|
311
|
+
if (process.env.PORTLESS_URL) return portlessConsoleUrl(token);
|
|
312
|
+
try {
|
|
313
|
+
const port = Number((await readFile(path.join(homedir(), ".portless", "proxy.port"), "utf-8")).trim());
|
|
314
|
+
if (Number.isInteger(port) && port > 0) return portlessConsoleUrlForOrigin(`https://${PORTLESS_APP_NAME}.localhost${port === 443 ? "" : `:${port}`}`, token);
|
|
315
|
+
} catch {}
|
|
316
|
+
return portlessConsoleUrl(token);
|
|
317
|
+
}
|
|
318
|
+
async function runWithPortless(rest) {
|
|
319
|
+
const portless = Bun.which("portless");
|
|
320
|
+
if (!portless) {
|
|
321
|
+
process.stderr.write("ay serve: Portless is required for local HTTPS. Install it with: npm install -g portless\n");
|
|
322
|
+
return 1;
|
|
323
|
+
}
|
|
324
|
+
const script = process.argv[1];
|
|
325
|
+
const self = script && /\.[cm]?[jt]s$/.test(script) ? [process.execPath, script] : [process.execPath];
|
|
326
|
+
return await Bun.spawn([
|
|
327
|
+
portless,
|
|
328
|
+
PORTLESS_APP_NAME,
|
|
329
|
+
...self,
|
|
330
|
+
"serve",
|
|
331
|
+
...rest
|
|
332
|
+
], {
|
|
333
|
+
stdio: [
|
|
334
|
+
"inherit",
|
|
335
|
+
"inherit",
|
|
336
|
+
"inherit"
|
|
337
|
+
],
|
|
338
|
+
env: {
|
|
339
|
+
...liveEnv(),
|
|
340
|
+
[PORTLESS_CHILD_ENV]: "1"
|
|
341
|
+
}
|
|
342
|
+
}).exited ?? 1;
|
|
343
|
+
}
|
|
240
344
|
/**
|
|
241
345
|
* Normalize a user-supplied GitHub-ish source into the standard
|
|
242
346
|
* `<owner>/<repo>/tree/<branch>` path that codehost/provision's `parseSpec`
|
|
@@ -325,7 +429,7 @@ function loginShellEnv() {
|
|
|
325
429
|
const delim = "_AY_SHELL_ENV_DELIM_";
|
|
326
430
|
const out = Bun.spawnSync([
|
|
327
431
|
shell,
|
|
328
|
-
"-
|
|
432
|
+
"-lc",
|
|
329
433
|
`printf %s "${delim}"; env -0; printf %s "${delim}"`
|
|
330
434
|
], {
|
|
331
435
|
stdin: "ignore",
|
|
@@ -710,7 +814,18 @@ async function readDaemonServeArgs(mgr) {
|
|
|
710
814
|
}
|
|
711
815
|
function portFromArgs(args) {
|
|
712
816
|
const m = /--port[=\s](\d+)/.exec(args.join(" "));
|
|
713
|
-
return m ? Number(m[1]) :
|
|
817
|
+
return m ? Number(m[1]) : null;
|
|
818
|
+
}
|
|
819
|
+
function argsUsePortless(args) {
|
|
820
|
+
return (args.some((a) => a.startsWith("--http") || a.startsWith("--share")) || !args.some((a) => a.startsWith("--webrtc"))) && portFromArgs(args) === null;
|
|
821
|
+
}
|
|
822
|
+
async function portlessAppPort() {
|
|
823
|
+
try {
|
|
824
|
+
const route = JSON.parse(await readFile(path.join(homedir(), ".portless", "routes.json"), "utf-8")).find((r) => r.hostname === `${PORTLESS_APP_NAME}.localhost`);
|
|
825
|
+
return typeof route?.port === "number" ? route.port : null;
|
|
826
|
+
} catch {
|
|
827
|
+
return null;
|
|
828
|
+
}
|
|
714
829
|
}
|
|
715
830
|
async function warnStrayServeProcesses() {
|
|
716
831
|
const stray = await listStrayServeProcesses();
|
|
@@ -731,6 +846,7 @@ function explicitWebrtcUrl(args) {
|
|
|
731
846
|
}
|
|
732
847
|
}
|
|
733
848
|
async function fetchDaemonVersion(port, token) {
|
|
849
|
+
if (port === null) return null;
|
|
734
850
|
try {
|
|
735
851
|
const r = await fetch(`http://127.0.0.1:${port}/api/version`, {
|
|
736
852
|
headers: { Authorization: `Bearer ${token}` },
|
|
@@ -818,7 +934,7 @@ async function cmdServeDaemon(sub, args) {
|
|
|
818
934
|
let shareLink = null;
|
|
819
935
|
let shareLinkMinted = false;
|
|
820
936
|
if (webrtcDaemon) try {
|
|
821
|
-
const { loadOrCreateShareRoom, shareLinkFromRoomUrl } = await import("./share-
|
|
937
|
+
const { loadOrCreateShareRoom, shareLinkFromRoomUrl } = await import("./share-_QYjPN7q.js");
|
|
822
938
|
const explicit = explicitWebrtcUrl(effArgs);
|
|
823
939
|
shareLink = shareLinkFromRoomUrl(explicit ?? await loadOrCreateShareRoom());
|
|
824
940
|
shareLinkMinted = !explicit;
|
|
@@ -833,7 +949,7 @@ async function cmdServeDaemon(sub, args) {
|
|
|
833
949
|
};
|
|
834
950
|
if (priorArgs !== null) {
|
|
835
951
|
const sameConfig = JSON.stringify(effArgs) === JSON.stringify(priorArgs);
|
|
836
|
-
const runningVer = await fetchDaemonVersion(portFromArgs(effArgs), token);
|
|
952
|
+
const runningVer = await fetchDaemonVersion(argsUsePortless(effArgs) ? await portlessAppPort() : portFromArgs(effArgs), token);
|
|
837
953
|
if (runningVer === current && sameConfig) {
|
|
838
954
|
await ensureBootAutostart(mgr);
|
|
839
955
|
process.stdout.write(`'${DAEMON_NAME}' already running v${current} (up to date)\n`);
|
|
@@ -900,7 +1016,8 @@ async function cmdServeDaemon(sub, args) {
|
|
|
900
1016
|
}).exited;
|
|
901
1017
|
if (code === 0) {
|
|
902
1018
|
const onBoot = await ensureBootAutostart(mgr);
|
|
903
|
-
const port = portFromArgs(effArgs);
|
|
1019
|
+
const port = argsUsePortless(effArgs) ? await portlessAppPort() : portFromArgs(effArgs);
|
|
1020
|
+
const localUrl = argsUsePortless(effArgs) ? await resolvedPortlessConsoleUrl(token) : null;
|
|
904
1021
|
const httpish = effArgs.some((a) => a.startsWith("--http") || a.startsWith("--share")) || !effArgs.some((a) => a.startsWith("--webrtc"));
|
|
905
1022
|
process.stdout.write(`\n${priorArgs !== null ? `rolled '${DAEMON_NAME}' forward to` : `installed '${DAEMON_NAME}' as a daemon via ${mgr.id} —`} v${current}\n`);
|
|
906
1023
|
if (mgr.id === "oxmgr" && process.platform === "win32") process.stdout.write(onBoot ? `start-on-login: enabled (Task Scheduler ONLOGON runs \`oxmgr daemon run\`)\n` : `start-on-login: not registered — run \`oxmgr service install\` to enable\n`);
|
|
@@ -909,8 +1026,11 @@ async function cmdServeDaemon(sub, args) {
|
|
|
909
1026
|
else process.stdout.write(onBoot ? `start-on-boot: enabled (pm2 startup registered with the system init)\n` : `start-on-boot: not registered — run \`pm2 startup\` (may need sudo) to enable\n`);
|
|
910
1027
|
process.stdout.write(`token: ${token}\n\n`);
|
|
911
1028
|
if (httpish) {
|
|
912
|
-
process.stdout.write(`
|
|
913
|
-
|
|
1029
|
+
if (argsUsePortless(effArgs)) process.stdout.write(` web console: ${localUrl}\n`);
|
|
1030
|
+
else if (port !== null) {
|
|
1031
|
+
process.stdout.write(` ay ls ${token}@<host>:${port}\n`);
|
|
1032
|
+
process.stdout.write(` ay remote add <alias> http://${token}@<host>:${port}\n`);
|
|
1033
|
+
}
|
|
914
1034
|
}
|
|
915
1035
|
process.stdout.write(` ay serve logs # view server logs\n`);
|
|
916
1036
|
process.stdout.write(` ay serve uninstall # remove daemon\n`);
|
|
@@ -941,7 +1061,9 @@ async function cmdServeStatus(args) {
|
|
|
941
1061
|
const daemonArgs = mgr ? await readDaemonServeArgs(mgr) : null;
|
|
942
1062
|
const installed = daemonArgs !== null;
|
|
943
1063
|
const a = daemonArgs ?? [];
|
|
944
|
-
const
|
|
1064
|
+
const local = argsUsePortless(a);
|
|
1065
|
+
const port = local ? await portlessAppPort() : portFromArgs(a);
|
|
1066
|
+
const localUrl = local ? await resolvedPortlessConsoleUrl(token ?? void 0) : null;
|
|
945
1067
|
const webrtcish = a.some((x) => x.startsWith("--webrtc") || x.startsWith("--share"));
|
|
946
1068
|
const httpish = a.some((x) => x.startsWith("--http") || x.startsWith("--share")) || !a.some((x) => x.startsWith("--webrtc"));
|
|
947
1069
|
const mode = httpish && webrtcish ? "http+webrtc" : webrtcish ? "webrtc" : "http";
|
|
@@ -953,6 +1075,7 @@ async function cmdServeStatus(args) {
|
|
|
953
1075
|
installed,
|
|
954
1076
|
mode,
|
|
955
1077
|
port: httpish ? port : null,
|
|
1078
|
+
localUrl: httpish ? localUrl : null,
|
|
956
1079
|
reachable: runningVersion !== null,
|
|
957
1080
|
runningVersion,
|
|
958
1081
|
currentVersion: current,
|
|
@@ -968,18 +1091,23 @@ async function cmdServeStatus(args) {
|
|
|
968
1091
|
w(`manager: ${mgr ? mgr.id : "none — install pm2 or oxmgr to daemonize"}`);
|
|
969
1092
|
if (installed) {
|
|
970
1093
|
w(`installed: yes (via ${mgr.id})`);
|
|
971
|
-
w(`mode: ${mode}${httpish ? ` (port ${port})` : ""}`);
|
|
1094
|
+
w(`mode: ${mode}${httpish ? local ? ` (${localUrl})` : ` (port ${port})` : ""}`);
|
|
972
1095
|
if (a.length) w(`args: ${a.join(" ")}`);
|
|
973
1096
|
} else w(`installed: no — start a daemon with: ay serve install [--share]`);
|
|
974
|
-
if (runningVersion !== null)
|
|
975
|
-
|
|
976
|
-
|
|
1097
|
+
if (runningVersion !== null) {
|
|
1098
|
+
const tag = runningVersion === current ? "up to date" : `outdated (current v${current})`;
|
|
1099
|
+
w(`http api: reachable ${local ? `via ${localUrl}` : `on 127.0.0.1:${port}`} — v${runningVersion} (${tag})`);
|
|
1100
|
+
} else if (mode === "webrtc") w(`http api: none (webrtc-only)`);
|
|
1101
|
+
else w(`http api: not reachable ${local ? `via ${localUrl}` : `on 127.0.0.1:${port}`} (not running)`);
|
|
977
1102
|
w(`token: ${token ?? "(none yet — created on first serve)"}`);
|
|
978
1103
|
if (shareLink) w(`share link: ${shareLink}`);
|
|
979
1104
|
if (token && httpish) {
|
|
980
1105
|
w();
|
|
981
|
-
w(`
|
|
982
|
-
|
|
1106
|
+
if (local) w(`console: ${localUrl}`);
|
|
1107
|
+
else if (port !== null) {
|
|
1108
|
+
w(`connect: ay ls ${token}@<host>:${port}`);
|
|
1109
|
+
w(` ay remote add <alias> http://${token}@<host>:${port}`);
|
|
1110
|
+
}
|
|
983
1111
|
}
|
|
984
1112
|
return 0;
|
|
985
1113
|
}
|
|
@@ -1000,7 +1128,13 @@ Modes (default: --http):
|
|
|
1000
1128
|
--share [URL] Legacy alias for --http --webrtc
|
|
1001
1129
|
|
|
1002
1130
|
Options:
|
|
1003
|
-
--port N
|
|
1131
|
+
--port N Bypass Portless and listen on a fixed HTTP port
|
|
1132
|
+
--host HOST Interface to bind (default: 127.0.0.1; use 0.0.0.0 to expose)
|
|
1133
|
+
--token TOKEN Auth token (auto-generated and saved if omitted)
|
|
1134
|
+
-d, --daemon Install these flags as a background daemon (pm2/oxmgr)
|
|
1135
|
+
(same as: ay serve install <flags>)
|
|
1136
|
+
--local Explicitly use Portless (the default for HTTP mode)
|
|
1137
|
+
${portlessConsoleUrl()}\n --allow-spawn Deprecated no-op — the console can always spawn agents\n --tls-cert FILE TLS certificate PEM\n --tls-key FILE TLS private key PEM\n\nSubcommands:\n ay serve install install as background daemon (oxmgr; pm2 fallback on Windows without the winfix build)\n ay serve status show daemon/server status (add --json for scripts)\n ay serve uninstall remove daemon\n ay serve logs view daemon logs\n\nOnce running, connect from another machine:\n ay ls <token>@<host>:<port>\n ay remote add <alias> http://<token>@<host>:<port>\n`);
|
|
1004
1138
|
return 0;
|
|
1005
1139
|
}
|
|
1006
1140
|
const sub = rest[0];
|
|
@@ -1020,8 +1154,7 @@ Options:
|
|
|
1020
1154
|
if (sub === "install" || sub === "uninstall" || sub === "logs") return cmdServeDaemon(sub, rest.slice(1));
|
|
1021
1155
|
const argv = await yargs(rest).usage("Usage: ay serve [options]").option("port", {
|
|
1022
1156
|
type: "number",
|
|
1023
|
-
|
|
1024
|
-
description: "Port to listen on"
|
|
1157
|
+
description: "Bypass Portless and listen on a fixed port"
|
|
1025
1158
|
}).option("host", {
|
|
1026
1159
|
type: "string",
|
|
1027
1160
|
default: "127.0.0.1",
|
|
@@ -1049,6 +1182,10 @@ Options:
|
|
|
1049
1182
|
type: "boolean",
|
|
1050
1183
|
default: false,
|
|
1051
1184
|
description: "Install as a background daemon (same as: ay serve install <flags>)"
|
|
1185
|
+
}).option("local", {
|
|
1186
|
+
type: "boolean",
|
|
1187
|
+
default: false,
|
|
1188
|
+
description: `Use Portless at ${portlessConsoleUrl()} (default for HTTP mode)`
|
|
1052
1189
|
}).option("allow-spawn", {
|
|
1053
1190
|
type: "boolean",
|
|
1054
1191
|
default: false,
|
|
@@ -1059,13 +1196,22 @@ Options:
|
|
|
1059
1196
|
description: "If another ay serve already hosts this home's WebRTC room, stop it and take the room"
|
|
1060
1197
|
}).help(false).version(false).exitProcess(false).parseAsync();
|
|
1061
1198
|
if (argv.daemon) return cmdServeDaemon("install", rest.filter((a) => a !== "--daemon" && a !== "-d"));
|
|
1199
|
+
const wantWebrtc = argv.webrtc !== void 0 || argv.share !== void 0;
|
|
1200
|
+
const wantHttp = argv.http === true || argv.share !== void 0 || argv.webrtc === void 0;
|
|
1201
|
+
const fixedPort = typeof argv.port === "number";
|
|
1202
|
+
const usePortless = wantHttp && !fixedPort;
|
|
1203
|
+
if (argv.local === true && fixedPort) {
|
|
1204
|
+
process.stderr.write("ay serve: --local and --port cannot be used together\n");
|
|
1205
|
+
return 1;
|
|
1206
|
+
}
|
|
1207
|
+
if (usePortless && process.env[PORTLESS_CHILD_ENV] !== "1" && process.env.PORTLESS !== "0") return runWithPortless(rest);
|
|
1062
1208
|
const stray = argv._.map(String);
|
|
1063
1209
|
if (stray.length) {
|
|
1064
1210
|
const hint = stray.includes("share") ? " (did you mean --share?)" : "";
|
|
1065
1211
|
process.stderr.write(`ay serve: ignoring unknown argument${stray.length > 1 ? "s" : ""}: ${stray.join(" ")}${hint}\n`);
|
|
1066
1212
|
}
|
|
1067
1213
|
delete process.env.AGENT_YES_PID;
|
|
1068
|
-
const port = argv.port ?? DEFAULT_PORT;
|
|
1214
|
+
const port = argv.port ?? (Number(process.env.PORT) || DEFAULT_PORT);
|
|
1069
1215
|
const host = argv.host ?? "127.0.0.1";
|
|
1070
1216
|
const tokenFlag = typeof argv.token === "string" ? argv.token : void 0;
|
|
1071
1217
|
const certPath = typeof argv["tls-cert"] === "string" ? argv["tls-cert"] : void 0;
|
|
@@ -1076,8 +1222,6 @@ Options:
|
|
|
1076
1222
|
}
|
|
1077
1223
|
const useHttps = !!(certPath && keyPath);
|
|
1078
1224
|
const scheme = useHttps ? "https" : "http";
|
|
1079
|
-
const wantWebrtc = argv.webrtc !== void 0 || argv.share !== void 0;
|
|
1080
|
-
const wantHttp = argv.http === true || argv.share !== void 0 || argv.webrtc === void 0;
|
|
1081
1225
|
let releaseHostLock = null;
|
|
1082
1226
|
if (wantWebrtc && process.env.AGENT_YES_NO_SERVE_LOCK !== "1") {
|
|
1083
1227
|
const lock = await acquireWebrtcHostLock({ takeover: argv.takeover === true });
|
|
@@ -1177,6 +1321,25 @@ Options:
|
|
|
1177
1321
|
return [];
|
|
1178
1322
|
}
|
|
1179
1323
|
};
|
|
1324
|
+
const statusTextCache = /* @__PURE__ */ new Map();
|
|
1325
|
+
const logStatusText = async (logFile) => {
|
|
1326
|
+
if (!logFile) return null;
|
|
1327
|
+
try {
|
|
1328
|
+
const { size, mtimeMs } = await stat(logFile);
|
|
1329
|
+
const hit = statusTextCache.get(logFile);
|
|
1330
|
+
if (hit && hit.size === size && hit.mtimeMs === mtimeMs) return hit.statusText;
|
|
1331
|
+
const lines = await renderLogTailLines(logFile, 40);
|
|
1332
|
+
const statusText = lines ? parseStatusText(lines) : null;
|
|
1333
|
+
statusTextCache.set(logFile, {
|
|
1334
|
+
size,
|
|
1335
|
+
mtimeMs,
|
|
1336
|
+
statusText
|
|
1337
|
+
});
|
|
1338
|
+
return statusText;
|
|
1339
|
+
} catch {
|
|
1340
|
+
return null;
|
|
1341
|
+
}
|
|
1342
|
+
};
|
|
1180
1343
|
const niCache = /* @__PURE__ */ new Map();
|
|
1181
1344
|
const logNeedsInput = async (logFile, cli) => {
|
|
1182
1345
|
if (!logFile) return null;
|
|
@@ -1338,6 +1501,7 @@ Options:
|
|
|
1338
1501
|
status: status === "exited" ? status : r.unresponsive ? "stuck" : question ? "needs_input" : status,
|
|
1339
1502
|
question,
|
|
1340
1503
|
title: await logTitle(r.log_file),
|
|
1504
|
+
status_text: status === "exited" ? null : await logStatusText(r.log_file),
|
|
1341
1505
|
git: status === "exited" ? null : await gitStatus(r.cwd),
|
|
1342
1506
|
tasks: status === "exited" ? null : await logTasks(r.log_file),
|
|
1343
1507
|
badges: status === "exited" ? [] : await logBadges(r.log_file).then(async (b) => await isUserTyping(r.pid) ? [...b, TYPING_BADGE.id] : b)
|
|
@@ -1345,6 +1509,53 @@ Options:
|
|
|
1345
1509
|
};
|
|
1346
1510
|
const presence = /* @__PURE__ */ new Map();
|
|
1347
1511
|
const PRESENCE_TTL_MS = 12e3;
|
|
1512
|
+
const negoApplied = /* @__PURE__ */ new Map();
|
|
1513
|
+
const negoTimers = /* @__PURE__ */ new Map();
|
|
1514
|
+
const NEGO_DEBOUNCE_MS = 350;
|
|
1515
|
+
const winsizePathFor = (pid) => path.join(process.env.AGENT_YES_HOME ?? path.join(homedir(), ".agent-yes"), "winsize", String(pid));
|
|
1516
|
+
const liveCapsFor = (pid) => {
|
|
1517
|
+
const now = Date.now();
|
|
1518
|
+
const caps = [];
|
|
1519
|
+
for (const v of presence.values()) if (now - v.ts <= PRESENCE_TTL_MS && String(v.agent) === String(pid) && v.cap) caps.push(v.cap);
|
|
1520
|
+
return caps;
|
|
1521
|
+
};
|
|
1522
|
+
const applyNego = async (pid) => {
|
|
1523
|
+
negoTimers.delete(pid);
|
|
1524
|
+
const eff = negotiateSize(liveCapsFor(pid));
|
|
1525
|
+
const file = winsizePathFor(pid);
|
|
1526
|
+
const prev = negoApplied.get(pid);
|
|
1527
|
+
try {
|
|
1528
|
+
if (eff) {
|
|
1529
|
+
if (prev && prev.cols === eff.cols && prev.rows === eff.rows) return;
|
|
1530
|
+
const content = `${eff.cols} ${eff.rows} ${Date.now()}\n`;
|
|
1531
|
+
await mkdir(path.dirname(file), { recursive: true });
|
|
1532
|
+
await writeFile(file, content);
|
|
1533
|
+
negoApplied.set(pid, {
|
|
1534
|
+
...eff,
|
|
1535
|
+
content
|
|
1536
|
+
});
|
|
1537
|
+
} else {
|
|
1538
|
+
if (!prev) return;
|
|
1539
|
+
negoApplied.delete(pid);
|
|
1540
|
+
try {
|
|
1541
|
+
if (await readFile(file, "utf-8") !== prev.content) return;
|
|
1542
|
+
await unlink(file);
|
|
1543
|
+
} catch {
|
|
1544
|
+
return;
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
try {
|
|
1548
|
+
process.kill(pid, "SIGWINCH");
|
|
1549
|
+
} catch {}
|
|
1550
|
+
} catch {}
|
|
1551
|
+
};
|
|
1552
|
+
const scheduleNego = (pid) => {
|
|
1553
|
+
if (!Number.isFinite(pid) || pid <= 0 || negoTimers.has(pid)) return;
|
|
1554
|
+
negoTimers.set(pid, setTimeout(() => void applyNego(pid), NEGO_DEBOUNCE_MS));
|
|
1555
|
+
};
|
|
1556
|
+
setInterval(() => {
|
|
1557
|
+
for (const pid of negoApplied.keys()) scheduleNego(pid);
|
|
1558
|
+
}, 5e3);
|
|
1348
1559
|
const apiFetch = async (req) => {
|
|
1349
1560
|
if (!checkAuth(req, token)) return new Response("Unauthorized", { status: 401 });
|
|
1350
1561
|
const url = new URL(req.url);
|
|
@@ -1657,7 +1868,7 @@ Options:
|
|
|
1657
1868
|
}
|
|
1658
1869
|
});
|
|
1659
1870
|
if (req.method === "GET" && p === "/api/ws") {
|
|
1660
|
-
const ws = await import("./ws-
|
|
1871
|
+
const ws = await import("./ws-CGNmxCPD.js");
|
|
1661
1872
|
try {
|
|
1662
1873
|
await ws.loadProvision();
|
|
1663
1874
|
} catch (e) {
|
|
@@ -1677,7 +1888,7 @@ Options:
|
|
|
1677
1888
|
if (req.method === "GET" && p === "/api/ws/status") {
|
|
1678
1889
|
const dirRaw = url.searchParams.get("path");
|
|
1679
1890
|
if (!dirRaw) return new Response("missing ?path=<workspace dir>", { status: 400 });
|
|
1680
|
-
const ws = await import("./ws-
|
|
1891
|
+
const ws = await import("./ws-CGNmxCPD.js");
|
|
1681
1892
|
let prov;
|
|
1682
1893
|
try {
|
|
1683
1894
|
prov = await ws.loadProvision();
|
|
@@ -1742,7 +1953,8 @@ Options:
|
|
|
1742
1953
|
return Response.json({
|
|
1743
1954
|
pid: record.pid,
|
|
1744
1955
|
cols: size?.cols ?? null,
|
|
1745
|
-
rows: size?.rows ?? null
|
|
1956
|
+
rows: size?.rows ?? null,
|
|
1957
|
+
nego: true
|
|
1746
1958
|
});
|
|
1747
1959
|
} catch (e) {
|
|
1748
1960
|
return new Response(e.message, { status: 404 });
|
|
@@ -2023,6 +2235,7 @@ Options:
|
|
|
2023
2235
|
}
|
|
2024
2236
|
const viewer = String(b.viewer ?? "").slice(0, 64);
|
|
2025
2237
|
if (!viewer) return new Response("missing viewer", { status: 400 });
|
|
2238
|
+
const prevAgent = presence.get(viewer)?.agent;
|
|
2026
2239
|
if (b.agent == null) presence.delete(viewer);
|
|
2027
2240
|
else presence.set(viewer, {
|
|
2028
2241
|
viewer,
|
|
@@ -2030,8 +2243,11 @@ Options:
|
|
|
2030
2243
|
cols: Math.max(0, Math.floor(Number(b.cols) || 0)),
|
|
2031
2244
|
rows: Math.max(0, Math.floor(Number(b.rows) || 0)),
|
|
2032
2245
|
sel: typeof b.sel === "string" ? b.sel.slice(0, 200) : null,
|
|
2246
|
+
cap: sanitizeCap(b.cap),
|
|
2033
2247
|
ts: Date.now()
|
|
2034
2248
|
});
|
|
2249
|
+
if (b.agent != null) scheduleNego(Number(b.agent));
|
|
2250
|
+
if (prevAgent != null && String(prevAgent) !== String(b.agent ?? "")) scheduleNego(Number(prevAgent));
|
|
2035
2251
|
return new Response(null, { status: 204 });
|
|
2036
2252
|
}
|
|
2037
2253
|
if (req.method === "GET" && p === "/api/presence") {
|
|
@@ -2233,7 +2449,7 @@ Options:
|
|
|
2233
2449
|
const perm = body.perm ?? "r";
|
|
2234
2450
|
if (perm !== "r" && perm !== "rw") return new Response(`invalid perm ${perm} (want r or rw)`, { status: 400 });
|
|
2235
2451
|
try {
|
|
2236
|
-
const { createScopedShare } = await import("./agentShare-
|
|
2452
|
+
const { createScopedShare } = await import("./agentShare-DdEbHtAn.js");
|
|
2237
2453
|
const share = await createScopedShare({
|
|
2238
2454
|
agent: body.agent,
|
|
2239
2455
|
perm,
|
|
@@ -2248,12 +2464,12 @@ Options:
|
|
|
2248
2464
|
}
|
|
2249
2465
|
}
|
|
2250
2466
|
if (req.method === "GET" && p === "/api/shares") {
|
|
2251
|
-
const { listShares } = await import("./agentShare-
|
|
2467
|
+
const { listShares } = await import("./agentShare-DdEbHtAn.js");
|
|
2252
2468
|
return Response.json(listShares());
|
|
2253
2469
|
}
|
|
2254
2470
|
const revokeM = /^\/api\/share\/([^/]+)$/.exec(p);
|
|
2255
2471
|
if (req.method === "DELETE" && revokeM) {
|
|
2256
|
-
const { revokeShare } = await import("./agentShare-
|
|
2472
|
+
const { revokeShare } = await import("./agentShare-DdEbHtAn.js");
|
|
2257
2473
|
const ok = revokeShare(decodeURIComponent(revokeM[1]));
|
|
2258
2474
|
return new Response(ok ? "revoked" : "no such share", { status: ok ? 200 : 404 });
|
|
2259
2475
|
}
|
|
@@ -2369,17 +2585,18 @@ Options:
|
|
|
2369
2585
|
throw e;
|
|
2370
2586
|
}
|
|
2371
2587
|
if (server) {
|
|
2588
|
+
const listenPort = server.port;
|
|
2372
2589
|
const uiHost = host === "0.0.0.0" || host === "::" ? "127.0.0.1" : host;
|
|
2373
|
-
process.stdout.write(`ay serve ${scheme}://${host}:${
|
|
2590
|
+
process.stdout.write(`ay serve ${scheme}://${host}:${listenPort}\n`);
|
|
2374
2591
|
process.stdout.write(`token: ${token}\n\n`);
|
|
2375
2592
|
process.stdout.write(`web console (token in the # is eaten on open):\n`);
|
|
2376
|
-
process.stdout.write(` ${scheme}://${uiHost}:${
|
|
2593
|
+
process.stdout.write(` ${usePortless ? portlessConsoleUrl(token) : `${scheme}://${uiHost}:${listenPort}/#k=${token}`}\n\n`);
|
|
2377
2594
|
process.stdout.write(`connect from another machine:\n`);
|
|
2378
|
-
process.stdout.write(` ay ls ${token}@<host>:${
|
|
2379
|
-
process.stdout.write(` ay tail ${token}@<host>:${
|
|
2380
|
-
process.stdout.write(` ay send ${token}@<host>:${
|
|
2595
|
+
process.stdout.write(` ay ls ${token}@<host>:${listenPort}\n`);
|
|
2596
|
+
process.stdout.write(` ay tail ${token}@<host>:${listenPort}:<keyword>\n`);
|
|
2597
|
+
process.stdout.write(` ay send ${token}@<host>:${listenPort}:<keyword> "message"\n\n`);
|
|
2381
2598
|
process.stdout.write(`save as alias:\n`);
|
|
2382
|
-
process.stdout.write(` ay remote add <alias> ${scheme}://${token}@<host>:${
|
|
2599
|
+
process.stdout.write(` ay remote add <alias> ${scheme}://${token}@<host>:${listenPort}\n\n`);
|
|
2383
2600
|
if (!useHttps) process.stdout.write("for HTTPS: ay serve --tls-cert cert.pem --tls-key key.pem\n openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes -subj '/CN=localhost'\n\n");
|
|
2384
2601
|
}
|
|
2385
2602
|
}
|
|
@@ -2388,7 +2605,7 @@ Options:
|
|
|
2388
2605
|
const webrtcVal = argv.webrtc ?? argv.share;
|
|
2389
2606
|
const explicitUrl = typeof webrtcVal === "string" && webrtcVal.startsWith("webrtc://") ? webrtcVal : void 0;
|
|
2390
2607
|
try {
|
|
2391
|
-
const { startShare, loadOrCreateShareRoom } = await import("./share-
|
|
2608
|
+
const { startShare, loadOrCreateShareRoom } = await import("./share-_QYjPN7q.js");
|
|
2392
2609
|
const linkFile = path.join(process.env.AGENT_YES_HOME ?? path.join(homedir(), ".agent-yes"), ".share-link");
|
|
2393
2610
|
const announce = async (room, link, rotated) => {
|
|
2394
2611
|
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)";
|
|
@@ -2438,7 +2655,7 @@ Options:
|
|
|
2438
2655
|
const shutdown = (resolve) => {
|
|
2439
2656
|
if (heartbeat) clearInterval(heartbeat);
|
|
2440
2657
|
closeShare?.();
|
|
2441
|
-
import("./agentShare-
|
|
2658
|
+
import("./agentShare-DdEbHtAn.js").then((m) => m.revokeAllShares()).catch(() => {});
|
|
2442
2659
|
server?.stop();
|
|
2443
2660
|
Promise.resolve(releaseHostLock?.()).catch(() => {}).then(resolve);
|
|
2444
2661
|
};
|
|
@@ -2451,4 +2668,4 @@ Options:
|
|
|
2451
2668
|
|
|
2452
2669
|
//#endregion
|
|
2453
2670
|
export { cmdServe };
|
|
2454
|
-
//# sourceMappingURL=serve-
|
|
2671
|
+
//# sourceMappingURL=serve-B-ZCy67S.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-B-ZCy67S.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-D6UBzk4-.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import "./e2e-jb0Hp43q.js";
|
|
2
|
-
import { n as shareLinkFromRoomUrl, r as startShare, t as loadOrCreateShareRoom } from "./share-
|
|
2
|
+
import { n as shareLinkFromRoomUrl, r as startShare, t as loadOrCreateShareRoom } from "./share-vchIyVd8.js";
|
|
3
3
|
|
|
4
4
|
export { loadOrCreateShareRoom, shareLinkFromRoomUrl, startShare };
|