@toon-protocol/client-mcp 0.29.6 → 0.30.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/{chunk-CY5GWIH5.js → chunk-2MY6AQK6.js} +44 -2
- package/dist/chunk-2MY6AQK6.js.map +1 -0
- package/dist/{chunk-WYWUAKYM.js → chunk-CH37M2I2.js} +2 -2
- package/dist/{chunk-OMEKDTJ4.js → chunk-TEMKXTNH.js} +2 -1
- package/dist/{chunk-OMEKDTJ4.js.map → chunk-TEMKXTNH.js.map} +1 -1
- package/dist/daemon.js +2 -2
- package/dist/index.d.ts +23 -0
- package/dist/index.js +3 -3
- package/dist/mcp.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-CY5GWIH5.js.map +0 -1
- /package/dist/{chunk-WYWUAKYM.js.map → chunk-CH37M2I2.js.map} +0 -0
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-
|
|
6
|
+
} from "./chunk-2MY6AQK6.js";
|
|
7
7
|
import {
|
|
8
8
|
ControlClient,
|
|
9
9
|
ToonClient,
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
resolveConfig,
|
|
17
17
|
spawnDaemonDetached,
|
|
18
18
|
waitForReady
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-TEMKXTNH.js";
|
|
20
20
|
import "./chunk-32QD72IL.js";
|
|
21
21
|
import "./chunk-QTDCFXPF.js";
|
|
22
22
|
import "./chunk-LR7W2ISE.js";
|
package/dist/index.d.ts
CHANGED
|
@@ -388,6 +388,14 @@ interface DaemonConfigFile {
|
|
|
388
388
|
* others are retained so switching chains needs only a `chain`/restart change.
|
|
389
389
|
*/
|
|
390
390
|
apexChains?: Partial<Record<SettlementChain, ApexNegotiationConfig>>;
|
|
391
|
+
/**
|
|
392
|
+
* Additional apex CHILD peers (last ILP segment, e.g. `["dvm","mill"]`)
|
|
393
|
+
* reachable via the SAME apex channel — used when publishing/swapping to
|
|
394
|
+
* `g.townhouse.dvm` / `g.townhouse.mill`. The runner injects the apex
|
|
395
|
+
* negotiation under each and points it at the open apex channel (no extra
|
|
396
|
+
* on-chain channel). The apex `peerId` itself is always handled.
|
|
397
|
+
*/
|
|
398
|
+
apexChildPeers?: string[];
|
|
391
399
|
/** Extra settlement overrides passed straight through to ToonClient. */
|
|
392
400
|
supportedChains?: string[];
|
|
393
401
|
settlementAddresses?: Record<string, string>;
|
|
@@ -406,6 +414,8 @@ interface ResolvedDaemonConfig {
|
|
|
406
414
|
destination: string;
|
|
407
415
|
feePerEvent: bigint;
|
|
408
416
|
apex?: ApexNegotiationConfig;
|
|
417
|
+
/** Apex child peers reached via the same apex channel (e.g. dvm, mill). */
|
|
418
|
+
apexChildPeers?: string[];
|
|
409
419
|
/** The active settlement chain for paid writes. */
|
|
410
420
|
chain: SettlementChain;
|
|
411
421
|
/** File mapping (destination, chain) → on-chain channelId for restart resume. */
|
|
@@ -533,6 +543,19 @@ declare class ClientRunner {
|
|
|
533
543
|
* peers (no relay-based discovery). Mirrors the docker entrypoint's approach.
|
|
534
544
|
*/
|
|
535
545
|
private injectApexNegotiation;
|
|
546
|
+
/**
|
|
547
|
+
* Route additional apex CHILD peers (e.g. `dvm`, `mill`) through the SAME
|
|
548
|
+
* apex payment channel. In the parent→child apex model the client holds ONE
|
|
549
|
+
* channel with the apex (g.townhouse) and pays via it regardless of which
|
|
550
|
+
* child the ILP destination addresses; but `ToonClient.resolvePeerId` keys off
|
|
551
|
+
* the destination's last segment (`town`/`dvm`/`mill`), so without this each
|
|
552
|
+
* child would (a) fail the "no negotiation for peer" guard and (b) try to open
|
|
553
|
+
* a SECOND on-chain channel to the same apex receive (which reverts —
|
|
554
|
+
* channel-exists). So: inject the same apex negotiation under each child peer
|
|
555
|
+
* AND pre-map its peer→channel to the already-open apex channel so
|
|
556
|
+
* `ensureChannel` reuses it (no second open; one shared nonce sequence).
|
|
557
|
+
*/
|
|
558
|
+
private routeChildPeersThroughApexChannel;
|
|
536
559
|
isReady(): boolean;
|
|
537
560
|
isBootstrapping(): boolean;
|
|
538
561
|
getStatus(): StatusResponse;
|
package/dist/index.js
CHANGED
|
@@ -5,11 +5,11 @@ import {
|
|
|
5
5
|
PublishRejectedError,
|
|
6
6
|
RelaySubscription,
|
|
7
7
|
registerRoutes
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-2MY6AQK6.js";
|
|
9
9
|
import {
|
|
10
10
|
TOOL_DEFINITIONS,
|
|
11
11
|
dispatchTool
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-CH37M2I2.js";
|
|
13
13
|
import {
|
|
14
14
|
ControlApiError,
|
|
15
15
|
ControlClient,
|
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
resolveMnemonic,
|
|
27
27
|
spawnDaemonDetached,
|
|
28
28
|
waitForReady
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-TEMKXTNH.js";
|
|
30
30
|
import "./chunk-32QD72IL.js";
|
|
31
31
|
import "./chunk-QTDCFXPF.js";
|
|
32
32
|
import "./chunk-LR7W2ISE.js";
|
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-
|
|
6
|
+
} from "./chunk-CH37M2I2.js";
|
|
7
7
|
import {
|
|
8
8
|
ControlClient,
|
|
9
9
|
defaultConfigPath,
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
readConfigFile,
|
|
12
12
|
spawnDaemonDetached,
|
|
13
13
|
waitForReady
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-TEMKXTNH.js";
|
|
15
15
|
import "./chunk-32QD72IL.js";
|
|
16
16
|
import "./chunk-QTDCFXPF.js";
|
|
17
17
|
import "./chunk-LR7W2ISE.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toon-protocol/client-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.30.0",
|
|
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":["../src/relay-subscription.ts","../src/daemon/apex-channel-store.ts","../src/daemon/client-runner.ts","../src/daemon/routes.ts"],"sourcesContent":["/**\n * Persistent town-relay Nostr-WS subscription — the read half of the TOON\n * client, which `@toon-protocol/client` does not provide (its bootstrap only\n * issues one-shot WS queries and `DiscoveryTracker` is passive).\n *\n * Reads are FREE: this opens a long-lived NIP-01 connection to the town relay,\n * keeps a bounded ring buffer of received events (de-duplicated by `event.id`),\n * and lets callers drain new events via a monotonic cursor. It auto-reconnects\n * with exponential backoff and re-issues every active REQ on reconnect.\n *\n * The WebSocket is injectable (`wsFactory`) so unit tests can drive the wire\n * protocol without a real relay; the default factory uses the `ws` package and,\n * when a `socks5h://` proxy is configured, routes through it so `.anyone`\n * hidden-service relays are reachable.\n */\n\nimport { createRequire } from 'node:module';\nimport type { NostrEvent } from 'nostr-tools/pure';\nimport type { NostrFilter } from './control-api.js';\n\n// ESM has no `require`; synchronously load the node-only `ws`/`socks-proxy-agent`\n// deps for the default WebSocket factory without forcing every caller (e.g. the\n// browser-style injected-factory path in tests) to pull them in.\nconst nodeRequire = createRequire(import.meta.url);\n\n/** Minimal WebSocket surface this module depends on (subset of `ws`). */\nexport interface MinimalWebSocket {\n send(data: string): void;\n close(): void;\n on(event: 'open' | 'close', cb: () => void): void;\n on(event: 'message', cb: (data: unknown) => void): void;\n on(event: 'error', cb: (err: unknown) => void): void;\n}\n\nexport type WebSocketFactory = (url: string) => MinimalWebSocket;\n\nexport interface RelaySubscriptionOptions {\n /** Town relay WS URL, e.g. `wss://<host>.anyone/` or `ws://localhost:7100`. */\n relayUrl: string;\n /** Optional `socks5h://host:port` proxy for `.anyone` relays. */\n socksProxy?: string;\n /** Max events retained in the ring buffer (oldest evicted). Default 5000. */\n bufferSize?: number;\n /** Base reconnect delay, ms. Default 1000. */\n reconnectBaseMs?: number;\n /** Max reconnect delay, ms. Default 30000. */\n reconnectMaxMs?: number;\n /** Inject a WebSocket factory (tests / proxy customisation). */\n wsFactory?: WebSocketFactory;\n /**\n * Decode an `EVENT` payload that arrived as a string. The TOON relay sends\n * events TOON-encoded (key/value text) rather than as a JSON object, so the\n * daemon injects a TOON decoder here. When the payload is already a JSON\n * object it is used directly and this is not called.\n */\n decodeEvent?: (raw: string) => NostrEvent;\n /** Optional logger. */\n logger?: (msg: string) => void;\n}\n\ninterface BufferedEvent {\n seq: number;\n subId: string;\n event: NostrEvent;\n}\n\n/** Result of {@link RelaySubscription.getEvents}. */\nexport interface DrainResult {\n events: NostrEvent[];\n cursor: number;\n hasMore: boolean;\n}\n\nconst DEFAULT_BUFFER = 5000;\nconst DEFAULT_BASE_MS = 1000;\nconst DEFAULT_MAX_MS = 30_000;\n\n/** Shared no-op used as the default logger. */\nconst noop = (): void => undefined;\n\nexport class RelaySubscription {\n private readonly relayUrl: string;\n private readonly socksProxy?: string;\n private readonly bufferSize: number;\n private readonly reconnectBaseMs: number;\n private readonly reconnectMaxMs: number;\n private readonly log: (msg: string) => void;\n private readonly wsFactory: WebSocketFactory;\n private readonly decodeEvent?: (raw: string) => NostrEvent;\n\n /** Active subscriptions: subId -> filters (re-sent on every (re)connect). */\n private readonly subscriptions = new Map<string, NostrFilter[]>();\n\n /** Ring buffer of received events, ordered by ascending `seq`. */\n private buffer: BufferedEvent[] = [];\n /** De-dup index: event.id -> seq (kept in lockstep with the buffer). */\n private readonly seen = new Set<string>();\n private seqCounter = 0;\n private subIdCounter = 0;\n\n private ws: MinimalWebSocket | null = null;\n private connected = false;\n private closing = false;\n private reconnectAttempts = 0;\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n\n constructor(opts: RelaySubscriptionOptions) {\n this.relayUrl = opts.relayUrl;\n this.socksProxy = opts.socksProxy;\n this.bufferSize = opts.bufferSize ?? DEFAULT_BUFFER;\n this.reconnectBaseMs = opts.reconnectBaseMs ?? DEFAULT_BASE_MS;\n this.reconnectMaxMs = opts.reconnectMaxMs ?? DEFAULT_MAX_MS;\n this.log = opts.logger ?? noop;\n this.wsFactory = opts.wsFactory ?? defaultWebSocketFactory(this.socksProxy);\n this.decodeEvent = opts.decodeEvent;\n }\n\n /** Whether the underlying socket is currently open. */\n isConnected(): boolean {\n return this.connected;\n }\n\n /** Number of events currently held in the buffer. */\n bufferedCount(): number {\n return this.buffer.length;\n }\n\n /** Active subscription ids. */\n activeSubscriptions(): string[] {\n return [...this.subscriptions.keys()];\n }\n\n /** Open the connection (idempotent). */\n start(): void {\n this.closing = false;\n if (this.ws) return;\n this.open();\n }\n\n /**\n * Register a persistent subscription and (if connected) send the REQ.\n * Returns the subscription id (caller-supplied or generated).\n */\n subscribe(filters: NostrFilter | NostrFilter[], subId?: string): string {\n const id = subId ?? `sub-${++this.subIdCounter}`;\n const list = Array.isArray(filters) ? filters : [filters];\n this.subscriptions.set(id, list);\n if (this.connected) this.sendReq(id, list);\n return id;\n }\n\n /** Cancel a subscription and send CLOSE if connected. */\n unsubscribe(subId: string): void {\n if (!this.subscriptions.delete(subId)) return;\n if (this.connected) this.sendRaw(['CLOSE', subId]);\n }\n\n /**\n * Drain events newer than `cursor`. The cursor is the highest `seq` returned;\n * pass it back to fetch only events received since. Filtering by `subId`\n * restricts to one subscription.\n */\n getEvents(\n opts: { subId?: string; cursor?: number; limit?: number } = {}\n ): DrainResult {\n const after = opts.cursor ?? 0;\n const limit = opts.limit ?? 200;\n const matches = this.buffer.filter(\n (b) =>\n b.seq > after && (opts.subId === undefined || b.subId === opts.subId)\n );\n const page = matches.slice(0, limit);\n const hasMore = matches.length > page.length;\n const last = page.at(-1);\n const cursor = last ? last.seq : after;\n return { events: page.map((b) => b.event), cursor, hasMore };\n }\n\n /** Close the connection permanently and stop reconnecting. */\n close(): void {\n this.closing = true;\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n if (this.ws) {\n try {\n this.ws.close();\n } catch {\n /* ignore */\n }\n this.ws = null;\n }\n this.connected = false;\n }\n\n // ── internals ────────────────────────────────────────────────────────────\n\n private open(): void {\n let ws: MinimalWebSocket;\n try {\n ws = this.wsFactory(this.relayUrl);\n } catch (err) {\n this.log(`[relay] connect failed: ${errMsg(err)}`);\n this.scheduleReconnect();\n return;\n }\n this.ws = ws;\n\n ws.on('open', () => {\n this.connected = true;\n this.reconnectAttempts = 0;\n this.log(`[relay] connected to ${this.relayUrl}`);\n // Re-issue every active subscription.\n for (const [id, filters] of this.subscriptions) this.sendReq(id, filters);\n });\n\n ws.on('message', (data: unknown) => this.handleMessage(data));\n\n ws.on('error', (err: unknown) => {\n this.log(`[relay] socket error: ${errMsg(err)}`);\n });\n\n ws.on('close', () => {\n this.connected = false;\n this.ws = null;\n if (!this.closing) {\n this.log('[relay] disconnected; scheduling reconnect');\n this.scheduleReconnect();\n }\n });\n }\n\n private scheduleReconnect(): void {\n if (this.closing || this.reconnectTimer) return;\n const delay = Math.min(\n this.reconnectMaxMs,\n this.reconnectBaseMs * 2 ** this.reconnectAttempts\n );\n this.reconnectAttempts += 1;\n this.reconnectTimer = setTimeout(() => {\n this.reconnectTimer = null;\n if (!this.closing) this.open();\n }, delay);\n // Don't keep the event loop alive solely for a reconnect timer.\n (this.reconnectTimer as { unref?: () => void }).unref?.();\n }\n\n private sendReq(subId: string, filters: NostrFilter[]): void {\n this.sendRaw(['REQ', subId, ...filters]);\n }\n\n private sendRaw(message: unknown[]): void {\n if (!this.ws || !this.connected) return;\n try {\n this.ws.send(JSON.stringify(message));\n } catch (err) {\n this.log(`[relay] send failed: ${errMsg(err)}`);\n }\n }\n\n private handleMessage(data: unknown): void {\n let parsed: unknown;\n try {\n parsed = JSON.parse(toText(data));\n } catch {\n return;\n }\n if (!Array.isArray(parsed) || parsed.length === 0) return;\n const type = parsed[0];\n switch (type) {\n case 'EVENT': {\n const subId = typeof parsed[1] === 'string' ? parsed[1] : '';\n const event = this.parseEventPayload(parsed[2]);\n if (event && typeof event.id === 'string')\n this.bufferEvent(subId, event);\n break;\n }\n case 'EOSE':\n // End of stored events — nothing to do; we keep streaming live events.\n break;\n case 'CLOSED':\n this.log(\n `[relay] subscription closed by relay: ${String(parsed[2] ?? '')}`\n );\n break;\n case 'NOTICE':\n this.log(`[relay] NOTICE: ${String(parsed[1] ?? '')}`);\n break;\n default:\n break;\n }\n }\n\n /**\n * Normalise an `EVENT` payload to a NostrEvent. Standard relays send a JSON\n * object; the TOON relay sends a TOON-encoded string, decoded via the injected\n * `decodeEvent`. Returns undefined when it can't be parsed.\n */\n private parseEventPayload(raw: unknown): NostrEvent | undefined {\n if (\n raw &&\n typeof raw === 'object' &&\n typeof (raw as NostrEvent).id === 'string'\n ) {\n return raw as NostrEvent;\n }\n if (typeof raw === 'string' && this.decodeEvent) {\n try {\n return this.decodeEvent(raw);\n } catch (err) {\n this.log(`[relay] event decode failed: ${errMsg(err)}`);\n return undefined;\n }\n }\n return undefined;\n }\n\n private bufferEvent(subId: string, event: NostrEvent): void {\n if (this.seen.has(event.id)) return; // de-dup by event.id\n this.seen.add(event.id);\n this.buffer.push({ seq: ++this.seqCounter, subId, event });\n if (this.buffer.length > this.bufferSize) {\n const evicted = this.buffer.shift();\n if (evicted) this.seen.delete(evicted.event.id);\n }\n }\n}\n\nfunction toText(data: unknown): string {\n if (typeof data === 'string') return data;\n if (data instanceof Uint8Array) return Buffer.from(data).toString('utf8');\n if (Buffer.isBuffer(data)) return data.toString('utf8');\n return String(data);\n}\n\nfunction errMsg(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\n/**\n * Default factory backed by the `ws` package. When a `socks5h://` proxy is\n * provided, routes the WebSocket through it via `socks-proxy-agent` (optional\n * dependency, loaded lazily so the bundle works without it).\n */\nfunction defaultWebSocketFactory(socksProxy?: string): WebSocketFactory {\n return (url: string): MinimalWebSocket => {\n const WebSocketImpl = nodeRequire('ws') as new (\n address: string,\n opts?: { agent?: unknown }\n ) => MinimalWebSocket;\n let agent: unknown;\n if (socksProxy) {\n const { SocksProxyAgent } = nodeRequire('socks-proxy-agent') as {\n SocksProxyAgent: new (proxy: string) => unknown;\n };\n agent = new SocksProxyAgent(socksProxy);\n }\n return new WebSocketImpl(url, agent ? { agent } : undefined);\n };\n}\n","/**\n * Persists the apex payment-channel id (+ its chain context) per\n * (destination, chain) so a daemon RESTART can resume the EXISTING on-chain\n * channel instead of opening a new one.\n *\n * Why this is needed: `ChannelManager` persists the off-chain nonce/cumulative\n * watermark (keyed by channelId) but NOT the peer→channelId mapping. So after a\n * restart `openChannel()` would open + re-deposit into a fresh channel, which\n * reverts on a chain where the deposit already exists. With the channelId saved\n * here, the runner instead calls `trackChannel(channelId, context)` — which\n * rehydrates the nonce from the channel store — and signs against the live\n * channel with zero on-chain writes.\n */\n\nimport { mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname } from 'node:path';\n\n/** Chain context needed to re-track a channel (matches ChannelManager.trackChannel). */\nexport interface PersistedChannelContext {\n chainType: string;\n chainId: number;\n tokenNetworkAddress: string;\n tokenAddress?: string;\n recipient?: string;\n}\n\nexport interface PersistedApexChannel {\n channelId: string;\n context: PersistedChannelContext;\n}\n\ntype Store = Record<string, PersistedApexChannel>;\n\nfunction key(destination: string, chain: string): string {\n return `${destination}|${chain}`;\n}\n\nfunction readStore(path: string): Store {\n try {\n return JSON.parse(readFileSync(path, 'utf8')) as Store;\n } catch {\n return {};\n }\n}\n\n/** Load the saved apex channel for (destination, chain), or null. */\nexport function loadApexChannel(\n path: string,\n destination: string,\n chain: string\n): PersistedApexChannel | null {\n return readStore(path)[key(destination, chain)] ?? null;\n}\n\n/** Save the apex channel for (destination, chain) with mode 0o600. */\nexport function saveApexChannel(\n path: string,\n destination: string,\n chain: string,\n record: PersistedApexChannel\n): void {\n const store = readStore(path);\n store[key(destination, chain)] = record;\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, JSON.stringify(store, null, 2), { mode: 0o600 });\n}\n","/**\n * ClientRunner — the daemon's connection owner. Wraps a single `ToonClient`\n * (BTP session + payment channels + signer) plus a persistent\n * `RelaySubscription` (free reads), and exposes the high-level operations the\n * HTTP routes map onto.\n *\n * Bootstrap is asynchronous and non-blocking: `start()` returns immediately and\n * the connection comes up in the background (the managed anon proxy alone can\n * take 30–90s). Until it is ready, write operations report `bootstrapping` so\n * tools surface \"retry\" rather than hang.\n *\n * The `ToonClient` is injected via `ToonClientLike` so unit tests can drive the\n * runner with a fake — no BTP/anon/on-chain dependency.\n */\n\nimport type { NostrEvent } from 'nostr-tools/pure';\nimport { decodeEventFromToon } from '@toon-protocol/core';\nimport { RelaySubscription } from '../relay-subscription.js';\nimport type {\n ChannelsResponse,\n ChainStatus,\n EventsResponse,\n PublishResponse,\n StatusResponse,\n SubscribeRequest,\n SubscribeResponse,\n SwapResponse,\n} from '../control-api.js';\nimport type {\n EventsQuery,\n PublishRequest,\n SwapRequest,\n} from '../control-api.js';\nimport type { ApexNegotiationConfig, ResolvedDaemonConfig } from './config.js';\nimport {\n loadApexChannel,\n saveApexChannel,\n type PersistedChannelContext,\n} from './apex-channel-store.js';\n\n/** The subset of `ToonClient` the runner depends on. */\nexport interface ToonClientLike {\n start(): Promise<{ peersDiscovered: number; mode: string }>;\n stop(): Promise<void>;\n getPublicKey(): string;\n getEvmAddress(): string | undefined;\n getSolanaAddress(): string | undefined;\n getMinaAddress(): string | undefined;\n getNetworkStatus(): { evm: string; solana: string; mina: string } | undefined;\n publishEvent(\n event: NostrEvent,\n options?: { destination?: string; claim?: unknown; ilpAmount?: bigint }\n ): Promise<{\n success: boolean;\n eventId?: string;\n data?: string;\n error?: string;\n }>;\n signBalanceProof(channelId: string, amount: bigint): Promise<unknown>;\n openChannel(destination?: string): Promise<string>;\n getTrackedChannels(): string[];\n getChannelNonce(channelId: string): number;\n getChannelCumulativeAmount(channelId: string): bigint;\n sendSwapPacket(params: {\n destination: string;\n amount: bigint;\n toonData: Uint8Array;\n claim?: unknown;\n }): Promise<{\n accepted: boolean;\n data?: string;\n code?: string;\n message?: string;\n }>;\n}\n\nexport interface ClientRunnerDeps {\n config: ResolvedDaemonConfig;\n /** Factory producing the (real or fake) ToonClient. */\n createClient: () => ToonClientLike;\n /** Factory producing the relay subscription (defaults to the real one). */\n createRelay?: () => RelaySubscription;\n logger?: (msg: string) => void;\n}\n\nexport class ClientRunner {\n private readonly config: ResolvedDaemonConfig;\n private readonly client: ToonClientLike;\n private readonly relay: RelaySubscription;\n private readonly log: (msg: string) => void;\n\n private readonly startedAt = Date.now();\n private bootstrapping = false;\n private ready = false;\n private lastError: string | undefined;\n /** Channel opened against the default apex destination. */\n private apexChannelId: string | undefined;\n private stopped = false;\n\n constructor(deps: ClientRunnerDeps) {\n this.config = deps.config;\n this.client = deps.createClient();\n this.relay =\n deps.createRelay?.() ??\n new RelaySubscription({\n relayUrl: deps.config.relayUrl,\n socksProxy: deps.config.socksProxy,\n logger: deps.logger,\n // The TOON relay sends events TOON-encoded (text) on reads, not as JSON.\n decodeEvent: (raw) =>\n decodeEventFromToon(new TextEncoder().encode(raw)),\n });\n this.log = deps.logger ?? ((): void => undefined);\n }\n\n /**\n * Begin bootstrapping in the background. Resolves once kicked off; the\n * connection becomes ready asynchronously. Awaitable for tests via the\n * returned promise of the underlying work.\n */\n start(): void {\n if (this.bootstrapping || this.ready) return;\n this.bootstrapping = true;\n // Reads can start immediately and independently of the paid-write path.\n this.relay.start();\n void this.bootstrap();\n }\n\n /** The background bootstrap routine (exposed for awaiting in tests). */\n async bootstrap(): Promise<void> {\n try {\n await this.client.start();\n this.injectApexNegotiation(this.config.apex);\n this.apexChannelId = await this.openOrResumeApexChannel();\n this.ready = true;\n this.lastError = undefined;\n this.log(`[runner] ready; apex channel ${this.apexChannelId}`);\n } catch (err) {\n this.lastError = err instanceof Error ? err.message : String(err);\n this.log(`[runner] bootstrap failed: ${this.lastError}`);\n } finally {\n this.bootstrapping = false;\n }\n }\n\n /**\n * Open the apex channel — or, on a restart, RESUME the existing one.\n *\n * `ChannelManager` persists the nonce watermark (by channelId) but not the\n * peer→channelId mapping, so a naive `openChannel()` after restart re-deposits\n * into a fresh channel and reverts on-chain. We persist the channelId here and,\n * when present, `trackChannel()` the live channel (which rehydrates the nonce\n * from the channel store) — no on-chain write, watermark continues.\n */\n private async openOrResumeApexChannel(): Promise<string> {\n const { destination, chain, apexChannelStorePath } = this.config;\n const saved = loadApexChannel(apexChannelStorePath, destination, chain);\n const cm = (\n this.client as unknown as {\n channelManager?: {\n trackChannel?: (id: string, ctx: PersistedChannelContext) => void;\n };\n }\n ).channelManager;\n\n if (saved && cm && typeof cm.trackChannel === 'function') {\n cm.trackChannel(saved.channelId, saved.context);\n this.log(\n `[runner] resumed apex channel ${saved.channelId} (tracked, no re-deposit)`\n );\n return saved.channelId;\n }\n\n // First open for this (destination, chain): open on-chain and persist the\n // channelId + context so the next restart resumes instead of re-depositing.\n const channelId = await this.client.openChannel(destination);\n if (this.config.apex) {\n const a = this.config.apex;\n saveApexChannel(apexChannelStorePath, destination, chain, {\n channelId,\n context: {\n chainType: a.chain,\n chainId: a.chainId,\n tokenNetworkAddress: a.tokenNetwork ?? '',\n tokenAddress: a.tokenAddress,\n recipient: a.settlementAddress,\n },\n });\n }\n return channelId;\n }\n\n /**\n * Inject the apex settlement negotiation directly into the ToonClient when\n * configured. Required for HS / direct-apex mode where bootstrap discovers 0\n * peers (no relay-based discovery). Mirrors the docker entrypoint's approach.\n */\n private injectApexNegotiation(apex: ApexNegotiationConfig | undefined): void {\n if (!apex) return;\n const negotiations = (\n this.client as unknown as {\n peerNegotiations?: Map<string, unknown>;\n }\n ).peerNegotiations;\n if (!(negotiations instanceof Map)) {\n throw new Error(\n 'ToonClient.peerNegotiations layout changed — cannot inject apex negotiation'\n );\n }\n negotiations.set(apex.peerId, {\n chain: apex.chainKey,\n chainType: apex.chain,\n chainId: apex.chainId,\n settlementAddress: apex.settlementAddress,\n tokenAddress: apex.tokenAddress,\n tokenNetwork: apex.tokenNetwork,\n });\n this.log(`[runner] injected apex negotiation for peer \"${apex.peerId}\"`);\n }\n\n isReady(): boolean {\n return this.ready;\n }\n\n isBootstrapping(): boolean {\n return this.bootstrapping;\n }\n\n getStatus(): StatusResponse {\n const net = this.client.getNetworkStatus();\n const network: ChainStatus[] | undefined = net\n ? (['evm', 'solana', 'mina'] as const).map((c) => ({\n chain: c,\n ready: net[c] === 'configured',\n detail: net[c],\n }))\n : undefined;\n return {\n uptimeMs: Date.now() - this.startedAt,\n bootstrapping: this.bootstrapping,\n ready: this.ready,\n settlementChain: this.config.chain,\n identity: {\n nostrPubkey: safe(() => this.client.getPublicKey()) ?? '',\n evmAddress: safe(() => this.client.getEvmAddress()),\n solanaAddress: safe(() => this.client.getSolanaAddress()),\n minaAddress: safe(() => this.client.getMinaAddress()),\n },\n transport: {\n type: this.config.socksProxy ? 'socks5' : 'direct',\n socksProxy: this.config.socksProxy,\n btpUrl: this.config.toonClientConfig.btpUrl,\n },\n relay: {\n url: this.config.relayUrl,\n connected: this.relay.isConnected(),\n buffered: this.relay.bufferedCount(),\n subscriptions: this.relay.activeSubscriptions(),\n },\n ...(network ? { network } : {}),\n ...(this.lastError ? { lastError: this.lastError } : {}),\n };\n }\n\n /** Pay-to-write a single event. Throws NOT_READY while bootstrapping. */\n async publish(req: PublishRequest): Promise<PublishResponse> {\n this.assertReady();\n const channelId =\n this.apexChannelId ?? (await this.client.openChannel(req.destination));\n const fee =\n req.fee !== undefined ? BigInt(req.fee) : this.config.feePerEvent;\n const claim = await this.client.signBalanceProof(channelId, fee);\n const result = await this.client.publishEvent(req.event, {\n destination: req.destination,\n claim,\n ilpAmount: fee,\n });\n if (!result.success) {\n throw new PublishRejectedError(result.error ?? 'relay rejected event');\n }\n return {\n eventId: result.eventId ?? req.event.id,\n data: result.data,\n channelId,\n nonce: this.client.getChannelNonce(channelId),\n };\n }\n\n /** Register a free-read subscription (does not require the paid path). */\n subscribe(req: SubscribeRequest): SubscribeResponse {\n const subId = this.relay.subscribe(req.filters, req.subId);\n return { subId };\n }\n\n /** Drain buffered events newer than the cursor (free read). */\n getEvents(query: EventsQuery): EventsResponse {\n const opts: { subId?: string; cursor?: number; limit?: number } = {};\n if (query.subId !== undefined) opts.subId = query.subId;\n if (query.cursor !== undefined) opts.cursor = query.cursor;\n if (query.limit !== undefined) opts.limit = query.limit;\n const { events, cursor, hasMore } = this.relay.getEvents(opts);\n return { events, cursor, hasMore };\n }\n\n /** Open (or return) a payment channel for a destination. */\n async openChannel(destination?: string): Promise<{ channelId: string }> {\n this.assertReady();\n const channelId = await this.client.openChannel(\n destination ?? this.config.destination\n );\n if (!destination || destination === this.config.destination) {\n this.apexChannelId = channelId;\n }\n return { channelId };\n }\n\n /** List tracked channels with their nonce watermark + cumulative amount. */\n getChannels(): ChannelsResponse {\n const channels = this.client.getTrackedChannels().map((channelId) => ({\n channelId,\n nonce: this.client.getChannelNonce(channelId),\n cumulativeAmount: this.client\n .getChannelCumulativeAmount(channelId)\n .toString(),\n }));\n return { channels };\n }\n\n /** Send a swap packet to a mill peer. */\n async swap(req: SwapRequest): Promise<SwapResponse> {\n this.assertReady();\n const toonData = req.toonData\n ? new Uint8Array(Buffer.from(req.toonData, 'base64'))\n : new Uint8Array(0);\n const result = await this.client.sendSwapPacket({\n destination: req.destination,\n amount: BigInt(req.amount),\n toonData,\n });\n return {\n accepted: result.accepted,\n data: result.data,\n code: result.code,\n message: result.message,\n };\n }\n\n /** Graceful teardown of the relay subscription + ToonClient. */\n async stop(): Promise<void> {\n if (this.stopped) return;\n this.stopped = true;\n this.relay.close();\n try {\n await this.client.stop();\n } catch (err) {\n this.log(\n `[runner] client stop error: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n }\n\n private assertReady(): void {\n if (!this.ready) {\n throw new NotReadyError(\n this.bootstrapping\n ? 'Daemon is still bootstrapping (BTP/anon coming up) — retry shortly.'\n : (this.lastError ?? 'Daemon is not ready.')\n );\n }\n }\n}\n\n/** Thrown by paid-write operations while the daemon is not yet ready. */\nexport class NotReadyError extends Error {\n readonly retryable = true;\n constructor(message: string) {\n super(message);\n this.name = 'NotReadyError';\n }\n}\n\n/** Thrown when the relay/connector rejects a paid write. */\nexport class PublishRejectedError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'PublishRejectedError';\n }\n}\n\nfunction safe<T>(fn: () => T): T | undefined {\n try {\n return fn();\n } catch {\n return undefined;\n }\n}\n","/**\n * Fastify route registration for the `toon-clientd` control plane. Each route\n * is a thin adapter: parse/validate the request, call the `ClientRunner`, map\n * errors to the uniform `ErrorResponse` envelope.\n *\n * Bound to loopback only by the daemon entry — there is no auth layer because\n * the surface never leaves `127.0.0.1`.\n */\n\nimport type { FastifyInstance, FastifyReply } from 'fastify';\nimport type { NostrEvent } from 'nostr-tools/pure';\nimport type { ClientRunner } from './client-runner.js';\nimport { NotReadyError, PublishRejectedError } from './client-runner.js';\nimport type {\n EventsQuery,\n OpenChannelRequest,\n PublishRequest,\n SubscribeRequest,\n SwapRequest,\n} from '../control-api.js';\n\nexport function registerRoutes(\n app: FastifyInstance,\n runner: ClientRunner\n): void {\n app.get('/status', async () => runner.getStatus());\n\n app.post<{ Body: PublishRequest }>('/publish', async (req, reply) => {\n const body = req.body;\n if (!body || !isSignedEvent(body.event)) {\n return sendError(reply, 400, 'invalid_event', {\n detail: 'body.event must be a fully-signed Nostr event (id + sig).',\n });\n }\n try {\n return await runner.publish(body);\n } catch (err) {\n return mapError(reply, err);\n }\n });\n\n app.post<{ Body: SubscribeRequest }>('/subscribe', async (req, reply) => {\n const body = req.body;\n if (!body || body.filters === undefined) {\n return sendError(reply, 400, 'invalid_filters', {\n detail:\n 'body.filters is required (a NIP-01 filter or array of filters).',\n });\n }\n return runner.subscribe(body);\n });\n\n app.get<{ Querystring: { subId?: string; cursor?: string; limit?: string } }>(\n '/events',\n async (req) => {\n const q = req.query;\n const query: EventsQuery = {};\n if (q.subId) query.subId = q.subId;\n if (q.cursor !== undefined) query.cursor = Number(q.cursor);\n if (q.limit !== undefined) query.limit = Number(q.limit);\n return runner.getEvents(query);\n }\n );\n\n app.post<{ Body: OpenChannelRequest }>('/channels', async (req, reply) => {\n try {\n return await runner.openChannel(req.body?.destination);\n } catch (err) {\n return mapError(reply, err);\n }\n });\n\n app.get('/channels', async () => runner.getChannels());\n\n app.post<{ Body: SwapRequest }>('/swap', async (req, reply) => {\n const body = req.body;\n if (!body || !body.destination || body.amount === undefined) {\n return sendError(reply, 400, 'invalid_swap', {\n detail: 'body.destination and body.amount are required.',\n });\n }\n try {\n return await runner.swap(body);\n } catch (err) {\n return mapError(reply, err);\n }\n });\n}\n\nfunction isSignedEvent(event: unknown): event is NostrEvent {\n if (typeof event !== 'object' || event === null) return false;\n const e = event as Record<string, unknown>;\n return (\n typeof e['id'] === 'string' &&\n typeof e['sig'] === 'string' &&\n typeof e['pubkey'] === 'string' &&\n typeof e['kind'] === 'number'\n );\n}\n\nfunction mapError(reply: FastifyReply, err: unknown): FastifyReply {\n if (err instanceof NotReadyError) {\n return sendError(reply, 503, 'bootstrapping', {\n detail: err.message,\n retryable: true,\n });\n }\n if (err instanceof PublishRejectedError) {\n return sendError(reply, 502, 'rejected', { detail: err.message });\n }\n return sendError(reply, 500, 'internal_error', {\n detail: err instanceof Error ? err.message : String(err),\n });\n}\n\nfunction sendError(\n reply: FastifyReply,\n status: number,\n error: string,\n extra: { detail?: string; retryable?: boolean } = {}\n): FastifyReply {\n return reply.status(status).send({\n error,\n ...(extra.detail ? { detail: extra.detail } : {}),\n ...(extra.retryable ? { retryable: true } : {}),\n });\n}\n"],"mappings":"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","names":[]}
|
|
File without changes
|