@toon-protocol/rig 4.7.0 → 5.0.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.
Files changed (29) hide show
  1. package/README.md +30 -22
  2. package/dist/{chunk-J6Y3U645.js → chunk-2TG4YHEC.js} +59 -41
  3. package/dist/chunk-2TG4YHEC.js.map +1 -0
  4. package/dist/{chunk-3IVO2MSZ.js → chunk-4WVH2ITA.js} +3 -3
  5. package/dist/{chunk-U5A7MHBP.js → chunk-7VIVQHRG.js} +1 -1
  6. package/dist/chunk-7VIVQHRG.js.map +1 -0
  7. package/dist/{chunk-XQ2AWNHQ.js → chunk-CHVOXCDV.js} +0 -0
  8. package/dist/{chunk-XCLUB2CY.js → chunk-LMZSAFAB.js} +44 -15
  9. package/dist/chunk-LMZSAFAB.js.map +1 -0
  10. package/dist/{chunk-S3KKQ4IC.js → chunk-QKL53YZS.js} +2 -2
  11. package/dist/{chunk-UE5TAFEU.js → chunk-ZQGXFFBP.js} +4 -4
  12. package/dist/chunk-ZQGXFFBP.js.map +1 -0
  13. package/dist/{ci-serve-T33FCQVT.js → ci-serve-BU7NDT7O.js} +7 -7
  14. package/dist/cli/rig.js +6 -6
  15. package/dist/index.js +3 -3
  16. package/dist/standalone/index.d.ts +109 -74
  17. package/dist/standalone/index.js +13 -3
  18. package/dist/{standalone-mode-FY3XVQQK.js → standalone-mode-AT4S5EZG.js} +135 -49
  19. package/dist/standalone-mode-AT4S5EZG.js.map +1 -0
  20. package/package.json +2 -2
  21. package/dist/chunk-J6Y3U645.js.map +0 -1
  22. package/dist/chunk-U5A7MHBP.js.map +0 -1
  23. package/dist/chunk-UE5TAFEU.js.map +0 -1
  24. package/dist/chunk-XCLUB2CY.js.map +0 -1
  25. package/dist/standalone-mode-FY3XVQQK.js.map +0 -1
  26. /package/dist/{chunk-3IVO2MSZ.js.map → chunk-4WVH2ITA.js.map} +0 -0
  27. /package/dist/{chunk-XQ2AWNHQ.js.map → chunk-CHVOXCDV.js.map} +0 -0
  28. /package/dist/{chunk-S3KKQ4IC.js.map → chunk-QKL53YZS.js.map} +0 -0
  29. /package/dist/{ci-serve-T33FCQVT.js.map → ci-serve-BU7NDT7O.js.map} +0 -0
@@ -1,31 +1,37 @@
1
1
  import {
2
2
  ConnectorPublisher
3
- } from "./chunk-UE5TAFEU.js";
3
+ } from "./chunk-ZQGXFFBP.js";
4
4
  import {
5
5
  shaResolverFor
6
- } from "./chunk-XQ2AWNHQ.js";
6
+ } from "./chunk-CHVOXCDV.js";
7
7
  import {
8
8
  fetchRemoteState,
9
9
  resolveIdentity
10
- } from "./chunk-S3KKQ4IC.js";
10
+ } from "./chunk-QKL53YZS.js";
11
11
  import {
12
+ CHANNEL_STORE_FILENAME,
12
13
  ChannelMapStore,
13
- RIG_CHANNEL_MAP_FILENAME
14
- } from "./chunk-XCLUB2CY.js";
14
+ RIG_CHANNEL_MAP_FILENAME,
15
+ isToonChannelStore,
16
+ legacyChannelFiles
17
+ } from "./chunk-LMZSAFAB.js";
15
18
  import {
16
19
  NonceLock,
17
20
  checkDaemonIdentity,
18
21
  deriveNostrKeyFromMnemonic,
19
22
  standaloneForced
20
- } from "./chunk-U5A7MHBP.js";
23
+ } from "./chunk-7VIVQHRG.js";
21
24
 
22
25
  // src/cli/standalone-mode.ts
23
26
  import { readFileSync } from "fs";
24
27
  import { homedir } from "os";
25
28
  import { join } from "path";
26
29
  import {
30
+ PAYMENT_CHANNELS_PROGRAM_ID,
27
31
  ToonClient,
28
- defaultDestinationFor
32
+ X402_BATCH_SETTLEMENT_ADDRESS,
33
+ defaultDestinationFor,
34
+ evmChainIdOf
29
35
  } from "@toon-protocol/client";
