agent-yes 1.122.2 → 1.123.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/default.config.yaml +19 -0
- package/dist/{SUPPORTED_CLIS-BTu2brih.js → SUPPORTED_CLIS-B4O2cFlt.js} +2 -2
- package/dist/SUPPORTED_CLIS-DHkqGoNv.js +8 -0
- package/dist/{agent-yes.config-z-IPzH5U.js → agent-yes.config-D6ycMApr.js} +2 -65
- package/dist/cli.js +6 -6
- package/dist/configShared-C5QaNPnz.js +71 -0
- package/dist/{globalPidIndex-gZuTvTBs.js → globalPidIndex-C7r2m6s7.js} +19 -20
- package/dist/index.js +4 -4
- package/dist/pidStore-C4c2O15q.js +5 -0
- package/dist/{pidStore-B5vBu8Px.js → pidStore-CGKIhaJO.js} +5 -4
- package/dist/reaper-BLVA780B.js +3 -0
- package/dist/{reaper-Dj8R7ltI.js → reaper-BkjPN7mw.js} +24 -2
- package/dist/{remotes-CpGcTr7A.js → remotes-BRCDVnR7.js} +1 -1
- package/dist/{remotes-D2fqaRU8.js → remotes-D8GvSbhf.js} +1 -1
- package/dist/{schedule-DgRrdA_n.js → schedule-DULdIkU9.js} +7 -7
- package/dist/{serve-tn7ZetZs.js → serve-r_2v9EKc.js} +202 -58
- package/dist/{setup-dZhgpNse.js → setup-DHa6fX8M.js} +3 -3
- package/dist/{share-CksllWW-.js → share-YuM6-Q6A.js} +78 -4
- package/dist/{subcommands-D9BWZilr.js → subcommands-B13Kto-u.js} +647 -32
- package/dist/subcommands-Tv6AwUkD.js +7 -0
- package/dist/{tray-DjCIyakK.js → tray-BVnJLThD.js} +1 -1
- package/dist/{ts-CIf0uaR7.js → ts-DgukRoEI.js} +10 -7
- package/dist/{versionChecker-DjxKi4qe.js → versionChecker-BqOr1YqC.js} +2 -2
- package/dist/{workspaceConfig-XP2NEWmV.js → workspaceConfig-BJO4fzEn.js} +1 -1
- package/lab/ui/console-logic.js +222 -10
- package/lab/ui/icon.svg +5 -0
- package/lab/ui/index.html +689 -14
- package/lab/ui/landing.html +276 -0
- package/lab/ui/manifest.webmanifest +14 -0
- package/lab/ui/sw.js +56 -0
- package/package.json +5 -1
- package/ts/agentTree.spec.ts +92 -0
- package/ts/agentTree.ts +149 -0
- package/ts/configShared.ts +4 -0
- package/ts/globalPidIndex.ts +28 -20
- package/ts/idleWaiter.spec.ts +7 -1
- package/ts/index.ts +9 -0
- package/ts/lsWatch.spec.ts +61 -0
- package/ts/lsWatch.ts +94 -0
- package/ts/needsInput.spec.ts +55 -0
- package/ts/needsInput.ts +68 -0
- package/ts/pidStore.ts +3 -0
- package/ts/reaper.spec.ts +26 -2
- package/ts/reaper.ts +25 -0
- package/ts/resultEnvelope.spec.ts +43 -0
- package/ts/resultEnvelope.ts +88 -0
- package/ts/serve.ts +276 -41
- package/ts/share.ts +156 -3
- package/ts/subcommands.ts +0 -0
- package/ts/todoParse.spec.ts +68 -0
- package/ts/todoParse.ts +88 -0
- package/ts/utils.spec.ts +4 -1
- package/dist/SUPPORTED_CLIS-DcOKE9Nz.js +0 -8
- package/dist/pidStore-7y1cTcAE.js +0 -5
- package/dist/reaper-HqcUms2d.js +0 -3
- package/dist/subcommands-D8sHibKu.js +0 -6
|
@@ -1,17 +1,19 @@
|
|
|
1
|
-
import "./ts-
|
|
1
|
+
import "./ts-DgukRoEI.js";
|
|
2
2
|
import "./logger-B9h0djqx.js";
|
|
3
|
-
import { r as getInstalledPackage } from "./versionChecker-
|
|
4
|
-
import "./pidStore-
|
|
5
|
-
import "./globalPidIndex-
|
|
6
|
-
import { t as
|
|
7
|
-
import "./
|
|
8
|
-
import {
|
|
3
|
+
import { r as getInstalledPackage } from "./versionChecker-BqOr1YqC.js";
|
|
4
|
+
import "./pidStore-CGKIhaJO.js";
|
|
5
|
+
import { a as updateGlobalPidStatus } from "./globalPidIndex-C7r2m6s7.js";
|
|
6
|
+
import { t as pgidForWrapper } from "./reaper-BkjPN7mw.js";
|
|
7
|
+
import "./configShared-C5QaNPnz.js";
|
|
8
|
+
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-B4O2cFlt.js";
|
|
9
|
+
import "./remotes-D8GvSbhf.js";
|
|
10
|
+
import { f as readNotes, g as snapshotStatus, m as resolveOne, o as extractTaskCounts, p as renderRawLog, r as controlCodeFromName, u as listRecords, v as writeToIpc } from "./subcommands-B13Kto-u.js";
|
|
9
11
|
import yargs from "yargs";
|
|
10
|
-
import { mkdir, open, readFile, writeFile } from "fs/promises";
|
|
12
|
+
import { mkdir, open, readFile, stat, writeFile } from "fs/promises";
|
|
11
13
|
import { homedir, hostname, userInfo } from "os";
|
|
12
14
|
import path from "path";
|
|
13
15
|
import { fileURLToPath } from "node:url";
|
|
14
|
-
import { watch } from "node:fs";
|
|
16
|
+
import { renameSync, watch, writeFileSync } from "node:fs";
|
|
15
17
|
import { randomBytes, timingSafeEqual } from "crypto";
|
|
16
18
|
|
|
17
19
|
//#region ts/serve.ts
|
|
@@ -22,6 +24,11 @@ function agentYesHome() {
|
|
|
22
24
|
function tokenPath() {
|
|
23
25
|
return path.join(agentYesHome(), ".serve-token");
|
|
24
26
|
}
|
|
27
|
+
function heartbeatPath() {
|
|
28
|
+
return path.join(agentYesHome(), ".serve-heartbeat");
|
|
29
|
+
}
|
|
30
|
+
const HEARTBEAT_WRITE_MS = 5e3;
|
|
31
|
+
const HEARTBEAT_STALE_MS = 15e3;
|
|
25
32
|
async function loadOrCreateToken(tokenFlag) {
|
|
26
33
|
if (tokenFlag) return tokenFlag;
|
|
27
34
|
try {
|
|
@@ -244,6 +251,16 @@ async function cmdServeDaemon(sub, args) {
|
|
|
244
251
|
]);
|
|
245
252
|
}
|
|
246
253
|
const serveArgv = ayServeArgv(effArgs);
|
|
254
|
+
const oxmgrHealth = effArgs.some((a) => a.startsWith("--webrtc") || a.startsWith("--share")) && mgr.id === "oxmgr" ? [
|
|
255
|
+
"--health-cmd",
|
|
256
|
+
ayServeArgv(["healthcheck"]).join(" "),
|
|
257
|
+
"--health-interval",
|
|
258
|
+
"10",
|
|
259
|
+
"--health-timeout",
|
|
260
|
+
"5",
|
|
261
|
+
"--health-max-failures",
|
|
262
|
+
"3"
|
|
263
|
+
] : [];
|
|
247
264
|
const startArgv = mgr.id === "oxmgr" ? [
|
|
248
265
|
mgr.bin,
|
|
249
266
|
"start",
|
|
@@ -251,7 +268,10 @@ async function cmdServeDaemon(sub, args) {
|
|
|
251
268
|
"--name",
|
|
252
269
|
DAEMON_NAME,
|
|
253
270
|
"--restart",
|
|
254
|
-
"always"
|
|
271
|
+
"always",
|
|
272
|
+
"--max-restarts",
|
|
273
|
+
"1000000",
|
|
274
|
+
...oxmgrHealth
|
|
255
275
|
] : [
|
|
256
276
|
mgr.bin,
|
|
257
277
|
"start",
|
|
@@ -395,6 +415,18 @@ Options:
|
|
|
395
415
|
}
|
|
396
416
|
const sub = rest[0];
|
|
397
417
|
if (sub === "status") return cmdServeStatus(rest.slice(1));
|
|
418
|
+
if (sub === "healthcheck") {
|
|
419
|
+
try {
|
|
420
|
+
const raw = (await readFile(heartbeatPath(), "utf-8")).trim();
|
|
421
|
+
const ts = Number(raw);
|
|
422
|
+
const age = Date.now() - ts;
|
|
423
|
+
if (raw.length > 0 && Number.isFinite(ts) && ts > 0 && age > HEARTBEAT_STALE_MS) {
|
|
424
|
+
process.stderr.write(`unhealthy: serve heartbeat stale by ${age}ms\n`);
|
|
425
|
+
return 1;
|
|
426
|
+
}
|
|
427
|
+
} catch {}
|
|
428
|
+
return 0;
|
|
429
|
+
}
|
|
398
430
|
if (sub === "install" || sub === "uninstall" || sub === "logs") return cmdServeDaemon(sub, rest.slice(1));
|
|
399
431
|
const argv = await yargs(rest).usage("Usage: ay serve [options]").option("port", {
|
|
400
432
|
type: "number",
|
|
@@ -482,21 +514,29 @@ Options:
|
|
|
482
514
|
return null;
|
|
483
515
|
}
|
|
484
516
|
};
|
|
485
|
-
const
|
|
486
|
-
const
|
|
487
|
-
|
|
488
|
-
if (!cwd) return null;
|
|
489
|
-
const now = Date.now();
|
|
490
|
-
const hit = gitCache.get(cwd);
|
|
491
|
-
if (hit && now - hit.at < GIT_TTL_MS) return hit.val;
|
|
492
|
-
let val = null;
|
|
517
|
+
const taskCache = /* @__PURE__ */ new Map();
|
|
518
|
+
const logTasks = async (logFile) => {
|
|
519
|
+
if (!logFile) return null;
|
|
493
520
|
try {
|
|
494
|
-
const
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
521
|
+
const { size, mtimeMs } = await stat(logFile);
|
|
522
|
+
const hit = taskCache.get(logFile);
|
|
523
|
+
if (hit && hit.size === size && hit.mtimeMs === mtimeMs) return hit.tasks;
|
|
524
|
+
const tasks = await extractTaskCounts(logFile);
|
|
525
|
+
taskCache.set(logFile, {
|
|
526
|
+
size,
|
|
527
|
+
mtimeMs,
|
|
528
|
+
tasks
|
|
529
|
+
});
|
|
530
|
+
return tasks;
|
|
531
|
+
} catch {
|
|
532
|
+
return null;
|
|
533
|
+
}
|
|
534
|
+
};
|
|
535
|
+
const GIT_DEBOUNCE_MS = 800;
|
|
536
|
+
const GIT_SAFETY_MS = 6e4;
|
|
537
|
+
const runGit = async (args, cwd) => {
|
|
538
|
+
try {
|
|
539
|
+
const proc = Bun.spawn(["git", ...args], {
|
|
500
540
|
cwd,
|
|
501
541
|
stdout: "pipe",
|
|
502
542
|
stderr: "ignore",
|
|
@@ -504,35 +544,89 @@ Options:
|
|
|
504
544
|
});
|
|
505
545
|
const out = await new Response(proc.stdout).text();
|
|
506
546
|
await proc.exited;
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
547
|
+
return proc.exitCode === 0 ? out : null;
|
|
548
|
+
} catch {
|
|
549
|
+
return null;
|
|
550
|
+
}
|
|
551
|
+
};
|
|
552
|
+
const parseGitStatus = (out) => {
|
|
553
|
+
const lines = out.split("\n");
|
|
554
|
+
const h = /^## (.+)$/.exec(lines[0] ?? "")?.[1] ?? "";
|
|
555
|
+
const unborn = /^No commits yet on (.+)$/.exec(h);
|
|
556
|
+
const branch = unborn ? unborn[1] : /^(.+?)(?:\.\.\.|\s|$)/.exec(h)?.[1] || null;
|
|
557
|
+
const ahead = Number(/\bahead (\d+)/.exec(h)?.[1] ?? 0);
|
|
558
|
+
const behind = Number(/\bbehind (\d+)/.exec(h)?.[1] ?? 0);
|
|
559
|
+
const changed = lines.slice(1).filter((l) => l.trim().length > 0).length;
|
|
560
|
+
return {
|
|
561
|
+
branch,
|
|
562
|
+
dirty: changed > 0,
|
|
563
|
+
changed,
|
|
564
|
+
ahead,
|
|
565
|
+
behind
|
|
566
|
+
};
|
|
567
|
+
};
|
|
568
|
+
const rootOfCwd = /* @__PURE__ */ new Map();
|
|
569
|
+
const resolveRoot = async (cwd) => {
|
|
570
|
+
const cached = rootOfCwd.get(cwd);
|
|
571
|
+
if (cached !== void 0) return cached;
|
|
572
|
+
const root = (await runGit(["rev-parse", "--show-toplevel"], cwd) ?? "").trim();
|
|
573
|
+
rootOfCwd.set(cwd, root);
|
|
574
|
+
return root;
|
|
575
|
+
};
|
|
576
|
+
const repoWatch = /* @__PURE__ */ new Map();
|
|
577
|
+
const recompute = (root, rw) => {
|
|
578
|
+
if (rw.timer) return;
|
|
579
|
+
rw.timer = setTimeout(async () => {
|
|
580
|
+
rw.timer = null;
|
|
581
|
+
if (rw.busy) return void recompute(root, rw);
|
|
582
|
+
rw.busy = true;
|
|
583
|
+
try {
|
|
584
|
+
const out = await runGit([
|
|
585
|
+
"status",
|
|
586
|
+
"--porcelain",
|
|
587
|
+
"--branch"
|
|
588
|
+
], root);
|
|
589
|
+
if (out != null) rw.val = parseGitStatus(out);
|
|
590
|
+
} finally {
|
|
591
|
+
rw.busy = false;
|
|
522
592
|
}
|
|
593
|
+
}, GIT_DEBOUNCE_MS);
|
|
594
|
+
};
|
|
595
|
+
const ensureRepoWatch = (root) => {
|
|
596
|
+
const existing = repoWatch.get(root);
|
|
597
|
+
if (existing) return existing;
|
|
598
|
+
const rw = {
|
|
599
|
+
val: null,
|
|
600
|
+
busy: false,
|
|
601
|
+
timer: null
|
|
602
|
+
};
|
|
603
|
+
repoWatch.set(root, rw);
|
|
604
|
+
recompute(root, rw);
|
|
605
|
+
const onChange = (file) => {
|
|
606
|
+
if (file.includes(".agent-yes") || file.includes("node_modules") || file.endsWith(".lock")) return;
|
|
607
|
+
recompute(root, rw);
|
|
608
|
+
};
|
|
609
|
+
try {
|
|
610
|
+
watch(root, { recursive: true }, (_e, f) => onChange(String(f ?? "")));
|
|
523
611
|
} catch {
|
|
524
|
-
|
|
612
|
+
try {
|
|
613
|
+
watch(path.join(root, ".git"), (_e, f) => onChange(".git/" + String(f ?? "")));
|
|
614
|
+
} catch {}
|
|
525
615
|
}
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
return
|
|
616
|
+
setInterval(() => recompute(root, rw), GIT_SAFETY_MS);
|
|
617
|
+
return rw;
|
|
618
|
+
};
|
|
619
|
+
const gitStatus = async (cwd) => {
|
|
620
|
+
if (!cwd) return null;
|
|
621
|
+
const root = await resolveRoot(cwd);
|
|
622
|
+
if (!root) return null;
|
|
623
|
+
return ensureRepoWatch(root).val;
|
|
531
624
|
};
|
|
532
625
|
const withMeta = async (r) => ({
|
|
533
626
|
...r,
|
|
534
627
|
title: await logTitle(r.log_file),
|
|
535
|
-
git: r.status === "exited" ? null : await gitStatus(r.cwd)
|
|
628
|
+
git: r.status === "exited" ? null : await gitStatus(r.cwd),
|
|
629
|
+
tasks: r.status === "exited" ? null : await logTasks(r.log_file)
|
|
536
630
|
});
|
|
537
631
|
const apiFetch = async (req) => {
|
|
538
632
|
if (!checkAuth(req, token)) return new Response("Unauthorized", { status: 401 });
|
|
@@ -791,6 +885,46 @@ Options:
|
|
|
791
885
|
return new Response(e.message, { status: 404 });
|
|
792
886
|
}
|
|
793
887
|
}
|
|
888
|
+
if (req.method === "POST" && p === "/api/kill") {
|
|
889
|
+
let body;
|
|
890
|
+
try {
|
|
891
|
+
body = await req.json();
|
|
892
|
+
} catch {
|
|
893
|
+
return new Response("invalid JSON body", { status: 400 });
|
|
894
|
+
}
|
|
895
|
+
const keyword = body.keyword;
|
|
896
|
+
if (!keyword || typeof keyword !== "string") return new Response("missing keyword", { status: 400 });
|
|
897
|
+
if (process.platform === "win32") return new Response("force-kill unsupported on a Windows serve", { status: 501 });
|
|
898
|
+
try {
|
|
899
|
+
const record = await resolveOne(keyword, defaultOpts({ all: true }));
|
|
900
|
+
const killed = [];
|
|
901
|
+
const sig = (target, label) => {
|
|
902
|
+
if (!target || target <= 1) return;
|
|
903
|
+
try {
|
|
904
|
+
process.kill(target, "SIGKILL");
|
|
905
|
+
killed.push(label);
|
|
906
|
+
} catch {}
|
|
907
|
+
};
|
|
908
|
+
const pgid = await pgidForWrapper(record.wrapper_pid ?? 0);
|
|
909
|
+
if (pgid && pgid > 1) try {
|
|
910
|
+
process.kill(-pgid, "SIGKILL");
|
|
911
|
+
killed.push(`group ${pgid}`);
|
|
912
|
+
} catch {}
|
|
913
|
+
sig(record.pid, `pid ${record.pid}`);
|
|
914
|
+
if (record.wrapper_pid && record.wrapper_pid !== record.pid) sig(record.wrapper_pid, `wrapper ${record.wrapper_pid}`);
|
|
915
|
+
await updateGlobalPidStatus(record.pid, {
|
|
916
|
+
status: "exited",
|
|
917
|
+
exit_reason: "force-killed via console"
|
|
918
|
+
}).catch(() => {});
|
|
919
|
+
return Response.json({
|
|
920
|
+
ok: true,
|
|
921
|
+
pid: record.pid,
|
|
922
|
+
killed
|
|
923
|
+
});
|
|
924
|
+
} catch (e) {
|
|
925
|
+
return new Response(e.message, { status: 404 });
|
|
926
|
+
}
|
|
927
|
+
}
|
|
794
928
|
const resizeM = /^\/api\/resize\/(.+)$/.exec(p);
|
|
795
929
|
if (req.method === "POST" && resizeM) {
|
|
796
930
|
const keyword = decodeURIComponent(resizeM[1]);
|
|
@@ -918,7 +1052,7 @@ Options:
|
|
|
918
1052
|
const webrtcVal = argv.webrtc ?? argv.share;
|
|
919
1053
|
const explicitUrl = typeof webrtcVal === "string" && webrtcVal.startsWith("webrtc://") ? webrtcVal : void 0;
|
|
920
1054
|
try {
|
|
921
|
-
const { startShare, loadOrCreateShareRoom } = await import("./share-
|
|
1055
|
+
const { startShare, loadOrCreateShareRoom } = await import("./share-YuM6-Q6A.js");
|
|
922
1056
|
const linkFile = path.join(process.env.AGENT_YES_HOME ?? path.join(homedir(), ".agent-yes"), ".share-link");
|
|
923
1057
|
const announce = async (room, link, rotated) => {
|
|
924
1058
|
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)";
|
|
@@ -945,22 +1079,32 @@ Options:
|
|
|
945
1079
|
if (!wantHttp) return 1;
|
|
946
1080
|
}
|
|
947
1081
|
}
|
|
1082
|
+
let heartbeat;
|
|
1083
|
+
if (wantWebrtc) {
|
|
1084
|
+
const stamp = () => {
|
|
1085
|
+
try {
|
|
1086
|
+
const tmp = `${heartbeatPath()}.tmp`;
|
|
1087
|
+
writeFileSync(tmp, String(Date.now()));
|
|
1088
|
+
renameSync(tmp, heartbeatPath());
|
|
1089
|
+
} catch {}
|
|
1090
|
+
};
|
|
1091
|
+
stamp();
|
|
1092
|
+
heartbeat = setInterval(stamp, HEARTBEAT_WRITE_MS);
|
|
1093
|
+
}
|
|
948
1094
|
process.stdout.write(`(Ctrl-C to stop)\n`);
|
|
1095
|
+
const shutdown = (resolve) => {
|
|
1096
|
+
if (heartbeat) clearInterval(heartbeat);
|
|
1097
|
+
closeShare?.();
|
|
1098
|
+
server?.stop();
|
|
1099
|
+
resolve();
|
|
1100
|
+
};
|
|
949
1101
|
await new Promise((resolve) => {
|
|
950
|
-
process.on("SIGINT", () =>
|
|
951
|
-
|
|
952
|
-
server?.stop();
|
|
953
|
-
resolve();
|
|
954
|
-
});
|
|
955
|
-
process.on("SIGTERM", () => {
|
|
956
|
-
closeShare?.();
|
|
957
|
-
server?.stop();
|
|
958
|
-
resolve();
|
|
959
|
-
});
|
|
1102
|
+
process.on("SIGINT", () => shutdown(resolve));
|
|
1103
|
+
process.on("SIGTERM", () => shutdown(resolve));
|
|
960
1104
|
});
|
|
961
1105
|
return 0;
|
|
962
1106
|
}
|
|
963
1107
|
|
|
964
1108
|
//#endregion
|
|
965
1109
|
export { cmdServe };
|
|
966
|
-
//# sourceMappingURL=serve-
|
|
1110
|
+
//# sourceMappingURL=serve-r_2v9EKc.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r as setWorkspaceRoot, t as getWorkspaceRoot } from "./workspaceConfig-
|
|
1
|
+
import { r as setWorkspaceRoot, t as getWorkspaceRoot } from "./workspaceConfig-BJO4fzEn.js";
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
3
|
import { stdin, stdout } from "node:process";
|
|
4
4
|
import { createInterface } from "node:readline/promises";
|
|
@@ -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-r_2v9EKc.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-DHa6fX8M.js.map
|
|
@@ -181,6 +181,13 @@ const SUB = "ay-signal-1";
|
|
|
181
181
|
const DEFAULT_SIGHOST = "s.agent-yes.com";
|
|
182
182
|
const HOST_HEARTBEAT_MS = 2e4;
|
|
183
183
|
const SIG_REFRESH_MS = 4 * 6e4;
|
|
184
|
+
const MAX_PEER_SETUP_FAILURES = 3;
|
|
185
|
+
const PEER_JOIN_GAP_MS = 300;
|
|
186
|
+
const STARTPEER_TIMEOUT_MS = 1e4;
|
|
187
|
+
const MAX_PEER_JOIN_QUEUE = 50;
|
|
188
|
+
const IDLE_RESTART_UPTIME_MS = 25 * 6e4;
|
|
189
|
+
const HARD_RESTART_UPTIME_MS = 45 * 6e4;
|
|
190
|
+
const IDLE_RESTART_CHECK_MS = 6e4;
|
|
184
191
|
const STUN = [{ urls: "stun:stun.l.google.com:19302" }];
|
|
185
192
|
let iceCache = null;
|
|
186
193
|
async function getIceServers() {
|
|
@@ -313,7 +320,7 @@ async function startShare(opts) {
|
|
|
313
320
|
if (!v2) throw new Error("refusing to host an unencrypted room — delete ~/.agent-yes/.share-room to rotate to an encrypted link");
|
|
314
321
|
let S = firstS;
|
|
315
322
|
const wsScheme = host.startsWith("localhost") || host.startsWith("127.") ? "ws" : "wss";
|
|
316
|
-
const ui = host === "s.agent-yes.com" ? "https://agent-yes.com" : "http://localhost:7778";
|
|
323
|
+
const ui = host === "s.agent-yes.com" ? "https://agent-yes.com/w" : "http://localhost:7778/w";
|
|
317
324
|
const suffix = host === "s.agent-yes.com" ? "" : "@" + host;
|
|
318
325
|
const mkLink = () => `${ui}/#${room}:${MARKER}${S}${suffix}`;
|
|
319
326
|
let authToken = await deriveAuthToken(S, room, host);
|
|
@@ -342,6 +349,40 @@ async function startShare(opts) {
|
|
|
342
349
|
const peers = /* @__PURE__ */ new Map();
|
|
343
350
|
let closed = false;
|
|
344
351
|
let currentWs;
|
|
352
|
+
let peerSetupFailures = 0;
|
|
353
|
+
const peerJoinQueue = [];
|
|
354
|
+
let drainingPeerJoins = false;
|
|
355
|
+
const drainPeerJoins = async () => {
|
|
356
|
+
if (drainingPeerJoins) return;
|
|
357
|
+
drainingPeerJoins = true;
|
|
358
|
+
try {
|
|
359
|
+
while (!closed && peerJoinQueue.length) {
|
|
360
|
+
const peerId = peerJoinQueue.shift();
|
|
361
|
+
const ws = currentWs;
|
|
362
|
+
if (!ws) continue;
|
|
363
|
+
try {
|
|
364
|
+
let timer;
|
|
365
|
+
const setup = startPeer(ws, peerId);
|
|
366
|
+
setup.catch(() => {});
|
|
367
|
+
await Promise.race([setup, new Promise((_, reject) => {
|
|
368
|
+
timer = setTimeout(() => reject(/* @__PURE__ */ new Error("startPeer timeout")), STARTPEER_TIMEOUT_MS);
|
|
369
|
+
})]).finally(() => clearTimeout(timer));
|
|
370
|
+
peerSetupFailures = 0;
|
|
371
|
+
} catch (err) {
|
|
372
|
+
peerSetupFailures++;
|
|
373
|
+
process.stderr.write(`[share] peer setup failed (${peerSetupFailures}/${MAX_PEER_SETUP_FAILURES}): ${err?.message ?? err}\n`);
|
|
374
|
+
closePeer(peerId);
|
|
375
|
+
if (peerSetupFailures >= MAX_PEER_SETUP_FAILURES) {
|
|
376
|
+
process.stderr.write("[share] WebRTC stack wedged after repeated peer-setup failures — exiting so the service manager restarts with a fresh stack\n");
|
|
377
|
+
process.exit(1);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
if (peerJoinQueue.length) await new Promise((r) => setTimeout(r, PEER_JOIN_GAP_MS));
|
|
381
|
+
}
|
|
382
|
+
} finally {
|
|
383
|
+
drainingPeerJoins = false;
|
|
384
|
+
}
|
|
385
|
+
};
|
|
345
386
|
const connectSignaling = (onReady) => {
|
|
346
387
|
if (closed) return;
|
|
347
388
|
const ws = new WebSocket(`${wsScheme}://${host}/${room}`, [SUB]);
|
|
@@ -394,8 +435,13 @@ async function startShare(opts) {
|
|
|
394
435
|
lastRecv = Date.now();
|
|
395
436
|
const m = JSON.parse(ev.data);
|
|
396
437
|
if (m.type === "pong") return;
|
|
397
|
-
if (m.type === "peer-join")
|
|
398
|
-
|
|
438
|
+
if (m.type === "peer-join") {
|
|
439
|
+
const pid = String(m.peer);
|
|
440
|
+
if (!peers.has(pid) && !peerJoinQueue.includes(pid) && peerJoinQueue.length < MAX_PEER_JOIN_QUEUE) {
|
|
441
|
+
peerJoinQueue.push(pid);
|
|
442
|
+
drainPeerJoins();
|
|
443
|
+
}
|
|
444
|
+
} else if (m.type === "answer") {
|
|
399
445
|
const peer = peers.get(m.from);
|
|
400
446
|
if (!peer) return;
|
|
401
447
|
try {
|
|
@@ -490,6 +536,16 @@ async function startShare(opts) {
|
|
|
490
536
|
};
|
|
491
537
|
const offer = await pc.createOffer();
|
|
492
538
|
await pc.setLocalDescription(offer);
|
|
539
|
+
if (peers.get(peerId) !== peer) {
|
|
540
|
+
try {
|
|
541
|
+
peer.pc.close();
|
|
542
|
+
} catch {}
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
if (ws.readyState !== WebSocket.OPEN) {
|
|
546
|
+
closePeer(peerId);
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
493
549
|
ws.send(JSON.stringify({
|
|
494
550
|
type: "offer",
|
|
495
551
|
to: peerId,
|
|
@@ -609,8 +665,26 @@ async function startShare(opts) {
|
|
|
609
665
|
}
|
|
610
666
|
}
|
|
611
667
|
await new Promise((resolve) => connectSignaling(resolve));
|
|
668
|
+
const startedAt = Date.now();
|
|
669
|
+
const proactiveRestart = process.stdout.isTTY ? void 0 : setInterval(() => {
|
|
670
|
+
if (closed) return;
|
|
671
|
+
const up = Date.now() - startedAt;
|
|
672
|
+
if (peers.size === 0 && up > IDLE_RESTART_UPTIME_MS) {
|
|
673
|
+
process.stderr.write("[share] proactive restart (idle): refreshing the WebRTC stack\n");
|
|
674
|
+
process.exit(0);
|
|
675
|
+
} else if (up > HARD_RESTART_UPTIME_MS) {
|
|
676
|
+
process.stderr.write("[share] proactive restart (max uptime): closing peers, refreshing the WebRTC stack\n");
|
|
677
|
+
try {
|
|
678
|
+
close();
|
|
679
|
+
} finally {
|
|
680
|
+
setTimeout(() => process.exit(0), 250);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
}, IDLE_RESTART_CHECK_MS);
|
|
684
|
+
proactiveRestart?.unref?.();
|
|
612
685
|
const close = () => {
|
|
613
686
|
closed = true;
|
|
687
|
+
if (proactiveRestart) clearInterval(proactiveRestart);
|
|
614
688
|
try {
|
|
615
689
|
currentWs?.close();
|
|
616
690
|
} catch {}
|
|
@@ -625,4 +699,4 @@ async function startShare(opts) {
|
|
|
625
699
|
|
|
626
700
|
//#endregion
|
|
627
701
|
export { loadOrCreateShareRoom, startShare };
|
|
628
|
-
//# sourceMappingURL=share-
|
|
702
|
+
//# sourceMappingURL=share-YuM6-Q6A.js.map
|