agent-yes 1.189.1 → 1.191.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-P3I0D3Ka.js +8 -0
- package/dist/{SUPPORTED_CLIS-DcA00P5Z.js → SUPPORTED_CLIS-mUaQHk4M.js} +2 -2
- package/dist/{agentShare-D5qYAXeL.js → agentShare-Boa9P2dK.js} +3 -3
- package/dist/cli.js +8 -8
- package/dist/expose-B3AGhVhM.js +549 -0
- package/dist/{forkNested-C8q7E6mp.js → forkNested-C1kKsXi5.js} +1 -1
- package/dist/index.js +2 -2
- package/dist/{notifyDaemon-BZmlbBqN.js → notifyDaemon-hKT38cBs.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-xhXsfVn-.js → rustBinary-Cu8q2m9s.js} +2 -2
- package/dist/{schedule-BUp_RUVP.js → schedule-Bb98iG9W.js} +5 -5
- package/dist/{serve-CM9NHaCH.js → serve-DVhbtjR4.js} +51 -15
- package/dist/{setup-BE9NyUE3.js → setup-Dj8Grg0K.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-BYKJkWNP.js → subcommands-CNCgJ48t.js} +15 -10
- package/dist/{subcommands-qe4h_Cvx.js → subcommands-Uerol8Tz.js} +2 -2
- package/dist/{tray-DKwVRBwr.js → tray-PMOWFOfk.js} +1 -1
- package/dist/{ts-1qx-QhAe.js → ts-BQh1-ljE.js} +2 -2
- package/dist/{versionChecker-Do5ex5Wm.js → versionChecker-DhCi7ncH.js} +2 -2
- package/dist/{webrtcRemote-BLEbZnbY.js → webrtcRemote-CD5EQ3uX.js} +1 -1
- package/dist/{workspaceConfig-DKd6UvVm.js → workspaceConfig-_GtAZtsi.js} +1 -1
- package/lab/ui/index.html +239 -3
- package/package.json +3 -2
- package/ts/expose.ts +314 -0
- package/ts/serve.ts +44 -0
- package/ts/subcommands.ts +6 -0
- package/dist/SUPPORTED_CLIS-D8ocFVqe.js +0 -8
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import "./ts-BQh1-ljE.js";
|
|
2
|
+
import "./logger-CDIsZ-Pp.js";
|
|
3
|
+
import "./versionChecker-DhCi7ncH.js";
|
|
4
|
+
import "./pidStore-BIvsBQ8X.js";
|
|
5
|
+
import "./globalPidIndex-CoNr7tS8.js";
|
|
6
|
+
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-mUaQHk4M.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-BQh1-ljE.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-mUaQHk4M.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-CNCgJ48t.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-Boa9P2dK.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-DhCi7ncH.js";
|
|
5
|
+
import { n as getRustBinary } from "./rustBinary-Cu8q2m9s.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-Uerol8Tz.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-P3I0D3Ka.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,549 @@
|
|
|
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-exposing a port 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
|
+
/**
|
|
373
|
+
* Start (or fail) one exposure. Resolves once the relay has accepted the daemon
|
|
374
|
+
* and the tunnel is live; rejects if the relay refuses this exposure (bad key).
|
|
375
|
+
* Reconnects with backoff for the life of the handle.
|
|
376
|
+
*/
|
|
377
|
+
function startExposure(opts) {
|
|
378
|
+
const relay = opts.relay ?? DEFAULT_RELAY;
|
|
379
|
+
const port = opts.port;
|
|
380
|
+
const log = opts.log ?? (() => {});
|
|
381
|
+
const relayUrl = new URL(relay);
|
|
382
|
+
const rec = loadOrCreateExposure(relayUrl.host, port);
|
|
383
|
+
const tunnelUrl = `${relayUrl.protocol === "http:" ? "ws:" : "wss:"}//${relayUrl.host}/_ay/tunnel/${rec.id}`;
|
|
384
|
+
const publicHost = relayUrl.host === "agent-yes.com" ? `${rec.id}.agent-yes.com` : relayUrl.host;
|
|
385
|
+
const publicUrl = `https://${publicHost}/`;
|
|
386
|
+
let stopped = false;
|
|
387
|
+
let sock = null;
|
|
388
|
+
let ready = false;
|
|
389
|
+
let backoff = RECONNECT_MIN_MS;
|
|
390
|
+
const handle = {
|
|
391
|
+
id: rec.id,
|
|
392
|
+
port,
|
|
393
|
+
publicHost,
|
|
394
|
+
url: publicUrl,
|
|
395
|
+
relayHost: relayUrl.host,
|
|
396
|
+
createdAt: Date.now(),
|
|
397
|
+
mintClaim() {
|
|
398
|
+
const token = randomBytes(18).toString("base64url");
|
|
399
|
+
const hash = createHash("sha256").update(token).digest("hex");
|
|
400
|
+
if (sock && sock.readyState === WebSocket.OPEN) sock.send(JSON.stringify({
|
|
401
|
+
t: "claim",
|
|
402
|
+
claims: [hash]
|
|
403
|
+
}));
|
|
404
|
+
return `https://${publicHost}/_ay/claim?t=${token}`;
|
|
405
|
+
},
|
|
406
|
+
stop() {
|
|
407
|
+
stopped = true;
|
|
408
|
+
try {
|
|
409
|
+
sock?.close();
|
|
410
|
+
} catch {}
|
|
411
|
+
}
|
|
412
|
+
};
|
|
413
|
+
return new Promise((resolve, reject) => {
|
|
414
|
+
const connect = () => {
|
|
415
|
+
if (stopped) return;
|
|
416
|
+
sock = new WebSocket(tunnelUrl);
|
|
417
|
+
sock.binaryType = "arraybuffer";
|
|
418
|
+
const ws = sock;
|
|
419
|
+
let ping = null;
|
|
420
|
+
ws.addEventListener("open", () => {
|
|
421
|
+
ws.send(JSON.stringify({
|
|
422
|
+
t: "hello",
|
|
423
|
+
key: rec.key,
|
|
424
|
+
port,
|
|
425
|
+
v: 1
|
|
426
|
+
}));
|
|
427
|
+
});
|
|
428
|
+
ws.addEventListener("message", (ev) => {
|
|
429
|
+
if (typeof ev.data !== "string") return;
|
|
430
|
+
let msg;
|
|
431
|
+
try {
|
|
432
|
+
msg = JSON.parse(ev.data);
|
|
433
|
+
} catch {
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
if (msg.t === "ready") {
|
|
437
|
+
backoff = RECONNECT_MIN_MS;
|
|
438
|
+
new TunnelHost(wsTransport(ws), { port });
|
|
439
|
+
ping = setInterval(() => {
|
|
440
|
+
if (ws.readyState === WebSocket.OPEN) ws.send("ping");
|
|
441
|
+
}, PING_MS);
|
|
442
|
+
if (!ready) {
|
|
443
|
+
ready = true;
|
|
444
|
+
log(`sharing 127.0.0.1:${port} at ${publicUrl}`);
|
|
445
|
+
resolve(handle);
|
|
446
|
+
} else log(`reconnected`);
|
|
447
|
+
}
|
|
448
|
+
});
|
|
449
|
+
ws.addEventListener("close", (ev) => {
|
|
450
|
+
if (ping) clearInterval(ping);
|
|
451
|
+
if (stopped) return;
|
|
452
|
+
if (ev.code === 1008) {
|
|
453
|
+
const err = /* @__PURE__ */ new Error(`relay refused exposure (${ev.reason || "forbidden"})`);
|
|
454
|
+
if (!ready) return reject(err);
|
|
455
|
+
log(err.message);
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
log(`connection lost, retrying in ${Math.round(backoff / 1e3)}s…`);
|
|
459
|
+
setTimeout(connect, backoff);
|
|
460
|
+
backoff = Math.min(backoff * 2, RECONNECT_MAX_MS);
|
|
461
|
+
});
|
|
462
|
+
ws.addEventListener("error", () => {});
|
|
463
|
+
};
|
|
464
|
+
connect();
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
const active = /* @__PURE__ */ new Map();
|
|
468
|
+
/** In-flight starts, so concurrent POSTs for the same port share one dial. */
|
|
469
|
+
const starting = /* @__PURE__ */ new Map();
|
|
470
|
+
/** Start an exposure for `port` (or reuse a running one). Idempotent per port. */
|
|
471
|
+
async function ensureExposure(port, relay) {
|
|
472
|
+
const existing = active.get(port);
|
|
473
|
+
if (existing) return existing;
|
|
474
|
+
const inflight = starting.get(port);
|
|
475
|
+
if (inflight) return inflight;
|
|
476
|
+
const p = startExposure({
|
|
477
|
+
port,
|
|
478
|
+
relay
|
|
479
|
+
}).then((h) => {
|
|
480
|
+
active.set(port, h);
|
|
481
|
+
starting.delete(port);
|
|
482
|
+
return h;
|
|
483
|
+
}).catch((e) => {
|
|
484
|
+
starting.delete(port);
|
|
485
|
+
throw e;
|
|
486
|
+
});
|
|
487
|
+
starting.set(port, p);
|
|
488
|
+
return p;
|
|
489
|
+
}
|
|
490
|
+
function listExposures() {
|
|
491
|
+
return [...active.values()].sort((a, b) => b.createdAt - a.createdAt).map((h) => ({
|
|
492
|
+
id: h.id,
|
|
493
|
+
port: h.port,
|
|
494
|
+
url: h.url,
|
|
495
|
+
createdAt: h.createdAt
|
|
496
|
+
}));
|
|
497
|
+
}
|
|
498
|
+
function stopExposure(port) {
|
|
499
|
+
const h = active.get(port);
|
|
500
|
+
if (!h) return false;
|
|
501
|
+
h.stop();
|
|
502
|
+
active.delete(port);
|
|
503
|
+
return true;
|
|
504
|
+
}
|
|
505
|
+
async function cmdExpose(args) {
|
|
506
|
+
let relay = DEFAULT_RELAY;
|
|
507
|
+
let port = 0;
|
|
508
|
+
for (let i = 0; i < args.length; i++) {
|
|
509
|
+
const a = args[i];
|
|
510
|
+
if (a === "--relay") relay = args[++i] ?? relay;
|
|
511
|
+
else if (a.startsWith("--relay=")) relay = a.slice(8);
|
|
512
|
+
else if (/^\d+$/.test(a)) port = Number(a);
|
|
513
|
+
else if (a === "-h" || a === "--help") {
|
|
514
|
+
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.`);
|
|
515
|
+
return 0;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
if (!port || port < 1 || port > 65535) {
|
|
519
|
+
console.error("usage: ay expose <port> [--relay https://…] (see ay expose --help)");
|
|
520
|
+
return 1;
|
|
521
|
+
}
|
|
522
|
+
let handle;
|
|
523
|
+
try {
|
|
524
|
+
handle = await startExposure({
|
|
525
|
+
port,
|
|
526
|
+
relay,
|
|
527
|
+
log: (m) => console.log(`[ay expose] ${m}`)
|
|
528
|
+
});
|
|
529
|
+
} catch (e) {
|
|
530
|
+
console.error(`[ay expose] ${e.message} — giving up`);
|
|
531
|
+
return 1;
|
|
532
|
+
}
|
|
533
|
+
const claimUrl = handle.mintClaim();
|
|
534
|
+
console.log(` url: ${handle.url}`);
|
|
535
|
+
console.log(` claim: ${claimUrl}`);
|
|
536
|
+
console.log(` (one-time link — opens access for 8h in that browser)`);
|
|
537
|
+
const shutdown = () => {
|
|
538
|
+
console.log("\n[ay expose] stopped — the URL now answers 502 until you expose again");
|
|
539
|
+
handle.stop();
|
|
540
|
+
process.exit(0);
|
|
541
|
+
};
|
|
542
|
+
process.on("SIGINT", shutdown);
|
|
543
|
+
process.on("SIGTERM", shutdown);
|
|
544
|
+
return new Promise(() => {});
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
//#endregion
|
|
548
|
+
export { cmdExpose, ensureExposure, listExposures, stopExposure };
|
|
549
|
+
//# sourceMappingURL=expose-B3AGhVhM.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-BQh1-ljE.js";
|
|
2
2
|
import "./logger-CDIsZ-Pp.js";
|
|
3
|
-
import "./versionChecker-
|
|
3
|
+
import "./versionChecker-DhCi7ncH.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-CNCgJ48t.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-hKT38cBs.js.map
|