agent-yes 1.189.0 → 1.190.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-B729E086.js +8 -0
- package/dist/{SUPPORTED_CLIS-DSoN4a9k.js → SUPPORTED_CLIS-BB7Br0fS.js} +2 -2
- package/dist/{agentShare-BKFQwbNa.js → agentShare-BXfWzPJu.js} +3 -3
- package/dist/cli.js +8 -8
- package/dist/expose-DUYH0tvh.js +466 -0
- package/dist/{forkNested-C8q7E6mp.js → forkNested-C1kKsXi5.js} +1 -1
- package/dist/index.js +2 -2
- package/dist/{notifyDaemon-BnFLuxir.js → notifyDaemon-APkkoCzC.js} +3 -3
- package/dist/{openBrowser-BO5RVYwL.js → openBrowser-DQVkxCyj.js} +1 -1
- package/dist/{remotes-DsTnQhyx.js → remotes-CgT91bMo.js} +2 -2
- package/dist/{remotes-BIHNxn1a.js → remotes-WVJBNajZ.js} +1 -1
- package/dist/{rustBinary-zY2frffa.js → rustBinary-C8YZUZGT.js} +2 -2
- package/dist/{schedule-Bh_4iurz.js → schedule-BYG6MGpH.js} +5 -5
- package/dist/{serve-BvVPG-aI.js → serve-Df3wcUU-.js} +15 -15
- package/dist/{setup-CKYwGT80.js → setup-C65eaHZ8.js} +3 -3
- package/dist/{spawnGate-C5nBiUZQ.js → spawnGate-BejtWiMJ.js} +1 -1
- package/dist/{spawnGate-Clp9xBPX.js → spawnGate-CA5QD7D4.js} +2 -2
- package/dist/{subcommands-DeX22Irv.js → subcommands-D6TbKFTF.js} +2 -2
- package/dist/{subcommands-B4QRPpZV.js → subcommands-Deaq1dOk.js} +15 -10
- package/dist/{tray-DKwVRBwr.js → tray-PMOWFOfk.js} +1 -1
- package/dist/{ts-RxUBjzhh.js → ts-CeGEhsC9.js} +2 -2
- package/dist/{versionChecker-C9xPw96g.js → versionChecker-DXwf5ip7.js} +2 -2
- package/dist/{webrtcRemote-BLEbZnbY.js → webrtcRemote-CD5EQ3uX.js} +1 -1
- package/dist/{workspaceConfig-DKd6UvVm.js → workspaceConfig-_GtAZtsi.js} +1 -1
- package/package.json +3 -2
- package/ts/expose.ts +188 -0
- package/ts/subcommands.ts +6 -0
- package/dist/SUPPORTED_CLIS-kLaIJQsX.js +0 -8
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import "./ts-CeGEhsC9.js";
|
|
2
|
+
import "./logger-CDIsZ-Pp.js";
|
|
3
|
+
import "./versionChecker-DXwf5ip7.js";
|
|
4
|
+
import "./pidStore-BIvsBQ8X.js";
|
|
5
|
+
import "./globalPidIndex-CoNr7tS8.js";
|
|
6
|
+
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-BB7Br0fS.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-CeGEhsC9.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-BB7Br0fS.js.map
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import "./logger-CDIsZ-Pp.js";
|
|
2
2
|
import "./globalPidIndex-CoNr7tS8.js";
|
|
3
3
|
import "./configShared-0MnIQ652.js";
|
|
4
|
-
import { j as resolveOne } from "./subcommands-
|
|
4
|
+
import { j as resolveOne } from "./subcommands-Deaq1dOk.js";
|
|
5
5
|
import "./e2e-BeKjLhmO.js";
|
|
6
6
|
import "./webrtcLink-BG0Xc4-W.js";
|
|
7
|
-
import "./remotes-
|
|
7
|
+
import "./remotes-CgT91bMo.js";
|
|
8
8
|
import { r as startShare } from "./share-29YbzpbU.js";
|
|
9
9
|
|
|
10
10
|
//#region ts/agentShare.ts
|
|
@@ -228,4 +228,4 @@ function transformEvent(rawEvent, agentId, forwarded) {
|
|
|
228
228
|
|
|
229
229
|
//#endregion
|
|
230
230
|
export { createScopedShare, listShares, revokeAllShares, revokeShare };
|
|
231
|
-
//# sourceMappingURL=agentShare-
|
|
231
|
+
//# sourceMappingURL=agentShare-BXfWzPJu.js.map
|
package/dist/cli.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import { t as invokedCliName } from "./invokedCli-uqM2YYA7.js";
|
|
3
3
|
import { n as logger } from "./logger-CDIsZ-Pp.js";
|
|
4
|
-
import { i as versionString, n as displayVersion, t as checkAndAutoUpdate } from "./versionChecker-
|
|
5
|
-
import { n as getRustBinary } from "./rustBinary-
|
|
4
|
+
import { i as versionString, n as displayVersion, t as checkAndAutoUpdate } from "./versionChecker-DXwf5ip7.js";
|
|
5
|
+
import { n as getRustBinary } from "./rustBinary-C8YZUZGT.js";
|
|
6
6
|
import { argv } from "process";
|
|
7
7
|
import { spawn } from "child_process";
|
|
8
8
|
import ms from "ms";
|
|
@@ -285,7 +285,7 @@ function buildRustArgs(argv, cliFromScript, supportedClis) {
|
|
|
285
285
|
const rawArg = process.argv[2];
|
|
286
286
|
const managerCommands = !invokedCliName(process.argv);
|
|
287
287
|
const isHelpFlag = rawArg === "-h" || rawArg === "--help";
|
|
288
|
-
const { isSubcommand, runSubcommand, cmdHelp } = await import("./subcommands-
|
|
288
|
+
const { isSubcommand, runSubcommand, cmdHelp } = await import("./subcommands-D6TbKFTF.js");
|
|
289
289
|
if (isHelpFlag && process.argv.length === 3) {
|
|
290
290
|
await cmdHelp(managerCommands);
|
|
291
291
|
process.exit(0);
|
|
@@ -299,16 +299,16 @@ await checkAndAutoUpdate();
|
|
|
299
299
|
logger.info(versionString());
|
|
300
300
|
const config = parseCliArgs(process.argv);
|
|
301
301
|
if (config.tray) {
|
|
302
|
-
const { startTray } = await import("./tray-
|
|
302
|
+
const { startTray } = await import("./tray-PMOWFOfk.js");
|
|
303
303
|
await startTray();
|
|
304
304
|
await new Promise(() => {});
|
|
305
305
|
}
|
|
306
306
|
{
|
|
307
|
-
const { ensureTray } = await import("./tray-
|
|
307
|
+
const { ensureTray } = await import("./tray-PMOWFOfk.js");
|
|
308
308
|
ensureTray();
|
|
309
309
|
}
|
|
310
310
|
if (!config.showVersion && !config.appendPrompt && !config.tray) {
|
|
311
|
-
const { waitForSpawnCapacity } = await import("./spawnGate-
|
|
311
|
+
const { waitForSpawnCapacity } = await import("./spawnGate-BejtWiMJ.js");
|
|
312
312
|
await waitForSpawnCapacity({
|
|
313
313
|
onWait: (reason, waitedMs) => {
|
|
314
314
|
if (config.verbose || waitedMs === 0) console.error(`[agent-yes] spawn gate: ${reason} — waiting…`);
|
|
@@ -327,14 +327,14 @@ if (config.useRust) {
|
|
|
327
327
|
}
|
|
328
328
|
}
|
|
329
329
|
if (rustBinary) {
|
|
330
|
-
const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-
|
|
330
|
+
const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-B729E086.js");
|
|
331
331
|
const rustArgs = buildRustArgs(process.argv, config.cli, SUPPORTED_CLIS);
|
|
332
332
|
if (config.verbose) {
|
|
333
333
|
console.log(`[rust] Using binary: ${rustBinary}`);
|
|
334
334
|
console.log(`[rust] Args: ${rustArgs.join(" ")}`);
|
|
335
335
|
}
|
|
336
336
|
const attach = config.attach || process.env.AGENT_YES_ATTACH === "1";
|
|
337
|
-
const { shouldForkNested, buildSpawnTutorial, waitForFifo } = await import("./forkNested-
|
|
337
|
+
const { shouldForkNested, buildSpawnTutorial, waitForFifo } = await import("./forkNested-C1kKsXi5.js");
|
|
338
338
|
if (shouldForkNested({
|
|
339
339
|
isTTY: Boolean(process.stdout.isTTY),
|
|
340
340
|
ayPid: process.env.AGENT_YES_PID,
|
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { chmodSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
5
|
+
|
|
6
|
+
//#region node_modules/codehost/src/tunnel/protocol.ts
|
|
7
|
+
let Op = /* @__PURE__ */ function(Op) {
|
|
8
|
+
Op[Op["HttpReq"] = 1] = "HttpReq";
|
|
9
|
+
Op[Op["HttpReqBody"] = 2] = "HttpReqBody";
|
|
10
|
+
Op[Op["HttpReqEnd"] = 3] = "HttpReqEnd";
|
|
11
|
+
Op[Op["HttpResHead"] = 4] = "HttpResHead";
|
|
12
|
+
Op[Op["HttpResBody"] = 5] = "HttpResBody";
|
|
13
|
+
Op[Op["HttpResEnd"] = 6] = "HttpResEnd";
|
|
14
|
+
Op[Op["WsOpen"] = 7] = "WsOpen";
|
|
15
|
+
Op[Op["WsText"] = 9] = "WsText";
|
|
16
|
+
Op[Op["WsBin"] = 10] = "WsBin";
|
|
17
|
+
Op[Op["WsClose"] = 11] = "WsClose";
|
|
18
|
+
Op[Op["WsOpenAck"] = 8] = "WsOpenAck";
|
|
19
|
+
Op[Op["Error"] = 12] = "Error";
|
|
20
|
+
Op[Op["WsCont"] = 13] = "WsCont";
|
|
21
|
+
return Op;
|
|
22
|
+
}({});
|
|
23
|
+
const FRAME_HEADER = 5;
|
|
24
|
+
const MAX_FRAME = 64 * 1024;
|
|
25
|
+
/** Max payload bytes per frame; larger bodies/messages are split across frames. */
|
|
26
|
+
const MAX_CHUNK = MAX_FRAME - FRAME_HEADER;
|
|
27
|
+
const enc = new TextEncoder();
|
|
28
|
+
const dec = new TextDecoder();
|
|
29
|
+
function encodeFrame(op, streamId, payload) {
|
|
30
|
+
const len = payload?.byteLength ?? 0;
|
|
31
|
+
const buf = new Uint8Array(5 + len);
|
|
32
|
+
buf[0] = op;
|
|
33
|
+
new DataView(buf.buffer).setUint32(1, streamId >>> 0, false);
|
|
34
|
+
if (payload && len) buf.set(payload, 5);
|
|
35
|
+
return buf;
|
|
36
|
+
}
|
|
37
|
+
function encodeJson(op, streamId, obj) {
|
|
38
|
+
return encodeFrame(op, streamId, enc.encode(JSON.stringify(obj)));
|
|
39
|
+
}
|
|
40
|
+
function decodeFrame(data) {
|
|
41
|
+
const u8 = data instanceof Uint8Array ? data : new Uint8Array(data);
|
|
42
|
+
return {
|
|
43
|
+
op: u8[0],
|
|
44
|
+
streamId: new DataView(u8.buffer, u8.byteOffset, u8.byteLength).getUint32(1, false),
|
|
45
|
+
payload: u8.subarray(5)
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function payloadJson(payload) {
|
|
49
|
+
return JSON.parse(dec.decode(payload));
|
|
50
|
+
}
|
|
51
|
+
/** Split a body into MAX_CHUNK-sized slices (copies, safe to transfer). */
|
|
52
|
+
function* chunk(body) {
|
|
53
|
+
for (let off = 0; off < body.byteLength; off += MAX_CHUNK) yield body.slice(off, Math.min(off + MAX_CHUNK, body.byteLength));
|
|
54
|
+
}
|
|
55
|
+
function concatBytes(parts) {
|
|
56
|
+
if (parts.length === 1) return parts[0];
|
|
57
|
+
const total = parts.reduce((n, p) => n + p.byteLength, 0);
|
|
58
|
+
const out = new Uint8Array(total);
|
|
59
|
+
let off = 0;
|
|
60
|
+
for (const p of parts) {
|
|
61
|
+
out.set(p, off);
|
|
62
|
+
off += p.byteLength;
|
|
63
|
+
}
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Frames for one WebSocket message. Messages that fit in a single frame emit
|
|
68
|
+
* just a WsText/WsBin frame (back-compatible). Larger ones are split into
|
|
69
|
+
* WsCont frames carrying the leading bytes, terminated by the WsText/WsBin
|
|
70
|
+
* frame with the final bytes; the receiver concatenates them in order.
|
|
71
|
+
*/
|
|
72
|
+
function* wsMessageFrames(terminal, streamId, payload) {
|
|
73
|
+
let off = 0;
|
|
74
|
+
while (payload.byteLength - off > MAX_CHUNK) {
|
|
75
|
+
yield encodeFrame(Op.WsCont, streamId, payload.subarray(off, off + MAX_CHUNK));
|
|
76
|
+
off += MAX_CHUNK;
|
|
77
|
+
}
|
|
78
|
+
yield encodeFrame(terminal, streamId, payload.subarray(off));
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Reassembles WsCont + terminal frames back into whole WebSocket messages,
|
|
82
|
+
* keyed by streamId. Feed it every WsCont/WsText/WsBin payload; it returns the
|
|
83
|
+
* complete message bytes on a terminal frame, or null while buffering.
|
|
84
|
+
*/
|
|
85
|
+
var WsReassembler = class {
|
|
86
|
+
pending = /* @__PURE__ */ new Map();
|
|
87
|
+
cont(streamId, payload) {
|
|
88
|
+
const buf = this.pending.get(streamId);
|
|
89
|
+
if (buf) buf.push(payload.slice());
|
|
90
|
+
else this.pending.set(streamId, [payload.slice()]);
|
|
91
|
+
}
|
|
92
|
+
finish(streamId, payload) {
|
|
93
|
+
const buf = this.pending.get(streamId);
|
|
94
|
+
if (!buf) return payload;
|
|
95
|
+
this.pending.delete(streamId);
|
|
96
|
+
buf.push(payload);
|
|
97
|
+
return concatBytes(buf);
|
|
98
|
+
}
|
|
99
|
+
drop(streamId) {
|
|
100
|
+
this.pending.delete(streamId);
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
//#endregion
|
|
105
|
+
//#region node_modules/codehost/src/tunnel/host.ts
|
|
106
|
+
const textDecoder = new TextDecoder();
|
|
107
|
+
const HIGH_WATER = 4 * 1024 * 1024;
|
|
108
|
+
const LOW_WATER = 1 * 1024 * 1024;
|
|
109
|
+
const HOP_BY_HOP = new Set([
|
|
110
|
+
"connection",
|
|
111
|
+
"keep-alive",
|
|
112
|
+
"proxy-authenticate",
|
|
113
|
+
"proxy-authorization",
|
|
114
|
+
"te",
|
|
115
|
+
"trailer",
|
|
116
|
+
"transfer-encoding",
|
|
117
|
+
"upgrade",
|
|
118
|
+
"content-length",
|
|
119
|
+
"host"
|
|
120
|
+
]);
|
|
121
|
+
/**
|
|
122
|
+
* Server half of the tunnel: bridges one transport to a local HTTP/WS server
|
|
123
|
+
* on 127.0.0.1. Multiplexes concurrent HTTP requests and WebSocket
|
|
124
|
+
* connections by streamId.
|
|
125
|
+
*/
|
|
126
|
+
var TunnelHost = class {
|
|
127
|
+
httpStreams = /* @__PURE__ */ new Map();
|
|
128
|
+
wsConns = /* @__PURE__ */ new Map();
|
|
129
|
+
wsRx = new WsReassembler();
|
|
130
|
+
origin;
|
|
131
|
+
wsOrigin;
|
|
132
|
+
port;
|
|
133
|
+
stripPrefix;
|
|
134
|
+
onLocal;
|
|
135
|
+
constructor(transport, opts) {
|
|
136
|
+
this.transport = transport;
|
|
137
|
+
this.port = opts.port;
|
|
138
|
+
this.stripPrefix = opts.stripPrefix;
|
|
139
|
+
this.onLocal = opts.onLocal;
|
|
140
|
+
this.origin = `http://127.0.0.1:${opts.port}`;
|
|
141
|
+
this.wsOrigin = `ws://127.0.0.1:${opts.port}`;
|
|
142
|
+
transport.setBufferedAmountLow?.(LOW_WATER, () => this.drainWaiter?.());
|
|
143
|
+
transport.onFrame((data) => void this.onFrame(data));
|
|
144
|
+
transport.onClose(() => this.closeAll());
|
|
145
|
+
}
|
|
146
|
+
/** Map a tunneled path to the local server's path, stripping the base prefix. */
|
|
147
|
+
localPath(path) {
|
|
148
|
+
if (this.stripPrefix && path.startsWith(this.stripPrefix)) {
|
|
149
|
+
const rest = path.slice(this.stripPrefix.length);
|
|
150
|
+
return rest.startsWith("/") ? rest : `/${rest}`;
|
|
151
|
+
}
|
|
152
|
+
return path;
|
|
153
|
+
}
|
|
154
|
+
async onFrame(data) {
|
|
155
|
+
const { op, streamId, payload } = decodeFrame(data);
|
|
156
|
+
switch (op) {
|
|
157
|
+
case Op.HttpReq:
|
|
158
|
+
this.httpStreams.set(streamId, {
|
|
159
|
+
head: payloadJson(payload),
|
|
160
|
+
body: []
|
|
161
|
+
});
|
|
162
|
+
break;
|
|
163
|
+
case Op.HttpReqBody:
|
|
164
|
+
this.httpStreams.get(streamId)?.body.push(payload.slice());
|
|
165
|
+
break;
|
|
166
|
+
case Op.HttpReqEnd:
|
|
167
|
+
await this.doHttp(streamId);
|
|
168
|
+
break;
|
|
169
|
+
case Op.WsOpen:
|
|
170
|
+
this.openWs(streamId, payloadJson(payload));
|
|
171
|
+
break;
|
|
172
|
+
case Op.WsCont:
|
|
173
|
+
this.wsRx.cont(streamId, payload);
|
|
174
|
+
break;
|
|
175
|
+
case Op.WsText:
|
|
176
|
+
this.wsConns.get(streamId)?.send(textDecoder.decode(this.wsRx.finish(streamId, payload)));
|
|
177
|
+
break;
|
|
178
|
+
case Op.WsBin:
|
|
179
|
+
this.wsConns.get(streamId)?.send(this.wsRx.finish(streamId, payload));
|
|
180
|
+
break;
|
|
181
|
+
case Op.WsClose:
|
|
182
|
+
this.wsRx.drop(streamId);
|
|
183
|
+
this.wsConns.get(streamId)?.close();
|
|
184
|
+
this.wsConns.delete(streamId);
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
async doHttp(streamId) {
|
|
189
|
+
const stream = this.httpStreams.get(streamId);
|
|
190
|
+
if (!stream) return;
|
|
191
|
+
this.httpStreams.delete(streamId);
|
|
192
|
+
const { method, path, headers } = stream.head;
|
|
193
|
+
const reqHeaders = new Headers();
|
|
194
|
+
let forwardedHost = "";
|
|
195
|
+
for (const [k, v] of Object.entries(headers)) {
|
|
196
|
+
const lk = k.toLowerCase();
|
|
197
|
+
if (lk === "x-forwarded-host") {
|
|
198
|
+
forwardedHost = v;
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
if (!HOP_BY_HOP.has(lk)) reqHeaders.set(k, v);
|
|
202
|
+
}
|
|
203
|
+
reqHeaders.set("host", forwardedHost || `127.0.0.1:${this.port}`);
|
|
204
|
+
const body = method !== "GET" && method !== "HEAD" && stream.body.length > 0 ? concatBytes(stream.body) : void 0;
|
|
205
|
+
const wantsGzip = reqHeaders.get("x-codehost-accept-gzip") === "1";
|
|
206
|
+
reqHeaders.delete("x-codehost-accept-gzip");
|
|
207
|
+
if (wantsGzip) reqHeaders.set("accept-encoding", "gzip");
|
|
208
|
+
try {
|
|
209
|
+
const local = this.onLocal?.({
|
|
210
|
+
method,
|
|
211
|
+
path,
|
|
212
|
+
headers: reqHeaders,
|
|
213
|
+
body
|
|
214
|
+
});
|
|
215
|
+
const res = local ? await local : await fetch(this.origin + this.localPath(path), {
|
|
216
|
+
method,
|
|
217
|
+
headers: reqHeaders,
|
|
218
|
+
body,
|
|
219
|
+
redirect: "manual",
|
|
220
|
+
...wantsGzip ? { decompress: false } : {}
|
|
221
|
+
});
|
|
222
|
+
const resHeaders = {};
|
|
223
|
+
res.headers.forEach((v, k) => {
|
|
224
|
+
if (!HOP_BY_HOP.has(k.toLowerCase())) resHeaders[k] = v;
|
|
225
|
+
});
|
|
226
|
+
await this.send(encodeJson(Op.HttpResHead, streamId, {
|
|
227
|
+
status: res.status,
|
|
228
|
+
statusText: res.statusText,
|
|
229
|
+
headers: resHeaders
|
|
230
|
+
}));
|
|
231
|
+
if (res.body) {
|
|
232
|
+
const reader = res.body.getReader();
|
|
233
|
+
for (;;) {
|
|
234
|
+
const { done, value } = await reader.read();
|
|
235
|
+
if (done) break;
|
|
236
|
+
for (const part of chunk(value)) await this.send(encodeFrame(Op.HttpResBody, streamId, part));
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
await this.send(encodeFrame(Op.HttpResEnd, streamId));
|
|
240
|
+
} catch (err) {
|
|
241
|
+
await this.send(encodeJson(Op.Error, streamId, { message: `proxy error: ${String(err)}` }));
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
openWs(streamId, info) {
|
|
245
|
+
let ws;
|
|
246
|
+
try {
|
|
247
|
+
ws = new WebSocket(this.wsOrigin + this.localPath(info.path), info.protocols);
|
|
248
|
+
} catch (err) {
|
|
249
|
+
this.send(encodeJson(Op.WsOpenAck, streamId, {
|
|
250
|
+
ok: false,
|
|
251
|
+
error: String(err)
|
|
252
|
+
}));
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
ws.binaryType = "arraybuffer";
|
|
256
|
+
this.wsConns.set(streamId, ws);
|
|
257
|
+
ws.onopen = () => {
|
|
258
|
+
this.send(encodeJson(Op.WsOpenAck, streamId, {
|
|
259
|
+
ok: true,
|
|
260
|
+
protocol: ws.protocol
|
|
261
|
+
}));
|
|
262
|
+
};
|
|
263
|
+
ws.onmessage = (ev) => {
|
|
264
|
+
const [terminal, u8] = typeof ev.data === "string" ? [Op.WsText, new TextEncoder().encode(ev.data)] : [Op.WsBin, ev.data instanceof ArrayBuffer ? new Uint8Array(ev.data) : new Uint8Array(ev.data)];
|
|
265
|
+
for (const frame of wsMessageFrames(terminal, streamId, u8)) this.send(frame);
|
|
266
|
+
};
|
|
267
|
+
ws.onclose = (ev) => {
|
|
268
|
+
this.send(encodeJson(Op.WsClose, streamId, {
|
|
269
|
+
code: ev.code,
|
|
270
|
+
reason: ev.reason
|
|
271
|
+
}));
|
|
272
|
+
this.wsConns.delete(streamId);
|
|
273
|
+
};
|
|
274
|
+
ws.onerror = () => {
|
|
275
|
+
this.send(encodeJson(Op.WsClose, streamId, {
|
|
276
|
+
code: 1006,
|
|
277
|
+
reason: "error"
|
|
278
|
+
}));
|
|
279
|
+
this.wsConns.delete(streamId);
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
sendTail = Promise.resolve();
|
|
283
|
+
send(frame) {
|
|
284
|
+
const p = this.sendTail.then(async () => {
|
|
285
|
+
await this.waitForDrain();
|
|
286
|
+
if (!this.transport.isOpen()) return;
|
|
287
|
+
this.transport.send(frame);
|
|
288
|
+
});
|
|
289
|
+
this.sendTail = p.catch(() => {});
|
|
290
|
+
return p;
|
|
291
|
+
}
|
|
292
|
+
drainWaiter = null;
|
|
293
|
+
waitForDrain() {
|
|
294
|
+
if (!this.transport.isOpen() || this.transport.bufferedAmount() < HIGH_WATER) return Promise.resolve();
|
|
295
|
+
return new Promise((resolve) => {
|
|
296
|
+
let settled = false;
|
|
297
|
+
const finish = () => {
|
|
298
|
+
if (settled) return;
|
|
299
|
+
settled = true;
|
|
300
|
+
clearInterval(timer);
|
|
301
|
+
this.drainWaiter = null;
|
|
302
|
+
resolve();
|
|
303
|
+
};
|
|
304
|
+
this.drainWaiter = finish;
|
|
305
|
+
const timer = setInterval(() => {
|
|
306
|
+
if (!this.transport.isOpen() || this.transport.bufferedAmount() < HIGH_WATER) finish();
|
|
307
|
+
}, 100);
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
closeAll() {
|
|
311
|
+
for (const ws of this.wsConns.values()) try {
|
|
312
|
+
ws.close();
|
|
313
|
+
} catch {}
|
|
314
|
+
this.wsConns.clear();
|
|
315
|
+
this.httpStreams.clear();
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
//#endregion
|
|
320
|
+
//#region ts/expose.ts
|
|
321
|
+
const DEFAULT_RELAY = "https://agent-yes.com";
|
|
322
|
+
const PING_MS = 25e3;
|
|
323
|
+
const RECONNECT_MIN_MS = 1e3;
|
|
324
|
+
const RECONNECT_MAX_MS = 3e4;
|
|
325
|
+
function exposuresPath() {
|
|
326
|
+
const home = process.env.AGENT_YES_HOME ?? path.join(homedir(), ".agent-yes");
|
|
327
|
+
mkdirSync(home, { recursive: true });
|
|
328
|
+
return path.join(home, "exposures.json");
|
|
329
|
+
}
|
|
330
|
+
/** Stable id+key per (relay, port): re-running `ay expose 5173` keeps its URL. */
|
|
331
|
+
function loadOrCreateExposure(relayHost, port) {
|
|
332
|
+
const file = exposuresPath();
|
|
333
|
+
let all = {};
|
|
334
|
+
try {
|
|
335
|
+
all = JSON.parse(readFileSync(file, "utf8"));
|
|
336
|
+
} catch {}
|
|
337
|
+
const slot = `${relayHost}:${port}`;
|
|
338
|
+
const existing = all[slot];
|
|
339
|
+
if (existing?.id && existing.key) return existing;
|
|
340
|
+
const rec = {
|
|
341
|
+
id: "x" + randomBytes(8).toString("hex").slice(0, 15),
|
|
342
|
+
key: randomBytes(32).toString("hex")
|
|
343
|
+
};
|
|
344
|
+
all[slot] = rec;
|
|
345
|
+
const tmp = file + ".tmp";
|
|
346
|
+
writeFileSync(tmp, JSON.stringify(all, null, 2) + "\n");
|
|
347
|
+
chmodSync(tmp, 384);
|
|
348
|
+
renameSync(tmp, file);
|
|
349
|
+
return rec;
|
|
350
|
+
}
|
|
351
|
+
/** Bun's browser-style WebSocket as a TunnelTransport. No bufferedAmountLow
|
|
352
|
+
* event exists here — TunnelHost's 100ms safety poll covers drain resume. */
|
|
353
|
+
function wsTransport(ws) {
|
|
354
|
+
return {
|
|
355
|
+
send: (frame) => {
|
|
356
|
+
const copy = new Uint8Array(frame.byteLength);
|
|
357
|
+
copy.set(frame);
|
|
358
|
+
ws.send(copy);
|
|
359
|
+
},
|
|
360
|
+
isOpen: () => ws.readyState === WebSocket.OPEN,
|
|
361
|
+
bufferedAmount: () => ws.bufferedAmount,
|
|
362
|
+
onFrame: (cb) => {
|
|
363
|
+
ws.addEventListener("message", (ev) => {
|
|
364
|
+
if (typeof ev.data === "string") return;
|
|
365
|
+
const d = ev.data;
|
|
366
|
+
cb(d instanceof Uint8Array ? d : new Uint8Array(d));
|
|
367
|
+
});
|
|
368
|
+
},
|
|
369
|
+
onClose: (cb) => ws.addEventListener("close", () => cb())
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
async function cmdExpose(args) {
|
|
373
|
+
let relay = DEFAULT_RELAY;
|
|
374
|
+
let port = 0;
|
|
375
|
+
for (let i = 0; i < args.length; i++) {
|
|
376
|
+
const a = args[i];
|
|
377
|
+
if (a === "--relay") relay = args[++i] ?? relay;
|
|
378
|
+
else if (a.startsWith("--relay=")) relay = a.slice(8);
|
|
379
|
+
else if (/^\d+$/.test(a)) port = Number(a);
|
|
380
|
+
else if (a === "-h" || a === "--help") {
|
|
381
|
+
console.log(`Usage: ay expose <port> [--relay ${DEFAULT_RELAY}]\n\nShares http://127.0.0.1:<port> at https://<id>.agent-yes.com (private:\nvisitors need the printed one-time claim link, which sets an 8h cookie).\nThe URL is stable per port on this machine. Ctrl-C stops sharing.`);
|
|
382
|
+
return 0;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
if (!port || port < 1 || port > 65535) {
|
|
386
|
+
console.error("usage: ay expose <port> [--relay https://…] (see ay expose --help)");
|
|
387
|
+
return 1;
|
|
388
|
+
}
|
|
389
|
+
const relayUrl = new URL(relay);
|
|
390
|
+
const rec = loadOrCreateExposure(relayUrl.host, port);
|
|
391
|
+
const tunnelUrl = `${relayUrl.protocol === "http:" ? "ws:" : "wss:"}//${relayUrl.host}/_ay/tunnel/${rec.id}`;
|
|
392
|
+
const publicHost = relayUrl.host === "agent-yes.com" ? `${rec.id}.agent-yes.com` : relayUrl.host;
|
|
393
|
+
const claimToken = randomBytes(18).toString("base64url");
|
|
394
|
+
const claimHash = createHash("sha256").update(claimToken).digest("hex");
|
|
395
|
+
let stopped = false;
|
|
396
|
+
let ws = null;
|
|
397
|
+
let backoff = RECONNECT_MIN_MS;
|
|
398
|
+
let announced = false;
|
|
399
|
+
const connect = () => {
|
|
400
|
+
if (stopped) return;
|
|
401
|
+
ws = new WebSocket(tunnelUrl);
|
|
402
|
+
ws.binaryType = "arraybuffer";
|
|
403
|
+
const sock = ws;
|
|
404
|
+
let ping = null;
|
|
405
|
+
sock.addEventListener("open", () => {
|
|
406
|
+
sock.send(JSON.stringify({
|
|
407
|
+
t: "hello",
|
|
408
|
+
key: rec.key,
|
|
409
|
+
port,
|
|
410
|
+
claims: [claimHash],
|
|
411
|
+
v: 1
|
|
412
|
+
}));
|
|
413
|
+
});
|
|
414
|
+
sock.addEventListener("message", (ev) => {
|
|
415
|
+
if (typeof ev.data !== "string") return;
|
|
416
|
+
let msg;
|
|
417
|
+
try {
|
|
418
|
+
msg = JSON.parse(ev.data);
|
|
419
|
+
} catch {
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
if (msg.t === "ready") {
|
|
423
|
+
backoff = RECONNECT_MIN_MS;
|
|
424
|
+
new TunnelHost(wsTransport(sock), { port });
|
|
425
|
+
ping = setInterval(() => {
|
|
426
|
+
if (sock.readyState === WebSocket.OPEN) sock.send("ping");
|
|
427
|
+
}, PING_MS);
|
|
428
|
+
if (!announced) {
|
|
429
|
+
announced = true;
|
|
430
|
+
console.log(`[ay expose] sharing 127.0.0.1:${port}`);
|
|
431
|
+
console.log(` url: https://${publicHost}/`);
|
|
432
|
+
console.log(` claim: https://${publicHost}/_ay/claim?t=${claimToken}`);
|
|
433
|
+
console.log(` (one-time link — opens access for 8h in that browser)`);
|
|
434
|
+
} else console.log(`[ay expose] reconnected`);
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
sock.addEventListener("close", (ev) => {
|
|
438
|
+
if (ping) clearInterval(ping);
|
|
439
|
+
if (stopped) return;
|
|
440
|
+
if (ev.code === 1008) {
|
|
441
|
+
console.error(`[ay expose] relay refused this exposure (${ev.reason || "forbidden"}) — giving up`);
|
|
442
|
+
process.exit(1);
|
|
443
|
+
}
|
|
444
|
+
console.log(`[ay expose] connection lost, retrying in ${Math.round(backoff / 1e3)}s…`);
|
|
445
|
+
setTimeout(connect, backoff);
|
|
446
|
+
backoff = Math.min(backoff * 2, RECONNECT_MAX_MS);
|
|
447
|
+
});
|
|
448
|
+
sock.addEventListener("error", () => {});
|
|
449
|
+
};
|
|
450
|
+
connect();
|
|
451
|
+
const shutdown = () => {
|
|
452
|
+
stopped = true;
|
|
453
|
+
console.log("\n[ay expose] stopped — the URL now answers 502 until you expose again");
|
|
454
|
+
try {
|
|
455
|
+
ws?.close();
|
|
456
|
+
} catch {}
|
|
457
|
+
process.exit(0);
|
|
458
|
+
};
|
|
459
|
+
process.on("SIGINT", shutdown);
|
|
460
|
+
process.on("SIGTERM", shutdown);
|
|
461
|
+
return new Promise(() => {});
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
//#endregion
|
|
465
|
+
export { cmdExpose };
|
|
466
|
+
//# sourceMappingURL=expose-DUYH0tvh.js.map
|
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-CeGEhsC9.js";
|
|
2
2
|
import "./logger-CDIsZ-Pp.js";
|
|
3
|
-
import "./versionChecker-
|
|
3
|
+
import "./versionChecker-DXwf5ip7.js";
|
|
4
4
|
import "./pidStore-BIvsBQ8X.js";
|
|
5
5
|
import "./globalPidIndex-CoNr7tS8.js";
|
|
6
6
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { n as logger } from "./logger-CDIsZ-Pp.js";
|
|
2
2
|
import "./globalPidIndex-CoNr7tS8.js";
|
|
3
3
|
import "./configShared-0MnIQ652.js";
|
|
4
|
-
import { G as hostId, J as readInbox, K as listInboxParents, O as renderLogTailLines, Q as notifyDir, S as listRecords, U as appendEvent, W as gcInboxes, X as daemonLockDir, Y as shouldStealLock, Z as daemonLockOwnerPath, _ as isPidAlive, c as deriveLiveState, q as liveWatchers } from "./subcommands-
|
|
4
|
+
import { G as hostId, J as readInbox, K as listInboxParents, O as renderLogTailLines, Q as notifyDir, S as listRecords, U as appendEvent, W as gcInboxes, X as daemonLockDir, Y as shouldStealLock, Z as daemonLockOwnerPath, _ as isPidAlive, c as deriveLiveState, q as liveWatchers } from "./subcommands-Deaq1dOk.js";
|
|
5
5
|
import "./e2e-BeKjLhmO.js";
|
|
6
6
|
import "./webrtcLink-BG0Xc4-W.js";
|
|
7
|
-
import "./remotes-
|
|
7
|
+
import "./remotes-CgT91bMo.js";
|
|
8
8
|
import { mkdir, readFile, rename, rm, stat, writeFile } from "fs/promises";
|
|
9
9
|
import { randomUUID } from "node:crypto";
|
|
10
10
|
import { execFile } from "node:child_process";
|
|
@@ -588,4 +588,4 @@ async function ensureDaemon() {
|
|
|
588
588
|
|
|
589
589
|
//#endregion
|
|
590
590
|
export { daemonStatus, ensureDaemon, requestDaemonStop, runDaemon };
|
|
591
|
-
//# sourceMappingURL=notifyDaemon-
|
|
591
|
+
//# sourceMappingURL=notifyDaemon-APkkoCzC.js.map
|
|
@@ -86,7 +86,7 @@ async function resolveRemoteSpec(spec) {
|
|
|
86
86
|
* The bridge lives for the rest of the process (torn down on `process.exit`).
|
|
87
87
|
*/
|
|
88
88
|
async function resolveWebrtc(link, keyword) {
|
|
89
|
-
const { startWebrtcBridge } = await import("./webrtcRemote-
|
|
89
|
+
const { startWebrtcBridge } = await import("./webrtcRemote-CD5EQ3uX.js");
|
|
90
90
|
const bridge = await startWebrtcBridge(link);
|
|
91
91
|
return {
|
|
92
92
|
url: bridge.baseUrl,
|
|
@@ -173,4 +173,4 @@ async function cmdRemote(rest) {
|
|
|
173
173
|
|
|
174
174
|
//#endregion
|
|
175
175
|
export { resolveRemoteSpec as a, readRemotes as i, deleteRemoteAlias as n, writeRemoteAlias as o, parseDirectRemoteSpec as r, cmdRemote as t };
|
|
176
|
-
//# sourceMappingURL=remotes-
|
|
176
|
+
//# sourceMappingURL=remotes-CgT91bMo.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import "./e2e-BeKjLhmO.js";
|
|
2
2
|
import "./webrtcLink-BG0Xc4-W.js";
|
|
3
|
-
import { a as resolveRemoteSpec, i as readRemotes, n as deleteRemoteAlias, o as writeRemoteAlias, r as parseDirectRemoteSpec, t as cmdRemote } from "./remotes-
|
|
3
|
+
import { a as resolveRemoteSpec, i as readRemotes, n as deleteRemoteAlias, o as writeRemoteAlias, r as parseDirectRemoteSpec, t as cmdRemote } from "./remotes-CgT91bMo.js";
|
|
4
4
|
|
|
5
5
|
export { cmdRemote };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r as getInstalledPackage } from "./versionChecker-
|
|
1
|
+
import { r as getInstalledPackage } from "./versionChecker-DXwf5ip7.js";
|
|
2
2
|
import { execFileSync } from "child_process";
|
|
3
3
|
import { existsSync, mkdirSync, unlinkSync } from "fs";
|
|
4
4
|
import { chmod, copyFile } from "fs/promises";
|
|
@@ -225,4 +225,4 @@ async function getRustBinary(options = {}) {
|
|
|
225
225
|
|
|
226
226
|
//#endregion
|
|
227
227
|
export { getRustBinary as n, findSpawnHiddenLauncher as t };
|
|
228
|
-
//# sourceMappingURL=rustBinary-
|
|
228
|
+
//# sourceMappingURL=rustBinary-C8YZUZGT.js.map
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import "./ts-
|
|
1
|
+
import "./ts-CeGEhsC9.js";
|
|
2
2
|
import "./logger-CDIsZ-Pp.js";
|
|
3
|
-
import "./versionChecker-
|
|
3
|
+
import "./versionChecker-DXwf5ip7.js";
|
|
4
4
|
import "./pidStore-BIvsBQ8X.js";
|
|
5
5
|
import "./globalPidIndex-CoNr7tS8.js";
|
|
6
|
-
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-
|
|
7
|
-
import { d as resolveSpawnCwd } from "./workspaceConfig-
|
|
6
|
+
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-BB7Br0fS.js";
|
|
7
|
+
import { d as resolveSpawnCwd } from "./workspaceConfig-_GtAZtsi.js";
|
|
8
8
|
import { createHash } from "node:crypto";
|
|
9
9
|
|
|
10
10
|
//#region ts/oxmgrService.ts
|
|
@@ -141,4 +141,4 @@ async function cmdSchedule(rest) {
|
|
|
141
141
|
|
|
142
142
|
//#endregion
|
|
143
143
|
export { cmdSchedule };
|
|
144
|
-
//# sourceMappingURL=schedule-
|
|
144
|
+
//# sourceMappingURL=schedule-BYG6MGpH.js.map
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import "./ts-
|
|
1
|
+
import "./ts-CeGEhsC9.js";
|
|
2
2
|
import "./logger-CDIsZ-Pp.js";
|
|
3
|
-
import { r as getInstalledPackage } from "./versionChecker-
|
|
4
|
-
import { t as findSpawnHiddenLauncher } from "./rustBinary-
|
|
3
|
+
import { r as getInstalledPackage } from "./versionChecker-DXwf5ip7.js";
|
|
4
|
+
import { t as findSpawnHiddenLauncher } from "./rustBinary-C8YZUZGT.js";
|
|
5
5
|
import "./pidStore-BIvsBQ8X.js";
|
|
6
6
|
import { a as updateGlobalPidStatus } from "./globalPidIndex-CoNr7tS8.js";
|
|
7
7
|
import { t as pgidForWrapper } from "./reaper-CWF2_ATd.js";
|
|
8
8
|
import "./configShared-0MnIQ652.js";
|
|
9
|
-
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-
|
|
10
|
-
import { $ as TYPING_BADGE, D as recentReadEdges, E as readPtysize, H as writeToIpc, I as snapshotStatus, O as renderLogTailLines, S as listRecords, T as readNotes, b as isUserTyping, f as extractNeedsInput, j as resolveOne, k as renderRawLog, l as deriveLiveStatus, o as controlCodeFromName, p as extractTaskCounts, u as extractBadges } from "./subcommands-
|
|
9
|
+
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-BB7Br0fS.js";
|
|
10
|
+
import { $ as TYPING_BADGE, D as recentReadEdges, E as readPtysize, H as writeToIpc, I as snapshotStatus, O as renderLogTailLines, S as listRecords, T as readNotes, b as isUserTyping, f as extractNeedsInput, j as resolveOne, k as renderRawLog, l as deriveLiveStatus, o as controlCodeFromName, p as extractTaskCounts, u as extractBadges } from "./subcommands-Deaq1dOk.js";
|
|
11
11
|
import "./e2e-BeKjLhmO.js";
|
|
12
12
|
import "./webrtcLink-BG0Xc4-W.js";
|
|
13
|
-
import "./remotes-
|
|
14
|
-
import { a as getSpawnHook, c as hasProvisionHook, d as resolveSpawnCwd, i as getProvisionRoot, l as hasSpawnHook, r as getProvisionHook, u as isProvisionAllowed } from "./workspaceConfig-
|
|
15
|
-
import { r as spawnRejectionReason } from "./spawnGate-
|
|
13
|
+
import "./remotes-CgT91bMo.js";
|
|
14
|
+
import { a as getSpawnHook, c as hasProvisionHook, d as resolveSpawnCwd, i as getProvisionRoot, l as hasSpawnHook, r as getProvisionHook, u as isProvisionAllowed } from "./workspaceConfig-_GtAZtsi.js";
|
|
15
|
+
import { r as spawnRejectionReason } from "./spawnGate-CA5QD7D4.js";
|
|
16
16
|
import yargs from "yargs";
|
|
17
17
|
import { mkdir, open, readFile, stat, unlink, writeFile } from "fs/promises";
|
|
18
18
|
import { arch, cpus, freemem, homedir, hostname, loadavg, platform, totalmem, uptime, userInfo } from "os";
|
|
@@ -515,7 +515,7 @@ async function cmdServeDaemon(sub, args) {
|
|
|
515
515
|
if (!webrtcDaemon) return;
|
|
516
516
|
if (shareLink) {
|
|
517
517
|
process.stdout.write(`\nshared over WebRTC — open this link (the token is eaten from the URL on open):\n ${shareLink}\n` + (shareLinkMinted ? ` (persistent room — same link across restarts; delete ~/.agent-yes/.share-room to rotate)\n` : ``));
|
|
518
|
-
const { offerOpenInBrowser } = await import("./openBrowser-
|
|
518
|
+
const { offerOpenInBrowser } = await import("./openBrowser-DQVkxCyj.js");
|
|
519
519
|
await offerOpenInBrowser(shareLink);
|
|
520
520
|
} else process.stdout.write("\nthe WebRTC share link carries a secret, so the daemon does NOT log it —\nread it from ~/.agent-yes/.share-link (mode 0600). The room persists in\n~/.agent-yes/.share-room, so the link survives restarts.\n");
|
|
521
521
|
};
|
|
@@ -1851,7 +1851,7 @@ Options:
|
|
|
1851
1851
|
const perm = body.perm ?? "r";
|
|
1852
1852
|
if (perm !== "r" && perm !== "rw") return new Response(`invalid perm ${perm} (want r or rw)`, { status: 400 });
|
|
1853
1853
|
try {
|
|
1854
|
-
const { createScopedShare } = await import("./agentShare-
|
|
1854
|
+
const { createScopedShare } = await import("./agentShare-BXfWzPJu.js");
|
|
1855
1855
|
const share = await createScopedShare({
|
|
1856
1856
|
agent: body.agent,
|
|
1857
1857
|
perm,
|
|
@@ -1866,12 +1866,12 @@ Options:
|
|
|
1866
1866
|
}
|
|
1867
1867
|
}
|
|
1868
1868
|
if (req.method === "GET" && p === "/api/shares") {
|
|
1869
|
-
const { listShares } = await import("./agentShare-
|
|
1869
|
+
const { listShares } = await import("./agentShare-BXfWzPJu.js");
|
|
1870
1870
|
return Response.json(listShares());
|
|
1871
1871
|
}
|
|
1872
1872
|
const revokeM = /^\/api\/share\/([^/]+)$/.exec(p);
|
|
1873
1873
|
if (req.method === "DELETE" && revokeM) {
|
|
1874
|
-
const { revokeShare } = await import("./agentShare-
|
|
1874
|
+
const { revokeShare } = await import("./agentShare-BXfWzPJu.js");
|
|
1875
1875
|
const ok = revokeShare(decodeURIComponent(revokeM[1]));
|
|
1876
1876
|
return new Response(ok ? "revoked" : "no such share", { status: ok ? 200 : 404 });
|
|
1877
1877
|
}
|
|
@@ -1977,7 +1977,7 @@ Options:
|
|
|
1977
1977
|
const persistNote = explicitUrl ? "\n" : ` (persistent room — same link across restarts; delete ~/.agent-yes/.share-room to rotate)\n\n`;
|
|
1978
1978
|
process.stdout.write(`${wantHttp ? "\n" : ""}${lead}:\n ${link}\n` + persistNote);
|
|
1979
1979
|
if (!rotated) {
|
|
1980
|
-
const { offerOpenInBrowser } = await import("./openBrowser-
|
|
1980
|
+
const { offerOpenInBrowser } = await import("./openBrowser-DQVkxCyj.js");
|
|
1981
1981
|
await offerOpenInBrowser(link);
|
|
1982
1982
|
}
|
|
1983
1983
|
} else {
|
|
@@ -2016,7 +2016,7 @@ Options:
|
|
|
2016
2016
|
const shutdown = (resolve) => {
|
|
2017
2017
|
if (heartbeat) clearInterval(heartbeat);
|
|
2018
2018
|
closeShare?.();
|
|
2019
|
-
import("./agentShare-
|
|
2019
|
+
import("./agentShare-BXfWzPJu.js").then((m) => m.revokeAllShares()).catch(() => {});
|
|
2020
2020
|
server?.stop();
|
|
2021
2021
|
resolve();
|
|
2022
2022
|
};
|
|
@@ -2029,4 +2029,4 @@ Options:
|
|
|
2029
2029
|
|
|
2030
2030
|
//#endregion
|
|
2031
2031
|
export { cmdServe };
|
|
2032
|
-
//# sourceMappingURL=serve-
|
|
2032
|
+
//# sourceMappingURL=serve-Df3wcUU-.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { f as setWorkspaceRoot, s as getWorkspaceRoot } from "./workspaceConfig-
|
|
1
|
+
import { f as setWorkspaceRoot, s as getWorkspaceRoot } from "./workspaceConfig-_GtAZtsi.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-Df3wcUU-.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-C65eaHZ8.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import "./logger-CDIsZ-Pp.js";
|
|
2
2
|
import "./globalPidIndex-CoNr7tS8.js";
|
|
3
|
-
import { i as waitForSpawnCapacity, n as spawnGateEnabled, r as spawnRejectionReason, t as memAvailableMb } from "./spawnGate-
|
|
3
|
+
import { i as waitForSpawnCapacity, n as spawnGateEnabled, r as spawnRejectionReason, t as memAvailableMb } from "./spawnGate-CA5QD7D4.js";
|
|
4
4
|
|
|
5
5
|
export { waitForSpawnCapacity };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { i as readGlobalPids } from "./globalPidIndex-CoNr7tS8.js";
|
|
2
|
-
import { n as getMinFreeMb, o as getSpawnWaitMs, t as getMaxAgents } from "./workspaceConfig-
|
|
2
|
+
import { n as getMinFreeMb, o as getSpawnWaitMs, t as getMaxAgents } from "./workspaceConfig-_GtAZtsi.js";
|
|
3
3
|
import { readFile } from "fs/promises";
|
|
4
4
|
|
|
5
5
|
//#region ts/spawnGate.ts
|
|
@@ -104,4 +104,4 @@ async function waitForSpawnCapacity(opts) {
|
|
|
104
104
|
|
|
105
105
|
//#endregion
|
|
106
106
|
export { waitForSpawnCapacity as i, spawnGateEnabled as n, spawnRejectionReason as r, memAvailableMb as t };
|
|
107
|
-
//# sourceMappingURL=spawnGate-
|
|
107
|
+
//# sourceMappingURL=spawnGate-CA5QD7D4.js.map
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import "./logger-CDIsZ-Pp.js";
|
|
2
2
|
import "./globalPidIndex-CoNr7tS8.js";
|
|
3
3
|
import "./configShared-0MnIQ652.js";
|
|
4
|
-
import { A as renderRawLogLines, B as waitForLogQuiet, C as matchKeyword, D as recentReadEdges, E as readPtysize, F as runSubcommand, H as writeToIpc, I as snapshotStatus, L as stdinActivityPath, M as resolveReadWindow, N as resolveResumeArgs, O as renderLogTailLines, P as restartHintLines, R as stopTipForCli, S as listRecords, T as readNotes, V as writeKeysPaced, _ as isPidAlive, a as cmdHelp, b as isUserTyping, c as deriveLiveState, d as extractMenu, f as extractNeedsInput, g as isExitRequest, h as isAgentStuck, i as backoffWhileTyping, j as resolveOne, k as renderRawLog, l as deriveLiveStatus, m as finalizedLines, n as READ_PAGE_DEFAULT, o as controlCodeFromName, p as extractTaskCounts, r as TYPING_WINDOW_MS, s as cursorAbs, t as GRACEFUL_EXIT_COMMANDS, u as extractBadges, v as isSlashCommand, w as menuSelectKeys, x as lastStdinAt, y as isSubcommand, z as submitAndConfirm } from "./subcommands-
|
|
4
|
+
import { A as renderRawLogLines, B as waitForLogQuiet, C as matchKeyword, D as recentReadEdges, E as readPtysize, F as runSubcommand, H as writeToIpc, I as snapshotStatus, L as stdinActivityPath, M as resolveReadWindow, N as resolveResumeArgs, O as renderLogTailLines, P as restartHintLines, R as stopTipForCli, S as listRecords, T as readNotes, V as writeKeysPaced, _ as isPidAlive, a as cmdHelp, b as isUserTyping, c as deriveLiveState, d as extractMenu, f as extractNeedsInput, g as isExitRequest, h as isAgentStuck, i as backoffWhileTyping, j as resolveOne, k as renderRawLog, l as deriveLiveStatus, m as finalizedLines, n as READ_PAGE_DEFAULT, o as controlCodeFromName, p as extractTaskCounts, r as TYPING_WINDOW_MS, s as cursorAbs, t as GRACEFUL_EXIT_COMMANDS, u as extractBadges, v as isSlashCommand, w as menuSelectKeys, x as lastStdinAt, y as isSubcommand, z as submitAndConfirm } from "./subcommands-Deaq1dOk.js";
|
|
5
5
|
import "./e2e-BeKjLhmO.js";
|
|
6
6
|
import "./webrtcLink-BG0Xc4-W.js";
|
|
7
|
-
import "./remotes-
|
|
7
|
+
import "./remotes-CgT91bMo.js";
|
|
8
8
|
|
|
9
9
|
export { cmdHelp, isSubcommand, runSubcommand };
|
|
@@ -4,7 +4,7 @@ import { t as agentYesHome } from "./agentYesHome-CtHb5b71.js";
|
|
|
4
4
|
import { a as updateGlobalPidStatus, i as readGlobalPids } from "./globalPidIndex-CoNr7tS8.js";
|
|
5
5
|
import { t as loadSharedCliDefaults } from "./configShared-0MnIQ652.js";
|
|
6
6
|
import { n as isWebrtcSpec } from "./webrtcLink-BG0Xc4-W.js";
|
|
7
|
-
import { a as resolveRemoteSpec, i as readRemotes } from "./remotes-
|
|
7
|
+
import { a as resolveRemoteSpec, i as readRemotes } from "./remotes-CgT91bMo.js";
|
|
8
8
|
import ms from "ms";
|
|
9
9
|
import yargs from "yargs";
|
|
10
10
|
import { appendFile, mkdir, open, readFile, readdir, rename, rm, stat, writeFile } from "fs/promises";
|
|
@@ -1086,6 +1086,7 @@ const SUBCOMMANDS = new Set([
|
|
|
1086
1086
|
"serve",
|
|
1087
1087
|
"schedule",
|
|
1088
1088
|
"remote",
|
|
1089
|
+
"expose",
|
|
1089
1090
|
"reap",
|
|
1090
1091
|
"help"
|
|
1091
1092
|
]);
|
|
@@ -1145,21 +1146,25 @@ async function runSubcommand(argv) {
|
|
|
1145
1146
|
case "restart": return await cmdRestart(rest);
|
|
1146
1147
|
case "note": return await cmdNote(rest);
|
|
1147
1148
|
case "serve": {
|
|
1148
|
-
const { cmdServe } = await import("./serve-
|
|
1149
|
+
const { cmdServe } = await import("./serve-Df3wcUU-.js");
|
|
1149
1150
|
return cmdServe(rest);
|
|
1150
1151
|
}
|
|
1151
1152
|
case "setup": {
|
|
1152
|
-
const { cmdSetup } = await import("./setup-
|
|
1153
|
+
const { cmdSetup } = await import("./setup-C65eaHZ8.js");
|
|
1153
1154
|
return cmdSetup(rest);
|
|
1154
1155
|
}
|
|
1155
1156
|
case "schedule": {
|
|
1156
|
-
const { cmdSchedule } = await import("./schedule-
|
|
1157
|
+
const { cmdSchedule } = await import("./schedule-BYG6MGpH.js");
|
|
1157
1158
|
return cmdSchedule(rest);
|
|
1158
1159
|
}
|
|
1159
1160
|
case "remote": {
|
|
1160
|
-
const { cmdRemote } = await import("./remotes-
|
|
1161
|
+
const { cmdRemote } = await import("./remotes-WVJBNajZ.js");
|
|
1161
1162
|
return cmdRemote(rest);
|
|
1162
1163
|
}
|
|
1164
|
+
case "expose": {
|
|
1165
|
+
const { cmdExpose } = await import("./expose-DUYH0tvh.js");
|
|
1166
|
+
return cmdExpose(rest);
|
|
1167
|
+
}
|
|
1163
1168
|
case "reap":
|
|
1164
1169
|
await (await import("./reaper-jfSmog15.js")).sweep();
|
|
1165
1170
|
return 0;
|
|
@@ -1194,7 +1199,7 @@ async function cmdHelp(managerCommands = true) {
|
|
|
1194
1199
|
const setupLine = managerCommands ? ` ay setup guided setup: pick a workspace, share to agent-yes.com\n` : ``;
|
|
1195
1200
|
const self = process.env.AGENT_YES_PID ? await resolveSender() : null;
|
|
1196
1201
|
const agentSection = self ? await buildAgentContextSection(self) : "";
|
|
1197
|
-
process.stdout.write(agentSection + "ay - agent-yes CLI\n\nManagement:\n ay ls [keyword] list running agents\n ay tail [-f] [-n N] <keyword> last N lines (96), -f to follow\n ay read <keyword> [page opts] paginate: --last/--head N, --range A:B,\n --before-line L [--limit N]\n ay cat <keyword> full log\n ay head <keyword> first N lines\n ay send <keyword> <msg> send a message\n ay key <keyword> <key...> send raw keystrokes (down/up/enter/esc/…) — drives menus\n ay select <keyword> <N> pick option N of a needs_input selection menu\n ay attach <keyword> interactive attach (detach: Ctrl-\\)\n ay stop <keyword> graceful shutdown (/exit for claude/codex)\n ay exit <keyword> [reason] graceful shutdown, recording who/why (= 'ay send <kw> exit')\n ay restart <keyword> [--fresh] stop (if live) + relaunch resuming the session; --fresh replays the prompt\n ay status <keyword> agent status snapshot\n ay result <keyword> [--wait] pull an agent's structured result envelope\n ay result set '<json>' (inside an agent) deposit your result envelope\n ay reap kill process groups leaked by dead agents\n\nRemote:\n" + setupLine + " ay schedule <when> <cli> -- <msg> run an agent on a schedule (HH:MM or cron)\n ay serve [--port N] start HTTP API server (prints token)\n ay serve status show serve daemon/server status\n ay remote add <alias> http://<token>@<host>:<port>\n ay remote ls / rm <alias> manage saved remotes\n ay ls <token>@<host>:<port> connect inline (no alias needed)\n ay send <token>@<host>:<port>:<kw> <msg>\n\nRun an agent:\n ay [claude|codex|gemini|...] [options] -- [prompt]\n ay claude -- \"fix the bug in auth.ts\"\n ay claude --help full agent-runner options\n\nLabs (examples at https://github.com/snomiao/agent-yes/tree/main/lab):\n local-role-play/ designer + builder on one machine\n http-remote/ ay serve remote access demo\n p2p-pairing/ libp2p P2P (needs: cargo build --features swarm)\n");
|
|
1202
|
+
process.stdout.write(agentSection + "ay - agent-yes CLI\n\nManagement:\n ay ls [keyword] list running agents\n ay tail [-f] [-n N] <keyword> last N lines (96), -f to follow\n ay read <keyword> [page opts] paginate: --last/--head N, --range A:B,\n --before-line L [--limit N]\n ay cat <keyword> full log\n ay head <keyword> first N lines\n ay send <keyword> <msg> send a message\n ay key <keyword> <key...> send raw keystrokes (down/up/enter/esc/…) — drives menus\n ay select <keyword> <N> pick option N of a needs_input selection menu\n ay attach <keyword> interactive attach (detach: Ctrl-\\)\n ay stop <keyword> graceful shutdown (/exit for claude/codex)\n ay exit <keyword> [reason] graceful shutdown, recording who/why (= 'ay send <kw> exit')\n ay restart <keyword> [--fresh] stop (if live) + relaunch resuming the session; --fresh replays the prompt\n ay status <keyword> agent status snapshot\n ay result <keyword> [--wait] pull an agent's structured result envelope\n ay result set '<json>' (inside an agent) deposit your result envelope\n ay reap kill process groups leaked by dead agents\n\nRemote:\n" + setupLine + " ay schedule <when> <cli> -- <msg> run an agent on a schedule (HH:MM or cron)\n ay serve [--port N] start HTTP API server (prints token)\n ay serve status show serve daemon/server status\n ay remote add <alias> http://<token>@<host>:<port>\n ay remote ls / rm <alias> manage saved remotes\n ay expose <port> share localhost:<port> at https://<id>.agent-yes.com (private link)\n ay ls <token>@<host>:<port> connect inline (no alias needed)\n ay send <token>@<host>:<port>:<kw> <msg>\n\nRun an agent:\n ay [claude|codex|gemini|...] [options] -- [prompt]\n ay claude -- \"fix the bug in auth.ts\"\n ay claude --help full agent-runner options\n\nLabs (examples at https://github.com/snomiao/agent-yes/tree/main/lab):\n local-role-play/ designer + builder on one machine\n http-remote/ ay serve remote access demo\n p2p-pairing/ libp2p P2P (needs: cargo build --features swarm)\n");
|
|
1198
1203
|
return 0;
|
|
1199
1204
|
}
|
|
1200
1205
|
function matchKeyword(record, keyword) {
|
|
@@ -1550,7 +1555,7 @@ async function fetchRemoteRecordsRaw(url, token, opts) {
|
|
|
1550
1555
|
let base = url;
|
|
1551
1556
|
let bearer = token;
|
|
1552
1557
|
if (isWebrtcSpec(url)) {
|
|
1553
|
-
const { startWebrtcBridge } = await import("./webrtcRemote-
|
|
1558
|
+
const { startWebrtcBridge } = await import("./webrtcRemote-CD5EQ3uX.js");
|
|
1554
1559
|
bridge = await startWebrtcBridge(url);
|
|
1555
1560
|
base = bridge.baseUrl;
|
|
1556
1561
|
bearer = bridge.token;
|
|
@@ -3991,7 +3996,7 @@ async function cmdNotify(rest) {
|
|
|
3991
3996
|
}
|
|
3992
3997
|
const ensure = async () => {
|
|
3993
3998
|
if (!argv["ensure-daemon"]) return;
|
|
3994
|
-
const { ensureDaemon } = await import("./notifyDaemon-
|
|
3999
|
+
const { ensureDaemon } = await import("./notifyDaemon-APkkoCzC.js");
|
|
3995
4000
|
await ensureDaemon().catch(() => null);
|
|
3996
4001
|
};
|
|
3997
4002
|
await heartbeatWatcher(parent, selfStartedAt);
|
|
@@ -4065,7 +4070,7 @@ async function cmdNotifyCursor(args) {
|
|
|
4065
4070
|
}
|
|
4066
4071
|
async function cmdNotifyd(rest) {
|
|
4067
4072
|
const sub = rest[0] ?? "status";
|
|
4068
|
-
const daemon = await import("./notifyDaemon-
|
|
4073
|
+
const daemon = await import("./notifyDaemon-APkkoCzC.js");
|
|
4069
4074
|
switch (sub) {
|
|
4070
4075
|
case "run": return daemon.runDaemon();
|
|
4071
4076
|
case "once": return daemon.runDaemon({ once: true });
|
|
@@ -4092,4 +4097,4 @@ async function cmdNotifyd(rest) {
|
|
|
4092
4097
|
|
|
4093
4098
|
//#endregion
|
|
4094
4099
|
export { TYPING_BADGE as $, renderRawLogLines as A, waitForLogQuiet as B, matchKeyword as C, recentReadEdges as D, readPtysize as E, runSubcommand as F, hostId as G, writeToIpc as H, snapshotStatus as I, readInbox as J, listInboxParents as K, stdinActivityPath as L, resolveReadWindow as M, resolveResumeArgs as N, renderLogTailLines as O, restartHintLines as P, notifyDir as Q, stopTipForCli as R, listRecords as S, readNotes as T, appendEvent as U, writeKeysPaced as V, gcInboxes as W, daemonLockDir as X, shouldStealLock as Y, daemonLockOwnerPath as Z, isPidAlive as _, cmdHelp as a, isUserTyping as b, deriveLiveState as c, extractMenu as d, extractNeedsInput as f, isExitRequest as g, isAgentStuck as h, backoffWhileTyping as i, resolveOne as j, renderRawLog as k, deriveLiveStatus as l, finalizedLines as m, READ_PAGE_DEFAULT as n, controlCodeFromName as o, extractTaskCounts as p, liveWatchers as q, TYPING_WINDOW_MS as r, cursorAbs as s, GRACEFUL_EXIT_COMMANDS as t, extractBadges as u, isSlashCommand as v, menuSelectKeys as w, lastStdinAt as x, isSubcommand as y, submitAndConfirm as z };
|
|
4095
|
-
//# sourceMappingURL=subcommands-
|
|
4100
|
+
//# sourceMappingURL=subcommands-Deaq1dOk.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as logger, t as addTransport } from "./logger-CDIsZ-Pp.js";
|
|
2
|
-
import { r as getInstalledPackage } from "./versionChecker-
|
|
2
|
+
import { r as getInstalledPackage } from "./versionChecker-DXwf5ip7.js";
|
|
3
3
|
import { t as agentYesHome } from "./agentYesHome-CtHb5b71.js";
|
|
4
4
|
import { i as shouldUseLock, r as releaseLock, t as acquireLock } from "./runningLock-CNMl13dC.js";
|
|
5
5
|
import { t as PidStore } from "./pidStore-BIvsBQ8X.js";
|
|
@@ -1824,4 +1824,4 @@ function sleep(ms) {
|
|
|
1824
1824
|
|
|
1825
1825
|
//#endregion
|
|
1826
1826
|
export { removeControlCharacters as a, AgentContext as i, agentYes as n, config as r, CLIS_CONFIG as t };
|
|
1827
|
-
//# sourceMappingURL=ts-
|
|
1827
|
+
//# sourceMappingURL=ts-CeGEhsC9.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.190.0";
|
|
11
11
|
|
|
12
12
|
//#endregion
|
|
13
13
|
//#region ts/versionChecker.ts
|
|
@@ -215,4 +215,4 @@ async function displayVersion() {
|
|
|
215
215
|
|
|
216
216
|
//#endregion
|
|
217
217
|
export { versionString as i, displayVersion as n, getInstalledPackage as r, checkAndAutoUpdate as t };
|
|
218
|
-
//# sourceMappingURL=versionChecker-
|
|
218
|
+
//# sourceMappingURL=versionChecker-DXwf5ip7.js.map
|
|
@@ -193,4 +193,4 @@ function resolveSpawnCwd(input) {
|
|
|
193
193
|
|
|
194
194
|
//#endregion
|
|
195
195
|
export { getSpawnHook as a, hasProvisionHook as c, resolveSpawnCwd as d, setWorkspaceRoot as f, getProvisionRoot as i, hasSpawnHook as l, getMinFreeMb as n, getSpawnWaitMs as o, getProvisionHook as r, getWorkspaceRoot as s, getMaxAgents as t, isProvisionAllowed as u };
|
|
196
|
-
//# sourceMappingURL=workspaceConfig-
|
|
196
|
+
//# sourceMappingURL=workspaceConfig-_GtAZtsi.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-yes",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.190.0",
|
|
4
4
|
"description": "A wrapper tool that automates interactions with various AI CLI tools by automatically handling common prompts and responses.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -150,7 +150,8 @@
|
|
|
150
150
|
"tsdown": "^0.20.3",
|
|
151
151
|
"vitest": "4.1.0",
|
|
152
152
|
"ws": "^8.20.0",
|
|
153
|
-
"zod": "^3.23.0"
|
|
153
|
+
"zod": "^3.23.0",
|
|
154
|
+
"codehost": "^0.32.0"
|
|
154
155
|
},
|
|
155
156
|
"peerDependencies": {
|
|
156
157
|
"node-pty": "latest",
|
package/ts/expose.ts
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
// `ay expose <port>` — share a local HTTP/WS server through the agent-yes edge
|
|
2
|
+
// relay: https://<id>.agent-yes.com/* ⇄ Exposure DO ⇄ this daemon ⇄ 127.0.0.1:<port>.
|
|
3
|
+
//
|
|
4
|
+
// The daemon dials OUT (wss://<relay>/_ay/tunnel/<id>), so it works behind any
|
|
5
|
+
// NAT, and runs the codehost tunnel protocol's host half against the local
|
|
6
|
+
// port. Private by default: visitors need the single-use claim link printed
|
|
7
|
+
// below (it swaps for an 8h HttpOnly cookie at the edge; unauthenticated
|
|
8
|
+
// requests never reach this machine). See lab/ui/cf/exposure.ts for the edge.
|
|
9
|
+
|
|
10
|
+
import { randomBytes, createHash } from "node:crypto";
|
|
11
|
+
import { chmodSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
12
|
+
import { homedir } from "node:os";
|
|
13
|
+
import path from "node:path";
|
|
14
|
+
import { TunnelHost } from "codehost/tunnel";
|
|
15
|
+
import type { TunnelTransport } from "codehost/tunnel";
|
|
16
|
+
|
|
17
|
+
const DEFAULT_RELAY = "https://agent-yes.com";
|
|
18
|
+
const PING_MS = 25_000;
|
|
19
|
+
const RECONNECT_MIN_MS = 1_000;
|
|
20
|
+
const RECONNECT_MAX_MS = 30_000;
|
|
21
|
+
|
|
22
|
+
interface ExposureRecord {
|
|
23
|
+
id: string;
|
|
24
|
+
key: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function exposuresPath(): string {
|
|
28
|
+
const home = process.env.AGENT_YES_HOME ?? path.join(homedir(), ".agent-yes");
|
|
29
|
+
mkdirSync(home, { recursive: true });
|
|
30
|
+
return path.join(home, "exposures.json");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Stable id+key per (relay, port): re-running `ay expose 5173` keeps its URL. */
|
|
34
|
+
function loadOrCreateExposure(relayHost: string, port: number): ExposureRecord {
|
|
35
|
+
const file = exposuresPath();
|
|
36
|
+
let all: Record<string, ExposureRecord> = {};
|
|
37
|
+
try {
|
|
38
|
+
all = JSON.parse(readFileSync(file, "utf8"));
|
|
39
|
+
} catch {
|
|
40
|
+
/* first run */
|
|
41
|
+
}
|
|
42
|
+
const slot = `${relayHost}:${port}`;
|
|
43
|
+
const existing = all[slot];
|
|
44
|
+
if (existing?.id && existing.key) return existing;
|
|
45
|
+
const rec: ExposureRecord = {
|
|
46
|
+
// "x" prefix: exposure hostnames can never collide with named subdomains.
|
|
47
|
+
id: "x" + randomBytes(8).toString("hex").slice(0, 15),
|
|
48
|
+
key: randomBytes(32).toString("hex"),
|
|
49
|
+
};
|
|
50
|
+
all[slot] = rec;
|
|
51
|
+
const tmp = file + ".tmp";
|
|
52
|
+
writeFileSync(tmp, JSON.stringify(all, null, 2) + "\n");
|
|
53
|
+
chmodSync(tmp, 0o600);
|
|
54
|
+
renameSync(tmp, file);
|
|
55
|
+
return rec;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Bun's browser-style WebSocket as a TunnelTransport. No bufferedAmountLow
|
|
59
|
+
* event exists here — TunnelHost's 100ms safety poll covers drain resume. */
|
|
60
|
+
function wsTransport(ws: WebSocket): TunnelTransport {
|
|
61
|
+
return {
|
|
62
|
+
// Copy into a fresh ArrayBuffer-backed view: Bun's ws.send mishandles a
|
|
63
|
+
// subarray view (byteOffset > 0), which the protocol's fragmentation emits.
|
|
64
|
+
send: (frame) => {
|
|
65
|
+
const copy = new Uint8Array(frame.byteLength);
|
|
66
|
+
copy.set(frame);
|
|
67
|
+
ws.send(copy);
|
|
68
|
+
},
|
|
69
|
+
isOpen: () => ws.readyState === WebSocket.OPEN,
|
|
70
|
+
bufferedAmount: () => ws.bufferedAmount,
|
|
71
|
+
onFrame: (cb) => {
|
|
72
|
+
ws.addEventListener("message", (ev) => {
|
|
73
|
+
if (typeof ev.data === "string") return;
|
|
74
|
+
const d = ev.data as ArrayBuffer | Uint8Array;
|
|
75
|
+
cb(d instanceof Uint8Array ? d : new Uint8Array(d));
|
|
76
|
+
});
|
|
77
|
+
},
|
|
78
|
+
onClose: (cb) => ws.addEventListener("close", () => cb()),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export async function cmdExpose(args: string[]): Promise<number> {
|
|
83
|
+
let relay = DEFAULT_RELAY;
|
|
84
|
+
let port = 0;
|
|
85
|
+
for (let i = 0; i < args.length; i++) {
|
|
86
|
+
const a = args[i]!;
|
|
87
|
+
if (a === "--relay") relay = args[++i] ?? relay;
|
|
88
|
+
else if (a.startsWith("--relay=")) relay = a.slice("--relay=".length);
|
|
89
|
+
else if (/^\d+$/.test(a)) port = Number(a);
|
|
90
|
+
else if (a === "-h" || a === "--help") {
|
|
91
|
+
console.log(
|
|
92
|
+
`Usage: ay expose <port> [--relay ${DEFAULT_RELAY}]\n\n` +
|
|
93
|
+
`Shares http://127.0.0.1:<port> at https://<id>.agent-yes.com (private:\n` +
|
|
94
|
+
`visitors need the printed one-time claim link, which sets an 8h cookie).\n` +
|
|
95
|
+
`The URL is stable per port on this machine. Ctrl-C stops sharing.`,
|
|
96
|
+
);
|
|
97
|
+
return 0;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (!port || port < 1 || port > 65535) {
|
|
101
|
+
console.error("usage: ay expose <port> [--relay https://…] (see ay expose --help)");
|
|
102
|
+
return 1;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const relayUrl = new URL(relay);
|
|
106
|
+
const rec = loadOrCreateExposure(relayUrl.host, port);
|
|
107
|
+
const wsProto = relayUrl.protocol === "http:" ? "ws:" : "wss:";
|
|
108
|
+
const tunnelUrl = `${wsProto}//${relayUrl.host}/_ay/tunnel/${rec.id}`;
|
|
109
|
+
// Public hostname: <id>.<zone> on the real relay; the relay host itself (with
|
|
110
|
+
// a Host-header spoof) when pointing at wrangler dev.
|
|
111
|
+
const publicHost = relayUrl.host === "agent-yes.com" ? `${rec.id}.agent-yes.com` : relayUrl.host;
|
|
112
|
+
|
|
113
|
+
// Fresh single-use claim token every run; only its hash goes to the edge.
|
|
114
|
+
const claimToken = randomBytes(18).toString("base64url");
|
|
115
|
+
const claimHash = createHash("sha256").update(claimToken).digest("hex");
|
|
116
|
+
|
|
117
|
+
let stopped = false;
|
|
118
|
+
let ws: WebSocket | null = null;
|
|
119
|
+
let backoff = RECONNECT_MIN_MS;
|
|
120
|
+
let announced = false;
|
|
121
|
+
|
|
122
|
+
const connect = () => {
|
|
123
|
+
if (stopped) return;
|
|
124
|
+
ws = new WebSocket(tunnelUrl);
|
|
125
|
+
ws.binaryType = "arraybuffer";
|
|
126
|
+
const sock = ws;
|
|
127
|
+
let ping: ReturnType<typeof setInterval> | null = null;
|
|
128
|
+
|
|
129
|
+
sock.addEventListener("open", () => {
|
|
130
|
+
sock.send(JSON.stringify({ t: "hello", key: rec.key, port, claims: [claimHash], v: 1 }));
|
|
131
|
+
});
|
|
132
|
+
sock.addEventListener("message", (ev) => {
|
|
133
|
+
if (typeof ev.data !== "string") return; // binary frames belong to the TunnelHost
|
|
134
|
+
let msg: { t?: string };
|
|
135
|
+
try {
|
|
136
|
+
msg = JSON.parse(ev.data);
|
|
137
|
+
} catch {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (msg.t === "ready") {
|
|
141
|
+
backoff = RECONNECT_MIN_MS;
|
|
142
|
+
new TunnelHost(wsTransport(sock), { port });
|
|
143
|
+
ping = setInterval(() => {
|
|
144
|
+
if (sock.readyState === WebSocket.OPEN) sock.send("ping");
|
|
145
|
+
}, PING_MS);
|
|
146
|
+
if (!announced) {
|
|
147
|
+
announced = true;
|
|
148
|
+
console.log(`[ay expose] sharing 127.0.0.1:${port}`);
|
|
149
|
+
console.log(` url: https://${publicHost}/`);
|
|
150
|
+
console.log(` claim: https://${publicHost}/_ay/claim?t=${claimToken}`);
|
|
151
|
+
console.log(` (one-time link — opens access for 8h in that browser)`);
|
|
152
|
+
} else {
|
|
153
|
+
console.log(`[ay expose] reconnected`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
sock.addEventListener("close", (ev) => {
|
|
158
|
+
if (ping) clearInterval(ping);
|
|
159
|
+
if (stopped) return;
|
|
160
|
+
if (ev.code === 1008) {
|
|
161
|
+
console.error(`[ay expose] relay refused this exposure (${ev.reason || "forbidden"}) — giving up`);
|
|
162
|
+
process.exit(1);
|
|
163
|
+
}
|
|
164
|
+
console.log(`[ay expose] connection lost, retrying in ${Math.round(backoff / 1000)}s…`);
|
|
165
|
+
setTimeout(connect, backoff);
|
|
166
|
+
backoff = Math.min(backoff * 2, RECONNECT_MAX_MS);
|
|
167
|
+
});
|
|
168
|
+
sock.addEventListener("error", () => {
|
|
169
|
+
/* close fires right after; retry there */
|
|
170
|
+
});
|
|
171
|
+
};
|
|
172
|
+
connect();
|
|
173
|
+
|
|
174
|
+
const shutdown = () => {
|
|
175
|
+
stopped = true;
|
|
176
|
+
console.log("\n[ay expose] stopped — the URL now answers 502 until you expose again");
|
|
177
|
+
try {
|
|
178
|
+
ws?.close();
|
|
179
|
+
} catch {
|
|
180
|
+
/* ignore */
|
|
181
|
+
}
|
|
182
|
+
process.exit(0);
|
|
183
|
+
};
|
|
184
|
+
process.on("SIGINT", shutdown);
|
|
185
|
+
process.on("SIGTERM", shutdown);
|
|
186
|
+
|
|
187
|
+
return new Promise<number>(() => {}); // runs until signal
|
|
188
|
+
}
|
package/ts/subcommands.ts
CHANGED
|
@@ -306,6 +306,7 @@ const SUBCOMMANDS = new Set([
|
|
|
306
306
|
"serve",
|
|
307
307
|
"schedule",
|
|
308
308
|
"remote",
|
|
309
|
+
"expose",
|
|
309
310
|
"reap",
|
|
310
311
|
"help",
|
|
311
312
|
]);
|
|
@@ -427,6 +428,10 @@ export async function runSubcommand(argv: string[]): Promise<number | null> {
|
|
|
427
428
|
const { cmdRemote } = await import("./remotes.ts");
|
|
428
429
|
return cmdRemote(rest);
|
|
429
430
|
}
|
|
431
|
+
case "expose": {
|
|
432
|
+
const { cmdExpose } = await import("./expose.ts");
|
|
433
|
+
return cmdExpose(rest);
|
|
434
|
+
}
|
|
430
435
|
case "reap": {
|
|
431
436
|
const reaper = await import("./reaper.ts");
|
|
432
437
|
await reaper.sweep();
|
|
@@ -546,6 +551,7 @@ export async function cmdHelp(managerCommands = true): Promise<number> {
|
|
|
546
551
|
` ay serve status show serve daemon/server status\n` +
|
|
547
552
|
` ay remote add <alias> http://<token>@<host>:<port>\n` +
|
|
548
553
|
` ay remote ls / rm <alias> manage saved remotes\n` +
|
|
554
|
+
` ay expose <port> share localhost:<port> at https://<id>.agent-yes.com (private link)\n` +
|
|
549
555
|
` ay ls <token>@<host>:<port> connect inline (no alias needed)\n` +
|
|
550
556
|
` ay send <token>@<host>:<port>:<kw> <msg>\n` +
|
|
551
557
|
`\n` +
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import "./ts-RxUBjzhh.js";
|
|
2
|
-
import "./logger-CDIsZ-Pp.js";
|
|
3
|
-
import "./versionChecker-C9xPw96g.js";
|
|
4
|
-
import "./pidStore-BIvsBQ8X.js";
|
|
5
|
-
import "./globalPidIndex-CoNr7tS8.js";
|
|
6
|
-
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-DSoN4a9k.js";
|
|
7
|
-
|
|
8
|
-
export { SUPPORTED_CLIS };
|