@toon-protocol/client-mcp 0.29.3 → 0.29.5

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.
@@ -9,7 +9,7 @@ import {
9
9
  startManagedAnonProxy,
10
10
  tcpProbe,
11
11
  waitForAnonSocks
12
- } from "./chunk-245J23EB.js";
12
+ } from "./chunk-SKQTKZIH.js";
13
13
  export {
14
14
  ANON_ASSETS,
15
15
  ANON_VERSION,
@@ -21,4 +21,4 @@ export {
21
21
  tcpProbe,
22
22
  waitForAnonSocks
23
23
  };
24
- //# sourceMappingURL=anon-proxy-6N362VEV-M7AX2QD7.js.map
24
+ //# sourceMappingURL=anon-proxy-W3KMM7GU-L5IBCUAX.js.map
@@ -1,7 +1,7 @@
1
1
  import { createRequire as __cr } from 'module'; const require = __cr(import.meta.url);
2
2
  import {
3
3
  decodeEventFromToon
4
- } from "./chunk-WMYY5I3H.js";
4
+ } from "./chunk-MX2QK3SR.js";
5
5
 
6
6
  // src/relay-subscription.ts
7
7
  import { createRequire } from "module";
@@ -622,4 +622,4 @@ export {
622
622
  PublishRejectedError,
623
623
  registerRoutes
624
624
  };