30
36
  function configDir(env) {
31
37
  return env["TOON_CLIENT_HOME"] ?? join(homedir(), ".toon-client");
@@ -113,7 +119,10 @@ function resolveConnectorSettings(args) {
113
119
  ...rpcUrl ? { rpcUrl } : {},
114
120
  transport,
115
121
  keyDerivation,
116
- channelStorePath: env["TOON_CLIENT_CHANNEL_STORE"] ?? file.channelStorePath ?? join(args.configDir, "channels.json"),
122
+ channelStorePath: env["TOON_CLIENT_CHANNEL_STORE"] ?? file.channelStorePath ?? join(args.configDir, CHANNEL_STORE_FILENAME),
123
+ ...(env["TOON_CLIENT_FACILITATOR_URL"] ?? file.facilitatorUrl) !== void 0 ? {
124
+ facilitatorUrl: env["TOON_CLIENT_FACILITATOR_URL"] ?? file.facilitatorUrl
125
+ } : {},
117
126
  ...depositRaw !== void 0 ? { deposit: BigInt(depositRaw) } : {},
118
127
  eventFee: BigInt(
119
128
  env["TOON_CLIENT_FEE_PER_EVENT"] ?? file.feePerEvent ?? "0"
@@ -159,72 +168,119 @@ function readSolanaKeyFile(path) {
159
168
  }
160
169
  return Uint8Array.from(parsed);
161
170
  }
162
- function recordFor(state, args) {
163
- const tokenNetwork = state.domain.tokenNetwork ?? state.domain.programId ?? "";
171
+ function recordFor(summary, args) {
172
+ const { channel } = summary;
173
+ const tokenNetwork = channel.chain === "evm" ? X402_BATCH_SETTLEMENT_ADDRESS : PAYMENT_CHANNELS_PROGRAM_ID;
164
174
  return {
165
- channelId: state.channelId,
175
+ channelId: channel.channelId,
166
176
  peerId: args.peerId,
167
177
  identity: args.identity,
168
178
  destination: args.destination,
169
- chain: state.domain.chain,
179
+ chain: channel.network,
170
180
  tokenNetwork,
171
181
  context: {
172
- chainType: state.chain,
173
- chainId: state.domain.chainId ?? 0,
182
+ chainType: channel.chain,
183
+ chainId: channel.chain === "evm" ? evmChainIdOf(channel.network) : 0,
174
184
  tokenNetworkAddress: tokenNetwork,
175
- tokenAddress: state.domain.token,
176
- recipient: state.domain.counterparty
185
+ tokenAddress: channel.config.token,
186
+ recipient: channel.config.receiver
177
187
  },
178
- depositTotal: state.depositTotal.toString()
188
+ depositTotal: summary.depositTotal.toString()
179
189
  };
180
190
  }
191
+ function recordChannel(channelMap, summary, args) {
192
+ const next = recordFor(summary, args);
193
+ const previous = channelMap.listFor(args.identity, args.destination).find(
194
+ (r) => r.chain === next.chain && r.tokenNetwork === next.tokenNetwork && r.channelId !== next.channelId
195
+ );
196
+ if (previous) channelMap.supersede(previous);
197
+ if (channelMap.list().some((r) => r.channelId === next.channelId)) {
198
+ channelMap.touch(
199
+ {
200
+ identity: next.identity,
201
+ destination: next.destination,
202
+ chain: next.chain,
203
+ tokenNetwork: next.tokenNetwork
204
+ },
205
+ { depositTotal: next.depositTotal ?? "0" }
206
+ );
207
+ } else {
208
+ channelMap.record(next);
209
+ }
210
+ }
211
+ function exitResultFor(results, channelId, step) {
212
+ const result = results.find((r) => r.channelId === channelId);
213
+ if (result?.error !== void 0) {
214
+ throw new Error(`channel ${step} of ${channelId} failed: ${result.error}`);
215
+ }
216
+ return result;
217
+ }
181
218
  function buildMoneyOps(args) {
182
219
  const { client, channelMap } = args;
220
+ const now = args.now ?? (() => BigInt(Math.floor(Date.now() / 1e3)));
183
221
  const known = (channelId) => channelMap.list().some((r) => r.channelId === channelId);
184
- const requireSameChannel = async (record) => {
185
- const state = await client.channel.state();
186
- if (state.channelId !== record.channelId) {
222
+ const held = (record2) => {
223
+ const summary = client.channel.channels().find((c) => c.channel.channelId === record2.channelId);
224
+ if (!summary) {
187
225
  throw new Error(
188
- `this identity's channel with ${args.destination} is ${state.channelId}, not the recorded ${record.channelId} \u2014 the record is stale or belongs to another node`
226
+ `channel ${record2.channelId} is not in this client's x402 channel store: the record belongs to another store (TOON_CLIENT_CHANNEL_STORE) or another node`
189
227
  );
190
228
  }
229
+ return summary;
191
230
  };
231
+ const record = (summary) => recordChannel(channelMap, summary, args);
192
232
  return {
193
233
  async openChannel(opts) {
194
- const before = await client.channel.state().catch(() => void 0);
195
- const resumed = before !== void 0 && known(before.channelId);
196
- const state = opts?.deposit !== void 0 && before?.status === "open" ? await client.channel.deposit(opts.deposit) : await client.channel.open(
197
- opts?.deposit !== void 0 ? { deposit: opts.deposit } : {}
198
- );
199
- channelMap.record(recordFor(state, args));
234
+ const before = await client.channel.current();
235
+ if (opts?.deposit !== void 0 && client.chain === "solana") {
236
+ throw new Error(
237
+ "a Solana x402 channel cannot be topped up: set TOON_CLIENT_DEPOSIT (config `deposit`) to what a channel opens with; the client opens a fresh sponsored channel when the current one runs out"
238
+ );
239
+ }
240
+ const resumed = before !== void 0 && known(before.channel.channelId);
241
+ let summary = before ?? await client.channel.open();
242
+ if (opts?.deposit !== void 0) {
243
+ summary = await client.channel.deposit(opts.deposit);
244
+ }
245
+ record(summary);
200
246
  return {
201
- channelId: state.channelId,
247
+ channelId: summary.channel.channelId,
202
248
  resumed,
203
249
  destination: args.destination,
204
- chain: state.domain.chain,
250
+ chain: summary.channel.network,
205
251
  peerId: args.peerId,
206
- depositTotal: state.depositTotal.toString(),
252
+ depositTotal: summary.depositTotal.toString(),
207
253
  ...opts?.deposit !== void 0 ? { depositAdded: opts.deposit.toString() } : {}
208
254
  };
209
255
  },
210
- async closeChannel(record) {
211
- await requireSameChannel(record);
212
- const result = await client.channel.close();
213
- const closedAt = result.closedAt ?? BigInt(Math.floor(Date.now() / 1e3));
256
+ async closeChannel(rec) {
257
+ held(rec);
258
+ const results = await client.channel.close();
259
+ const result = exitResultFor(results, rec.channelId, "close");
260
+ const after = held(rec);
261
+ const closedAt = after.closedAt ?? now();
214
262
  return {
215
- channelId: record.channelId,
216
- ...result.txHash ? { txHash: result.txHash } : {},
263
+ channelId: rec.channelId,
264
+ ...result?.transaction ? { txHash: result.transaction } : {},
217
265
  closedAt: closedAt.toString(),
218
- settleableAt: (result.settleableAt ?? closedAt).toString()
266
+ settleableAt: (result?.settleableAt ?? after.settleableAt ?? closedAt).toString()
219
267
  };
220
268
  },
221
- async settleChannel(record) {
222
- await requireSameChannel(record);
223
- const result = await client.channel.settle();
224
- channelMap.supersede(record);
269
+ async settleChannel(rec) {
270
+ held(rec);
271
+ const results = await client.channel.settle();
272
+ const result = exitResultFor(results, rec.channelId, "settle");
273
+ const after = held(rec);
274
+ if (after.settledAt === void 0) {
275
+ const at = after.settleableAt;
276
+ throw new Error(
277
+ `channel ${rec.channelId} is not settleable yet` + (at !== void 0 ? ` (settleable at ${new Date(Number(at) * 1e3).toISOString()})` : "") + ": nothing was spent; re-run after that time"
278
+ );
279
+ }
280
+ channelMap.supersede(rec);
225
281
  return {
226
- channelId: record.channelId,
227
- ...result.txHash ? { txHash: result.txHash } : {}
282
+ channelId: rec.channelId,
283
+ ...result?.transaction ? { txHash: result.transaction } : {}
228
284
  };
229
285
  },
230
286
  async walletChainBalances() {
@@ -295,10 +351,23 @@ async function createStandaloneContext(options) {
295
351
  throw missing;
296
352
  }
297
353
  const connectorUrl = settings.connectorUrl;
354
+ if (isToonChannelStore(settings.channelStorePath)) {
355
+ throw new Error(
356
+ `${settings.channelStorePath} is a client 3.x toon-channel store, which a current connector no longer honours: point TOON_CLIENT_CHANNEL_STORE (config channelStorePath) at a new file, e.g. ${join(dir, CHANNEL_STORE_FILENAME)}; the old file is left as it is`
357
+ );
358
+ }
359
+ for (const legacy of legacyChannelFiles(dir)) {
360
+ if (legacy === settings.channelStorePath) continue;
361
+ warn(
362
+ `rig: ${legacy} is client 3.x toon-channel state; client 4.x ignores it and keeps its x402 channels in ${settings.channelStorePath}`
363
+ );
364
+ }
298
365
  if (!standaloneForced(env)) await checkDaemonIdentity(nostr.pubkey);
299
366
  const lock = await NonceLock.acquire(nostr.pubkey);
300
367
  const clients = [];
368
+ let recordHeld;
301
369
  const stop = async () => {
370
+ await recordHeld?.().catch(() => void 0);
302
371
  for (const c of clients) await c.close();
303
372
  lock.release();
304
373
  };
@@ -315,8 +384,14 @@ async function createStandaloneContext(options) {
315
384
  ...settings.chain ? { chain: settings.chain } : {},
316
385
  ...settings.rpcUrl ? { rpcUrl: settings.rpcUrl } : {},
317
386
  transport: settings.transport,
387
+ // The first paid send opens the channel (on Solana the connector
388
+ // sponsors the open: USDC for the deposit, no SOL), and a send the
389
+ // channel cannot cover opens a fresh one.
318
390
  autoOpenChannel: true,
319
- ...settings.deposit !== void 0 ? { deposit: settings.deposit } : {}
391
+ ...settings.deposit !== void 0 ? { deposit: settings.deposit } : {},
392
+ // A Base deposit goes through a facilitator; Solana names none, so an
393
+ // unset facilitator on Solana is '' rather than the devnet default.
394
+ ...settings.facilitatorUrl !== void 0 ? { facilitatorUrl: settings.facilitatorUrl } : settings.chain === "solana" ? { facilitatorUrl: "" } : {}
320
395
  };
321
396
  if (settings.chain && !settings.rpcUrl) {
322
397
  warn(
@@ -331,6 +406,11 @@ async function createStandaloneContext(options) {
331
406
  clients.push(client);
332
407
  const desc = await client.describe();
333
408
  const destinations = resolveDestinations(desc, settings, connectorUrl);
409
+ if (!settings.chain && desc.batchSettlements.length > 1) {
410
+ warn(
411
+ `rig: paying on ${client.chain}, the first chain ${connectorUrl} offers that this identity holds a key for (it offers ${desc.batchSettlements.map((t) => t.network).join(", ")}); set TOON_CLIENT_CHAIN (config chain) to choose`
412
+ );
413
+ }
334
414
  let storeClient = client;
335
415
  if (settings.storeConnectorUrl && normalizeUrl(settings.storeConnectorUrl) !== normalizeUrl(connectorUrl)) {
336
416
  storeClient = await ToonClient.create({
@@ -359,13 +439,16 @@ async function createStandaloneContext(options) {
359
439
  mapPath: join(dir, RIG_CHANNEL_MAP_FILENAME),
360
440
  watermarkPath: settings.channelStorePath
361
441
  });
362
- const money = buildMoneyOps({
363
- client,
364
- channelMap,
442
+ const anchor = {
365
443
  identity: nostr.pubkey,
366
444
  destination: options.channelDestination ?? destinations.publish,
367
445
  peerId: desc.edgeIdentity?.keyId ?? connectorUrl
368
- });
446
+ };
447
+ const money = buildMoneyOps({ client, channelMap, ...anchor });
448
+ recordHeld = async () => {
449
+ const current = await client.channel.current();
450
+ if (current) recordChannel(channelMap, current, anchor);
451
+ };
369
452
  return {
370
453
  ownerPubkey: nostr.pubkey,
371
454
  identitySource: identity.source,
@@ -390,12 +473,15 @@ function normalizeUrl(url) {
390
473
  }
391
474
  export {
392
475
  MissingUplinkError,
476
+ buildMoneyOps,
393
477
  chainFamilyOf,
394
478
  createStandaloneContext,
395
479
  defaultStoreDestinationFor,
396
480
  readClientConfig,
397
481
  readSolanaKeyFile,
482
+ recordChannel,
483
+ recordFor,
398
484
  resolveConnectorSettings,
399
485
  resolveDestinations
400
486
  };
401
- //# sourceMappingURL=standalone-mode-FY3XVQQK.js.map
487
+ //# sourceMappingURL=standalone-mode-AT4S5EZG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/standalone-mode.ts"],"sourcesContent":["/**\n * The standalone (embedded-client) session behind every paid command.\n *\n * One URL is the whole configuration. A TOON connector describes itself on\n * `GET /ilp` (connector ADR 0050): its ILP addresses, the routes it prices,\n * the x402 `batch-settlement` terms it accepts per chain and the key a payload\n * is sealed to. The client (`@toon-protocol/client` 4.x) reads that once,\n * opens or resumes an x402 channel with the node, and pays each request with a\n * signed voucher naming the running total (connector ADR 0075; the\n * toon-channel claims of client 3.x are refused). There is nothing to\n * discover and nothing to negotiate: kind:10032 announces were removed by\n * ADR 0046, and a node's word about itself replaced them.\n *\n * What this module resolves, in precedence order (env > config file):\n *\n * - `TOON_CONNECTOR` / `connectorUrl` — the node's client-edge URL. The\n * legacy `TOON_CLIENT_PROXY_URL` / `proxyUrl` (…`/ilp`) is still read; the\n * client normalizes a trailing `/ilp` away. `btpUrl` is ignored (the client\n * reads the BTP endpoint from `GET /ilp`).\n * - `TOON_CLIENT_PUBLISH_DESTINATION` / `publishDestination` — the route\n * events go to. Default: the first address the node publishes for itself\n * that it also prices (`defaultDestinationFor`).\n * - `TOON_CLIENT_STORE_DESTINATION` / `storeDestination` — the route git\n * objects go to. Default: the node's first priced route named `*.store`,\n * else `*.ario`. A `--via` override (`rig name`) outranks both.\n * - `TOON_CLIENT_STORE_CONNECTOR_URL` / `storeConnectorUrl` — when the store\n * route terminates on ANOTHER node that holds its own channel: that node's\n * URL. Uploads then ride a second client with its own watermark file.\n * `storeSealTo` alone (same channel, different terminating identity) makes\n * the edge's client seal to that node instead.\n * - `TOON_CLIENT_CHAIN` / `chain` — `evm` or `solana` (a full id such as\n * `evm:8453` is read by its family). Default: the first chain in the node's\n * `batchSettlements[]` this identity holds a key for. A phrase holds both,\n * so name one when the node offers several.\n * - `TOON_CLIENT_RPC_URL` / `rpcUrl` / `chainRpcUrls[<chain>]`.\n * - `TOON_CLIENT_CHANNEL_STORE` / `channelStorePath`: the client's x402\n * channel store. Default `<dir>/channels-x402.json` (+ `.peers.json`). A\n * client 3.x toon-channel store is refused here and never written.\n * - `TOON_CLIENT_FACILITATOR_URL` / `facilitatorUrl`: the x402 facilitator\n * that submits a Base deposit and pays its gas. Solana needs none: the\n * connector sponsors the open.\n * - `RIG_SOLANA_KEY_FILE` / `solanaKeyFile`, `RIG_EVM_PRIVATE_KEY` /\n * `evmPrivateKey` — pay with THIS key instead of the phrase's derived one.\n * The author stays the phrase's Nostr key; who pays and who signs the\n * event are independent facts (the connector attributes payment from the\n * voucher, never from the event).\n *\n * Key derivation: a rig phrase yields its Nostr key at `m/44'/1237'/0'/0/i`,\n * and its EVM account has always been that same secp256k1 key — which the\n * client calls `keyDerivation: 'legacy'`. That is the default here, so every\n * channel an existing identity funded is still its own. `keyDerivation:\n * 'standard'` / `TOON_CLIENT_KEY_DERIVATION=standard` opts into the\n * MetaMask-importable path for a fresh identity.\n */\n\nimport { readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport {\n PAYMENT_CHANNELS_PROGRAM_ID,\n ToonClient,\n X402_BATCH_SETTLEMENT_ADDRESS,\n defaultDestinationFor,\n evmChainIdOf,\n type BatchChannelSummary,\n type BatchExitResult,\n type ChannelFacade,\n type NodeSelfDescription,\n type ToonClientConfig,\n} from '@toon-protocol/client';\nimport { shaResolverFor } from '../git-sha-resolver.js';\nimport { fetchRemoteState } from '../remote-state.js';\nimport {\n CHANNEL_STORE_FILENAME,\n ChannelMapStore,\n RIG_CHANNEL_MAP_FILENAME,\n isToonChannelStore,\n legacyChannelFiles,\n type ChannelMapRecord,\n} from '../standalone/channel-map.js';\nimport { ConnectorPublisher } from '../standalone/connector-publisher.js';\nimport type {\n ChannelCloseOutcome,\n ChannelOpenOutcome,\n ChannelSettleOutcome,\n StandaloneMoneyOps,\n WalletChainBalanceInfo,\n} from '../standalone/money.js';\nimport {\n NonceLock,\n checkDaemonIdentity,\n standaloneForced,\n} from '../standalone/nonce-guard.js';\nimport { deriveNostrKeyFromMnemonic } from '../standalone/nostr-identity.js';\nimport { resolveIdentity } from './identity.js';\nimport type {\n StandaloneContext,\n StandaloneLoadOptions,\n} from './standalone-context.js';\n\n// ---------------------------------------------------------------------------\n// Config file\n// ---------------------------------------------------------------------------\n\n/** The shared `~/.toon-client/config.json` fields rig reads. */\nexport interface ClientConfigFile {\n /** The connector's client-edge URL (`GET /ilp` lives here). */\n connectorUrl?: string;\n /** @deprecated the pre-2.0 spelling: the connector's `…/ilp` ingress URL. */\n proxyUrl?: string;\n /** @deprecated ignored — the client reads the BTP endpoint from `GET /ilp`. */\n btpUrl?: string;\n /** Free-read relay (WebSocket) for the identity's kind:0 and NIP-34 reads. */\n relayUrl?: string;\n /** @deprecated alias of `publishDestination`. */\n destination?: string;\n publishDestination?: string;\n storeDestination?: string;\n storeConnectorUrl?: string;\n storeSealTo?: string;\n feePerEvent?: string;\n /** The client's x402 channel store; default `<dir>/channels-x402.json`. */\n channelStorePath?: string;\n /** The x402 facilitator for a Base deposit. */\n facilitatorUrl?: string;\n /** `evm` | `solana`, or a full id (`evm:8453`) read by its family. */\n chain?: string;\n /** @deprecated pre-2.0 chain list; the first entry is read as `chain`. */\n supportedChains?: string[];\n rpcUrl?: string;\n chainRpcUrls?: Record<string, string>;\n transport?: 'auto' | 'http' | 'btp';\n keyDerivation?: 'standard' | 'legacy';\n mnemonicAccountIndex?: number;\n /** What each x402 channel is opened with, base units. */\n deposit?: string;\n /** Path to a Solana keypair JSON (64-byte array) that pays instead of the phrase's key. */\n solanaKeyFile?: string;\n /** Hex EVM private key that pays instead of the phrase's key. */\n evmPrivateKey?: string;\n /** Per-packet timeout for uploads, ms. */\n uploadTimeoutMs?: number;\n [key: string]: unknown;\n}\n\nfunction configDir(env: NodeJS.ProcessEnv): string {\n return env['TOON_CLIENT_HOME'] ?? join(homedir(), '.toon-client');\n}\n\nexport function readClientConfig(path: string): ClientConfigFile {\n try {\n return JSON.parse(readFileSync(path, 'utf8')) as ClientConfigFile;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return {};\n throw new Error(\n `failed to read client config at ${path}: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n}\n\n/** An identity was resolved, but there is no connector to pay. */\nexport class MissingUplinkError extends Error {\n constructor(configPath: string) {\n super(\n \"no connector configured: set TOON_CONNECTOR to the node's URL \" +\n '(the one whose GET /ilp describes it), run `rig entry <url>`, or add ' +\n `connectorUrl to ${configPath}`\n );\n this.name = 'MissingUplinkError';\n }\n}\n\n// ---------------------------------------------------------------------------\n// Settings (pure: env + file → what the client is built from)\n// ---------------------------------------------------------------------------\n\nexport type ChainFamily = 'evm' | 'solana';\n\nexport interface ConnectorSettings {\n connectorUrl?: string;\n connectorSource: 'env' | 'config' | 'legacy-env' | 'legacy-config' | null;\n publishDestination?: string;\n storeDestination?: string;\n storeConnectorUrl?: string;\n storeSealTo?: string;\n relayUrl?: string;\n chain?: ChainFamily;\n /** The full chain id the user named, when they named one (`evm:8453`). */\n chainKey?: string;\n rpcUrl?: string;\n transport: 'auto' | 'http' | 'btp';\n keyDerivation: 'standard' | 'legacy';\n channelStorePath: string;\n facilitatorUrl?: string;\n deposit?: bigint;\n eventFee: bigint;\n solanaKeyFile?: string;\n evmPrivateKey?: string;\n uploadTimeoutMs?: number;\n /** Notes for the caller's stderr: deprecated spellings met on the way. */\n warnings: string[];\n}\n\n/** `evm:8453` → `evm`; `solana` → `solana`; anything else → undefined. */\nexport function chainFamilyOf(\n chain: string | undefined\n): ChainFamily | undefined {\n if (!chain) return undefined;\n const family = chain.split(':')[0];\n return family === 'evm' || family === 'solana' ? family : undefined;\n}\n\nexport function resolveConnectorSettings(args: {\n env: NodeJS.ProcessEnv;\n file: ClientConfigFile;\n configDir: string;\n storeDestinationOverride?: string;\n}): ConnectorSettings {\n const { env, file } = args;\n const warnings: string[] = [];\n\n let connectorUrl: string | undefined;\n let connectorSource: ConnectorSettings['connectorSource'] = null;\n if (env['TOON_CONNECTOR']) {\n connectorUrl = env['TOON_CONNECTOR'];\n connectorSource = 'env';\n } else if (env['TOON_CLIENT_PROXY_URL']) {\n connectorUrl = env['TOON_CLIENT_PROXY_URL'];\n connectorSource = 'legacy-env';\n warnings.push(\n 'rig: TOON_CLIENT_PROXY_URL is the pre-2.0 spelling — set TOON_CONNECTOR to the node URL instead'\n );\n } else if (file.connectorUrl) {\n connectorUrl = file.connectorUrl;\n connectorSource = 'config';\n } else if (file.proxyUrl) {\n connectorUrl = file.proxyUrl;\n connectorSource = 'legacy-config';\n warnings.push(\n 'rig: config `proxyUrl` is the pre-2.0 spelling — `rig entry <url>` records `connectorUrl`'\n );\n }\n if (env['TOON_CLIENT_BTP_URL'] || file.btpUrl) {\n warnings.push(\n \"rig: btpUrl is ignored since 4.0 — the client reads the BTP endpoint from the node's GET /ilp\"\n );\n }\n\n const chainKey =\n env['TOON_CLIENT_CHAIN'] ?? file.chain ?? file.supportedChains?.[0];\n const chain = chainFamilyOf(chainKey);\n if (chainKey && !chain) {\n warnings.push(\n `rig: chain ${JSON.stringify(chainKey)} is not a settlement chain the connector supports (evm | solana) — ignoring it`\n );\n }\n const rpcUrl =\n env['TOON_CLIENT_RPC_URL'] ??\n file.rpcUrl ??\n (chainKey ? file.chainRpcUrls?.[chainKey] : undefined) ??\n (chain ? file.chainRpcUrls?.[chain] : undefined);\n\n const transportRaw = env['TOON_CLIENT_TRANSPORT'] ?? file.transport;\n const transport: ConnectorSettings['transport'] =\n transportRaw === 'http' || transportRaw === 'btp' ? transportRaw : 'auto';\n\n const derivationRaw = env['TOON_CLIENT_KEY_DERIVATION'] ?? file.keyDerivation;\n const keyDerivation: ConnectorSettings['keyDerivation'] =\n derivationRaw === 'standard' ? 'standard' : 'legacy';\n\n const depositRaw = env['TOON_CLIENT_DEPOSIT'] ?? file.deposit;\n const publishDestination =\n env['TOON_CLIENT_PUBLISH_DESTINATION'] ??\n env['TOON_CLIENT_DESTINATION'] ??\n file.publishDestination ??\n file.destination;\n const storeDestination =\n args.storeDestinationOverride ??\n env['TOON_CLIENT_STORE_DESTINATION'] ??\n file.storeDestination;\n const relayUrl = env['TOON_CLIENT_RELAY_URL'] ?? file.relayUrl;\n\n return {\n ...(connectorUrl ? { connectorUrl } : {}),\n connectorSource,\n ...(publishDestination ? { publishDestination } : {}),\n ...(storeDestination ? { storeDestination } : {}),\n ...((env['TOON_CLIENT_STORE_CONNECTOR_URL'] ?? file.storeConnectorUrl)\n ? {\n storeConnectorUrl:\n env['TOON_CLIENT_STORE_CONNECTOR_URL'] ?? file.storeConnectorUrl,\n }\n : {}),\n ...((env['TOON_CLIENT_STORE_SEAL_TO'] ?? file.storeSealTo)\n ? { storeSealTo: env['TOON_CLIENT_STORE_SEAL_TO'] ?? file.storeSealTo }\n : {}),\n ...(relayUrl ? { relayUrl } : {}),\n ...(chain ? { chain } : {}),\n ...(chainKey ? { chainKey } : {}),\n ...(rpcUrl ? { rpcUrl } : {}),\n transport,\n keyDerivation,\n channelStorePath:\n env['TOON_CLIENT_CHANNEL_STORE'] ??\n file.channelStorePath ??\n join(args.configDir, CHANNEL_STORE_FILENAME),\n ...((env['TOON_CLIENT_FACILITATOR_URL'] ?? file.facilitatorUrl) !==\n undefined\n ? {\n facilitatorUrl:\n env['TOON_CLIENT_FACILITATOR_URL'] ?? file.facilitatorUrl,\n }\n : {}),\n ...(depositRaw !== undefined ? { deposit: BigInt(depositRaw) } : {}),\n eventFee: BigInt(\n env['TOON_CLIENT_FEE_PER_EVENT'] ?? file.feePerEvent ?? '0'\n ),\n ...((env['RIG_SOLANA_KEY_FILE'] ?? file.solanaKeyFile)\n ? { solanaKeyFile: env['RIG_SOLANA_KEY_FILE'] ?? file.solanaKeyFile }\n : {}),\n ...((env['RIG_EVM_PRIVATE_KEY'] ?? file.evmPrivateKey)\n ? { evmPrivateKey: env['RIG_EVM_PRIVATE_KEY'] ?? file.evmPrivateKey }\n : {}),\n ...(file.uploadTimeoutMs !== undefined\n ? { uploadTimeoutMs: file.uploadTimeoutMs }\n : {}),\n warnings,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Destinations (pure: settings + the node's self-description)\n// ---------------------------------------------------------------------------\n\n/**\n * The store route a node offers, when the caller named none: its first priced\n * route spelled `*.store`, else `*.ario` (the AR.IO-backed store this\n * repository has always uploaded to), else nothing.\n */\nexport function defaultStoreDestinationFor(\n desc: NodeSelfDescription\n): string | undefined {\n const prefixes = desc.routes.map((r) => r.prefix);\n return (\n prefixes.find((p) => p === 'store' || p.endsWith('.store')) ??\n prefixes.find((p) => p === 'ario' || p.endsWith('.ario')) ??\n prefixes.find((p) => /store/i.test(p))\n );\n}\n\nexport function resolveDestinations(\n desc: NodeSelfDescription,\n settings: Pick<ConnectorSettings, 'publishDestination' | 'storeDestination'>,\n connectorUrl: string\n): { publish: string; store: string } {\n const publish = settings.publishDestination ?? defaultDestinationFor(desc);\n if (!publish) {\n throw new Error(\n `the connector at ${connectorUrl} publishes no priced address of its own — ` +\n 'set publishDestination / TOON_CLIENT_PUBLISH_DESTINATION to one of its GET /ilp routes'\n );\n }\n const store = settings.storeDestination ?? defaultStoreDestinationFor(desc);\n if (!store) {\n throw new Error(\n `the connector at ${connectorUrl} prices no store route (none named *.store or *.ario) — ` +\n 'set storeDestination / TOON_CLIENT_STORE_DESTINATION to the route its store answers on'\n );\n }\n return { publish, store };\n}\n\n// ---------------------------------------------------------------------------\n// Key files\n// ---------------------------------------------------------------------------\n\n/** A Solana keypair file: the 64-byte JSON array `solana-keygen` writes. */\nexport function readSolanaKeyFile(path: string): Uint8Array {\n let parsed: unknown;\n try {\n parsed = JSON.parse(readFileSync(path, 'utf8'));\n } catch (err) {\n throw new Error(\n `failed to read the Solana key file ${path}: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n if (\n !Array.isArray(parsed) ||\n (parsed.length !== 64 && parsed.length !== 32) ||\n !parsed.every((n) => Number.isInteger(n) && n >= 0 && n <= 255)\n ) {\n throw new Error(\n `${path} is not a Solana keypair file (expected a JSON array of 64 bytes)`\n );\n }\n return Uint8Array.from(parsed as number[]);\n}\n\n// ---------------------------------------------------------------------------\n// Money ops on the client's channel and wallet facades\n// ---------------------------------------------------------------------------\n\n/** One x402 channel as the peer→channel map records it. */\nexport function recordFor(\n summary: BatchChannelSummary,\n args: { identity: string; destination: string; peerId: string }\n): Omit<ChannelMapRecord, 'openedAt' | 'lastUsedAt'> {\n const { channel } = summary;\n const tokenNetwork =\n channel.chain === 'evm'\n ? X402_BATCH_SETTLEMENT_ADDRESS\n : PAYMENT_CHANNELS_PROGRAM_ID;\n return {\n channelId: channel.channelId,\n peerId: args.peerId,\n identity: args.identity,\n destination: args.destination,\n chain: channel.network,\n tokenNetwork,\n context: {\n chainType: channel.chain,\n chainId: channel.chain === 'evm' ? evmChainIdOf(channel.network) : 0,\n tokenNetworkAddress: tokenNetwork,\n tokenAddress: channel.config.token,\n recipient: channel.config.receiver,\n },\n depositTotal: summary.depositTotal.toString(),\n };\n}\n\n/**\n * Record `summary` in the peer→channel map, or refresh its deposit when it is\n * already there. A different channel recorded under the same key (one the\n * client replaced when it ran out) is superseded, not overwritten: it still\n * holds what it has left, and `rig channel close/settle` find it by listing.\n */\nexport function recordChannel(\n channelMap: ChannelMapStore,\n summary: BatchChannelSummary,\n args: { identity: string; destination: string; peerId: string }\n): void {\n const next = recordFor(summary, args);\n const previous = channelMap\n .listFor(args.identity, args.destination)\n .find(\n (r) =>\n r.chain === next.chain &&\n r.tokenNetwork === next.tokenNetwork &&\n r.channelId !== next.channelId\n );\n if (previous) channelMap.supersede(previous);\n if (channelMap.list().some((r) => r.channelId === next.channelId)) {\n channelMap.touch(\n {\n identity: next.identity,\n destination: next.destination,\n chain: next.chain,\n tokenNetwork: next.tokenNetwork,\n },\n { depositTotal: next.depositTotal ?? '0' }\n );\n } else {\n channelMap.record(next);\n }\n}\n\n/** The slice of `ToonClient` the money ops drive (structural, so tests fake it). */\nexport interface MoneyClientLike {\n readonly chain: 'evm' | 'solana';\n readonly channel: ChannelFacade;\n readonly wallet: Pick<ToonClient['wallet'], 'balances'>;\n}\n\n/** This channel's step out of a close()/settle() that walks every channel. */\nfunction exitResultFor(\n results: BatchExitResult[],\n channelId: string,\n step: 'close' | 'settle'\n): BatchExitResult | undefined {\n const result = results.find((r) => r.channelId === channelId);\n if (result?.error !== undefined) {\n throw new Error(`channel ${step} of ${channelId} failed: ${result.error}`);\n }\n return result;\n}\n\nexport function buildMoneyOps(args: {\n client: MoneyClientLike;\n channelMap: ChannelMapStore;\n identity: string;\n destination: string;\n peerId: string;\n now?: () => bigint;\n}): StandaloneMoneyOps {\n const { client, channelMap } = args;\n const now = args.now ?? (() => BigInt(Math.floor(Date.now() / 1000)));\n const known = (channelId: string) =>\n channelMap.list().some((r) => r.channelId === channelId);\n /**\n * The client's own record of a recorded channel. Exit is by channel config,\n * which only the client's store holds; a record it does not know belongs to\n * another store or another node.\n */\n const held = (record: ChannelMapRecord): BatchChannelSummary => {\n const summary = client.channel\n .channels()\n .find((c) => c.channel.channelId === record.channelId);\n if (!summary) {\n throw new Error(\n `channel ${record.channelId} is not in this client's x402 channel store: ` +\n 'the record belongs to another store (TOON_CLIENT_CHANNEL_STORE) or another node'\n );\n }\n return summary;\n };\n const record = (summary: BatchChannelSummary) =>\n recordChannel(channelMap, summary, args);\n return {\n async openChannel(opts): Promise<ChannelOpenOutcome> {\n const before = await client.channel.current();\n // A Solana channel has no top-up that costs no SOL: the client opens a\n // fresh sponsored one when the next packet no longer fits instead. Say\n // so before anything is opened, rather than open and then refuse.\n if (opts?.deposit !== undefined && client.chain === 'solana') {\n throw new Error(\n 'a Solana x402 channel cannot be topped up: set TOON_CLIENT_DEPOSIT ' +\n '(config `deposit`) to what a channel opens with; the client opens a ' +\n 'fresh sponsored channel when the current one runs out'\n );\n }\n const resumed = before !== undefined && known(before.channel.channelId);\n let summary = before ?? (await client.channel.open());\n if (opts?.deposit !== undefined) {\n summary = await client.channel.deposit(opts.deposit);\n }\n record(summary);\n return {\n channelId: summary.channel.channelId,\n resumed,\n destination: args.destination,\n chain: summary.channel.network,\n peerId: args.peerId,\n depositTotal: summary.depositTotal.toString(),\n ...(opts?.deposit !== undefined\n ? { depositAdded: opts.deposit.toString() }\n : {}),\n };\n },\n async closeChannel(rec): Promise<ChannelCloseOutcome> {\n held(rec);\n // The client leaves EVERY channel still open with the node in one call:\n // a channel replaced when it ran out still holds what it has left.\n const results = await client.channel.close();\n const result = exitResultFor(results, rec.channelId, 'close');\n const after = held(rec);\n const closedAt = after.closedAt ?? now();\n return {\n channelId: rec.channelId,\n ...(result?.transaction ? { txHash: result.transaction } : {}),\n closedAt: closedAt.toString(),\n settleableAt: (\n result?.settleableAt ??\n after.settleableAt ??\n closedAt\n ).toString(),\n };\n },\n async settleChannel(rec): Promise<ChannelSettleOutcome> {\n held(rec);\n const results = await client.channel.settle();\n const result = exitResultFor(results, rec.channelId, 'settle');\n const after = held(rec);\n if (after.settledAt === undefined) {\n const at = after.settleableAt;\n throw new Error(\n `channel ${rec.channelId} is not settleable yet` +\n (at !== undefined\n ? ` (settleable at ${new Date(Number(at) * 1000).toISOString()})`\n : '') +\n ': nothing was spent; re-run after that time'\n );\n }\n channelMap.supersede(rec);\n return {\n channelId: rec.channelId,\n ...(result?.transaction ? { txHash: result.transaction } : {}),\n };\n },\n async walletChainBalances(): Promise<WalletChainBalanceInfo[]> {\n return (await client.wallet.balances()).map((b) => ({\n chain: b.chain,\n chainKey: b.chainKey,\n address: b.address,\n ...(b.native ? { native: b.native } : {}),\n tokens: b.tokens,\n ...(b.unreadable !== undefined ? { unreadable: b.unreadable } : {}),\n ...(b.error !== undefined ? { error: b.error } : {}),\n }));\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// The context factory\n// ---------------------------------------------------------------------------\n\n/** What `rig balance` gets when nothing is configured to pay with. */\nclass UnconfiguredPublisher {\n constructor(private readonly error: MissingUplinkError) {}\n getFeeRates(): Promise<never> {\n return Promise.reject(this.error);\n }\n uploadGitObject(): Promise<never> {\n return Promise.reject(this.error);\n }\n publishEvent(): Promise<never> {\n return Promise.reject(this.error);\n }\n}\n\nexport async function createStandaloneContext(\n options: StandaloneLoadOptions\n): Promise<StandaloneContext> {\n const { env } = options;\n const warn = (line: string) => options.warn(line);\n const dir = configDir(env);\n const configPath = join(dir, 'config.json');\n const file = readClientConfig(configPath);\n const identity = await resolveIdentity(options);\n const nostr = deriveNostrKeyFromMnemonic(\n identity.mnemonic,\n identity.accountIndex\n );\n\n const settings = resolveConnectorSettings({\n env,\n file,\n configDir: dir,\n ...(options.storeDestination\n ? { storeDestinationOverride: options.storeDestination }\n : {}),\n });\n for (const line of settings.warnings) warn(line);\n const defaultRelayUrls = settings.relayUrl ? [settings.relayUrl] : [];\n\n if (!settings.connectorUrl) {\n const missing = new MissingUplinkError(configPath);\n if (options.requireUplink === false) {\n return {\n ownerPubkey: nostr.pubkey,\n identitySource: identity.source,\n identitySourceLabel: identity.sourceLabel,\n publisher: new UnconfiguredPublisher(missing),\n defaultRelayUrls,\n // SHAs the 30618 object map does not cover are resolved against\n // the configured gateway's GraphQL endpoint, not arweave.net (#183).\n fetchRemote: (args) =>\n fetchRemoteState({\n ...args,\n resolveSha: shaResolverFor(undefined, env),\n }),\n stop: () => Promise.resolve(),\n };\n }\n throw missing;\n }\n const connectorUrl = settings.connectorUrl;\n\n // Client 4.x pays with x402 vouchers from its own store. A 3.x toon-channel\n // store is never handed to it (the connector refuses those claims, and the\n // file is what the 3.x rig needs to take its deposits back): only named.\n if (isToonChannelStore(settings.channelStorePath)) {\n throw new Error(\n `${settings.channelStorePath} is a client 3.x toon-channel store, which a current ` +\n 'connector no longer honours: point TOON_CLIENT_CHANNEL_STORE (config ' +\n `channelStorePath) at a new file, e.g. ${join(dir, CHANNEL_STORE_FILENAME)}; ` +\n 'the old file is left as it is'\n );\n }\n for (const legacy of legacyChannelFiles(dir)) {\n if (legacy === settings.channelStorePath) continue;\n warn(\n `rig: ${legacy} is client 3.x toon-channel state; client 4.x ignores it and keeps its ` +\n `x402 channels in ${settings.channelStorePath}`\n );\n }\n\n // One writer per identity per machine: two rig processes signing vouchers on\n // one channel race the running total, and the connector refuses the loser.\n if (!standaloneForced(env)) await checkDaemonIdentity(nostr.pubkey);\n const lock = await NonceLock.acquire(nostr.pubkey);\n\n const clients: ToonClient[] = [];\n // Set once the client is up: records the channel paid work drew on (the\n // client opens one on first use), so `rig channel list/close/settle` and\n // `rig balance` see it. Best effort: a failure here loses no money, the\n // client's own store still holds the channel.\n let recordHeld: (() => Promise<void>) | undefined;\n const stop = async () => {\n await recordHeld?.().catch(() => undefined);\n for (const c of clients) await c.close();\n lock.release();\n };\n\n try {\n const payerKeys: Pick<\n ToonClientConfig,\n | 'mnemonic'\n | 'accountIndex'\n | 'keyDerivation'\n | 'solanaSecretKey'\n | 'evmPrivateKey'\n > = {\n mnemonic: identity.mnemonic,\n accountIndex: identity.accountIndex,\n keyDerivation: settings.keyDerivation,\n ...(settings.solanaKeyFile\n ? { solanaSecretKey: readSolanaKeyFile(settings.solanaKeyFile) }\n : {}),\n ...(settings.evmPrivateKey\n ? { evmPrivateKey: settings.evmPrivateKey }\n : {}),\n };\n const shared: Omit<ToonClientConfig, 'connector' | 'channelStore'> = {\n ...payerKeys,\n ...(settings.chain ? { chain: settings.chain } : {}),\n ...(settings.rpcUrl ? { rpcUrl: settings.rpcUrl } : {}),\n transport: settings.transport,\n // The first paid send opens the channel (on Solana the connector\n // sponsors the open: USDC for the deposit, no SOL), and a send the\n // channel cannot cover opens a fresh one.\n autoOpenChannel: true,\n ...(settings.deposit !== undefined ? { deposit: settings.deposit } : {}),\n // A Base deposit goes through a facilitator; Solana names none, so an\n // unset facilitator on Solana is '' rather than the devnet default.\n ...(settings.facilitatorUrl !== undefined\n ? { facilitatorUrl: settings.facilitatorUrl }\n : settings.chain === 'solana'\n ? { facilitatorUrl: '' }\n : {}),\n };\n if (settings.chain && !settings.rpcUrl) {\n warn(\n `rig: no rpcUrl configured for ${settings.chain} — the client falls back to its devnet preset; ` +\n 'set TOON_CLIENT_RPC_URL (or config rpcUrl) for a mainnet node'\n );\n }\n\n const client = await ToonClient.create({\n ...shared,\n connector: connectorUrl,\n channelStore: settings.channelStorePath,\n });\n clients.push(client);\n const desc = await client.describe();\n const destinations = resolveDestinations(desc, settings, connectorUrl);\n if (!settings.chain && desc.batchSettlements.length > 1) {\n warn(\n `rig: paying on ${client.chain}, the first chain ${connectorUrl} offers that this ` +\n `identity holds a key for (it offers ${desc.batchSettlements.map((t) => t.network).join(', ')}); ` +\n 'set TOON_CLIENT_CHAIN (config chain) to choose'\n );\n }\n\n // The store leg: the same client unless the store terminates on another\n // node that holds its own channel (then its own client + channel store).\n let storeClient = client;\n if (\n settings.storeConnectorUrl &&\n normalizeUrl(settings.storeConnectorUrl) !== normalizeUrl(connectorUrl)\n ) {\n storeClient = await ToonClient.create({\n ...shared,\n connector: settings.storeConnectorUrl,\n channelStore: settings.channelStorePath.replace(\n /(\\.json)?$/,\n '.store.json'\n ),\n });\n clients.push(storeClient);\n }\n\n const publisher = new ConnectorPublisher({\n publish: { client, destination: destinations.publish },\n store: {\n client: storeClient,\n destination: destinations.store,\n ...(storeClient === client && settings.storeSealTo\n ? { sealTo: settings.storeSealTo }\n : {}),\n },\n nostrSecretKey: nostr.secretKey,\n eventFee: settings.eventFee,\n ...(settings.uploadTimeoutMs !== undefined\n ? { uploadTimeoutMs: settings.uploadTimeoutMs }\n : {}),\n warn,\n });\n\n const channelMap = new ChannelMapStore({\n mapPath: join(dir, RIG_CHANNEL_MAP_FILENAME),\n watermarkPath: settings.channelStorePath,\n });\n const anchor = {\n identity: nostr.pubkey,\n destination: options.channelDestination ?? destinations.publish,\n peerId: desc.edgeIdentity?.keyId ?? connectorUrl,\n };\n const money = buildMoneyOps({ client, channelMap, ...anchor });\n recordHeld = async () => {\n const current = await client.channel.current();\n if (current) recordChannel(channelMap, current, anchor);\n };\n\n return {\n ownerPubkey: nostr.pubkey,\n identitySource: identity.source,\n identitySourceLabel: identity.sourceLabel,\n publisher,\n defaultRelayUrls,\n // As above (#183): the GraphQL fallback follows RIG_ARWEAVE_GATEWAY.\n fetchRemote: (args) =>\n fetchRemoteState({\n ...args,\n resolveSha: shaResolverFor(undefined, env),\n }),\n money,\n stop,\n };\n } catch (err) {\n await stop().catch(() => undefined);\n throw err;\n }\n}\n\nfunction normalizeUrl(url: string): string {\n return url.replace(/\\/+$/, '').replace(/\\/ilp$/, '');\n}\n"],"mappings":"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","names":["record"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toon-protocol/rig",
3
- "version": "4.7.0",
3
+ "version": "5.0.0",
4
4
  "description": "Git-to-TOON write path core — build git objects and NIP-34 events for the Rig control plane",
5
5
  "license": "MIT",
6
6
  "author": "Jonathan Green",
@@ -45,7 +45,7 @@
45
45
  "@scure/bip32": "^2.0.1",
46
46
  "@scure/bip39": "^2.0.1",
47
47
  "@toon-format/toon": "^1.0.0",
48
- "@toon-protocol/client": "^3.0.0",
48
+ "@toon-protocol/client": "^4.1.2",
49
49
  "@toon-protocol/core": "^3.5.0",
50
50
  "nostr-tools": "^2.20.0",
51
51
  "viem": "^2.0.0",