agent-yes 1.98.0 → 1.99.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-BGUPuqya.js +8 -0
- package/dist/{SUPPORTED_CLIS-C0a9K6I5.js → SUPPORTED_CLIS-eIjVu8HF.js} +2 -2
- package/dist/cli.js +3 -3
- package/dist/index.js +2 -2
- package/dist/{serve-DPY37v0u.js → serve-SQYFRbm3.js} +22 -14
- package/dist/{share-D-r6y3xD.js → share-BsCeIfQM.js} +19 -2
- package/dist/{subcommands-fCkYXyTe.js → subcommands-D3Z9cD9u.js} +1 -1
- package/dist/{subcommands-D4Muugfr.js → subcommands-z8Y8gcD_.js} +2 -2
- package/dist/{ts-BuFWTNL9.js → ts-BECoCPV1.js} +2 -2
- package/dist/{versionChecker-CpNUvHBx.js → versionChecker-pct2j3wR.js} +2 -2
- package/lab/ui/index.html +68 -0
- package/lab/ui/room-client.js +299 -228
- package/package.json +1 -1
- package/ts/serve.ts +38 -10
- package/ts/share.ts +29 -1
- package/dist/SUPPORTED_CLIS-BtLklR5y.js +0 -8
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import "./ts-BECoCPV1.js";
|
|
2
|
+
import "./logger-B9h0djqx.js";
|
|
3
|
+
import "./versionChecker-pct2j3wR.js";
|
|
4
|
+
import "./pidStore-DBjlqzo8.js";
|
|
5
|
+
import "./globalPidIndex-yVd3mbsV.js";
|
|
6
|
+
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-eIjVu8HF.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-BECoCPV1.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-eIjVu8HF.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-pct2j3wR.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-D3Z9cD9u.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-BGUPuqya.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-BECoCPV1.js";
|
|
2
2
|
import "./logger-B9h0djqx.js";
|
|
3
|
-
import "./versionChecker-
|
|
3
|
+
import "./versionChecker-pct2j3wR.js";
|
|
4
4
|
import "./pidStore-DBjlqzo8.js";
|
|
5
5
|
import "./globalPidIndex-yVd3mbsV.js";
|
|
6
6
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import "./ts-
|
|
1
|
+
import "./ts-BECoCPV1.js";
|
|
2
2
|
import "./logger-B9h0djqx.js";
|
|
3
|
-
import "./versionChecker-
|
|
3
|
+
import "./versionChecker-pct2j3wR.js";
|
|
4
4
|
import "./pidStore-DBjlqzo8.js";
|
|
5
5
|
import "./globalPidIndex-yVd3mbsV.js";
|
|
6
|
-
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-
|
|
6
|
+
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-eIjVu8HF.js";
|
|
7
7
|
import "./remotes-C3xPRtfg.js";
|
|
8
|
-
import { c as readNotes, f as snapshotStatus, l as renderRawLog, m as writeToIpc, o as listRecords, r as controlCodeFromName, u as resolveOne } from "./subcommands-
|
|
8
|
+
import { c as readNotes, f as snapshotStatus, l as renderRawLog, m as writeToIpc, o as listRecords, r as controlCodeFromName, u as resolveOne } from "./subcommands-z8Y8gcD_.js";
|
|
9
9
|
import yargs from "yargs";
|
|
10
10
|
import { mkdir, open, readFile, writeFile } from "fs/promises";
|
|
11
11
|
import { homedir } from "os";
|
|
@@ -60,8 +60,9 @@ async function cmdServeDaemon(sub, args) {
|
|
|
60
60
|
}
|
|
61
61
|
if (sub === "install") {
|
|
62
62
|
const token = await loadOrCreateToken(void 0);
|
|
63
|
+
const ayBin = Bun.which("ay");
|
|
63
64
|
const serveCmd = [
|
|
64
|
-
"ay",
|
|
65
|
+
...ayBin ? [process.execPath, ayBin] : ["ay"],
|
|
65
66
|
"serve",
|
|
66
67
|
...args
|
|
67
68
|
].join(" ");
|
|
@@ -81,13 +82,17 @@ async function cmdServeDaemon(sub, args) {
|
|
|
81
82
|
if (code === 0) {
|
|
82
83
|
const portM = /--port[=\s](\d+)/.exec(args.join(" "));
|
|
83
84
|
const port = portM ? Number(portM[1]) : DEFAULT_PORT;
|
|
85
|
+
const webrtcish = args.some((a) => a.startsWith("--webrtc") || a.startsWith("--share"));
|
|
86
|
+
const httpish = args.some((a) => a.startsWith("--http") || a.startsWith("--share")) || !args.some((a) => a.startsWith("--webrtc"));
|
|
84
87
|
process.stdout.write(`\ninstalled '${DAEMON_NAME}' as a daemon via oxmgr\n`);
|
|
85
88
|
process.stdout.write(`token: ${token}\n\n`);
|
|
86
|
-
|
|
87
|
-
|
|
89
|
+
if (httpish) {
|
|
90
|
+
process.stdout.write(` ay ls ${token}@<host>:${port}\n`);
|
|
91
|
+
process.stdout.write(` ay remote add <alias> http://${token}@<host>:${port}\n`);
|
|
92
|
+
}
|
|
88
93
|
process.stdout.write(` ay serve logs # view server logs\n`);
|
|
89
94
|
process.stdout.write(` ay serve uninstall # remove daemon\n`);
|
|
90
|
-
if (
|
|
95
|
+
if (webrtcish) process.stdout.write("\nthe WebRTC share link is printed by the daemon — see: ay serve logs\n(the room persists in ~/.agent-yes/.share-room, so the link survives restarts)\n");
|
|
91
96
|
}
|
|
92
97
|
return code ?? 1;
|
|
93
98
|
}
|
|
@@ -124,10 +129,12 @@ Modes (default: --http):
|
|
|
124
129
|
--webrtc [URL] Share over WebRTC (bare flag mints a room+link on
|
|
125
130
|
agent-yes.com, or pass webrtc://room:token@host).
|
|
126
131
|
Alone it needs NO port — combine with --http for both.
|
|
132
|
+
The minted room persists in ~/.agent-yes/.share-room
|
|
133
|
+
(stable link across restarts; delete the file to rotate).
|
|
127
134
|
--share [URL] Legacy alias for --http --webrtc
|
|
128
135
|
|
|
129
136
|
Options:
|
|
130
|
-
--port N Port to listen on (default: ${DEFAULT_PORT})\n --host HOST Interface to bind (default: 127.0.0.1; use 0.0.0.0 to expose)\n --token TOKEN Auth token (auto-generated and saved if omitted)\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 via oxmgr\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>:${DEFAULT_PORT}\n ay remote add <alias> http://<token>@<host>:${DEFAULT_PORT}\n`);
|
|
137
|
+
--port N Port to listen on (default: ${DEFAULT_PORT})\n --host HOST Interface to bind (default: 127.0.0.1; use 0.0.0.0 to expose)\n --token TOKEN Auth token (auto-generated and saved if omitted)\n -d, --daemon Install these flags as a background daemon via oxmgr\n (same as: ay serve install <flags>)\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 via oxmgr\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>:${DEFAULT_PORT}\n ay remote add <alias> http://<token>@<host>:${DEFAULT_PORT}\n`);
|
|
131
138
|
return 0;
|
|
132
139
|
}
|
|
133
140
|
const sub = rest[0];
|
|
@@ -168,6 +175,7 @@ Options:
|
|
|
168
175
|
default: false,
|
|
169
176
|
description: "Deprecated no-op — the console can always spawn agents"
|
|
170
177
|
}).help(false).version(false).exitProcess(false).parseAsync();
|
|
178
|
+
if (argv.daemon) return cmdServeDaemon("install", rest.filter((a) => a !== "--daemon" && a !== "-d"));
|
|
171
179
|
const port = argv.port ?? DEFAULT_PORT;
|
|
172
180
|
const host = argv.host ?? "127.0.0.1";
|
|
173
181
|
const tokenFlag = typeof argv.token === "string" ? argv.token : void 0;
|
|
@@ -513,15 +521,15 @@ Options:
|
|
|
513
521
|
}
|
|
514
522
|
if (wantWebrtc) {
|
|
515
523
|
const webrtcVal = argv.webrtc ?? argv.share;
|
|
516
|
-
const
|
|
524
|
+
const explicitUrl = typeof webrtcVal === "string" && webrtcVal.startsWith("webrtc://") ? webrtcVal : void 0;
|
|
517
525
|
try {
|
|
518
|
-
const { startShare } = await import("./share-
|
|
526
|
+
const { startShare, loadOrCreateShareRoom } = await import("./share-BsCeIfQM.js");
|
|
519
527
|
const { link } = await startShare({
|
|
520
|
-
url:
|
|
528
|
+
url: explicitUrl ?? await loadOrCreateShareRoom(),
|
|
521
529
|
localFetch: apiFetch,
|
|
522
530
|
apiToken: token
|
|
523
531
|
});
|
|
524
|
-
process.stdout.write(`${wantHttp ? "\n" : ""}shared over WebRTC — open this link (the token is eaten from the URL on open):\n ${link}\n\n`);
|
|
532
|
+
process.stdout.write(`${wantHttp ? "\n" : ""}shared over WebRTC — open this link (the token is eaten from the URL on open):\n ${link}\n` + (explicitUrl ? "\n" : ` (persistent room — same link across restarts; delete ~/.agent-yes/.share-room to rotate)\n\n`));
|
|
525
533
|
} catch (e) {
|
|
526
534
|
process.stderr.write(`ay serve --webrtc failed: ${e.message}\n`);
|
|
527
535
|
if (!wantHttp) return 1;
|
|
@@ -543,4 +551,4 @@ Options:
|
|
|
543
551
|
|
|
544
552
|
//#endregion
|
|
545
553
|
export { cmdServe };
|
|
546
|
-
//# sourceMappingURL=serve-
|
|
554
|
+
//# sourceMappingURL=serve-SQYFRbm3.js.map
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
2
|
+
import { homedir } from "os";
|
|
3
|
+
import path from "path";
|
|
1
4
|
import { randomBytes } from "crypto";
|
|
2
5
|
|
|
3
6
|
//#region ts/share.ts
|
|
@@ -5,6 +8,20 @@ const SUB = "ay-signal-1";
|
|
|
5
8
|
const ICE = [{ urls: "stun:stun.l.google.com:19302" }];
|
|
6
9
|
const MAX_CHUNK = 15e3;
|
|
7
10
|
const DEFAULT_SIGHOST = "s.agent-yes.com";
|
|
11
|
+
function shareRoomPath() {
|
|
12
|
+
const home = process.env.AGENT_YES_HOME ?? path.join(homedir(), ".agent-yes");
|
|
13
|
+
return path.join(home, ".share-room");
|
|
14
|
+
}
|
|
15
|
+
async function loadOrCreateShareRoom(sighost = DEFAULT_SIGHOST) {
|
|
16
|
+
try {
|
|
17
|
+
const url = (await readFile(shareRoomPath(), "utf-8")).trim();
|
|
18
|
+
if (url.startsWith("webrtc://")) return url;
|
|
19
|
+
} catch {}
|
|
20
|
+
const url = `webrtc://${"r" + randomBytes(3).toString("hex")}:${randomBytes(32).toString("hex")}@${sighost}`;
|
|
21
|
+
await mkdir(path.dirname(shareRoomPath()), { recursive: true });
|
|
22
|
+
await writeFile(shareRoomPath(), url, { mode: 384 });
|
|
23
|
+
return url;
|
|
24
|
+
}
|
|
8
25
|
function parseShareUrl(s) {
|
|
9
26
|
const m = /^webrtc:\/\/([^:@/]+):([^@/]+)@(.+)$/.exec(s);
|
|
10
27
|
if (!m) throw new Error(`bad --share url: ${s} (want webrtc://room:token@host)`);
|
|
@@ -180,5 +197,5 @@ async function startShare(opts) {
|
|
|
180
197
|
}
|
|
181
198
|
|
|
182
199
|
//#endregion
|
|
183
|
-
export { startShare };
|
|
184
|
-
//# sourceMappingURL=share-
|
|
200
|
+
export { loadOrCreateShareRoom, startShare };
|
|
201
|
+
//# sourceMappingURL=share-BsCeIfQM.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "./logger-B9h0djqx.js";
|
|
2
2
|
import "./globalPidIndex-yVd3mbsV.js";
|
|
3
3
|
import "./remotes-C3xPRtfg.js";
|
|
4
|
-
import { a as isSubcommand, c as readNotes, d as runSubcommand, f as snapshotStatus, i as isPidAlive, l as renderRawLog, m as writeToIpc, n as cmdHelp, o as listRecords, p as stopTipForCli, r as controlCodeFromName, s as matchKeyword, t as GRACEFUL_EXIT_COMMANDS, u as resolveOne } from "./subcommands-
|
|
4
|
+
import { a as isSubcommand, c as readNotes, d as runSubcommand, f as snapshotStatus, i as isPidAlive, l as renderRawLog, m as writeToIpc, n as cmdHelp, o as listRecords, p as stopTipForCli, r as controlCodeFromName, s as matchKeyword, t as GRACEFUL_EXIT_COMMANDS, u as resolveOne } from "./subcommands-z8Y8gcD_.js";
|
|
5
5
|
|
|
6
6
|
export { cmdHelp, isSubcommand, runSubcommand };
|
|
@@ -163,7 +163,7 @@ async function runSubcommand(argv) {
|
|
|
163
163
|
case "restart": return await cmdRestart(rest);
|
|
164
164
|
case "note": return await cmdNote(rest);
|
|
165
165
|
case "serve": {
|
|
166
|
-
const { cmdServe } = await import("./serve-
|
|
166
|
+
const { cmdServe } = await import("./serve-SQYFRbm3.js");
|
|
167
167
|
return cmdServe(rest);
|
|
168
168
|
}
|
|
169
169
|
case "setup": {
|
|
@@ -1452,4 +1452,4 @@ async function cmdStatus(rest) {
|
|
|
1452
1452
|
|
|
1453
1453
|
//#endregion
|
|
1454
1454
|
export { isSubcommand as a, readNotes as c, runSubcommand as d, snapshotStatus as f, isPidAlive as i, renderRawLog as l, writeToIpc as m, cmdHelp as n, listRecords as o, stopTipForCli as p, controlCodeFromName as r, matchKeyword as s, GRACEFUL_EXIT_COMMANDS as t, resolveOne as u };
|
|
1455
|
-
//# sourceMappingURL=subcommands-
|
|
1455
|
+
//# sourceMappingURL=subcommands-z8Y8gcD_.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-pct2j3wR.js";
|
|
3
3
|
import { n as agentYesHome, t as PidStore } from "./pidStore-DBjlqzo8.js";
|
|
4
4
|
import { i as shouldUseLock, r as releaseLock, t as acquireLock } from "./runningLock-CJxsoGdb.js";
|
|
5
5
|
import { i as readGlobalPids } from "./globalPidIndex-yVd3mbsV.js";
|
|
@@ -1714,4 +1714,4 @@ function sleep(ms) {
|
|
|
1714
1714
|
|
|
1715
1715
|
//#endregion
|
|
1716
1716
|
export { removeControlCharacters as a, AgentContext as i, agentYes as n, config as r, CLIS_CONFIG as t };
|
|
1717
|
-
//# sourceMappingURL=ts-
|
|
1717
|
+
//# sourceMappingURL=ts-BECoCPV1.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.99.0";
|
|
11
11
|
|
|
12
12
|
//#endregion
|
|
13
13
|
//#region ts/versionChecker.ts
|
|
@@ -221,4 +221,4 @@ async function displayVersion() {
|
|
|
221
221
|
|
|
222
222
|
//#endregion
|
|
223
223
|
export { versionString as i, displayVersion as n, getInstalledPackage as r, checkAndAutoUpdate as t };
|
|
224
|
-
//# sourceMappingURL=versionChecker-
|
|
224
|
+
//# sourceMappingURL=versionChecker-pct2j3wR.js.map
|
package/lab/ui/index.html
CHANGED
|
@@ -316,6 +316,24 @@
|
|
|
316
316
|
.newbtn:hover {
|
|
317
317
|
filter: brightness(1.12);
|
|
318
318
|
}
|
|
319
|
+
.viewbtn {
|
|
320
|
+
background: transparent;
|
|
321
|
+
border: 1px solid var(--line);
|
|
322
|
+
border-radius: 7px;
|
|
323
|
+
color: var(--muted);
|
|
324
|
+
font-size: 11.5px;
|
|
325
|
+
padding: 3px 9px;
|
|
326
|
+
cursor: pointer;
|
|
327
|
+
line-height: 1.6;
|
|
328
|
+
}
|
|
329
|
+
.viewbtn:hover {
|
|
330
|
+
color: var(--fg);
|
|
331
|
+
border-color: var(--accent);
|
|
332
|
+
}
|
|
333
|
+
.viewbtn.on {
|
|
334
|
+
color: var(--accent);
|
|
335
|
+
border-color: var(--accent);
|
|
336
|
+
}
|
|
319
337
|
.lcard .nfield {
|
|
320
338
|
margin-top: 12px;
|
|
321
339
|
}
|
|
@@ -428,6 +446,32 @@
|
|
|
428
446
|
text-overflow: ellipsis;
|
|
429
447
|
white-space: nowrap;
|
|
430
448
|
}
|
|
449
|
+
/* compact view: one line per agent — dot + cli + live title (or prompt), age */
|
|
450
|
+
.row.crow {
|
|
451
|
+
display: flex;
|
|
452
|
+
align-items: center;
|
|
453
|
+
gap: 8px;
|
|
454
|
+
padding: 6px 18px;
|
|
455
|
+
}
|
|
456
|
+
.crow .cname {
|
|
457
|
+
font-weight: 600;
|
|
458
|
+
flex: none;
|
|
459
|
+
}
|
|
460
|
+
.crow .ctitle {
|
|
461
|
+
flex: 1;
|
|
462
|
+
min-width: 0;
|
|
463
|
+
font-size: 12.5px;
|
|
464
|
+
overflow: hidden;
|
|
465
|
+
text-overflow: ellipsis;
|
|
466
|
+
white-space: nowrap;
|
|
467
|
+
}
|
|
468
|
+
.crow .ctitle.dim {
|
|
469
|
+
color: var(--muted);
|
|
470
|
+
}
|
|
471
|
+
.crow .age {
|
|
472
|
+
margin-left: 0;
|
|
473
|
+
flex: none;
|
|
474
|
+
}
|
|
431
475
|
.rowtags {
|
|
432
476
|
display: flex;
|
|
433
477
|
flex-wrap: wrap;
|
|
@@ -589,6 +633,7 @@
|
|
|
589
633
|
<div class="meta">
|
|
590
634
|
<span id="count"></span>
|
|
591
635
|
<span class="metaright">
|
|
636
|
+
<button id="viewbtn" class="viewbtn" title="toggle compact list">☰</button>
|
|
592
637
|
<button id="newbtn" class="newbtn" title="spawn a new agent on this fleet">
|
|
593
638
|
+ New agent
|
|
594
639
|
</button>
|
|
@@ -1005,10 +1050,28 @@
|
|
|
1005
1050
|
});
|
|
1006
1051
|
}
|
|
1007
1052
|
|
|
1053
|
+
// Compact list: one line per agent (dot + cli + title), persisted per device.
|
|
1054
|
+
let compactList = localStorage.getItem("ay.compactList") === "1";
|
|
1055
|
+
|
|
1008
1056
|
function renderList() {
|
|
1009
1057
|
const toks = $("q").value.trim().split(/\s+/).filter(Boolean);
|
|
1010
1058
|
const shown = entries.filter((e) => matches(e, toks));
|
|
1011
1059
|
$("count").textContent = `${shown.length} / ${entries.length} agents`;
|
|
1060
|
+
$("viewbtn").classList.toggle("on", compactList);
|
|
1061
|
+
if (compactList) {
|
|
1062
|
+
$("list").innerHTML =
|
|
1063
|
+
shown
|
|
1064
|
+
.map((e) => {
|
|
1065
|
+
const t = e.title || e.prompt || "";
|
|
1066
|
+
return `<div class="row crow ${String(e.pid) === sel ? "sel" : ""}" data-pid="${e.pid}">
|
|
1067
|
+
<span class="dot ${esc(e.status)}"></span>
|
|
1068
|
+
<span class="cname">${esc(e.cli)}</span>
|
|
1069
|
+
<span class="ctitle ${e.title ? "" : "dim"}" title="${esc(t)}">${esc(t)}</span>
|
|
1070
|
+
<span class="age">${age(e)}</span></div>`;
|
|
1071
|
+
})
|
|
1072
|
+
.join("") || `<div class="empty">no match</div>`;
|
|
1073
|
+
return;
|
|
1074
|
+
}
|
|
1012
1075
|
$("list").innerHTML =
|
|
1013
1076
|
shown
|
|
1014
1077
|
.map((e) => {
|
|
@@ -1186,6 +1249,11 @@
|
|
|
1186
1249
|
if (row) select(row.dataset.pid);
|
|
1187
1250
|
});
|
|
1188
1251
|
$("q").addEventListener("input", renderList);
|
|
1252
|
+
$("viewbtn").addEventListener("click", () => {
|
|
1253
|
+
compactList = !compactList;
|
|
1254
|
+
localStorage.setItem("ay.compactList", compactList ? "1" : "0");
|
|
1255
|
+
renderList();
|
|
1256
|
+
});
|
|
1189
1257
|
window.addEventListener("resize", () => {
|
|
1190
1258
|
if (fit)
|
|
1191
1259
|
try {
|
package/lab/ui/room-client.js
CHANGED
|
@@ -1,97 +1,105 @@
|
|
|
1
|
-
function
|
|
1
|
+
function F() {
|
|
2
2
|
return crypto.randomUUID();
|
|
3
3
|
}
|
|
4
|
-
var
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
var v = 60000,
|
|
5
|
+
w = 1e4,
|
|
6
|
+
M = 1000,
|
|
7
|
+
u = 120000,
|
|
8
|
+
b = 25000;
|
|
9
|
+
class H {
|
|
7
10
|
opts;
|
|
8
11
|
peerId;
|
|
9
12
|
ws = null;
|
|
10
13
|
closed = !1;
|
|
11
|
-
reconnectDelay =
|
|
14
|
+
reconnectDelay = M;
|
|
12
15
|
reconnectTimer = null;
|
|
16
|
+
dormant = !1;
|
|
13
17
|
heartbeat = null;
|
|
14
18
|
stableTimer = null;
|
|
15
19
|
openedAt = 0;
|
|
16
|
-
constructor(
|
|
17
|
-
this.opts =
|
|
18
|
-
this.peerId =
|
|
20
|
+
constructor(z) {
|
|
21
|
+
this.opts = z;
|
|
22
|
+
this.peerId = z.peerId ?? F();
|
|
19
23
|
}
|
|
20
24
|
connect() {
|
|
21
25
|
((this.closed = !1), this.attachWakeListeners(), this.open());
|
|
22
26
|
}
|
|
23
27
|
onWake = () => {
|
|
24
28
|
if (this.closed) return;
|
|
25
|
-
let
|
|
26
|
-
if (
|
|
27
|
-
if (
|
|
29
|
+
let z = this.ws?.readyState;
|
|
30
|
+
if (z === 1) return;
|
|
31
|
+
if (z === 0) {
|
|
28
32
|
try {
|
|
29
33
|
this.ws?.close();
|
|
30
34
|
} catch {}
|
|
31
35
|
return;
|
|
32
36
|
}
|
|
33
|
-
if (this.reconnectTimer != null)
|
|
37
|
+
if (this.dormant || this.reconnectTimer != null)
|
|
38
|
+
((this.dormant = !1), this.clearReconnectTimer(), this.open());
|
|
34
39
|
};
|
|
40
|
+
hidden() {
|
|
41
|
+
return globalThis.document?.visibilityState === "hidden";
|
|
42
|
+
}
|
|
35
43
|
attachWakeListeners() {
|
|
36
44
|
globalThis.document?.addEventListener("visibilitychange", this.onWake);
|
|
37
|
-
let
|
|
38
|
-
(
|
|
45
|
+
let K = globalThis.window;
|
|
46
|
+
(K?.addEventListener("focus", this.onWake), K?.addEventListener("online", this.onWake));
|
|
39
47
|
}
|
|
40
48
|
detachWakeListeners() {
|
|
41
49
|
globalThis.document?.removeEventListener("visibilitychange", this.onWake);
|
|
42
|
-
let
|
|
43
|
-
(
|
|
50
|
+
let K = globalThis.window;
|
|
51
|
+
(K?.removeEventListener("focus", this.onWake), K?.removeEventListener("online", this.onWake));
|
|
44
52
|
}
|
|
45
53
|
roomUrl() {
|
|
46
54
|
return `${this.opts.url.replace(/\/+$/, "")}/room/${encodeURIComponent(this.opts.token)}`;
|
|
47
55
|
}
|
|
48
56
|
open() {
|
|
49
|
-
let
|
|
50
|
-
this.ws =
|
|
51
|
-
let
|
|
52
|
-
if (
|
|
57
|
+
let z = new WebSocket(this.roomUrl());
|
|
58
|
+
this.ws = z;
|
|
59
|
+
let K = setTimeout(() => {
|
|
60
|
+
if (z.readyState === 0)
|
|
53
61
|
try {
|
|
54
|
-
|
|
62
|
+
z.close();
|
|
55
63
|
} catch {}
|
|
56
|
-
},
|
|
57
|
-
((
|
|
58
|
-
(clearTimeout(
|
|
64
|
+
}, w);
|
|
65
|
+
((z.onopen = () => {
|
|
66
|
+
(clearTimeout(K),
|
|
59
67
|
(this.openedAt = Date.now()),
|
|
60
68
|
this.clearStableTimer(),
|
|
61
69
|
(this.stableTimer = setTimeout(() => {
|
|
62
|
-
this.reconnectDelay =
|
|
63
|
-
},
|
|
64
|
-
let
|
|
70
|
+
this.reconnectDelay = M;
|
|
71
|
+
}, v)));
|
|
72
|
+
let Q = {
|
|
65
73
|
type: "hello",
|
|
66
74
|
role: this.opts.role,
|
|
67
75
|
peerId: this.peerId,
|
|
68
76
|
...(this.opts.meta ? { meta: this.opts.meta } : {}),
|
|
69
77
|
};
|
|
70
|
-
(
|
|
78
|
+
(z.send(JSON.stringify(Q)), this.startHeartbeat(), this.opts.onOpen?.());
|
|
71
79
|
}),
|
|
72
|
-
(
|
|
73
|
-
let
|
|
80
|
+
(z.onmessage = (Q) => {
|
|
81
|
+
let Y;
|
|
74
82
|
try {
|
|
75
|
-
|
|
83
|
+
Y = JSON.parse(String(Q.data));
|
|
76
84
|
} catch {
|
|
77
85
|
return;
|
|
78
86
|
}
|
|
79
|
-
if (
|
|
80
|
-
else if (
|
|
87
|
+
if (Y.type === "peers") this.opts.onPeers?.(Y.peers);
|
|
88
|
+
else if (Y.type === "signal") this.opts.onSignal?.(Y.from, Y.data);
|
|
81
89
|
}),
|
|
82
|
-
(
|
|
83
|
-
(clearTimeout(
|
|
84
|
-
let
|
|
90
|
+
(z.onclose = (Q) => {
|
|
91
|
+
(clearTimeout(K), this.clearStableTimer(), this.stopHeartbeat());
|
|
92
|
+
let Y = this.openedAt ? Date.now() - this.openedAt : 0;
|
|
85
93
|
if (
|
|
86
94
|
((this.openedAt = 0),
|
|
87
|
-
this.opts.onClose?.({ code:
|
|
95
|
+
this.opts.onClose?.({ code: Q?.code ?? 0, reason: Q?.reason ?? "", ms: Y }),
|
|
88
96
|
!this.closed)
|
|
89
97
|
)
|
|
90
98
|
this.scheduleReconnect();
|
|
91
99
|
}),
|
|
92
|
-
(
|
|
100
|
+
(z.onerror = () => {
|
|
93
101
|
try {
|
|
94
|
-
|
|
102
|
+
z.close();
|
|
95
103
|
} catch {}
|
|
96
104
|
}));
|
|
97
105
|
}
|
|
@@ -101,7 +109,7 @@ class U {
|
|
|
101
109
|
try {
|
|
102
110
|
this.ws?.send(JSON.stringify({ type: "ping" }));
|
|
103
111
|
} catch {}
|
|
104
|
-
},
|
|
112
|
+
}, b)));
|
|
105
113
|
}
|
|
106
114
|
stopHeartbeat() {
|
|
107
115
|
if (this.heartbeat != null) (clearInterval(this.heartbeat), (this.heartbeat = null));
|
|
@@ -110,29 +118,39 @@ class U {
|
|
|
110
118
|
if (this.stableTimer != null) (clearTimeout(this.stableTimer), (this.stableTimer = null));
|
|
111
119
|
}
|
|
112
120
|
scheduleReconnect() {
|
|
113
|
-
|
|
114
|
-
|
|
121
|
+
if (this.hidden()) {
|
|
122
|
+
this.dormant = !0;
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
let z = Math.round(this.reconnectDelay * (0.75 + Math.random() * 0.5));
|
|
126
|
+
((this.reconnectDelay = Math.min(this.reconnectDelay * 2, u)),
|
|
115
127
|
this.clearReconnectTimer(),
|
|
116
128
|
(this.reconnectTimer = setTimeout(() => {
|
|
117
|
-
if (((this.reconnectTimer = null),
|
|
118
|
-
|
|
129
|
+
if (((this.reconnectTimer = null), this.closed)) return;
|
|
130
|
+
if (this.hidden()) {
|
|
131
|
+
this.dormant = !0;
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
this.open();
|
|
135
|
+
}, z)));
|
|
119
136
|
}
|
|
120
137
|
clearReconnectTimer() {
|
|
121
138
|
if (this.reconnectTimer != null)
|
|
122
139
|
(clearTimeout(this.reconnectTimer), (this.reconnectTimer = null));
|
|
123
140
|
}
|
|
124
|
-
sendSignal(
|
|
125
|
-
let
|
|
126
|
-
this.ws?.send(JSON.stringify(
|
|
141
|
+
sendSignal(z, K) {
|
|
142
|
+
let Q = { type: "signal", to: z, data: K };
|
|
143
|
+
this.ws?.send(JSON.stringify(Q));
|
|
127
144
|
}
|
|
128
|
-
updateMeta(
|
|
129
|
-
if (((this.opts.meta =
|
|
130
|
-
let
|
|
131
|
-
this.ws.send(JSON.stringify(
|
|
145
|
+
updateMeta(z) {
|
|
146
|
+
if (((this.opts.meta = z), this.ws?.readyState === 1)) {
|
|
147
|
+
let K = { type: "meta", meta: z };
|
|
148
|
+
this.ws.send(JSON.stringify(K));
|
|
132
149
|
}
|
|
133
150
|
}
|
|
134
151
|
close() {
|
|
135
152
|
((this.closed = !0),
|
|
153
|
+
(this.dormant = !1),
|
|
136
154
|
this.detachWakeListeners(),
|
|
137
155
|
this.clearReconnectTimer(),
|
|
138
156
|
this.stopHeartbeat(),
|
|
@@ -142,21 +160,21 @@ class U {
|
|
|
142
160
|
} catch {}
|
|
143
161
|
}
|
|
144
162
|
}
|
|
145
|
-
var
|
|
146
|
-
|
|
147
|
-
class
|
|
163
|
+
var R = ["stun:stun.l.google.com:19302", "stun:stun1.l.google.com:19302"],
|
|
164
|
+
A = "codehost";
|
|
165
|
+
class x {
|
|
148
166
|
opts;
|
|
149
167
|
pc;
|
|
150
168
|
channel = null;
|
|
151
|
-
constructor(
|
|
152
|
-
this.opts =
|
|
153
|
-
((this.pc = new RTCPeerConnection({ iceServers:
|
|
154
|
-
(this.pc.onicecandidate = (
|
|
155
|
-
if (
|
|
169
|
+
constructor(z) {
|
|
170
|
+
this.opts = z;
|
|
171
|
+
((this.pc = new RTCPeerConnection({ iceServers: R.map((K) => ({ urls: K })) })),
|
|
172
|
+
(this.pc.onicecandidate = (K) => {
|
|
173
|
+
if (K.candidate)
|
|
156
174
|
this.opts.sendSignal({
|
|
157
175
|
kind: "candidate",
|
|
158
|
-
candidate:
|
|
159
|
-
mid:
|
|
176
|
+
candidate: K.candidate.candidate,
|
|
177
|
+
mid: K.candidate.sdpMid ?? "0",
|
|
160
178
|
});
|
|
161
179
|
}),
|
|
162
180
|
(this.pc.onconnectionstatechange = () => {
|
|
@@ -164,29 +182,64 @@ class B {
|
|
|
164
182
|
}));
|
|
165
183
|
}
|
|
166
184
|
async start() {
|
|
167
|
-
let
|
|
168
|
-
((
|
|
169
|
-
(this.channel =
|
|
170
|
-
(
|
|
171
|
-
(
|
|
172
|
-
let
|
|
173
|
-
(await this.pc.setLocalDescription(
|
|
174
|
-
this.opts.sendSignal({ kind: "offer", type: "offer", sdp:
|
|
175
|
-
}
|
|
176
|
-
async handleSignal(
|
|
177
|
-
let
|
|
178
|
-
if (!
|
|
179
|
-
if (
|
|
180
|
-
else if (
|
|
185
|
+
let z = this.pc.createDataChannel(A, { ordered: !0 });
|
|
186
|
+
((z.binaryType = "arraybuffer"),
|
|
187
|
+
(this.channel = z),
|
|
188
|
+
(z.onopen = () => this.opts.onOpen?.(z)),
|
|
189
|
+
(z.onclose = () => this.opts.onClose?.()));
|
|
190
|
+
let K = await this.pc.createOffer();
|
|
191
|
+
(await this.pc.setLocalDescription(K),
|
|
192
|
+
this.opts.sendSignal({ kind: "offer", type: "offer", sdp: K.sdp ?? "" }));
|
|
193
|
+
}
|
|
194
|
+
async handleSignal(z) {
|
|
195
|
+
let K = z;
|
|
196
|
+
if (!K || typeof K !== "object") return;
|
|
197
|
+
if (K.kind === "answer") await this.pc.setRemoteDescription({ type: "answer", sdp: K.sdp });
|
|
198
|
+
else if (K.kind === "candidate")
|
|
181
199
|
try {
|
|
182
|
-
await this.pc.addIceCandidate({ candidate:
|
|
183
|
-
} catch (
|
|
184
|
-
console.error("[rtc] addIceCandidate failed:",
|
|
200
|
+
await this.pc.addIceCandidate({ candidate: K.candidate, sdpMid: K.mid });
|
|
201
|
+
} catch (Q) {
|
|
202
|
+
console.error("[rtc] addIceCandidate failed:", Q);
|
|
185
203
|
}
|
|
186
204
|
}
|
|
187
205
|
get dataChannel() {
|
|
188
206
|
return this.channel;
|
|
189
207
|
}
|
|
208
|
+
async selectedPath() {
|
|
209
|
+
try {
|
|
210
|
+
let z = await this.pc.getStats(),
|
|
211
|
+
K = null;
|
|
212
|
+
z.forEach(($) => {
|
|
213
|
+
if ($.type === "transport" && $.selectedCandidatePairId) K = $.selectedCandidatePairId;
|
|
214
|
+
});
|
|
215
|
+
let Q = null;
|
|
216
|
+
if (
|
|
217
|
+
(z.forEach(($) => {
|
|
218
|
+
if (
|
|
219
|
+
K ? $.id === K : $.type === "candidate-pair" && $.state === "succeeded" && $.nominated
|
|
220
|
+
)
|
|
221
|
+
Q = $;
|
|
222
|
+
}),
|
|
223
|
+
!Q)
|
|
224
|
+
)
|
|
225
|
+
return null;
|
|
226
|
+
let { localCandidateId: Y, remoteCandidateId: Z } = Q,
|
|
227
|
+
q = !0,
|
|
228
|
+
X = 0;
|
|
229
|
+
if (
|
|
230
|
+
(z.forEach(($) => {
|
|
231
|
+
if ($.id === Y || $.id === Z) {
|
|
232
|
+
if ((X++, $.candidateType !== "host")) q = !1;
|
|
233
|
+
}
|
|
234
|
+
}),
|
|
235
|
+
X < 2)
|
|
236
|
+
)
|
|
237
|
+
return null;
|
|
238
|
+
return q ? "lan" : "p2p";
|
|
239
|
+
} catch {
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
190
243
|
close() {
|
|
191
244
|
try {
|
|
192
245
|
this.channel?.close();
|
|
@@ -196,254 +249,272 @@ class B {
|
|
|
196
249
|
} catch {}
|
|
197
250
|
}
|
|
198
251
|
}
|
|
199
|
-
var
|
|
200
|
-
|
|
201
|
-
function
|
|
202
|
-
let
|
|
203
|
-
|
|
204
|
-
if (((
|
|
205
|
-
return
|
|
252
|
+
var g = new TextEncoder(),
|
|
253
|
+
N = new TextDecoder();
|
|
254
|
+
function D(z, K, Q) {
|
|
255
|
+
let Y = Q?.byteLength ?? 0,
|
|
256
|
+
Z = new Uint8Array(5 + Y);
|
|
257
|
+
if (((Z[0] = z), new DataView(Z.buffer).setUint32(1, K >>> 0, !1), Q && Y)) Z.set(Q, 5);
|
|
258
|
+
return Z;
|
|
206
259
|
}
|
|
207
|
-
function
|
|
208
|
-
return
|
|
260
|
+
function k(z, K, Q) {
|
|
261
|
+
return D(z, K, g.encode(JSON.stringify(Q)));
|
|
209
262
|
}
|
|
210
|
-
function
|
|
211
|
-
let
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
return { op:
|
|
263
|
+
function S(z) {
|
|
264
|
+
let K = z instanceof Uint8Array ? z : new Uint8Array(z),
|
|
265
|
+
Q = K[0],
|
|
266
|
+
Y = new DataView(K.buffer, K.byteOffset, K.byteLength).getUint32(1, !1),
|
|
267
|
+
Z = K.subarray(5);
|
|
268
|
+
return { op: Q, streamId: Y, payload: Z };
|
|
216
269
|
}
|
|
217
|
-
function
|
|
218
|
-
return JSON.parse(
|
|
270
|
+
function U(z) {
|
|
271
|
+
return JSON.parse(N.decode(z));
|
|
219
272
|
}
|
|
220
|
-
function
|
|
221
|
-
return
|
|
273
|
+
function T(z) {
|
|
274
|
+
return N.decode(z);
|
|
222
275
|
}
|
|
223
|
-
function*
|
|
224
|
-
for (let
|
|
276
|
+
function* _(z) {
|
|
277
|
+
for (let K = 0; K < z.byteLength; K += 65531) yield z.slice(K, Math.min(K + 65531, z.byteLength));
|
|
225
278
|
}
|
|
226
|
-
function
|
|
227
|
-
if (
|
|
228
|
-
let
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
for (let
|
|
232
|
-
return
|
|
279
|
+
function O(z) {
|
|
280
|
+
if (z.length === 1) return z[0];
|
|
281
|
+
let K = z.reduce((Z, q) => Z + q.byteLength, 0),
|
|
282
|
+
Q = new Uint8Array(K),
|
|
283
|
+
Y = 0;
|
|
284
|
+
for (let Z of z) (Q.set(Z, Y), (Y += Z.byteLength));
|
|
285
|
+
return Q;
|
|
233
286
|
}
|
|
234
|
-
function*
|
|
235
|
-
let
|
|
236
|
-
while (
|
|
237
|
-
yield
|
|
287
|
+
function* J(z, K, Q) {
|
|
288
|
+
let Y = 0;
|
|
289
|
+
while (Q.byteLength - Y > 65531) (yield D(13, K, Q.subarray(Y, Y + 65531)), (Y += 65531));
|
|
290
|
+
yield D(z, K, Q.subarray(Y));
|
|
238
291
|
}
|
|
239
|
-
class
|
|
292
|
+
class W {
|
|
240
293
|
pending = new Map();
|
|
241
|
-
cont(
|
|
242
|
-
let
|
|
243
|
-
if (
|
|
244
|
-
else this.pending.set(
|
|
294
|
+
cont(z, K) {
|
|
295
|
+
let Q = this.pending.get(z);
|
|
296
|
+
if (Q) Q.push(K.slice());
|
|
297
|
+
else this.pending.set(z, [K.slice()]);
|
|
245
298
|
}
|
|
246
|
-
finish(
|
|
247
|
-
let
|
|
248
|
-
if (!
|
|
249
|
-
return (this.pending.delete(
|
|
299
|
+
finish(z, K) {
|
|
300
|
+
let Q = this.pending.get(z);
|
|
301
|
+
if (!Q) return K;
|
|
302
|
+
return (this.pending.delete(z), Q.push(K), O(Q));
|
|
250
303
|
}
|
|
251
|
-
drop(
|
|
252
|
-
this.pending.delete(
|
|
304
|
+
drop(z) {
|
|
305
|
+
this.pending.delete(z);
|
|
253
306
|
}
|
|
254
307
|
}
|
|
255
|
-
class
|
|
308
|
+
class j {
|
|
256
309
|
channel;
|
|
257
310
|
nextStreamId = 1;
|
|
258
311
|
https = new Map();
|
|
259
312
|
wss = new Map();
|
|
260
|
-
wsRx = new
|
|
313
|
+
wsRx = new W();
|
|
261
314
|
textEncoder = new TextEncoder();
|
|
262
|
-
constructor(
|
|
263
|
-
this.channel =
|
|
264
|
-
((
|
|
315
|
+
constructor(z) {
|
|
316
|
+
this.channel = z;
|
|
317
|
+
((z.binaryType = "arraybuffer"), z.addEventListener("message", (K) => this.onFrame(K.data)));
|
|
265
318
|
}
|
|
266
319
|
allocId() {
|
|
267
|
-
let
|
|
268
|
-
return ((this.nextStreamId = (this.nextStreamId + 1) >>> 0 || 1),
|
|
320
|
+
let z = this.nextStreamId;
|
|
321
|
+
return ((this.nextStreamId = (this.nextStreamId + 1) >>> 0 || 1), z);
|
|
269
322
|
}
|
|
270
|
-
onFrame(
|
|
271
|
-
if (typeof
|
|
272
|
-
let { op:
|
|
273
|
-
switch (
|
|
323
|
+
onFrame(z) {
|
|
324
|
+
if (typeof z === "string") return;
|
|
325
|
+
let { op: K, streamId: Q, payload: Y } = S(z);
|
|
326
|
+
switch (K) {
|
|
274
327
|
case 4:
|
|
275
|
-
this.https.get(
|
|
328
|
+
this.https.get(Q)?.onHead(U(Y));
|
|
276
329
|
break;
|
|
277
330
|
case 5:
|
|
278
|
-
this.https.get(
|
|
331
|
+
this.https.get(Q)?.onBody(Y.slice());
|
|
279
332
|
break;
|
|
280
333
|
case 6:
|
|
281
|
-
(this.https.get(
|
|
334
|
+
(this.https.get(Q)?.onEnd(), this.https.delete(Q));
|
|
282
335
|
break;
|
|
283
336
|
case 12: {
|
|
284
|
-
let
|
|
285
|
-
if (
|
|
337
|
+
let Z = this.https.get(Q);
|
|
338
|
+
if (Z) (Z.onError(U(Y).message), this.https.delete(Q));
|
|
286
339
|
break;
|
|
287
340
|
}
|
|
288
341
|
case 8: {
|
|
289
|
-
let
|
|
290
|
-
this.wss.get(
|
|
342
|
+
let Z = U(Y);
|
|
343
|
+
this.wss.get(Q)?.onOpenAck(Z.ok, Z.protocol);
|
|
291
344
|
break;
|
|
292
345
|
}
|
|
293
346
|
case 13:
|
|
294
|
-
this.wsRx.cont(
|
|
347
|
+
this.wsRx.cont(Q, Y);
|
|
295
348
|
break;
|
|
296
349
|
case 9:
|
|
297
|
-
this.wss.get(
|
|
350
|
+
this.wss.get(Q)?.onText(T(this.wsRx.finish(Q, Y)));
|
|
298
351
|
break;
|
|
299
352
|
case 10:
|
|
300
|
-
this.wss.get(
|
|
353
|
+
this.wss.get(Q)?.onBin(this.wsRx.finish(Q, Y).slice());
|
|
301
354
|
break;
|
|
302
355
|
case 11: {
|
|
303
|
-
let
|
|
304
|
-
(this.wsRx.drop(
|
|
305
|
-
this.wss.get(
|
|
306
|
-
this.wss.delete(
|
|
356
|
+
let Z = U(Y);
|
|
357
|
+
(this.wsRx.drop(Q),
|
|
358
|
+
this.wss.get(Q)?.onClose(Z.code ?? 1000, Z.reason ?? ""),
|
|
359
|
+
this.wss.delete(Q));
|
|
307
360
|
break;
|
|
308
361
|
}
|
|
309
362
|
}
|
|
310
363
|
}
|
|
311
|
-
fetch(
|
|
312
|
-
let
|
|
313
|
-
return new Promise((
|
|
314
|
-
let
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
start: (
|
|
318
|
-
|
|
364
|
+
fetch(z, K, Q, Y) {
|
|
365
|
+
let Z = this.allocId();
|
|
366
|
+
return new Promise((q, X) => {
|
|
367
|
+
let $ = null,
|
|
368
|
+
P = null,
|
|
369
|
+
V = new ReadableStream({
|
|
370
|
+
start: (G) => {
|
|
371
|
+
P = G;
|
|
319
372
|
},
|
|
320
|
-
})
|
|
373
|
+
}),
|
|
374
|
+
C = typeof DecompressionStream < "u" ? { ...Q, "x-codehost-accept-gzip": "1" } : Q;
|
|
321
375
|
if (
|
|
322
|
-
(this.https.set(
|
|
323
|
-
onHead: (
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
376
|
+
(this.https.set(Z, {
|
|
377
|
+
onHead: (G) => {
|
|
378
|
+
$ = G;
|
|
379
|
+
let B = new Headers(G.headers),
|
|
380
|
+
L = V;
|
|
381
|
+
if (B.get("content-encoding") === "gzip")
|
|
382
|
+
((L = V.pipeThrough(new DecompressionStream("gzip"))),
|
|
383
|
+
B.delete("content-encoding"),
|
|
384
|
+
B.delete("content-length"));
|
|
385
|
+
q(
|
|
386
|
+
new Response(L, {
|
|
387
|
+
status: G.status === 204 || G.status === 304 ? G.status : G.status,
|
|
388
|
+
statusText: G.statusText,
|
|
389
|
+
headers: B,
|
|
390
|
+
}),
|
|
391
|
+
);
|
|
332
392
|
},
|
|
333
|
-
onBody: (
|
|
393
|
+
onBody: (G) => {
|
|
334
394
|
try {
|
|
335
|
-
|
|
395
|
+
P?.enqueue(G);
|
|
336
396
|
} catch {}
|
|
337
397
|
},
|
|
338
398
|
onEnd: () => {
|
|
339
399
|
try {
|
|
340
|
-
|
|
400
|
+
P?.close();
|
|
341
401
|
} catch {}
|
|
342
|
-
if (
|
|
402
|
+
if (!$) X(Error("stream ended before head"));
|
|
343
403
|
},
|
|
344
|
-
onError: (
|
|
404
|
+
onError: (G) => {
|
|
345
405
|
try {
|
|
346
|
-
|
|
406
|
+
P?.error(Error(G));
|
|
347
407
|
} catch {}
|
|
348
|
-
if (
|
|
408
|
+
if (!$) X(Error(G));
|
|
349
409
|
},
|
|
350
410
|
}),
|
|
351
|
-
this.send(
|
|
352
|
-
|
|
411
|
+
this.send(k(1, Z, { method: z, path: K, headers: C })),
|
|
412
|
+
Y && Y.byteLength)
|
|
353
413
|
)
|
|
354
|
-
for (let
|
|
355
|
-
this.send(
|
|
414
|
+
for (let G of _(Y)) this.send(D(2, Z, G));
|
|
415
|
+
this.send(D(3, Z));
|
|
356
416
|
});
|
|
357
417
|
}
|
|
358
|
-
openWs(
|
|
359
|
-
let
|
|
418
|
+
openWs(z, K, Q) {
|
|
419
|
+
let Y = this.allocId();
|
|
360
420
|
return (
|
|
361
|
-
this.wss.set(
|
|
362
|
-
this.send(
|
|
421
|
+
this.wss.set(Y, Q),
|
|
422
|
+
this.send(k(7, Y, { path: z, protocols: K })),
|
|
363
423
|
{
|
|
364
|
-
sendText: (
|
|
365
|
-
for (let
|
|
424
|
+
sendText: (Z) => {
|
|
425
|
+
for (let q of J(9, Y, this.textEncoder.encode(Z))) this.send(q);
|
|
366
426
|
},
|
|
367
|
-
sendBin: (
|
|
368
|
-
for (let
|
|
427
|
+
sendBin: (Z) => {
|
|
428
|
+
for (let q of J(10, Y, Z)) this.send(q);
|
|
369
429
|
},
|
|
370
|
-
close: (
|
|
371
|
-
(this.send(
|
|
430
|
+
close: (Z, q) => {
|
|
431
|
+
(this.send(k(11, Y, { code: Z, reason: q })), this.wss.delete(Y));
|
|
372
432
|
},
|
|
373
433
|
}
|
|
374
434
|
);
|
|
375
435
|
}
|
|
376
|
-
send(
|
|
436
|
+
send(z) {
|
|
377
437
|
if (this.channel.readyState === "open") {
|
|
378
|
-
let
|
|
379
|
-
(
|
|
438
|
+
let K = new Uint8Array(z.byteLength);
|
|
439
|
+
(K.set(z), this.channel.send(K.buffer));
|
|
380
440
|
}
|
|
381
441
|
}
|
|
382
442
|
get ready() {
|
|
383
443
|
return this.channel.readyState === "open";
|
|
384
444
|
}
|
|
385
445
|
}
|
|
386
|
-
var
|
|
387
|
-
|
|
446
|
+
var f = "wss://signal.codehost.dev",
|
|
447
|
+
y = 1e4;
|
|
448
|
+
class E {
|
|
388
449
|
peers = [];
|
|
389
450
|
signaling;
|
|
390
451
|
rtcs = new Map();
|
|
391
452
|
tunnels = new Map();
|
|
453
|
+
dialFailedAt = new Map();
|
|
392
454
|
closed = !1;
|
|
393
|
-
constructor(
|
|
394
|
-
((this.signaling = new
|
|
395
|
-
url:
|
|
396
|
-
token:
|
|
455
|
+
constructor(z) {
|
|
456
|
+
((this.signaling = new H({
|
|
457
|
+
url: z.signalUrl ?? f,
|
|
458
|
+
token: z.token,
|
|
397
459
|
role: "viewer",
|
|
398
|
-
onOpen: () =>
|
|
399
|
-
onClose: () =>
|
|
400
|
-
onPeers: (
|
|
401
|
-
((this.peers =
|
|
460
|
+
onOpen: () => z.onStatus?.(!0),
|
|
461
|
+
onClose: () => z.onStatus?.(!1),
|
|
462
|
+
onPeers: (K) => {
|
|
463
|
+
((this.peers = K.filter((Q) => Q.role === "server")), z.onPeers?.(this.peers));
|
|
402
464
|
},
|
|
403
|
-
onSignal: (
|
|
465
|
+
onSignal: (K, Q) => void this.rtcs.get(K)?.handleSignal(Q),
|
|
404
466
|
})),
|
|
405
467
|
this.signaling.connect());
|
|
406
468
|
}
|
|
407
|
-
async fetch(
|
|
408
|
-
let
|
|
409
|
-
|
|
410
|
-
return
|
|
411
|
-
}
|
|
412
|
-
dial(
|
|
413
|
-
let
|
|
414
|
-
if (
|
|
415
|
-
let
|
|
416
|
-
|
|
469
|
+
async fetch(z, K, Q, Y = {}) {
|
|
470
|
+
let Z = await this.dial(z),
|
|
471
|
+
q = typeof Y.body === "string" ? new TextEncoder().encode(Y.body) : Y.body;
|
|
472
|
+
return Z.fetch(K, Q, Y.headers ?? {}, q);
|
|
473
|
+
}
|
|
474
|
+
dial(z) {
|
|
475
|
+
let K = this.tunnels.get(z);
|
|
476
|
+
if (K) return K;
|
|
477
|
+
let Q = this.dialFailedAt.get(z);
|
|
478
|
+
if (Q != null && Date.now() - Q < y)
|
|
479
|
+
return Promise.reject(Error("dial failed recently; cooling down"));
|
|
480
|
+
let Y = () => {
|
|
481
|
+
(this.tunnels.delete(z), this.rtcs.get(z)?.close(), this.rtcs.delete(z));
|
|
417
482
|
},
|
|
418
|
-
|
|
419
|
-
let
|
|
420
|
-
(
|
|
483
|
+
Z = new Promise((q, X) => {
|
|
484
|
+
let $ = setTimeout(() => {
|
|
485
|
+
(Y(), X(Error("dial timed out")));
|
|
421
486
|
}, 15000),
|
|
422
|
-
|
|
423
|
-
sendSignal: (
|
|
424
|
-
onOpen: (
|
|
425
|
-
(clearTimeout(
|
|
487
|
+
P = new x({
|
|
488
|
+
sendSignal: (V) => this.signaling.sendSignal(z, V),
|
|
489
|
+
onOpen: (V) => {
|
|
490
|
+
(clearTimeout($), this.dialFailedAt.delete(z), q(new j(V)));
|
|
426
491
|
},
|
|
427
|
-
onClose:
|
|
428
|
-
onState: (
|
|
429
|
-
if (
|
|
492
|
+
onClose: Y,
|
|
493
|
+
onState: (V) => {
|
|
494
|
+
if (V === "failed" || V === "disconnected") Y();
|
|
430
495
|
},
|
|
431
496
|
});
|
|
432
|
-
(this.rtcs.set(
|
|
433
|
-
|
|
434
|
-
(clearTimeout(
|
|
497
|
+
(this.rtcs.set(z, P),
|
|
498
|
+
P.start().catch((V) => {
|
|
499
|
+
(clearTimeout($), Y(), X(V));
|
|
435
500
|
}));
|
|
436
501
|
});
|
|
437
|
-
return (
|
|
502
|
+
return (
|
|
503
|
+
this.tunnels.set(z, Z),
|
|
504
|
+
Z.catch(() => {
|
|
505
|
+
(this.dialFailedAt.set(z, Date.now()), this.tunnels.delete(z));
|
|
506
|
+
}),
|
|
507
|
+
Z
|
|
508
|
+
);
|
|
438
509
|
}
|
|
439
510
|
close() {
|
|
440
511
|
if (this.closed) return;
|
|
441
512
|
this.closed = !0;
|
|
442
|
-
for (let
|
|
513
|
+
for (let z of this.rtcs.values()) z.close();
|
|
443
514
|
(this.rtcs.clear(), this.tunnels.clear(), this.signaling.close());
|
|
444
515
|
}
|
|
445
516
|
}
|
|
446
|
-
function
|
|
447
|
-
return new
|
|
517
|
+
function a(z) {
|
|
518
|
+
return new E(z);
|
|
448
519
|
}
|
|
449
|
-
export {
|
|
520
|
+
export { a as joinRoom, f as DEFAULT_SIGNAL_URL, E as CodehostRoom };
|
package/package.json
CHANGED
package/ts/serve.ts
CHANGED
|
@@ -83,8 +83,11 @@ async function cmdServeDaemon(sub: string, args: string[]): Promise<number> {
|
|
|
83
83
|
|
|
84
84
|
if (sub === "install") {
|
|
85
85
|
const token = await loadOrCreateToken(undefined);
|
|
86
|
-
// Build the ay serve command with forwarded args (port, host, --webrtc, etc.)
|
|
87
|
-
|
|
86
|
+
// Build the ay serve command with forwarded args (port, host, --webrtc, etc.).
|
|
87
|
+
// Absolute paths: oxmgr's daemon environment may not have ~/.bun/bin in
|
|
88
|
+
// PATH, so a bare `ay` (or its `#!/usr/bin/env bun` shebang) fails to spawn.
|
|
89
|
+
const ayBin = Bun.which("ay");
|
|
90
|
+
const serveCmd = [...(ayBin ? [process.execPath, ayBin] : ["ay"]), "serve", ...args].join(" ");
|
|
88
91
|
const proc = Bun.spawn(
|
|
89
92
|
[oxmgrBin, "start", serveCmd, "--name", DAEMON_NAME, "--restart", "always"],
|
|
90
93
|
{ stdio: ["ignore", "inherit", "inherit"] },
|
|
@@ -93,15 +96,23 @@ async function cmdServeDaemon(sub: string, args: string[]): Promise<number> {
|
|
|
93
96
|
if (code === 0) {
|
|
94
97
|
const portM = /--port[=\s](\d+)/.exec(args.join(" "));
|
|
95
98
|
const port = portM ? Number(portM[1]) : DEFAULT_PORT;
|
|
99
|
+
// Mirror cmdServe's mode resolution: webrtc-only daemons open no HTTP port.
|
|
100
|
+
const webrtcish = args.some((a) => a.startsWith("--webrtc") || a.startsWith("--share"));
|
|
101
|
+
const httpish =
|
|
102
|
+
args.some((a) => a.startsWith("--http") || a.startsWith("--share")) ||
|
|
103
|
+
!args.some((a) => a.startsWith("--webrtc"));
|
|
96
104
|
process.stdout.write(`\ninstalled '${DAEMON_NAME}' as a daemon via oxmgr\n`);
|
|
97
105
|
process.stdout.write(`token: ${token}\n\n`);
|
|
98
|
-
|
|
99
|
-
|
|
106
|
+
if (httpish) {
|
|
107
|
+
process.stdout.write(` ay ls ${token}@<host>:${port}\n`);
|
|
108
|
+
process.stdout.write(` ay remote add <alias> http://${token}@<host>:${port}\n`);
|
|
109
|
+
}
|
|
100
110
|
process.stdout.write(` ay serve logs # view server logs\n`);
|
|
101
111
|
process.stdout.write(` ay serve uninstall # remove daemon\n`);
|
|
102
|
-
if (
|
|
112
|
+
if (webrtcish) {
|
|
103
113
|
process.stdout.write(
|
|
104
|
-
`\nthe WebRTC share link is printed by the daemon — see: ay serve logs\n
|
|
114
|
+
`\nthe WebRTC share link is printed by the daemon — see: ay serve logs\n` +
|
|
115
|
+
`(the room persists in ~/.agent-yes/.share-room, so the link survives restarts)\n`,
|
|
105
116
|
);
|
|
106
117
|
}
|
|
107
118
|
}
|
|
@@ -140,11 +151,15 @@ export async function cmdServe(rest: string[]): Promise<number> {
|
|
|
140
151
|
` --webrtc [URL] Share over WebRTC (bare flag mints a room+link on\n` +
|
|
141
152
|
` agent-yes.com, or pass webrtc://room:token@host).\n` +
|
|
142
153
|
` Alone it needs NO port — combine with --http for both.\n` +
|
|
154
|
+
` The minted room persists in ~/.agent-yes/.share-room\n` +
|
|
155
|
+
` (stable link across restarts; delete the file to rotate).\n` +
|
|
143
156
|
` --share [URL] Legacy alias for --http --webrtc\n\n` +
|
|
144
157
|
`Options:\n` +
|
|
145
158
|
` --port N Port to listen on (default: ${DEFAULT_PORT})\n` +
|
|
146
159
|
` --host HOST Interface to bind (default: 127.0.0.1; use 0.0.0.0 to expose)\n` +
|
|
147
160
|
` --token TOKEN Auth token (auto-generated and saved if omitted)\n` +
|
|
161
|
+
` -d, --daemon Install these flags as a background daemon via oxmgr\n` +
|
|
162
|
+
` (same as: ay serve install <flags>)\n` +
|
|
148
163
|
` --allow-spawn Deprecated no-op — the console can always spawn agents\n` +
|
|
149
164
|
` --tls-cert FILE TLS certificate PEM\n` +
|
|
150
165
|
` --tls-key FILE TLS private key PEM\n\n` +
|
|
@@ -205,6 +220,14 @@ export async function cmdServe(rest: string[]): Promise<number> {
|
|
|
205
220
|
.exitProcess(false);
|
|
206
221
|
|
|
207
222
|
const argv = await y.parseAsync();
|
|
223
|
+
|
|
224
|
+
// --daemon/-d: install these exact flags as the oxmgr daemon instead of
|
|
225
|
+
// serving in the foreground (sugar for `ay serve install <flags>`).
|
|
226
|
+
if (argv.daemon) {
|
|
227
|
+
const fwd = rest.filter((a) => a !== "--daemon" && a !== "-d");
|
|
228
|
+
return cmdServeDaemon("install", fwd);
|
|
229
|
+
}
|
|
230
|
+
|
|
208
231
|
const port = (argv.port as number) ?? DEFAULT_PORT;
|
|
209
232
|
const host = (argv.host as string) ?? "127.0.0.1";
|
|
210
233
|
const tokenFlag = typeof argv.token === "string" ? argv.token : undefined;
|
|
@@ -651,17 +674,22 @@ export async function cmdServe(rest: string[]): Promise<number> {
|
|
|
651
674
|
// webrtc:// value joins an explicit one.
|
|
652
675
|
if (wantWebrtc) {
|
|
653
676
|
const webrtcVal = (argv.webrtc ?? argv.share) as string | undefined;
|
|
654
|
-
const
|
|
677
|
+
const explicitUrl =
|
|
655
678
|
typeof webrtcVal === "string" && webrtcVal.startsWith("webrtc://") ? webrtcVal : undefined;
|
|
656
679
|
try {
|
|
657
|
-
const { startShare } = await import("./share.ts");
|
|
680
|
+
const { startShare, loadOrCreateShareRoom } = await import("./share.ts");
|
|
681
|
+
// No explicit webrtc:// URL → reuse the persisted room (minted once and
|
|
682
|
+
// saved like the serve token), so the link is stable across restarts.
|
|
658
683
|
const { link } = await startShare({
|
|
659
|
-
url:
|
|
684
|
+
url: explicitUrl ?? (await loadOrCreateShareRoom()),
|
|
660
685
|
localFetch: apiFetch,
|
|
661
686
|
apiToken: token,
|
|
662
687
|
});
|
|
663
688
|
process.stdout.write(
|
|
664
|
-
`${wantHttp ? "\n" : ""}shared over WebRTC — open this link (the token is eaten from the URL on open):\n ${link}\n
|
|
689
|
+
`${wantHttp ? "\n" : ""}shared over WebRTC — open this link (the token is eaten from the URL on open):\n ${link}\n` +
|
|
690
|
+
(explicitUrl
|
|
691
|
+
? "\n"
|
|
692
|
+
: ` (persistent room — same link across restarts; delete ~/.agent-yes/.share-room to rotate)\n\n`),
|
|
665
693
|
);
|
|
666
694
|
} catch (e) {
|
|
667
695
|
process.stderr.write(`ay serve --webrtc failed: ${(e as Error).message}\n`);
|
package/ts/share.ts
CHANGED
|
@@ -5,6 +5,9 @@
|
|
|
5
5
|
// lab/ui/cf/worker.ts for the signaling protocol and lab/ui/index.html for the
|
|
6
6
|
// browser side.
|
|
7
7
|
import { randomBytes } from "crypto";
|
|
8
|
+
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
9
|
+
import { homedir } from "os";
|
|
10
|
+
import path from "path";
|
|
8
11
|
|
|
9
12
|
const SUB = "ay-signal-1";
|
|
10
13
|
const ICE = [{ urls: "stun:stun.l.google.com:19302" }];
|
|
@@ -12,7 +15,8 @@ const MAX_CHUNK = 15_000; // keep DataChannel messages under the SCTP limit
|
|
|
12
15
|
const DEFAULT_SIGHOST = "s.agent-yes.com";
|
|
13
16
|
|
|
14
17
|
export interface ShareOpts {
|
|
15
|
-
/** webrtc://room:token@host, or undefined to mint a fresh
|
|
18
|
+
/** webrtc://room:token@host, or undefined to mint a fresh (unpersisted)
|
|
19
|
+
* room+token — callers wanting a stable room use loadOrCreateShareRoom() */
|
|
16
20
|
url?: string;
|
|
17
21
|
/** signaling host when minting (default s.agent-yes.com) */
|
|
18
22
|
sighost?: string;
|
|
@@ -22,6 +26,30 @@ export interface ShareOpts {
|
|
|
22
26
|
apiToken: string;
|
|
23
27
|
}
|
|
24
28
|
|
|
29
|
+
// The room+token persist like the serve token, so the share link (and any
|
|
30
|
+
// browser that saved the room) survives restarts — important for daemons,
|
|
31
|
+
// which would otherwise mint a new link on every restart. Delete the file to
|
|
32
|
+
// rotate the room.
|
|
33
|
+
function shareRoomPath(): string {
|
|
34
|
+
const home = process.env.AGENT_YES_HOME ?? path.join(homedir(), ".agent-yes");
|
|
35
|
+
return path.join(home, ".share-room");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export async function loadOrCreateShareRoom(sighost = DEFAULT_SIGHOST): Promise<string> {
|
|
39
|
+
try {
|
|
40
|
+
const url = (await readFile(shareRoomPath(), "utf-8")).trim();
|
|
41
|
+
if (url.startsWith("webrtc://")) return url;
|
|
42
|
+
} catch {
|
|
43
|
+
/* not yet minted */
|
|
44
|
+
}
|
|
45
|
+
const room = "r" + randomBytes(3).toString("hex");
|
|
46
|
+
const token = randomBytes(32).toString("hex");
|
|
47
|
+
const url = `webrtc://${room}:${token}@${sighost}`;
|
|
48
|
+
await mkdir(path.dirname(shareRoomPath()), { recursive: true });
|
|
49
|
+
await writeFile(shareRoomPath(), url, { mode: 0o600 });
|
|
50
|
+
return url;
|
|
51
|
+
}
|
|
52
|
+
|
|
25
53
|
function parseShareUrl(s: string): { room: string; token: string; host: string } {
|
|
26
54
|
const m = /^webrtc:\/\/([^:@/]+):([^@/]+)@(.+)$/.exec(s);
|
|
27
55
|
if (!m) throw new Error(`bad --share url: ${s} (want webrtc://room:token@host)`);
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import "./ts-BuFWTNL9.js";
|
|
2
|
-
import "./logger-B9h0djqx.js";
|
|
3
|
-
import "./versionChecker-CpNUvHBx.js";
|
|
4
|
-
import "./pidStore-DBjlqzo8.js";
|
|
5
|
-
import "./globalPidIndex-yVd3mbsV.js";
|
|
6
|
-
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-C0a9K6I5.js";
|
|
7
|
-
|
|
8
|
-
export { SUPPORTED_CLIS };
|