625
- //# sourceMappingURL=chunk-2SGZPDGE.js.map
625
+ //# sourceMappingURL=chunk-HA25XT52.js.map
@@ -8926,7 +8926,7 @@ async function resolveTransport(transport, originalBtpUrl, originalConnectorUrl,
8926
8926
  const hasExplicitProxy = !!transport && (transport.type === "socks5" || transport.type === "gateway");
8927
8927
  const envProxy = process.env["ANYONE_PROXY_URLS"];
8928
8928
  if (!hasExplicitProxy && managedProxyOptions?.managedAnonProxy !== false && !envProxy && isAnyoneHost(originalBtpUrl)) {
8929
- const { startManagedAnonProxy: startManagedAnonProxy2 } = await import("./anon-proxy-6N362VEV-M7AX2QD7.js");
8929
+ const { startManagedAnonProxy: startManagedAnonProxy2 } = await import("./anon-proxy-W3KMM7GU-L5IBCUAX.js");
8930
8930
  const { createSocks5WebSocketFactory, createSocks5Fetch } = await import("./socks5-WTJBYGME-IXWLQDE7.js");
8931
8931
  const proxy = await startManagedAnonProxy2({
8932
8932
  ...managedProxyOptions?.managedAnonSocksPort !== void 0 ? { socksPort: managedProxyOptions.managedAnonSocksPort } : {}
@@ -10815,4 +10815,4 @@ export {
10815
10815
  @scure/bip32/lib/esm/index.js:
10816
10816
  (*! scure-bip32 - MIT License (c) 2022 Patricio Palladino, Paul Miller (paulmillr.com) *)
10817
10817
  */
10818
- //# sourceMappingURL=chunk-WMYY5I3H.js.map
10818
+ //# sourceMappingURL=chunk-MX2QK3SR.js.map
@@ -1,6 +1,6 @@
1
1
  import { createRequire as __cr } from 'module'; const require = __cr(import.meta.url);
2
2
 
3
- // ../client/dist/chunk-4YBYBFSN.js
3
+ // ../client/dist/chunk-WHAEQLIW.js
4
4
  import { createRequire } from "module";
5
5
  var nodeRequire = createRequire(import.meta.url);
6
6
  var ANON_VERSION = "v0.4.10.0-beta";
@@ -12,15 +12,15 @@ var ANON_ASSETS = {
12
12
  },
13
13
  "darwin-x64": {
14
14
  assetName: "anon-beta-macos-amd64.zip",
15
- sha256: null
15
+ sha256: "aad277849b1e63baa75891b9e5109683534e488776ff190e884e34caa04a6d54"
16
16
  },
17
17
  "linux-x64": {
18
18
  assetName: "anon-beta-linux-amd64.zip",
19
- sha256: null
19
+ sha256: "370c86f366e7f4cad896e2ef4bbd366a4e78a832c8d58064012f86c88c411a6b"
20
20
  },
21
21
  "linux-arm64": {
22
22
  assetName: "anon-beta-linux-arm64.zip",
23
- sha256: null
23
+ sha256: "382d21db1052b6a0f1581bf38c9cf79b370719e313781c0eba53ef0d9570334a"
24
24
  }
25
25
  };
26
26
  function selectAnonAsset(platform, arch) {
@@ -275,4 +275,4 @@ export {
275
275
  waitForAnonSocks,
276
276
  startManagedAnonProxy
277
277
  };
278
- //# sourceMappingURL=chunk-245J23EB.js.map
278
+ //# sourceMappingURL=chunk-SKQTKZIH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../client/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":[]}
@@ -2,7 +2,7 @@ import { createRequire as __cr } from 'module'; const require = __cr(import.meta
2
2
  import {
3
3
  ControlApiError,
4
4
  DaemonUnreachableError
5
- } from "./chunk-WMYY5I3H.js";
5
+ } from "./chunk-MX2QK3SR.js";
6
6
 
7
7
  // src/mcp-tools.ts
8
8
  var TOOL_DEFINITIONS = [
@@ -202,4 +202,4 @@ export {
202
202
  TOOL_DEFINITIONS,
203
203
  dispatchTool
204
204
  };
205
- //# sourceMappingURL=chunk-5YIZ2JQO.js.map
205
+ //# sourceMappingURL=chunk-UHZRN63P.js.map
package/dist/daemon.js CHANGED
@@ -3,7 +3,7 @@ import { createRequire as __cr } from 'module'; const require = __cr(import.meta
3
3
  import {
4
4
  ClientRunner,
5
5
  registerRoutes
6
- } from "./chunk-2SGZPDGE.js";
6
+ } from "./chunk-HA25XT52.js";
7
7
  import {
8
8
  ControlClient,
9
9
  ToonClient,
@@ -16,12 +16,12 @@ import {
16
16
  resolveConfig,
17
17
  spawnDaemonDetached,
18
18
  waitForReady
19
- } from "./chunk-WMYY5I3H.js";
19
+ } from "./chunk-MX2QK3SR.js";
20
20
  import "./chunk-32QD72IL.js";
21
21
  import "./chunk-QTDCFXPF.js";
22
22
  import "./chunk-LR7W2ISE.js";
23
23
  import "./chunk-VA7XC4FD.js";
24
- import "./chunk-245J23EB.js";
24
+ import "./chunk-SKQTKZIH.js";
25
25
 
26
26
  // src/daemon.ts
27
27
  import Fastify from "fastify";
package/dist/index.js CHANGED
@@ -5,11 +5,11 @@ import {
5
5
  PublishRejectedError,
6
6
  RelaySubscription,
7
7
  registerRoutes
8
- } from "./chunk-2SGZPDGE.js";
8
+ } from "./chunk-HA25XT52.js";
9
9
  import {
10
10
  TOOL_DEFINITIONS,
11
11
  dispatchTool
12
- } from "./chunk-5YIZ2JQO.js";
12
+ } from "./chunk-UHZRN63P.js";
13
13
  import {
14
14
  ControlApiError,
15
15
  ControlClient,
@@ -26,12 +26,12 @@ import {
26
26
  resolveMnemonic,
27
27
  spawnDaemonDetached,
28
28
  waitForReady
29
- } from "./chunk-WMYY5I3H.js";
29
+ } from "./chunk-MX2QK3SR.js";
30
30
  import "./chunk-32QD72IL.js";
31
31
  import "./chunk-QTDCFXPF.js";
32
32
  import "./chunk-LR7W2ISE.js";
33
33
  import "./chunk-VA7XC4FD.js";
34
- import "./chunk-245J23EB.js";
34
+ import "./chunk-SKQTKZIH.js";
35
35
  export {
36
36
  ClientRunner,
37
37
  ControlApiError,
package/dist/mcp.js CHANGED
@@ -3,7 +3,7 @@ import { createRequire as __cr } from 'module'; const require = __cr(import.meta
3
3
  import {
4
4
  TOOL_DEFINITIONS,
5
5
  dispatchTool
6
- } from "./chunk-5YIZ2JQO.js";
6
+ } from "./chunk-UHZRN63P.js";
7
7
  import {
8
8
  ControlClient,
9
9
  defaultConfigPath,
@@ -11,12 +11,12 @@ import {
11
11
  readConfigFile,
12
12
  spawnDaemonDetached,
13
13
  waitForReady
14
- } from "./chunk-WMYY5I3H.js";
14
+ } from "./chunk-MX2QK3SR.js";
15
15
  import "./chunk-32QD72IL.js";
16
16
  import "./chunk-QTDCFXPF.js";
17
17
  import "./chunk-LR7W2ISE.js";
18
18
  import "./chunk-VA7XC4FD.js";
19
- import "./chunk-245J23EB.js";
19
+ import "./chunk-SKQTKZIH.js";
20
20
 
21
21
  // src/mcp.ts
22
22
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toon-protocol/client-mcp",
3
- "version": "0.29.3",
3
+ "version": "0.29.5",
4
4
  "description": "Always-on local daemon + MCP server letting a Claude agent (Desktop or Code) act as a TOON Protocol client: pay-to-write publishing, free reads, channel/balance management, and mill swaps.",
5
5
  "license": "MIT",
6
6
  "author": "Jonathan Green",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../client/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; entries with `sha256: null` are recognised (correct asset name) but\n * cannot be auto-downloaded until a checksum is pinned — cross-reference issue #204\n * to add the remaining hashes (linux amd64/arm64, macos amd64).\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 tracks filling in the nulls):\n * - darwin-arm64 (anon-beta-macos-arm64.zip): verified working 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: null,\n },\n 'linux-x64': {\n assetName: 'anon-beta-linux-amd64.zip',\n sha256: null,\n },\n 'linux-arm64': {\n assetName: 'anon-beta-linux-arm64.zip',\n sha256: null,\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":[]}