agent-yes 1.122.3 → 1.124.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-Cvm7yo5d.js +8 -0
- package/dist/{SUPPORTED_CLIS-BleNYXA2.js → SUPPORTED_CLIS-D_-bIOlW.js} +2 -2
- 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-e4f7NlA2.js → schedule-D2cn8N7o.js} +7 -7
- package/dist/{serve-CzztmZ_N.js → serve-Bo3bDXQG.js} +202 -58
- package/dist/{setup-CPyRNiIA.js → setup-CvOr258q.js} +3 -3
- package/dist/{share-CS9XVrLF.js → share-YuM6-Q6A.js} +71 -13
- package/dist/{subcommands-CQowpr1t.js → subcommands-ClVHy-xI.js} +647 -32
- package/dist/subcommands-Llf9o8nh.js +7 -0
- package/dist/{tray-DjCIyakK.js → tray-BVnJLThD.js} +1 -1
- package/dist/{ts-9GThuc3w.js → ts-DGIglR4L.js} +10 -7
- package/dist/{versionChecker-Bv9XKddN.js → versionChecker-gaQkM2Hy.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 +1152 -28
- 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 +144 -27
- 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-ClaOErso.js +0 -8
- package/dist/pidStore-7y1cTcAE.js +0 -5
- package/dist/reaper-HqcUms2d.js +0 -3
- package/dist/subcommands-KAbIcd8_.js +0 -6
|
@@ -1,17 +1,19 @@
|
|
|
1
|
-
import "./ts-
|
|
1
|
+
import "./ts-DGIglR4L.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-gaQkM2Hy.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-D_-bIOlW.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-ClVHy-xI.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-Bo3bDXQG.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-Bo3bDXQG.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-CvOr258q.js.map
|
|
@@ -182,6 +182,12 @@ const DEFAULT_SIGHOST = "s.agent-yes.com";
|
|
|
182
182
|
const HOST_HEARTBEAT_MS = 2e4;
|
|
183
183
|
const SIG_REFRESH_MS = 4 * 6e4;
|
|
184
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;
|
|
185
191
|
const STUN = [{ urls: "stun:stun.l.google.com:19302" }];
|
|
186
192
|
let iceCache = null;
|
|
187
193
|
async function getIceServers() {
|
|
@@ -314,7 +320,7 @@ async function startShare(opts) {
|
|
|
314
320
|
if (!v2) throw new Error("refusing to host an unencrypted room — delete ~/.agent-yes/.share-room to rotate to an encrypted link");
|
|
315
321
|
let S = firstS;
|
|
316
322
|
const wsScheme = host.startsWith("localhost") || host.startsWith("127.") ? "ws" : "wss";
|
|
317
|
-
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";
|
|
318
324
|
const suffix = host === "s.agent-yes.com" ? "" : "@" + host;
|
|
319
325
|
const mkLink = () => `${ui}/#${room}:${MARKER}${S}${suffix}`;
|
|
320
326
|
let authToken = await deriveAuthToken(S, room, host);
|
|
@@ -344,6 +350,39 @@ async function startShare(opts) {
|
|
|
344
350
|
let closed = false;
|
|
345
351
|
let currentWs;
|
|
346
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
|
+
};
|
|
347
386
|
const connectSignaling = (onReady) => {
|
|
348
387
|
if (closed) return;
|
|
349
388
|
const ws = new WebSocket(`${wsScheme}://${host}/${room}`, [SUB]);
|
|
@@ -396,18 +435,13 @@ async function startShare(opts) {
|
|
|
396
435
|
lastRecv = Date.now();
|
|
397
436
|
const m = JSON.parse(ev.data);
|
|
398
437
|
if (m.type === "pong") return;
|
|
399
|
-
if (m.type === "peer-join")
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
closePeer(m.peer);
|
|
405
|
-
if (peerSetupFailures >= MAX_PEER_SETUP_FAILURES) {
|
|
406
|
-
process.stderr.write("[share] WebRTC stack wedged after repeated peer-setup failures — exiting so the service manager restarts with a fresh stack\n");
|
|
407
|
-
process.exit(1);
|
|
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();
|
|
408
443
|
}
|
|
409
|
-
})
|
|
410
|
-
else if (m.type === "answer") {
|
|
444
|
+
} else if (m.type === "answer") {
|
|
411
445
|
const peer = peers.get(m.from);
|
|
412
446
|
if (!peer) return;
|
|
413
447
|
try {
|
|
@@ -502,6 +536,12 @@ async function startShare(opts) {
|
|
|
502
536
|
};
|
|
503
537
|
const offer = await pc.createOffer();
|
|
504
538
|
await pc.setLocalDescription(offer);
|
|
539
|
+
if (peers.get(peerId) !== peer) {
|
|
540
|
+
try {
|
|
541
|
+
peer.pc.close();
|
|
542
|
+
} catch {}
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
505
545
|
if (ws.readyState !== WebSocket.OPEN) {
|
|
506
546
|
closePeer(peerId);
|
|
507
547
|
return;
|
|
@@ -625,8 +665,26 @@ async function startShare(opts) {
|
|
|
625
665
|
}
|
|
626
666
|
}
|
|
627
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?.();
|
|
628
685
|
const close = () => {
|
|
629
686
|
closed = true;
|
|
687
|
+
if (proactiveRestart) clearInterval(proactiveRestart);
|
|
630
688
|
try {
|
|
631
689
|
currentWs?.close();
|
|
632
690
|
} catch {}
|
|
@@ -641,4 +699,4 @@ async function startShare(opts) {
|
|
|
641
699
|
|
|
642
700
|
//#endregion
|
|
643
701
|
export { loadOrCreateShareRoom, startShare };
|
|
644
|
-
//# sourceMappingURL=share-
|
|
702
|
+
//# sourceMappingURL=share-YuM6-Q6A.js.map
|