@toon-protocol/client 0.11.0 → 0.13.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/README.md +1 -26
- package/dist/index.d.ts +141 -180
- package/dist/index.js +181 -193
- package/dist/index.js.map +1 -1
- package/package.json +1 -2
- package/dist/anon-proxy-W3KMM7GU.js +0 -23
- package/dist/anon-proxy-W3KMM7GU.js.map +0 -1
- package/dist/chunk-WHAEQLIW.js +0 -276
- package/dist/chunk-WHAEQLIW.js.map +0 -1
- package/dist/gateway-QOK47RKS.js +0 -13
- package/dist/gateway-QOK47RKS.js.map +0 -1
- package/dist/socks5-WTJBYGME.js +0 -136
- package/dist/socks5-WTJBYGME.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toon-protocol/client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "TOON client for ILP-gated Nostr publishing with multi-hop routing and payment channels",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Jonathan Green",
|
|
@@ -45,7 +45,6 @@
|
|
|
45
45
|
"@toon-protocol/mina-zkapp": "^0.1.0",
|
|
46
46
|
"mina-signer": "^3.0.0",
|
|
47
47
|
"o1js": "2.14.0",
|
|
48
|
-
"socks-proxy-agent": "^8.0.5",
|
|
49
48
|
"ws": "^8.0.0"
|
|
50
49
|
},
|
|
51
50
|
"devDependencies": {
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ANON_ASSETS,
|
|
3
|
-
ANON_VERSION,
|
|
4
|
-
defaultCacheDir,
|
|
5
|
-
ensureAnonBinary,
|
|
6
|
-
renderTorrc,
|
|
7
|
-
selectAnonAsset,
|
|
8
|
-
startManagedAnonProxy,
|
|
9
|
-
tcpProbe,
|
|
10
|
-
waitForAnonSocks
|
|
11
|
-
} from "./chunk-WHAEQLIW.js";
|
|
12
|
-
export {
|
|
13
|
-
ANON_ASSETS,
|
|
14
|
-
ANON_VERSION,
|
|
15
|
-
defaultCacheDir,
|
|
16
|
-
ensureAnonBinary,
|
|
17
|
-
renderTorrc,
|
|
18
|
-
selectAnonAsset,
|
|
19
|
-
startManagedAnonProxy,
|
|
20
|
-
tcpProbe,
|
|
21
|
-
waitForAnonSocks
|
|
22
|
-
};
|
|
23
|
-
//# sourceMappingURL=anon-proxy-W3KMM7GU.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/dist/chunk-WHAEQLIW.js
DELETED
|
@@ -1,276 +0,0 @@
|
|
|
1
|
-
// src/transport/anon-proxy.ts
|
|
2
|
-
import { createRequire } from "module";
|
|
3
|
-
var nodeRequire = createRequire(import.meta.url);
|
|
4
|
-
var ANON_VERSION = "v0.4.10.0-beta";
|
|
5
|
-
var RELEASE_BASE = `https://github.com/anyone-protocol/ator-protocol/releases/download/${ANON_VERSION}`;
|
|
6
|
-
var ANON_ASSETS = {
|
|
7
|
-
"darwin-arm64": {
|
|
8
|
-
assetName: "anon-beta-macos-arm64.zip",
|
|
9
|
-
sha256: "3b8724afc56354aa93d2fe804d6b8a685d3bff65dac0ca3384cae1ef010977b2"
|
|
10
|
-
},
|
|
11
|
-
"darwin-x64": {
|
|
12
|
-
assetName: "anon-beta-macos-amd64.zip",
|
|
13
|
-
sha256: "aad277849b1e63baa75891b9e5109683534e488776ff190e884e34caa04a6d54"
|
|
14
|
-
},
|
|
15
|
-
"linux-x64": {
|
|
16
|
-
assetName: "anon-beta-linux-amd64.zip",
|
|
17
|
-
sha256: "370c86f366e7f4cad896e2ef4bbd366a4e78a832c8d58064012f86c88c411a6b"
|
|
18
|
-
},
|
|
19
|
-
"linux-arm64": {
|
|
20
|
-
assetName: "anon-beta-linux-arm64.zip",
|
|
21
|
-
sha256: "382d21db1052b6a0f1581bf38c9cf79b370719e313781c0eba53ef0d9570334a"
|
|
22
|
-
}
|
|
23
|
-
};
|
|
24
|
-
function selectAnonAsset(platform, arch) {
|
|
25
|
-
const key = `${platform}-${arch}`;
|
|
26
|
-
const asset = ANON_ASSETS[key];
|
|
27
|
-
if (!asset) {
|
|
28
|
-
throw new Error(
|
|
29
|
-
`No managed anon binary available for platform "${platform}" arch "${arch}". Supported: ${Object.keys(ANON_ASSETS).join(", ")}. Provide an explicit transport.socksProxy or set ANYONE_PROXY_URLS to use your own proxy.`
|
|
30
|
-
);
|
|
31
|
-
}
|
|
32
|
-
return asset;
|
|
33
|
-
}
|
|
34
|
-
function defaultCacheDir() {
|
|
35
|
-
const os = nodeRequire("node:os");
|
|
36
|
-
const path = nodeRequire("node:path");
|
|
37
|
-
const xdg = process.env["XDG_CACHE_HOME"];
|
|
38
|
-
if (xdg) {
|
|
39
|
-
return path.join(xdg, "toon-client", "anon", ANON_VERSION);
|
|
40
|
-
}
|
|
41
|
-
return path.join(os.homedir(), ".toon-client", "anon", ANON_VERSION);
|
|
42
|
-
}
|
|
43
|
-
function renderTorrc(cacheDir, socksPort) {
|
|
44
|
-
const path = nodeRequire("node:path");
|
|
45
|
-
return [
|
|
46
|
-
"AgreeToTerms 1",
|
|
47
|
-
`DataDirectory ${path.join(cacheDir, "data")}`,
|
|
48
|
-
`SOCKSPort 127.0.0.1:${socksPort}`,
|
|
49
|
-
"SOCKSPolicy accept *",
|
|
50
|
-
`GeoIPFile ${path.join(cacheDir, "geoip")}`,
|
|
51
|
-
`GeoIPv6File ${path.join(cacheDir, "geoip6")}`,
|
|
52
|
-
"Log notice stdout",
|
|
53
|
-
"RunAsDaemon 0",
|
|
54
|
-
""
|
|
55
|
-
].join("\n");
|
|
56
|
-
}
|
|
57
|
-
async function tcpProbe(host, port, timeoutMs) {
|
|
58
|
-
const net = nodeRequire("node:net");
|
|
59
|
-
return new Promise((resolve, reject) => {
|
|
60
|
-
const sock = net.createConnection({ host, port }, () => {
|
|
61
|
-
sock.destroy();
|
|
62
|
-
resolve();
|
|
63
|
-
});
|
|
64
|
-
sock.once("error", (err) => {
|
|
65
|
-
sock.destroy();
|
|
66
|
-
reject(err);
|
|
67
|
-
});
|
|
68
|
-
sock.setTimeout(timeoutMs, () => {
|
|
69
|
-
sock.destroy();
|
|
70
|
-
reject(new Error("timeout"));
|
|
71
|
-
});
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
async function sha256File(filePath) {
|
|
75
|
-
const fs = nodeRequire("node:fs");
|
|
76
|
-
const crypto = nodeRequire("node:crypto");
|
|
77
|
-
return new Promise((resolve, reject) => {
|
|
78
|
-
const hash = crypto.createHash("sha256");
|
|
79
|
-
const stream = fs.createReadStream(filePath);
|
|
80
|
-
stream.on("error", reject);
|
|
81
|
-
stream.on("data", (chunk) => hash.update(chunk));
|
|
82
|
-
stream.on("end", () => resolve(hash.digest("hex")));
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
async function downloadToFile(url, destPath) {
|
|
86
|
-
const fs = nodeRequire("node:fs");
|
|
87
|
-
const https = nodeRequire("node:https");
|
|
88
|
-
const fetchOnce = (u, redirectsLeft) => new Promise((resolve, reject) => {
|
|
89
|
-
const req = https.get(u, (res) => {
|
|
90
|
-
const status = res.statusCode ?? 0;
|
|
91
|
-
if (status >= 300 && status < 400 && res.headers.location) {
|
|
92
|
-
res.resume();
|
|
93
|
-
if (redirectsLeft <= 0) {
|
|
94
|
-
reject(new Error(`Too many redirects downloading ${url}`));
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
resolve(fetchOnce(res.headers.location, redirectsLeft - 1));
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
if (status !== 200) {
|
|
101
|
-
res.resume();
|
|
102
|
-
reject(new Error(`Download failed (HTTP ${status}) for ${u}`));
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
const out = fs.createWriteStream(destPath);
|
|
106
|
-
res.pipe(out);
|
|
107
|
-
out.on("error", reject);
|
|
108
|
-
out.on("finish", () => out.close(() => resolve()));
|
|
109
|
-
});
|
|
110
|
-
req.on("error", reject);
|
|
111
|
-
req.setTimeout(12e4, () => {
|
|
112
|
-
req.destroy(new Error(`Download timeout for ${u}`));
|
|
113
|
-
});
|
|
114
|
-
});
|
|
115
|
-
await fetchOnce(url, 5);
|
|
116
|
-
}
|
|
117
|
-
async function extractZip(zipPath, destDir) {
|
|
118
|
-
const cp = nodeRequire("node:child_process");
|
|
119
|
-
await new Promise((resolve, reject) => {
|
|
120
|
-
const child = cp.spawn("unzip", ["-o", zipPath, "-d", destDir], {
|
|
121
|
-
stdio: ["ignore", "ignore", "pipe"]
|
|
122
|
-
});
|
|
123
|
-
let stderr = "";
|
|
124
|
-
child.stderr?.on("data", (d) => {
|
|
125
|
-
stderr += d.toString();
|
|
126
|
-
});
|
|
127
|
-
child.on(
|
|
128
|
-
"error",
|
|
129
|
-
(err) => reject(
|
|
130
|
-
new Error(`Failed to spawn unzip (is it installed?): ${err.message}`)
|
|
131
|
-
)
|
|
132
|
-
);
|
|
133
|
-
child.on("exit", (code) => {
|
|
134
|
-
if (code === 0) resolve();
|
|
135
|
-
else
|
|
136
|
-
reject(
|
|
137
|
-
new Error(`unzip exited ${code} extracting ${zipPath}: ${stderr}`)
|
|
138
|
-
);
|
|
139
|
-
});
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
async function ensureAnonBinary(opts) {
|
|
143
|
-
const fs = nodeRequire("node:fs");
|
|
144
|
-
const path = nodeRequire("node:path");
|
|
145
|
-
const download = opts.download ?? downloadToFile;
|
|
146
|
-
const extract = opts.extract ?? extractZip;
|
|
147
|
-
const asset = selectAnonAsset(opts.platform, opts.arch);
|
|
148
|
-
const anonPath = path.join(opts.cacheDir, "anon");
|
|
149
|
-
if (fs.existsSync(anonPath)) {
|
|
150
|
-
return anonPath;
|
|
151
|
-
}
|
|
152
|
-
if (asset.sha256 === null) {
|
|
153
|
-
throw new Error(
|
|
154
|
-
`Managed anon binary for "${opts.platform}-${opts.arch}" (${asset.assetName}) has no pinned checksum yet (see issue #204). Provide an explicit transport.socksProxy to use your own proxy.`
|
|
155
|
-
);
|
|
156
|
-
}
|
|
157
|
-
fs.mkdirSync(opts.cacheDir, { recursive: true });
|
|
158
|
-
const zipPath = path.join(opts.cacheDir, asset.assetName);
|
|
159
|
-
const url = `${RELEASE_BASE}/${asset.assetName}`;
|
|
160
|
-
await download(url, zipPath);
|
|
161
|
-
const actual = await sha256File(zipPath);
|
|
162
|
-
if (actual !== asset.sha256) {
|
|
163
|
-
try {
|
|
164
|
-
fs.rmSync(zipPath, { force: true });
|
|
165
|
-
} catch {
|
|
166
|
-
}
|
|
167
|
-
throw new Error(
|
|
168
|
-
`Checksum mismatch for ${asset.assetName}: expected ${asset.sha256}, got ${actual}. Refusing to run an unverified anon binary.`
|
|
169
|
-
);
|
|
170
|
-
}
|
|
171
|
-
await extract(zipPath, opts.cacheDir);
|
|
172
|
-
if (!fs.existsSync(anonPath)) {
|
|
173
|
-
throw new Error(
|
|
174
|
-
`Extraction of ${asset.assetName} did not produce an "anon" binary at ${anonPath}.`
|
|
175
|
-
);
|
|
176
|
-
}
|
|
177
|
-
try {
|
|
178
|
-
fs.chmodSync(anonPath, 493);
|
|
179
|
-
} catch {
|
|
180
|
-
}
|
|
181
|
-
return anonPath;
|
|
182
|
-
}
|
|
183
|
-
async function waitForAnonSocks(opts) {
|
|
184
|
-
const probe = opts.probe ?? tcpProbe;
|
|
185
|
-
const sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
186
|
-
opts.log(`[anon] waiting for SOCKS5 bind on 127.0.0.1:${opts.port}\u2026`);
|
|
187
|
-
let lastErr = null;
|
|
188
|
-
while (Date.now() < opts.deadlineMs) {
|
|
189
|
-
if (opts.childExited()) {
|
|
190
|
-
throw new Error("[anon] process exited before SOCKS5 port bound");
|
|
191
|
-
}
|
|
192
|
-
try {
|
|
193
|
-
await probe("127.0.0.1", opts.port, 2e3);
|
|
194
|
-
opts.log(`[anon] SOCKS5 bound on 127.0.0.1:${opts.port}`);
|
|
195
|
-
return;
|
|
196
|
-
} catch (err) {
|
|
197
|
-
const msg = err.message;
|
|
198
|
-
if (msg !== lastErr) {
|
|
199
|
-
opts.log(`[anon] SOCKS5 not ready: ${msg}`);
|
|
200
|
-
lastErr = msg;
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
await sleep(2e3);
|
|
204
|
-
}
|
|
205
|
-
throw new Error(
|
|
206
|
-
`[anon] SOCKS5 never bound on 127.0.0.1:${opts.port} by deadline`
|
|
207
|
-
);
|
|
208
|
-
}
|
|
209
|
-
async function startManagedAnonProxy(options = {}) {
|
|
210
|
-
const fs = nodeRequire("node:fs");
|
|
211
|
-
const path = nodeRequire("node:path");
|
|
212
|
-
const os = nodeRequire("node:os");
|
|
213
|
-
const cp = nodeRequire("node:child_process");
|
|
214
|
-
const platform = options.platform ?? os.platform();
|
|
215
|
-
const arch = options.arch ?? os.arch();
|
|
216
|
-
const cacheDir = options.cacheDir ?? defaultCacheDir();
|
|
217
|
-
const socksPort = options.socksPort ?? 9050;
|
|
218
|
-
const bootstrapTimeoutMs = options.bootstrapTimeoutMs ?? 18e4;
|
|
219
|
-
const log = options.log ?? (() => {
|
|
220
|
-
});
|
|
221
|
-
const anonPath = await ensureAnonBinary({ cacheDir, platform, arch });
|
|
222
|
-
fs.mkdirSync(path.join(cacheDir, "data"), { recursive: true });
|
|
223
|
-
const torrcPath = path.join(cacheDir, "torrc");
|
|
224
|
-
fs.writeFileSync(torrcPath, renderTorrc(cacheDir, socksPort), {
|
|
225
|
-
mode: 420
|
|
226
|
-
});
|
|
227
|
-
log(`[anon] spawning: ${anonPath} -f ${torrcPath}`);
|
|
228
|
-
const child = cp.spawn(anonPath, ["-f", torrcPath], {
|
|
229
|
-
stdio: ["ignore", "inherit", "inherit"],
|
|
230
|
-
detached: false
|
|
231
|
-
});
|
|
232
|
-
let exited = false;
|
|
233
|
-
child.on("exit", (code, signal) => {
|
|
234
|
-
exited = true;
|
|
235
|
-
log(`[anon] child exited code=${code} signal=${signal}`);
|
|
236
|
-
});
|
|
237
|
-
child.on("error", (err) => {
|
|
238
|
-
log(`[anon] spawn error: ${err.message}`);
|
|
239
|
-
});
|
|
240
|
-
const stop = async () => {
|
|
241
|
-
if (!child.killed && !exited) {
|
|
242
|
-
try {
|
|
243
|
-
child.kill("SIGTERM");
|
|
244
|
-
} catch {
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
};
|
|
248
|
-
try {
|
|
249
|
-
await waitForAnonSocks({
|
|
250
|
-
port: socksPort,
|
|
251
|
-
deadlineMs: Date.now() + bootstrapTimeoutMs,
|
|
252
|
-
childExited: () => exited,
|
|
253
|
-
log
|
|
254
|
-
});
|
|
255
|
-
} catch (err) {
|
|
256
|
-
await stop();
|
|
257
|
-
throw err;
|
|
258
|
-
}
|
|
259
|
-
return {
|
|
260
|
-
socksProxy: `socks5h://127.0.0.1:${socksPort}`,
|
|
261
|
-
stop
|
|
262
|
-
};
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
export {
|
|
266
|
-
ANON_VERSION,
|
|
267
|
-
ANON_ASSETS,
|
|
268
|
-
selectAnonAsset,
|
|
269
|
-
defaultCacheDir,
|
|
270
|
-
renderTorrc,
|
|
271
|
-
tcpProbe,
|
|
272
|
-
ensureAnonBinary,
|
|
273
|
-
waitForAnonSocks,
|
|
274
|
-
startManagedAnonProxy
|
|
275
|
-
};
|
|
276
|
-
//# sourceMappingURL=chunk-WHAEQLIW.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/transport/anon-proxy.ts"],"sourcesContent":["/**\n * Self-managed `anon` (anyone-protocol / ATOR) SOCKS5h proxy (Node.js only).\n *\n * Lets a `@toon-protocol/client` consumer reach a `.anyone` hidden service with\n * ZERO manual proxy setup: the SDK downloads, verifies, extracts, and spawns its\n * own `anon` daemon, waits for it to bootstrap + bind a loopback SOCKS5 port, and\n * hands back a `socks5h://127.0.0.1:<port>` URL. The proven reference is the\n * server-side pod entrypoint `docker/src/entrypoint-toon-client.ts` (`writeTorrc`,\n * `spawnAnon`, `waitForAnonSocks`, `tcpProbe`); this module ports that daemon\n * logic into the client package and adds the binary download + checksum gate so it\n * works without an OS-level `anon` install.\n *\n * BROWSER SAFETY: this module is dynamically imported only from `resolveTransport`\n * when a managed proxy is actually needed (Node-only path). Every Node built-in is\n * pulled in lazily via the ESM-safe `require(...)` built off `import.meta.url`\n * (the same pattern as `socks5.ts`), so a browser bundler that statically analyses\n * the package never reaches `node:child_process`/`node:fs`/`node:https`/`node:net`.\n */\n\nimport { createRequire } from 'node:module';\nimport type childProcessModule from 'node:child_process';\nimport type fsModule from 'node:fs';\nimport type netModule from 'node:net';\nimport type osModule from 'node:os';\nimport type pathModule from 'node:path';\nimport type httpsModule from 'node:https';\nimport type * as cryptoModule from 'node:crypto';\n\n// ESM-safe require — see socks5.ts for the full rationale. The published bundle\n// is ESM with Node built-ins external; a bare `require` would be rewritten into a\n// throwing `__require` shim. Building a real require off import.meta.url keeps the\n// synchronous, browser-guarded `require(...)` calls below working. This file is\n// only ever dynamically imported on the Node path, so browser bundlers that tree-\n// shake the static graph never include it.\nconst nodeRequire = createRequire(import.meta.url);\n\n/**\n * Pinned `anon` release. \"beta\" is the channel slug embedded in the per-platform\n * zip asset names (e.g. `anon-beta-macos-arm64.zip`).\n */\nexport const ANON_VERSION = 'v0.4.10.0-beta';\n\nconst RELEASE_BASE = `https://github.com/anyone-protocol/ator-protocol/releases/download/${ANON_VERSION}`;\n\n/**\n * Per-platform `anon` zip asset descriptor. `sha256` is the pinned checksum of the\n * release zip. All supported platforms are pinned (issue #204); the type stays\n * `string | null` and the download gate still defensively refuses a `null` entry,\n * so adding a new (not-yet-hashed) platform fails closed rather than skipping\n * verification.\n */\nexport interface AnonAsset {\n /** Release asset file name, e.g. `anon-beta-macos-arm64.zip`. */\n assetName: string;\n /** Pinned sha256 of the zip, or null when not yet pinned (issue #204). */\n sha256: string | null;\n}\n\n/**\n * Platform → asset map keyed by `${os.platform()}-${os.arch()}` (Node values).\n * Only macOS + Linux on x64/arm64 are supported (the `anon` releases that ship a\n * SOCKS-capable binary). Windows is intentionally absent.\n *\n * Pinned checksums (issue #204): all four supported platforms are pinned to the\n * sha256 of the `v0.4.10.0-beta` release zips (downloaded + hashed; the\n * darwin-arm64 value matches the previously-verified manual flow).\n */\nexport const ANON_ASSETS: Record<string, AnonAsset> = {\n 'darwin-arm64': {\n assetName: 'anon-beta-macos-arm64.zip',\n sha256: '3b8724afc56354aa93d2fe804d6b8a685d3bff65dac0ca3384cae1ef010977b2',\n },\n 'darwin-x64': {\n assetName: 'anon-beta-macos-amd64.zip',\n sha256: 'aad277849b1e63baa75891b9e5109683534e488776ff190e884e34caa04a6d54',\n },\n 'linux-x64': {\n assetName: 'anon-beta-linux-amd64.zip',\n sha256: '370c86f366e7f4cad896e2ef4bbd366a4e78a832c8d58064012f86c88c411a6b',\n },\n 'linux-arm64': {\n assetName: 'anon-beta-linux-arm64.zip',\n sha256: '382d21db1052b6a0f1581bf38c9cf79b370719e313781c0eba53ef0d9570334a',\n },\n};\n\n/**\n * Resolves the `anon` release asset for a platform/arch pair (Node\n * `os.platform()` / `os.arch()` values).\n *\n * @throws If the platform/arch combination has no known `anon` asset.\n */\nexport function selectAnonAsset(platform: string, arch: string): AnonAsset {\n const key = `${platform}-${arch}`;\n const asset = ANON_ASSETS[key];\n if (!asset) {\n throw new Error(\n `No managed anon binary available for platform \"${platform}\" arch \"${arch}\". ` +\n `Supported: ${Object.keys(ANON_ASSETS).join(', ')}. ` +\n 'Provide an explicit transport.socksProxy or set ANYONE_PROXY_URLS to use your own proxy.'\n );\n }\n return asset;\n}\n\n/**\n * Default cache directory for the downloaded/extracted `anon` binary.\n * Honours `XDG_CACHE_HOME`; otherwise `~/.toon-client/anon`.\n */\nexport function defaultCacheDir(): string {\n const os = nodeRequire('node:os') as typeof osModule;\n const path = nodeRequire('node:path') as typeof pathModule;\n const xdg = process.env['XDG_CACHE_HOME'];\n if (xdg) {\n return path.join(xdg, 'toon-client', 'anon', ANON_VERSION);\n }\n return path.join(os.homedir(), '.toon-client', 'anon', ANON_VERSION);\n}\n\n/**\n * Renders a SOCKS-only torrc. Mirrors `writeTorrc` in the proven docker\n * entrypoint. `AgreeToTerms 1` is REQUIRED — omitting it makes `anon` exit\n * immediately.\n */\nexport function renderTorrc(cacheDir: string, socksPort: number): string {\n const path = nodeRequire('node:path') as typeof pathModule;\n return [\n 'AgreeToTerms 1',\n `DataDirectory ${path.join(cacheDir, 'data')}`,\n `SOCKSPort 127.0.0.1:${socksPort}`,\n 'SOCKSPolicy accept *',\n `GeoIPFile ${path.join(cacheDir, 'geoip')}`,\n `GeoIPv6File ${path.join(cacheDir, 'geoip6')}`,\n 'Log notice stdout',\n 'RunAsDaemon 0',\n '',\n ].join('\\n');\n}\n\n/**\n * Simple TCP connect probe — confirms the SOCKS5 port has bound and accepts\n * connections. Mirrors `tcpProbe` in the docker entrypoint / `probeSocks5Proxy`.\n */\nexport async function tcpProbe(\n host: string,\n port: number,\n timeoutMs: number\n): Promise<void> {\n const net = nodeRequire('node:net') as typeof netModule;\n return new Promise<void>((resolve, reject) => {\n const sock = net.createConnection({ host, port }, () => {\n sock.destroy();\n resolve();\n });\n sock.once('error', (err: Error) => {\n sock.destroy();\n reject(err);\n });\n sock.setTimeout(timeoutMs, () => {\n sock.destroy();\n reject(new Error('timeout'));\n });\n });\n}\n\n/**\n * Computes the sha256 (hex) of a file using node:crypto streaming.\n */\nasync function sha256File(filePath: string): Promise<string> {\n const fs = nodeRequire('node:fs') as typeof fsModule;\n const crypto = nodeRequire('node:crypto') as typeof cryptoModule;\n return new Promise<string>((resolve, reject) => {\n const hash = crypto.createHash('sha256');\n const stream = fs.createReadStream(filePath);\n stream.on('error', reject);\n stream.on('data', (chunk) => hash.update(chunk));\n stream.on('end', () => resolve(hash.digest('hex')));\n });\n}\n\n/**\n * Downloads a URL to a file, following GitHub release redirects. Node-only\n * (node:https + node:fs).\n */\nasync function downloadToFile(url: string, destPath: string): Promise<void> {\n const fs = nodeRequire('node:fs') as typeof fsModule;\n const https = nodeRequire('node:https') as typeof httpsModule;\n\n const fetchOnce = (u: string, redirectsLeft: number): Promise<void> =>\n new Promise<void>((resolve, reject) => {\n const req = https.get(u, (res) => {\n const status = res.statusCode ?? 0;\n // GitHub release assets redirect to a signed S3 URL.\n if (status >= 300 && status < 400 && res.headers.location) {\n res.resume();\n if (redirectsLeft <= 0) {\n reject(new Error(`Too many redirects downloading ${url}`));\n return;\n }\n resolve(fetchOnce(res.headers.location, redirectsLeft - 1));\n return;\n }\n if (status !== 200) {\n res.resume();\n reject(new Error(`Download failed (HTTP ${status}) for ${u}`));\n return;\n }\n const out = fs.createWriteStream(destPath);\n res.pipe(out);\n out.on('error', reject);\n out.on('finish', () => out.close(() => resolve()));\n });\n req.on('error', reject);\n req.setTimeout(120_000, () => {\n req.destroy(new Error(`Download timeout for ${u}`));\n });\n });\n\n await fetchOnce(url, 5);\n}\n\n/**\n * Extracts a zip into a directory by shelling out to the system `unzip` binary\n * (present on macOS + Linux). Kept here (not a JS unzip dep) to avoid adding a\n * runtime dependency to the browser-facing client package.\n */\nasync function extractZip(zipPath: string, destDir: string): Promise<void> {\n const cp = nodeRequire('node:child_process') as typeof childProcessModule;\n await new Promise<void>((resolve, reject) => {\n const child = cp.spawn('unzip', ['-o', zipPath, '-d', destDir], {\n stdio: ['ignore', 'ignore', 'pipe'],\n });\n let stderr = '';\n child.stderr?.on('data', (d: Buffer) => {\n stderr += d.toString();\n });\n child.on('error', (err: Error) =>\n reject(\n new Error(`Failed to spawn unzip (is it installed?): ${err.message}`)\n )\n );\n child.on('exit', (code) => {\n if (code === 0) resolve();\n else\n reject(\n new Error(`unzip exited ${code} extracting ${zipPath}: ${stderr}`)\n );\n });\n });\n}\n\n/**\n * Ensures a verified `anon` binary exists in the cache directory, downloading +\n * checksum-verifying + extracting it if not. Returns the absolute path to the\n * extracted `anon` executable.\n *\n * Skips re-download when a previously extracted `anon` binary is already present\n * (the checksum gate runs on the freshly downloaded zip; an already-extracted\n * binary in a version-pinned cache dir is trusted).\n */\nexport async function ensureAnonBinary(opts: {\n cacheDir: string;\n platform: string;\n arch: string;\n /** Injectable downloader (tests). Default: node:https GET with redirects. */\n download?: (url: string, destPath: string) => Promise<void>;\n /** Injectable extractor (tests). Default: shell out to `unzip`. */\n extract?: (zipPath: string, destDir: string) => Promise<void>;\n}): Promise<string> {\n const fs = nodeRequire('node:fs') as typeof fsModule;\n const path = nodeRequire('node:path') as typeof pathModule;\n\n const download = opts.download ?? downloadToFile;\n const extract = opts.extract ?? extractZip;\n\n const asset = selectAnonAsset(opts.platform, opts.arch);\n const anonPath = path.join(opts.cacheDir, 'anon');\n\n // Fast path: already extracted (version-pinned cache dir).\n if (fs.existsSync(anonPath)) {\n return anonPath;\n }\n\n if (asset.sha256 === null) {\n throw new Error(\n `Managed anon binary for \"${opts.platform}-${opts.arch}\" ` +\n `(${asset.assetName}) has no pinned checksum yet (see issue #204). ` +\n 'Provide an explicit transport.socksProxy to use your own proxy.'\n );\n }\n\n fs.mkdirSync(opts.cacheDir, { recursive: true });\n const zipPath = path.join(opts.cacheDir, asset.assetName);\n const url = `${RELEASE_BASE}/${asset.assetName}`;\n\n await download(url, zipPath);\n\n const actual = await sha256File(zipPath);\n if (actual !== asset.sha256) {\n // Remove the bad artifact so a retry re-downloads cleanly.\n try {\n fs.rmSync(zipPath, { force: true });\n } catch {\n /* best-effort cleanup */\n }\n throw new Error(\n `Checksum mismatch for ${asset.assetName}: expected ${asset.sha256}, got ${actual}. ` +\n 'Refusing to run an unverified anon binary.'\n );\n }\n\n await extract(zipPath, opts.cacheDir);\n\n if (!fs.existsSync(anonPath)) {\n throw new Error(\n `Extraction of ${asset.assetName} did not produce an \"anon\" binary at ${anonPath}.`\n );\n }\n // Ensure executable (zip may not preserve the bit on all platforms).\n try {\n fs.chmodSync(anonPath, 0o755);\n } catch {\n /* best-effort */\n }\n return anonPath;\n}\n\n/**\n * Polls for the SOCKS5 port to bind. `anon` typically takes 30-90s to bootstrap\n * (build a circuit + consensus) before SOCKS5 accepts connections. Mirrors\n * `waitForAnonSocks` in the docker entrypoint, but also fails fast if the child\n * exits before binding.\n */\nexport async function waitForAnonSocks(opts: {\n port: number;\n deadlineMs: number;\n childExited: () => boolean;\n log: (msg: string) => void;\n probe?: (host: string, port: number, timeoutMs: number) => Promise<void>;\n sleep?: (ms: number) => Promise<void>;\n}): Promise<void> {\n const probe = opts.probe ?? tcpProbe;\n const sleep =\n opts.sleep ?? ((ms: number) => new Promise((r) => setTimeout(r, ms)));\n opts.log(`[anon] waiting for SOCKS5 bind on 127.0.0.1:${opts.port}…`);\n let lastErr: string | null = null;\n while (Date.now() < opts.deadlineMs) {\n if (opts.childExited()) {\n throw new Error('[anon] process exited before SOCKS5 port bound');\n }\n try {\n await probe('127.0.0.1', opts.port, 2_000);\n opts.log(`[anon] SOCKS5 bound on 127.0.0.1:${opts.port}`);\n return;\n } catch (err) {\n const msg = (err as Error).message;\n if (msg !== lastErr) {\n opts.log(`[anon] SOCKS5 not ready: ${msg}`);\n lastErr = msg;\n }\n }\n await sleep(2_000);\n }\n throw new Error(\n `[anon] SOCKS5 never bound on 127.0.0.1:${opts.port} by deadline`\n );\n}\n\n/**\n * Handle returned by `startManagedAnonProxy`. `socksProxy` is the loopback\n * `socks5h://` URL to wire into `transport: { type: 'socks5', socksProxy }`.\n * `stop()` SIGTERMs the daemon and is idempotent.\n */\nexport interface ManagedAnonProxy {\n socksProxy: string;\n stop(): Promise<void>;\n}\n\n/**\n * Options for `startManagedAnonProxy`. All have sensible defaults; tests inject\n * the deps to avoid real downloads/spawns.\n */\nexport interface StartManagedAnonProxyOptions {\n /** Cache dir for the binary + torrc + data. Default: {@link defaultCacheDir}. */\n cacheDir?: string;\n /** Loopback SOCKS5 port. Default 9050. */\n socksPort?: number;\n /** Bootstrap deadline in ms. Default 180_000. */\n bootstrapTimeoutMs?: number;\n /** Logger. Default: no-op. */\n log?: (msg: string) => void;\n /** os.platform() override (tests). */\n platform?: string;\n /** os.arch() override (tests). */\n arch?: string;\n}\n\n/**\n * Downloads (if needed) + spawns a managed `anon` daemon and waits for its SOCKS5\n * port to bind. Returns a {@link ManagedAnonProxy} whose `socksProxy` is ready for\n * `transport: { type: 'socks5', socksProxy }`.\n *\n * @throws If the platform is unsupported, the checksum fails, or anon never binds.\n */\nexport async function startManagedAnonProxy(\n options: StartManagedAnonProxyOptions = {}\n): Promise<ManagedAnonProxy> {\n const fs = nodeRequire('node:fs') as typeof fsModule;\n const path = nodeRequire('node:path') as typeof pathModule;\n const os = nodeRequire('node:os') as typeof osModule;\n const cp = nodeRequire('node:child_process') as typeof childProcessModule;\n\n const platform = options.platform ?? os.platform();\n const arch = options.arch ?? os.arch();\n const cacheDir = options.cacheDir ?? defaultCacheDir();\n const socksPort = options.socksPort ?? 9050;\n const bootstrapTimeoutMs = options.bootstrapTimeoutMs ?? 180_000;\n const log =\n options.log ??\n ((): void => {\n /* default: silent */\n });\n\n const anonPath = await ensureAnonBinary({ cacheDir, platform, arch });\n\n // Write the SOCKS-only torrc.\n fs.mkdirSync(path.join(cacheDir, 'data'), { recursive: true });\n const torrcPath = path.join(cacheDir, 'torrc');\n fs.writeFileSync(torrcPath, renderTorrc(cacheDir, socksPort), {\n mode: 0o644,\n });\n\n log(`[anon] spawning: ${anonPath} -f ${torrcPath}`);\n const child = cp.spawn(anonPath, ['-f', torrcPath], {\n stdio: ['ignore', 'inherit', 'inherit'],\n detached: false,\n });\n let exited = false;\n child.on('exit', (code, signal) => {\n exited = true;\n log(`[anon] child exited code=${code} signal=${signal}`);\n });\n child.on('error', (err: Error) => {\n log(`[anon] spawn error: ${err.message}`);\n });\n\n const stop = async (): Promise<void> => {\n if (!child.killed && !exited) {\n try {\n child.kill('SIGTERM');\n } catch {\n /* best-effort */\n }\n }\n };\n\n try {\n await waitForAnonSocks({\n port: socksPort,\n deadlineMs: Date.now() + bootstrapTimeoutMs,\n childExited: () => exited,\n log,\n });\n } catch (err) {\n await stop();\n throw err;\n }\n\n return {\n socksProxy: `socks5h://127.0.0.1:${socksPort}`,\n stop,\n };\n}\n"],"mappings":"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","names":[]}
|
package/dist/gateway-QOK47RKS.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
// src/transport/gateway.ts
|
|
2
|
-
function rewriteUrlsForGateway(gatewayUrl, btpUrl, connectorUrl) {
|
|
3
|
-
const base = gatewayUrl.replace(/\/$/, "");
|
|
4
|
-
const wsBase = base.replace(/^https:/, "wss:").replace(/^http:/, "ws:");
|
|
5
|
-
return {
|
|
6
|
-
btpUrl: btpUrl ? `${wsBase}/btp` : void 0,
|
|
7
|
-
connectorUrl: connectorUrl ? `${base}/api` : void 0
|
|
8
|
-
};
|
|
9
|
-
}
|
|
10
|
-
export {
|
|
11
|
-
rewriteUrlsForGateway
|
|
12
|
-
};
|
|
13
|
-
//# sourceMappingURL=gateway-QOK47RKS.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/transport/gateway.ts"],"sourcesContent":["/**\n * Gateway transport for browser environments.\n *\n * Rewrites BTP and HTTP URLs to route through an ator gateway that handles\n * SOCKS5 proxying server-side. Browser clients connect to the gateway via\n * standard WebSocket/HTTP — no special transport code needed.\n */\n\n/**\n * Rewrites btpUrl and connectorUrl to route through a gateway.\n *\n * Gateway endpoint conventions:\n * - WebSocket: `ws(s)://<gateway>/btp` — proxies BTP connections\n * - HTTP: `http(s)://<gateway>/api` — proxies connector admin API\n *\n * @param gatewayUrl - Base URL of the ator gateway (http:// or https://)\n * @param btpUrl - Original BTP WebSocket URL (optional)\n * @param connectorUrl - Original connector HTTP URL (optional)\n */\nexport function rewriteUrlsForGateway(\n gatewayUrl: string,\n btpUrl?: string,\n connectorUrl?: string\n): { btpUrl?: string; connectorUrl?: string } {\n const base = gatewayUrl.replace(/\\/$/, '');\n\n // Derive WebSocket scheme from HTTP scheme\n const wsBase = base.replace(/^https:/, 'wss:').replace(/^http:/, 'ws:');\n\n return {\n btpUrl: btpUrl ? `${wsBase}/btp` : undefined,\n connectorUrl: connectorUrl ? `${base}/api` : undefined,\n };\n}\n"],"mappings":";AAmBO,SAAS,sBACd,YACA,QACA,cAC4C;AAC5C,QAAM,OAAO,WAAW,QAAQ,OAAO,EAAE;AAGzC,QAAM,SAAS,KAAK,QAAQ,WAAW,MAAM,EAAE,QAAQ,UAAU,KAAK;AAEtE,SAAO;AAAA,IACL,QAAQ,SAAS,GAAG,MAAM,SAAS;AAAA,IACnC,cAAc,eAAe,GAAG,IAAI,SAAS;AAAA,EAC/C;AACF;","names":[]}
|
package/dist/socks5-WTJBYGME.js
DELETED
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
// src/transport/socks5.ts
|
|
2
|
-
import { createConnection } from "net";
|
|
3
|
-
import { createRequire } from "module";
|
|
4
|
-
var require2 = createRequire(import.meta.url);
|
|
5
|
-
function validateSocks5hUrl(socksProxy) {
|
|
6
|
-
if (!socksProxy.startsWith("socks5h://")) {
|
|
7
|
-
throw new Error(
|
|
8
|
-
`SOCKS5 proxy URL must use socks5h:// scheme (got "${socksProxy.split("://")[0]}://"). The "h" suffix ensures DNS resolution happens at the proxy, preventing leaks of .anyone hostnames.`
|
|
9
|
-
);
|
|
10
|
-
}
|
|
11
|
-
const httpUrl = socksProxy.replace(/^socks5h:\/\//, "http://");
|
|
12
|
-
let parsed;
|
|
13
|
-
try {
|
|
14
|
-
parsed = new URL(httpUrl);
|
|
15
|
-
} catch {
|
|
16
|
-
throw new Error(`Malformed SOCKS5 proxy URL: "${socksProxy}"`);
|
|
17
|
-
}
|
|
18
|
-
const host = parsed.hostname;
|
|
19
|
-
const port = parsed.port ? parseInt(parsed.port, 10) : 1080;
|
|
20
|
-
if (!host) {
|
|
21
|
-
throw new Error(`SOCKS5 proxy URL missing host: "${socksProxy}"`);
|
|
22
|
-
}
|
|
23
|
-
if (port < 0 || port > 65535 || !Number.isFinite(port)) {
|
|
24
|
-
throw new Error(`SOCKS5 proxy port out of range (0\u201365535): ${parsed.port}`);
|
|
25
|
-
}
|
|
26
|
-
return { host, port };
|
|
27
|
-
}
|
|
28
|
-
function createSocks5WebSocketFactory(socksProxy) {
|
|
29
|
-
validateSocks5hUrl(socksProxy);
|
|
30
|
-
const { SocksProxyAgent } = require2("socks-proxy-agent");
|
|
31
|
-
const WS = require2("ws");
|
|
32
|
-
const agent = new SocksProxyAgent(socksProxy, { timeout: 12e4 });
|
|
33
|
-
const ws = WS;
|
|
34
|
-
const WSClass = typeof ws === "function" ? ws : typeof ws.default === "function" ? ws.default : typeof ws.WebSocket === "function" ? ws.WebSocket : null;
|
|
35
|
-
if (WSClass === null) {
|
|
36
|
-
throw new Error(
|
|
37
|
-
"createSocks5WebSocketFactory: require('ws') did not yield a constructor on .default, .WebSocket, or the module root."
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
return (url) => (
|
|
41
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
42
|
-
new WSClass(url, { agent })
|
|
43
|
-
);
|
|
44
|
-
}
|
|
45
|
-
function createSocks5Fetch(socksProxy) {
|
|
46
|
-
validateSocks5hUrl(socksProxy);
|
|
47
|
-
const { SocksProxyAgent } = require2("socks-proxy-agent");
|
|
48
|
-
const http = require2("node:http");
|
|
49
|
-
const https = require2("node:https");
|
|
50
|
-
const agent = new SocksProxyAgent(socksProxy);
|
|
51
|
-
return (input, init) => {
|
|
52
|
-
const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
|
|
53
|
-
const parsedUrl = new URL(url);
|
|
54
|
-
const isHttps = parsedUrl.protocol === "https:";
|
|
55
|
-
const transport = isHttps ? https : http;
|
|
56
|
-
return new Promise((resolve, reject) => {
|
|
57
|
-
const method = init?.method ?? "GET";
|
|
58
|
-
const headers = init?.headers ? Object.fromEntries(
|
|
59
|
-
init.headers instanceof Headers ? init.headers.entries() : Array.isArray(init.headers) ? init.headers : Object.entries(init.headers)
|
|
60
|
-
) : {};
|
|
61
|
-
const req = transport.request(
|
|
62
|
-
url,
|
|
63
|
-
{ method, headers, agent, timeout: 3e4 },
|
|
64
|
-
(res) => {
|
|
65
|
-
const chunks = [];
|
|
66
|
-
res.on("data", (chunk) => chunks.push(chunk));
|
|
67
|
-
res.on("end", () => {
|
|
68
|
-
const body = Buffer.concat(chunks);
|
|
69
|
-
const responseHeaders = new Headers();
|
|
70
|
-
for (const [key, val] of Object.entries(res.headers)) {
|
|
71
|
-
if (val)
|
|
72
|
-
responseHeaders.set(
|
|
73
|
-
key,
|
|
74
|
-
Array.isArray(val) ? val.join(", ") : val
|
|
75
|
-
);
|
|
76
|
-
}
|
|
77
|
-
resolve(
|
|
78
|
-
new Response(body, {
|
|
79
|
-
status: res.statusCode ?? 200,
|
|
80
|
-
statusText: res.statusMessage ?? "",
|
|
81
|
-
headers: responseHeaders
|
|
82
|
-
})
|
|
83
|
-
);
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
);
|
|
87
|
-
req.on("error", reject);
|
|
88
|
-
req.on("timeout", () => {
|
|
89
|
-
req.destroy();
|
|
90
|
-
reject(new Error("SOCKS5 proxied request timeout"));
|
|
91
|
-
});
|
|
92
|
-
if (init?.signal) {
|
|
93
|
-
init.signal.addEventListener("abort", () => {
|
|
94
|
-
req.destroy();
|
|
95
|
-
reject(new Error("Aborted"));
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
if (init?.body) {
|
|
99
|
-
req.write(typeof init.body === "string" ? init.body : init.body);
|
|
100
|
-
}
|
|
101
|
-
req.end();
|
|
102
|
-
});
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
async function probeSocks5Proxy(socksProxy, timeoutMs = 2e3) {
|
|
106
|
-
const { host, port } = validateSocks5hUrl(socksProxy);
|
|
107
|
-
return new Promise((resolve, reject) => {
|
|
108
|
-
const socket = createConnection({ host, port }, () => {
|
|
109
|
-
socket.destroy();
|
|
110
|
-
resolve();
|
|
111
|
-
});
|
|
112
|
-
socket.setTimeout(timeoutMs, () => {
|
|
113
|
-
socket.destroy();
|
|
114
|
-
reject(
|
|
115
|
-
new Error(
|
|
116
|
-
`SOCKS5 proxy at ${host}:${port} unreachable (timeout ${timeoutMs}ms). Refusing to start without privacy transport (fail-closed).`
|
|
117
|
-
)
|
|
118
|
-
);
|
|
119
|
-
});
|
|
120
|
-
socket.on("error", (err) => {
|
|
121
|
-
socket.destroy();
|
|
122
|
-
reject(
|
|
123
|
-
new Error(
|
|
124
|
-
`SOCKS5 proxy at ${host}:${port} unreachable: ${err.message}. Refusing to start without privacy transport (fail-closed).`
|
|
125
|
-
)
|
|
126
|
-
);
|
|
127
|
-
});
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
export {
|
|
131
|
-
createSocks5Fetch,
|
|
132
|
-
createSocks5WebSocketFactory,
|
|
133
|
-
probeSocks5Proxy,
|
|
134
|
-
validateSocks5hUrl
|
|
135
|
-
};
|
|
136
|
-
//# sourceMappingURL=socks5-WTJBYGME.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/transport/socks5.ts"],"sourcesContent":["/**\n * SOCKS5 transport helpers for Node.js environments.\n *\n * This module is dynamically imported only when `transport.type === 'socks5'`\n * is configured, keeping `ws` and `socks-proxy-agent` out of browser bundles.\n */\n\nimport { createConnection } from 'node:net';\nimport { createRequire } from 'node:module';\nimport type SocksProxyAgentModule from 'socks-proxy-agent';\nimport type WSModule from 'ws';\nimport type httpModule from 'node:http';\nimport type httpsModule from 'node:https';\n\n// ESM-safe require. This module builds as ESM (tsup `format: ['esm']`) with\n// `socks-proxy-agent`/`ws` external, so a bare `require(...)` would be rewritten\n// by esbuild into a `__require` shim that throws\n// `Dynamic require of \"socks-proxy-agent\" is not supported` for any pure-ESM\n// consumer — breaking the SOCKS5/ATOR transport entirely (the npm-consumer\n// toon-client image surfaced this). Building a real require off `import.meta.url`\n// (the same pattern as docker/esbuild.config.mjs's banner) keeps the\n// synchronous, browser-guarded `require(...)` calls below working in the\n// published ESM bundle while leaving the deps external. Browser bundlers never\n// reach this code: the module is dynamically imported only when\n// `transport.type === 'socks5'`, which is Node-only.\nconst require = createRequire(import.meta.url);\n\n/**\n * Parses and validates a `socks5h://` URL.\n * Enforces `socks5h://` scheme (not `socks5://`) to prevent DNS leaks —\n * `.anyone` hostnames must be resolved by the proxy, not locally.\n *\n * Mirrors the connector's `transport/socks-url.ts` validation logic.\n */\nexport function validateSocks5hUrl(socksProxy: string): {\n host: string;\n port: number;\n} {\n if (!socksProxy.startsWith('socks5h://')) {\n throw new Error(\n `SOCKS5 proxy URL must use socks5h:// scheme (got \"${socksProxy.split('://')[0]}://\"). ` +\n 'The \"h\" suffix ensures DNS resolution happens at the proxy, preventing leaks of .anyone hostnames.'\n );\n }\n\n // Parse by converting to http:// for URL constructor compatibility\n const httpUrl = socksProxy.replace(/^socks5h:\\/\\//, 'http://');\n let parsed: URL;\n try {\n parsed = new URL(httpUrl);\n } catch {\n throw new Error(`Malformed SOCKS5 proxy URL: \"${socksProxy}\"`);\n }\n\n const host = parsed.hostname;\n const port = parsed.port ? parseInt(parsed.port, 10) : 1080;\n\n if (!host) {\n throw new Error(`SOCKS5 proxy URL missing host: \"${socksProxy}\"`);\n }\n if (port < 0 || port > 65535 || !Number.isFinite(port)) {\n throw new Error(`SOCKS5 proxy port out of range (0–65535): ${parsed.port}`);\n }\n\n return { host, port };\n}\n\n/**\n * Creates a WebSocket factory that routes connections through a SOCKS5 proxy.\n * Uses the `ws` npm package (Node.js only) which accepts an `agent` option.\n */\nexport function createSocks5WebSocketFactory(\n socksProxy: string\n): (url: string) => WebSocket {\n validateSocks5hUrl(socksProxy);\n\n // Resolved via the module-scoped ESM-safe `require` (createRequire) above.\n const { SocksProxyAgent } =\n require('socks-proxy-agent') as typeof SocksProxyAgentModule;\n const WS = require('ws') as typeof WSModule;\n\n // 120s timeout: the socks library's default is 30s, which is too short for\n // the ATOR network to build circuits to fresh hidden services from certain\n // network paths (e.g., Akash datacenter → public ATOR proxy → local HS).\n // 120s gives the proxy time to find a working introduction-point circuit.\n const agent = new SocksProxyAgent(socksProxy, { timeout: 120_000 });\n\n // CJS/ESM interop: `require('ws')` can return any of three shapes depending on\n // the bundler/loader: the class directly (pure CJS); `{ default: WSClass, ... }`\n // (esModuleInterop=true / synthetic default); or `{ WebSocket: WSClass, ... }`\n // (named export, no default). Walk the ladder so this factory works in all\n // three environments. Pass 2 code review 2026-05-18: previous `(WS as any).default ?? WS`\n // would accept a namespace object as a \"constructor\" and throw cryptically.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const ws = WS as any;\n const WSClass = (typeof ws === 'function'\n ? ws\n : typeof ws.default === 'function'\n ? ws.default\n : typeof ws.WebSocket === 'function'\n ? ws.WebSocket\n : null) as unknown as typeof WSModule.prototype.constructor;\n if (WSClass === null) {\n throw new Error(\n \"createSocks5WebSocketFactory: require('ws') did not yield a constructor on .default, .WebSocket, or the module root.\"\n );\n }\n\n return (url: string) =>\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n new (WSClass as any)(url, { agent }) as unknown as WebSocket;\n}\n\n/**\n * Creates a fetch wrapper that routes HTTP requests through a SOCKS5 proxy.\n * Uses `socks-proxy-agent` with Node.js `http`/`https` modules (not native\n * fetch, which uses undici and doesn't support SocksProxyAgent's dispatcher).\n */\nexport function createSocks5Fetch(socksProxy: string): typeof fetch {\n validateSocks5hUrl(socksProxy);\n\n // Resolved via the module-scoped ESM-safe `require` (createRequire) above.\n const { SocksProxyAgent } =\n require('socks-proxy-agent') as typeof SocksProxyAgentModule;\n const http = require('node:http') as typeof httpModule;\n const https = require('node:https') as typeof httpsModule;\n\n const agent = new SocksProxyAgent(socksProxy);\n\n return (input: string | URL | Request, init?: RequestInit) => {\n const url =\n typeof input === 'string'\n ? input\n : input instanceof URL\n ? input.href\n : input.url;\n const parsedUrl = new URL(url);\n const isHttps = parsedUrl.protocol === 'https:';\n const transport = isHttps ? https : http;\n\n return new Promise<Response>((resolve, reject) => {\n const method = init?.method ?? 'GET';\n const headers = init?.headers\n ? Object.fromEntries(\n init.headers instanceof Headers\n ? init.headers.entries()\n : Array.isArray(init.headers)\n ? init.headers\n : Object.entries(init.headers)\n )\n : {};\n\n const req = transport.request(\n url,\n { method, headers, agent, timeout: 30_000 },\n (res) => {\n const chunks: Buffer[] = [];\n res.on('data', (chunk: Buffer) => chunks.push(chunk));\n res.on('end', () => {\n const body = Buffer.concat(chunks);\n const responseHeaders = new Headers();\n for (const [key, val] of Object.entries(res.headers)) {\n if (val)\n responseHeaders.set(\n key,\n Array.isArray(val) ? val.join(', ') : val\n );\n }\n resolve(\n new Response(body, {\n status: res.statusCode ?? 200,\n statusText: res.statusMessage ?? '',\n headers: responseHeaders,\n })\n );\n });\n }\n );\n\n req.on('error', reject);\n req.on('timeout', () => {\n req.destroy();\n reject(new Error('SOCKS5 proxied request timeout'));\n });\n\n if (init?.signal) {\n init.signal.addEventListener('abort', () => {\n req.destroy();\n reject(new Error('Aborted'));\n });\n }\n\n if (init?.body) {\n req.write(typeof init.body === 'string' ? init.body : init.body);\n }\n req.end();\n });\n };\n}\n\n/**\n * Probes SOCKS5 proxy reachability with a TCP connection test.\n * Fail-closed: throws if the proxy is unreachable within the timeout.\n *\n * @param socksProxy - `socks5h://host:port` URL\n * @param timeoutMs - Connection timeout in milliseconds (default: 2000)\n */\nexport async function probeSocks5Proxy(\n socksProxy: string,\n timeoutMs = 2000\n): Promise<void> {\n const { host, port } = validateSocks5hUrl(socksProxy);\n\n return new Promise<void>((resolve, reject) => {\n const socket = createConnection({ host, port }, () => {\n socket.destroy();\n resolve();\n });\n\n socket.setTimeout(timeoutMs, () => {\n socket.destroy();\n reject(\n new Error(\n `SOCKS5 proxy at ${host}:${port} unreachable (timeout ${timeoutMs}ms). ` +\n 'Refusing to start without privacy transport (fail-closed).'\n )\n );\n });\n\n socket.on('error', (err) => {\n socket.destroy();\n reject(\n new Error(\n `SOCKS5 proxy at ${host}:${port} unreachable: ${err.message}. ` +\n 'Refusing to start without privacy transport (fail-closed).'\n )\n );\n });\n });\n}\n"],"mappings":";AAOA,SAAS,wBAAwB;AACjC,SAAS,qBAAqB;AAiB9B,IAAMA,WAAU,cAAc,YAAY,GAAG;AAStC,SAAS,mBAAmB,YAGjC;AACA,MAAI,CAAC,WAAW,WAAW,YAAY,GAAG;AACxC,UAAM,IAAI;AAAA,MACR,qDAAqD,WAAW,MAAM,KAAK,EAAE,CAAC,CAAC;AAAA,IAEjF;AAAA,EACF;AAGA,QAAM,UAAU,WAAW,QAAQ,iBAAiB,SAAS;AAC7D,MAAI;AACJ,MAAI;AACF,aAAS,IAAI,IAAI,OAAO;AAAA,EAC1B,QAAQ;AACN,UAAM,IAAI,MAAM,gCAAgC,UAAU,GAAG;AAAA,EAC/D;AAEA,QAAM,OAAO,OAAO;AACpB,QAAM,OAAO,OAAO,OAAO,SAAS,OAAO,MAAM,EAAE,IAAI;AAEvD,MAAI,CAAC,MAAM;AACT,UAAM,IAAI,MAAM,mCAAmC,UAAU,GAAG;AAAA,EAClE;AACA,MAAI,OAAO,KAAK,OAAO,SAAS,CAAC,OAAO,SAAS,IAAI,GAAG;AACtD,UAAM,IAAI,MAAM,kDAA6C,OAAO,IAAI,EAAE;AAAA,EAC5E;AAEA,SAAO,EAAE,MAAM,KAAK;AACtB;AAMO,SAAS,6BACd,YAC4B;AAC5B,qBAAmB,UAAU;AAG7B,QAAM,EAAE,gBAAgB,IACtBA,SAAQ,mBAAmB;AAC7B,QAAM,KAAKA,SAAQ,IAAI;AAMvB,QAAM,QAAQ,IAAI,gBAAgB,YAAY,EAAE,SAAS,KAAQ,CAAC;AASlE,QAAM,KAAK;AACX,QAAM,UAAW,OAAO,OAAO,aAC3B,KACA,OAAO,GAAG,YAAY,aACpB,GAAG,UACH,OAAO,GAAG,cAAc,aACtB,GAAG,YACH;AACR,MAAI,YAAY,MAAM;AACpB,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,SAAO,CAAC;AAAA;AAAA,IAEN,IAAK,QAAgB,KAAK,EAAE,MAAM,CAAC;AAAA;AACvC;AAOO,SAAS,kBAAkB,YAAkC;AAClE,qBAAmB,UAAU;AAG7B,QAAM,EAAE,gBAAgB,IACtBA,SAAQ,mBAAmB;AAC7B,QAAM,OAAOA,SAAQ,WAAW;AAChC,QAAM,QAAQA,SAAQ,YAAY;AAElC,QAAM,QAAQ,IAAI,gBAAgB,UAAU;AAE5C,SAAO,CAAC,OAA+B,SAAuB;AAC5D,UAAM,MACJ,OAAO,UAAU,WACb,QACA,iBAAiB,MACf,MAAM,OACN,MAAM;AACd,UAAM,YAAY,IAAI,IAAI,GAAG;AAC7B,UAAM,UAAU,UAAU,aAAa;AACvC,UAAM,YAAY,UAAU,QAAQ;AAEpC,WAAO,IAAI,QAAkB,CAAC,SAAS,WAAW;AAChD,YAAM,SAAS,MAAM,UAAU;AAC/B,YAAM,UAAU,MAAM,UAClB,OAAO;AAAA,QACL,KAAK,mBAAmB,UACpB,KAAK,QAAQ,QAAQ,IACrB,MAAM,QAAQ,KAAK,OAAO,IACxB,KAAK,UACL,OAAO,QAAQ,KAAK,OAAO;AAAA,MACnC,IACA,CAAC;AAEL,YAAM,MAAM,UAAU;AAAA,QACpB;AAAA,QACA,EAAE,QAAQ,SAAS,OAAO,SAAS,IAAO;AAAA,QAC1C,CAAC,QAAQ;AACP,gBAAM,SAAmB,CAAC;AAC1B,cAAI,GAAG,QAAQ,CAAC,UAAkB,OAAO,KAAK,KAAK,CAAC;AACpD,cAAI,GAAG,OAAO,MAAM;AAClB,kBAAM,OAAO,OAAO,OAAO,MAAM;AACjC,kBAAM,kBAAkB,IAAI,QAAQ;AACpC,uBAAW,CAAC,KAAK,GAAG,KAAK,OAAO,QAAQ,IAAI,OAAO,GAAG;AACpD,kBAAI;AACF,gCAAgB;AAAA,kBACd;AAAA,kBACA,MAAM,QAAQ,GAAG,IAAI,IAAI,KAAK,IAAI,IAAI;AAAA,gBACxC;AAAA,YACJ;AACA;AAAA,cACE,IAAI,SAAS,MAAM;AAAA,gBACjB,QAAQ,IAAI,cAAc;AAAA,gBAC1B,YAAY,IAAI,iBAAiB;AAAA,gBACjC,SAAS;AAAA,cACX,CAAC;AAAA,YACH;AAAA,UACF,CAAC;AAAA,QACH;AAAA,MACF;AAEA,UAAI,GAAG,SAAS,MAAM;AACtB,UAAI,GAAG,WAAW,MAAM;AACtB,YAAI,QAAQ;AACZ,eAAO,IAAI,MAAM,gCAAgC,CAAC;AAAA,MACpD,CAAC;AAED,UAAI,MAAM,QAAQ;AAChB,aAAK,OAAO,iBAAiB,SAAS,MAAM;AAC1C,cAAI,QAAQ;AACZ,iBAAO,IAAI,MAAM,SAAS,CAAC;AAAA,QAC7B,CAAC;AAAA,MACH;AAEA,UAAI,MAAM,MAAM;AACd,YAAI,MAAM,OAAO,KAAK,SAAS,WAAW,KAAK,OAAO,KAAK,IAAI;AAAA,MACjE;AACA,UAAI,IAAI;AAAA,IACV,CAAC;AAAA,EACH;AACF;AASA,eAAsB,iBACpB,YACA,YAAY,KACG;AACf,QAAM,EAAE,MAAM,KAAK,IAAI,mBAAmB,UAAU;AAEpD,SAAO,IAAI,QAAc,CAAC,SAAS,WAAW;AAC5C,UAAM,SAAS,iBAAiB,EAAE,MAAM,KAAK,GAAG,MAAM;AACpD,aAAO,QAAQ;AACf,cAAQ;AAAA,IACV,CAAC;AAED,WAAO,WAAW,WAAW,MAAM;AACjC,aAAO,QAAQ;AACf;AAAA,QACE,IAAI;AAAA,UACF,mBAAmB,IAAI,IAAI,IAAI,yBAAyB,SAAS;AAAA,QAEnE;AAAA,MACF;AAAA,IACF,CAAC;AAED,WAAO,GAAG,SAAS,CAAC,QAAQ;AAC1B,aAAO,QAAQ;AACf;AAAA,QACE,IAAI;AAAA,UACF,mBAAmB,IAAI,IAAI,IAAI,iBAAiB,IAAI,OAAO;AAAA,QAE7D;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AACH;","names":["require"]}
|