@toon-protocol/rig 2.13.1 → 2.14.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-FQXNLPUR.js → chunk-LCYCKWAT.js} +10 -1
- package/dist/chunk-LCYCKWAT.js.map +1 -0
- package/dist/{chunk-AA4RBCSI.js → chunk-VKEUIYQO.js} +14 -9
- package/dist/chunk-VKEUIYQO.js.map +1 -0
- package/dist/cli/rig.js +105 -12
- package/dist/cli/rig.js.map +1 -1
- package/dist/standalone/index.d.ts +27 -4
- package/dist/standalone/index.js +2 -2
- package/dist/{standalone-mode-D2YSANBJ.js → standalone-mode-OT63GEPM.js} +50 -9
- package/dist/standalone-mode-OT63GEPM.js.map +1 -0
- package/package.json +2 -2
- package/dist/chunk-AA4RBCSI.js.map +0 -1
- package/dist/chunk-FQXNLPUR.js.map +0 -1
- package/dist/standalone-mode-D2YSANBJ.js.map +0 -1
|
@@ -368,9 +368,22 @@ interface ToonClientLike {
|
|
|
368
368
|
/**
|
|
369
369
|
* Free FULL multi-chain wallet view (#299) — native coin + configured tokens
|
|
370
370
|
* per chain — works on an UNSTARTED client (Solana/Mina addresses are derived
|
|
371
|
-
* from the mnemonic on demand).
|
|
371
|
+
* from the mnemonic on demand). `fallback` supplies Solana/Mina channel params
|
|
372
|
+
* to read WHEN the client config has none, without touching settlement config.
|
|
372
373
|
*/
|
|
373
|
-
getWalletBalances?(): Promise<WalletChainBalanceInfo[]>;
|
|
374
|
+
getWalletBalances?(fallback?: WalletViewFallback): Promise<WalletChainBalanceInfo[]>;
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Wallet-view-only Solana/Mina channel defaults (#299): RPC/GraphQL + token used
|
|
378
|
+
* to READ the balance of a chain the identity has no configured channel for, so
|
|
379
|
+
* `rig balance` shows all three chains (0 for a not-yet-on-chain account). These
|
|
380
|
+
* NEVER enter the client's settlement config — they cannot influence chain
|
|
381
|
+
* negotiation — so a preset default here is safe even for a chain the wallet
|
|
382
|
+
* cannot yet settle on.
|
|
383
|
+
*/
|
|
384
|
+
interface WalletViewFallback {
|
|
385
|
+
solanaChannel?: ToonClientConfig['solanaChannel'];
|
|
386
|
+
minaChannel?: ToonClientConfig['minaChannel'];
|
|
374
387
|
}
|
|
375
388
|
interface StandalonePublisherOptions {
|
|
376
389
|
/**
|
|
@@ -417,6 +430,14 @@ interface StandalonePublisherOptions {
|
|
|
417
430
|
};
|
|
418
431
|
/** Daemon control API port probed by the nonce guard. */
|
|
419
432
|
daemonPort?: number;
|
|
433
|
+
/**
|
|
434
|
+
* Skip Guard 1 (the same-identity daemon refusal, {@link checkDaemonIdentity})
|
|
435
|
+
* — the force-standalone override ({@link standaloneForced} / RIG_STANDALONE
|
|
436
|
+
* / `rig --standalone`). Guard 2, the exclusive per-identity {@link NonceLock},
|
|
437
|
+
* ALWAYS runs regardless, so two standalone processes still can't race the
|
|
438
|
+
* channel watermark. Default false (daemon-aware).
|
|
439
|
+
*/
|
|
440
|
+
skipDaemonCheck?: boolean;
|
|
420
441
|
/** Directory for the per-identity advisory lockfile. */
|
|
421
442
|
lockDir?: string;
|
|
422
443
|
/** Fetch impl for the daemon probe (tests). */
|
|
@@ -477,6 +498,7 @@ declare class StandalonePublisher implements Publisher {
|
|
|
477
498
|
private readonly uploadFeePerByte;
|
|
478
499
|
private readonly routePrices;
|
|
479
500
|
private readonly daemonPort;
|
|
501
|
+
private readonly skipDaemonCheck;
|
|
480
502
|
private readonly lockDir;
|
|
481
503
|
private readonly fetchImpl;
|
|
482
504
|
private readonly channelMap;
|
|
@@ -577,9 +599,10 @@ declare class StandalonePublisher implements Publisher {
|
|
|
577
599
|
* client (no nonce guard, no uplink, no channel). The client derives the
|
|
578
600
|
* Solana/Mina addresses from the mnemonic on demand, so ALL configured chains
|
|
579
601
|
* appear even before a start. Best-effort per chain (an unreachable RPC yields
|
|
580
|
-
* an `unreadable` chain, not a failure).
|
|
602
|
+
* an `unreadable` chain, not a failure). `fallback` supplies Solana/Mina RPC
|
|
603
|
+
* defaults (e.g. the network preset) for chains the config has no channel for.
|
|
581
604
|
*/
|
|
582
|
-
readWalletChainBalances(): Promise<WalletChainBalanceInfo[]>;
|
|
605
|
+
readWalletChainBalances(fallback?: WalletViewFallback): Promise<WalletChainBalanceInfo[]>;
|
|
583
606
|
/**
|
|
584
607
|
* Fee rates for `planPush` estimation: the flat per-event fee and the
|
|
585
608
|
* per-byte upload rate this publisher pays (daemon `feePerEvent` and seed
|
package/dist/standalone/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
StandalonePublisher,
|
|
4
4
|
deriveRouteDestinations,
|
|
5
5
|
extractArweaveTxId
|
|
6
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-VKEUIYQO.js";
|
|
7
7
|
import {
|
|
8
8
|
ChannelMapCorruptError,
|
|
9
9
|
ChannelMapStore,
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
defaultLockDir,
|
|
19
19
|
recordKey,
|
|
20
20
|
resolveChannelPaths
|
|
21
|
-
} from "../chunk-
|
|
21
|
+
} from "../chunk-LCYCKWAT.js";
|
|
22
22
|
import "../chunk-35ALWUDJ.js";
|
|
23
23
|
export {
|
|
24
24
|
ChannelMapCorruptError,
|
|
@@ -11,11 +11,12 @@ import {
|
|
|
11
11
|
import {
|
|
12
12
|
StandalonePublisher,
|
|
13
13
|
deriveRouteDestinations
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-VKEUIYQO.js";
|
|
15
15
|
import {
|
|
16
16
|
ChannelMapStore,
|
|
17
|
-
RIG_CHANNEL_MAP_FILENAME
|
|
18
|
-
|
|
17
|
+
RIG_CHANNEL_MAP_FILENAME,
|
|
18
|
+
standaloneForced
|
|
19
|
+
} from "./chunk-LCYCKWAT.js";
|
|
19
20
|
import "./chunk-35ALWUDJ.js";
|
|
20
21
|
import {
|
|
21
22
|
DISCOVERY_TIMEOUT_MS,
|
|
@@ -47,7 +48,8 @@ import {
|
|
|
47
48
|
} from "@toon-protocol/client";
|
|
48
49
|
import {
|
|
49
50
|
decodeEventFromToon,
|
|
50
|
-
encodeEventToToon
|
|
51
|
+
encodeEventToToon,
|
|
52
|
+
resolveClientNetwork
|
|
51
53
|
} from "@toon-protocol/core";
|
|
52
54
|
var MissingUplinkError = class extends Error {
|
|
53
55
|
constructor(configPath, relayUrl) {
|
|
@@ -91,6 +93,24 @@ function buildMinaAutoDeploy(dir, identityPubkey, chain, warn) {
|
|
|
91
93
|
zkAppPrivateKey: existing.zkAppPrivateKey
|
|
92
94
|
}
|
|
93
95
|
} : {},
|
|
96
|
+
// Persist the zkApp key BEFORE the deploy tx is sent (bug #3): a crash or
|
|
97
|
+
// failed cache-invalidation retry between send and confirmation then finds
|
|
98
|
+
// this record next run and REDEPLOYS the same address, instead of paying
|
|
99
|
+
// the ~1.1-MINA account-creation fee for a brand-new zkApp every attempt.
|
|
100
|
+
onDeploying: (record) => {
|
|
101
|
+
store.save({
|
|
102
|
+
identity: identityPubkey,
|
|
103
|
+
chain,
|
|
104
|
+
zkAppAddress: record.zkAppAddress,
|
|
105
|
+
zkAppPrivateKey: record.zkAppPrivateKey,
|
|
106
|
+
feePayer: record.feePayer,
|
|
107
|
+
deployedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
108
|
+
source: "rig auto-deploy (npm @toon-protocol/mina-zkapp build, pending)"
|
|
109
|
+
});
|
|
110
|
+
warn(
|
|
111
|
+
`rig: recorded pending Mina zkApp ${record.zkAppAddress} before deploy \u2014 key saved to ${store.filePath} (reused on retry)`
|
|
112
|
+
);
|
|
113
|
+
},
|
|
94
114
|
onDeployed: (record) => {
|
|
95
115
|
store.save({
|
|
96
116
|
identity: identityPubkey,
|
|
@@ -398,7 +418,11 @@ function isNetworkTopology(value) {
|
|
|
398
418
|
var NON_RECOVERABLE_START_ERRORS = /* @__PURE__ */ new Set([
|
|
399
419
|
"DaemonIdentityConflictError",
|
|
400
420
|
"StandaloneLockError",
|
|
401
|
-
"ChannelMapCorruptError"
|
|
421
|
+
"ChannelMapCorruptError",
|
|
422
|
+
// An unfunded Mina fee payer is a wallet-funding problem, not topology
|
|
423
|
+
// staleness — re-resolving the topology cannot fix it, so retrying would
|
|
424
|
+
// just re-run the (already fail-fast) preflight for nothing.
|
|
425
|
+
"MinaFeePayerUnfundedError"
|
|
402
426
|
]);
|
|
403
427
|
var TopologyRecoveringPublisher = class {
|
|
404
428
|
inner;
|
|
@@ -468,8 +492,8 @@ var TopologyRecoveringPublisher = class {
|
|
|
468
492
|
return p.settleRecordedChannel(record);
|
|
469
493
|
}
|
|
470
494
|
/** Free read on the unstarted client — no bootstrap, no recovery needed. */
|
|
471
|
-
readWalletChainBalances() {
|
|
472
|
-
return this.inner.readWalletChainBalances();
|
|
495
|
+
readWalletChainBalances(...args) {
|
|
496
|
+
return this.inner.readWalletChainBalances(...args);
|
|
473
497
|
}
|
|
474
498
|
async stop() {
|
|
475
499
|
await this.inner.stop();
|
|
@@ -492,6 +516,12 @@ async function createStandaloneContext(options) {
|
|
|
492
516
|
const configPath = join(dir, "config.json");
|
|
493
517
|
const file = readClientConfig(configPath);
|
|
494
518
|
const identity = await resolveIdentity(options);
|
|
519
|
+
const skipDaemonCheck = standaloneForced(env);
|
|
520
|
+
if (skipDaemonCheck) {
|
|
521
|
+
warn(
|
|
522
|
+
"rig: force-standalone (RIG_STANDALONE) \u2014 bypassing any running toon-clientd; this rig process signs its own claims (stop the daemon if it is also writing on this identity)"
|
|
523
|
+
);
|
|
524
|
+
}
|
|
495
525
|
const genesisSeed = loadGenesisSeed();
|
|
496
526
|
const relayUrl = options.relayUrl ?? env["TOON_CLIENT_RELAY_URL"] ?? file.relayUrl ?? genesisSeed?.relayUrl ?? // Last resort: the genesis seed normally supplies the relay, so this is
|
|
497
527
|
// only reached when no seed/config/env relay is present. Point at the
|
|
@@ -630,6 +660,7 @@ async function createStandaloneContext(options) {
|
|
|
630
660
|
eventFee,
|
|
631
661
|
channelMap,
|
|
632
662
|
warn,
|
|
663
|
+
...skipDaemonCheck ? { skipDaemonCheck: true } : {},
|
|
633
664
|
...topo.publishDestination ? { publishDestination: topo.publishDestination } : {},
|
|
634
665
|
...topo.storeDestination ? { storeDestination: topo.storeDestination } : {},
|
|
635
666
|
// Route-price floors (F06): every per-packet claim must advance the
|
|
@@ -664,6 +695,15 @@ async function createStandaloneContext(options) {
|
|
|
664
695
|
} : void 0,
|
|
665
696
|
warn
|
|
666
697
|
);
|
|
698
|
+
const walletViewNetwork = env["TOON_CLIENT_NETWORK"] ?? file.network;
|
|
699
|
+
let walletViewFallback;
|
|
700
|
+
if (walletViewNetwork === "devnet" || walletViewNetwork === "testnet" || walletViewNetwork === "mainnet") {
|
|
701
|
+
const presets = resolveClientNetwork(walletViewNetwork);
|
|
702
|
+
walletViewFallback = {
|
|
703
|
+
...presets.solanaChannel ? { solanaChannel: presets.solanaChannel } : {},
|
|
704
|
+
...presets.minaChannel ? { minaChannel: presets.minaChannel } : {}
|
|
705
|
+
};
|
|
706
|
+
}
|
|
667
707
|
return {
|
|
668
708
|
ownerPubkey: publisher.getPublicKey(),
|
|
669
709
|
identitySource: identity.source,
|
|
@@ -677,14 +717,15 @@ async function createStandaloneContext(options) {
|
|
|
677
717
|
openChannel: (opts) => publisher.openChannelExplicit(opts),
|
|
678
718
|
closeChannel: (record) => publisher.closeRecordedChannel(record),
|
|
679
719
|
settleChannel: (record) => publisher.settleRecordedChannel(record),
|
|
680
|
-
walletChainBalances: () => publisher.readWalletChainBalances()
|
|
720
|
+
walletChainBalances: () => publisher.readWalletChainBalances(walletViewFallback)
|
|
681
721
|
},
|
|
682
722
|
stop: () => publisher.stop()
|
|
683
723
|
};
|
|
684
724
|
}
|
|
685
725
|
export {
|
|
686
726
|
MissingUplinkError,
|
|
727
|
+
buildMinaAutoDeploy,
|
|
687
728
|
createStandaloneContext,
|
|
688
729
|
resolveNetworkTopology
|
|
689
730
|
};
|
|
690
|
-
//# sourceMappingURL=standalone-mode-
|
|
731
|
+
//# sourceMappingURL=standalone-mode-OT63GEPM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/standalone-mode.ts"],"sourcesContent":["/**\n * The embedded-client (standalone) publisher backing every paid `rig`\n * command: build a nonce-guarded {@link StandalonePublisher} from the\n * caller's own identity and config.\n *\n * Identity comes from the #248 precedence chain (`./identity.ts`:\n * RIG_MNEMONIC env → TOON_CLIENT_MNEMONIC env alias → project `.env` →\n * `~/.toon-client` keystore/config). The remaining config resolution\n * DUPLICATES the toon-clientd conventions\n * (`packages/client-mcp/src/daemon/config.ts`) the same way\n * `../standalone/nonce-guard.ts` does — this package must not import\n * `@toon-protocol/client-mcp` (circular; see that module's doc). Keep in sync:\n *\n * - state dir: `TOON_CLIENT_HOME`, else `~/.toon-client`; config `config.json`\n * - env overrides: `TOON_CLIENT_PROXY_URL`, `TOON_CLIENT_BTP_URL`,\n * `TOON_CLIENT_RELAY_URL`, `TOON_CLIENT_DESTINATION`,\n * `TOON_CLIENT_PUBLISH_DESTINATION`, `TOON_CLIENT_STORE_DESTINATION`,\n * `TOON_CLIENT_CHAIN`\n *\n * NETWORK BOOTSTRAP (#264): what explicit config does not pin is resolved\n * from the network itself — the payment peer's live kind:10032 announce on\n * the relay-origin, falling back to `@toon-protocol/core`'s committed\n * genesis peer seed. Uplink, channel anchor, publish/store routes, the\n * settlement chain and its TokenNetwork/token/RPC parameters all follow the\n * `explicit config > live announce > genesis seed` order documented in\n * `../standalone/network-bootstrap.ts`. The pure resolution lives in\n * {@link resolveNetworkTopology} (unit-testable without any network).\n *\n * TOPOLOGY CACHE (#279): the resolved topology is persisted under\n * `TOON_CLIENT_HOME` (`../standalone/topology-cache.ts`) keyed by\n * relay-origin + identity + an explicit-config fingerprint, with a 15-min\n * TTL (`RIG_TOPOLOGY_TTL_MS` overrides; `0` disables). A cache hit skips\n * announce discovery and the funded-chain probes entirely; a cached\n * topology that then fails to BOOTSTRAP is invalidated and re-resolved\n * live in-process ({@link TopologyRecoveringPublisher}), so staleness costs\n * one failed attempt, never a broken run. Only paid-path resolutions\n * (`requireUplink !== false`) are written — a free-read topology may lack\n * an uplink and must not shadow the paid path's MissingUplinkError.\n *\n * CORE COEXISTENCE NOTE: rig performs discovery with ITS OWN\n * `@toon-protocol/core` (^2.0.x — live genesis seed), while the embedded\n * `@toon-protocol/client` keeps its internal core (^1.6.x) for its own\n * bootstrap/negotiation. The two never exchange class instances — rig feeds\n * the client plain config (`knownPeers`, settlement maps), so the version\n * split is safe by construction.\n *\n * This module statically imports `@toon-protocol/client` (heavy: viem,\n * noble, nostr-tools), so it must only ever be reached through the dynamic\n * import in `push.ts` (see `./standalone-context.ts`).\n */\n\nimport { readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport type { ToonClientConfig } from '@toon-protocol/client';\nimport {\n EvmSigner,\n deriveFullIdentity,\n deriveNostrKeyFromMnemonic,\n} from '@toon-protocol/client';\nimport {\n decodeEventFromToon,\n encodeEventToToon,\n resolveClientNetwork,\n} from '@toon-protocol/core';\nimport type {\n BlobUpload,\n FeeRates,\n GitObjectUpload,\n Publisher,\n PublishReceipt,\n UploadReceipt,\n} from '../publisher.js';\nimport type { UnsignedEvent } from '../nip34-events.js';\nimport {\n ChannelMapStore,\n RIG_CHANNEL_MAP_FILENAME,\n type ChannelMapRecord,\n} from '../standalone/channel-map.js';\nimport {\n TOPOLOGY_CACHE_FILENAME,\n TOPOLOGY_TTL_ENV,\n TopologyCache,\n explicitConfigFingerprint,\n topologyCacheKey,\n topologyCacheTtlMs,\n} from '../standalone/topology-cache.js';\nimport {\n DISCOVERY_TIMEOUT_MS,\n MinaChannelUnderivableError,\n SolanaChannelUnderivableError,\n TokenNetworkUnderivableError,\n discoverAnnouncedPeers,\n evmChainIdOf,\n evmTokenBalance,\n genesisSeedPubkeys,\n loadGenesisSeed,\n pickPaymentPeer,\n resolveChainSettlement,\n selectSettlementChain,\n solanaTokenBalance,\n type AnnouncedPeer,\n type ChainSelection,\n type ChainSettlement,\n type ChannelRecordLike,\n type EvmBalanceProbe,\n type ExplicitChainConfig,\n type SolanaBalanceProbe,\n} from '../standalone/network-bootstrap.js';\nimport {\n StandalonePublisher,\n deriveRouteDestinations,\n type WalletViewFallback,\n} from '../standalone/standalone-publisher.js';\nimport { standaloneForced } from '../standalone/nonce-guard.js';\nimport { fetchRemoteState } from '../remote-state.js';\nimport { MinaZkAppStore } from '../standalone/mina-zkapp-store.js';\nimport { resolveIdentity } from './identity.js';\nimport type {\n StandaloneContext,\n StandaloneLoadOptions,\n} from './standalone-context.js';\n\n/** The subset of the shared client config file standalone mode consumes. */\nexport interface ClientConfigFile {\n network?: 'mainnet' | 'testnet' | 'devnet' | 'custom';\n mnemonicAccountIndex?: number;\n btpUrl?: string;\n proxyUrl?: string;\n relayUrl?: string;\n destination?: string;\n publishDestination?: string;\n storeDestination?: string;\n feePerEvent?: string;\n channelStorePath?: string;\n /** Settlement chain: family (`evm`) or full id (`evm:31337`). */\n chain?: string;\n supportedChains?: string[];\n settlementAddresses?: Record<string, string>;\n preferredTokens?: Record<string, string>;\n tokenNetworks?: Record<string, string>;\n chainRpcUrls?: Record<string, string>;\n solanaChannel?: ToonClientConfig['solanaChannel'];\n minaChannel?: ToonClientConfig['minaChannel'];\n}\n\n/** An identity was resolved, but there is no way to send paid writes. */\nexport class MissingUplinkError extends Error {\n constructor(configPath: string, relayUrl: string | undefined) {\n const discovered = relayUrl\n ? `no announce with a btp/http endpoint was found on ${relayUrl} and ` +\n 'the genesis seed has none; '\n : '';\n super(\n `no write uplink configured: ${discovered}set TOON_CLIENT_PROXY_URL ` +\n '(connector payment proxy) or TOON_CLIENT_BTP_URL, or add ' +\n `proxyUrl/btpUrl to ${configPath}`\n );\n this.name = 'MissingUplinkError';\n }\n}\n\nfunction configDir(env: NodeJS.ProcessEnv): string {\n return env['TOON_CLIENT_HOME'] ?? join(homedir(), '.toon-client');\n}\n\nfunction 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/** `https://host/ilp` → `https://host` (the client re-derives `/ilp`). */\nfunction proxyBaseOf(httpEndpoint: string): string {\n return httpEndpoint.replace(/\\/+$/, '').replace(/\\/ilp$/i, '');\n}\n\n// ---------------------------------------------------------------------------\n// Pure topology resolution (#264) — exported for tests\n// ---------------------------------------------------------------------------\n\n/** A genesis-seed peer as this module consumes it (rig's core 3.x shape). */\nexport interface GenesisSeedLike {\n pubkey: string;\n relayUrl: string;\n ilpAddress: string;\n btpEndpoint: string;\n}\n\n/** Inputs to {@link resolveNetworkTopology} (side-effect free). */\nexport interface NetworkTopologyInputs {\n env: NodeJS.ProcessEnv;\n file: ClientConfigFile;\n /** For error messages (\"add X to <configPath>\"). */\n configPath: string;\n /** Resolved relay-origin (already precedence-resolved). */\n relayUrl: string;\n /** The discovered payment-peer announce, if any. */\n announce: AnnouncedPeer | undefined;\n /** The committed genesis seed entry, if any. */\n genesisSeed: GenesisSeedLike | undefined;\n identity: { mnemonic: string; accountIndex: number; pubkey: string };\n /**\n * #262 channel-map records for this identity (chain selection input).\n * A thunk so the (possibly corrupt-throwing) map read only happens when\n * chain selection actually needs it.\n */\n channelRecords: () => ChannelRecordLike[];\n /** EVM balance probe override (tests); default: raw `eth_call`. */\n probeBalance?: EvmBalanceProbe;\n /**\n * Solana balance probe override (tests); default: raw\n * `getTokenAccountsByOwner`.\n */\n probeSolanaBalance?: SolanaBalanceProbe;\n /**\n * When false (#263 free reads, e.g. `rig balance`), a missing uplink is\n * tolerated instead of throwing {@link MissingUplinkError}.\n */\n requireUplink?: boolean;\n warn: (line: string) => void;\n}\n\n/** The resolved payment topology feeding the embedded client config. */\nexport interface NetworkTopology {\n proxyUrl?: string;\n btpUrl?: string;\n /** Channel anchor / default ILP destination. */\n destination: string;\n publishDestination?: string;\n storeDestination?: string;\n /** The peer the embedded client bootstraps + negotiates with. */\n knownPeers: { pubkey: string; relayUrl: string; btpEndpoint: string }[];\n /**\n * FLAT per-packet route prices the peer announces for the effective\n * publish/store destinations (kind:10032 `capabilities`; base-unit integer\n * strings — this document is JSON-cached). The connector rejects any\n * balance-proof claim advancing the channel by less than the destination\n * route's price (F06), so the publisher floors its per-packet fees here.\n */\n routePrices?: { publish?: string; store?: string };\n /** The selected settlement chain + rationale (absent: nothing known). */\n selection?: ChainSelection;\n supportedChains?: string[];\n preferredTokens?: Record<string, string>;\n tokenNetworks?: Record<string, string>;\n chainRpcUrls?: Record<string, string>;\n /**\n * Solana channel parameters derived for the (first) `solana:*` chain in\n * play — the config-ready `ToonClientConfig.solanaChannel` shape. Absent\n * when no Solana chain is supported or an explicit `solanaChannel` config\n * object already covers it (explicit rides through verbatim).\n */\n solanaChannel?: { rpcUrl: string; programId: string; tokenMint?: string };\n /**\n * Mina channel parameters derived for the (first) `mina:*` chain in play —\n * the config-ready `ToonClientConfig.minaChannel` shape. Absent when no Mina\n * chain is supported or an explicit `minaChannel` config object already\n * covers it (explicit rides through verbatim).\n */\n minaChannel?: {\n graphqlUrl: string;\n zkAppAddress: string;\n tokenId?: string;\n networkId?: 'devnet' | 'mainnet';\n };\n}\n\n/**\n * Build the embedded client's `minaChannel` from one resolved settlement — or\n * throw the actionable {@link MinaChannelUnderivableError} naming the missing\n * pieces. A Mina chain in play with no way to open its channel must fail HERE,\n * not later as the embedded client's generic \"Mina channel config not\n * provided\". The required pieces are `graphqlUrl` (from a core preset) and\n * `zkAppAddress` (from the live announce); `tokenId`/`networkId` ride through\n * when present (a Mina channel with no token id opens on native MINA).\n */\n/**\n * The `autoDeploy` block attached to a DERIVED `minaChannel` (an explicit\n * config object keeps the pin-a-zkApp semantics and never auto-deploys):\n * reuse the recorded per-pair zkApp when one exists, persist a fresh\n * deployment's key BEFORE the open proceeds (losing it strands the ~1.1 MINA\n * the deployment cost), and route the slow compile/deploy/inclusion progress\n * to the warn stream.\n */\nexport function buildMinaAutoDeploy(\n dir: string,\n identityPubkey: string,\n chain: string,\n warn: (line: string) => void\n): NonNullable<NonNullable<ToonClientConfig['minaChannel']>['autoDeploy']> {\n const store = MinaZkAppStore.forHome(dir);\n let existing: ReturnType<MinaZkAppStore['lookup']>;\n try {\n existing = store.lookup(identityPubkey, chain);\n } catch (err) {\n warn(\n `rig: unreadable Mina zkApp store at ${store.filePath} ` +\n `(${err instanceof Error ? err.message : String(err)}) — a fresh ` +\n 'zkApp may be deployed'\n );\n }\n return {\n ...(existing\n ? {\n deployed: {\n zkAppAddress: existing.zkAppAddress,\n zkAppPrivateKey: existing.zkAppPrivateKey,\n },\n }\n : {}),\n // Persist the zkApp key BEFORE the deploy tx is sent (bug #3): a crash or\n // failed cache-invalidation retry between send and confirmation then finds\n // this record next run and REDEPLOYS the same address, instead of paying\n // the ~1.1-MINA account-creation fee for a brand-new zkApp every attempt.\n onDeploying: (record) => {\n store.save({\n identity: identityPubkey,\n chain,\n zkAppAddress: record.zkAppAddress,\n zkAppPrivateKey: record.zkAppPrivateKey,\n feePayer: record.feePayer,\n deployedAt: new Date().toISOString(),\n source:\n 'rig auto-deploy (npm @toon-protocol/mina-zkapp build, pending)',\n });\n warn(\n `rig: recorded pending Mina zkApp ${record.zkAppAddress} before ` +\n `deploy — key saved to ${store.filePath} (reused on retry)`\n );\n },\n onDeployed: (record) => {\n store.save({\n identity: identityPubkey,\n chain,\n zkAppAddress: record.zkAppAddress,\n zkAppPrivateKey: record.zkAppPrivateKey,\n feePayer: record.feePayer,\n ...(record.deployTxHash !== undefined\n ? { deployTxHash: record.deployTxHash }\n : {}),\n ...(record.vkHash !== undefined ? { vkHash: record.vkHash } : {}),\n deployedAt: new Date().toISOString(),\n source: 'rig auto-deploy (npm @toon-protocol/mina-zkapp build)',\n });\n warn(\n `rig: deployed a dedicated Mina PaymentChannel zkApp ` +\n `${record.zkAppAddress} — key saved to ${store.filePath}`\n );\n },\n onProgress: (line) => warn(`rig: mina: ${line}`),\n };\n}\n\nfunction deriveMinaChannel(\n settlement: ChainSettlement,\n announce: AnnouncedPeer | undefined,\n relayUrl: string\n): NonNullable<NetworkTopology['minaChannel']> {\n const { rpcUrl, zkAppAddress, minaTokenId, networkId } = settlement;\n if (!rpcUrl || !zkAppAddress) {\n const missing = [\n ...(rpcUrl ? [] : ['graphqlUrl']),\n ...(zkAppAddress ? [] : ['zkAppAddress']),\n ];\n throw new MinaChannelUnderivableError(\n settlement.chain,\n missing,\n announce,\n relayUrl\n );\n }\n return {\n graphqlUrl: rpcUrl,\n zkAppAddress,\n ...(minaTokenId ? { tokenId: minaTokenId } : {}),\n ...(networkId ? { networkId } : {}),\n };\n}\n\n/**\n * Build the embedded client's `solanaChannel` from one resolved settlement —\n * or throw the actionable {@link SolanaChannelUnderivableError} naming the\n * missing pieces. A Solana chain in play with no way to open its channel must\n * fail HERE, not later as the embedded client's generic \"Solana channel\n * config not provided\".\n */\nfunction deriveSolanaChannel(\n settlement: ChainSettlement,\n announce: AnnouncedPeer | undefined,\n relayUrl: string\n): NonNullable<NetworkTopology['solanaChannel']> {\n const { rpcUrl, programId, tokenAddress } = settlement;\n if (!rpcUrl || !programId || !tokenAddress) {\n const missing = [\n ...(rpcUrl ? [] : ['rpcUrl']),\n ...(programId ? [] : ['programId']),\n ...(tokenAddress ? [] : ['tokenMint']),\n ];\n throw new SolanaChannelUnderivableError(\n settlement.chain,\n missing,\n announce,\n relayUrl\n );\n }\n return { rpcUrl, programId, tokenMint: tokenAddress };\n}\n\n/**\n * Resolve the payment topology per the #264 precedence order —\n * `explicit config > live announce > genesis seed` for every field, plus the\n * documented settlement-chain selection rule (see\n * `../standalone/network-bootstrap.ts`).\n *\n * @throws {MissingUplinkError} when no source yields an uplink.\n * @throws {TokenNetworkUnderivableError} when the selected EVM chain's\n * TokenNetwork cannot be derived from config, announce, or chain preset.\n */\nexport async function resolveNetworkTopology(\n inputs: NetworkTopologyInputs\n): Promise<NetworkTopology> {\n const { env, file, configPath, relayUrl, announce, genesisSeed, warn } =\n inputs;\n\n // ── Explicit config (always wins, per field) ─────────────────────────────\n const explicitProxyUrl = env['TOON_CLIENT_PROXY_URL'] ?? file.proxyUrl;\n const explicitBtpUrl = env['TOON_CLIENT_BTP_URL'] ?? file.btpUrl;\n const explicitDestination =\n env['TOON_CLIENT_DESTINATION'] ?? file.destination;\n const explicitPublish =\n env['TOON_CLIENT_PUBLISH_DESTINATION'] ?? file.publishDestination;\n const explicitStore =\n env['TOON_CLIENT_STORE_DESTINATION'] ?? file.storeDestination;\n const explicitChain = env['TOON_CLIENT_CHAIN'] ?? file.chain;\n const explicitMaps: ExplicitChainConfig = {\n ...(file.chainRpcUrls ? { chainRpcUrls: file.chainRpcUrls } : {}),\n ...(file.preferredTokens ? { preferredTokens: file.preferredTokens } : {}),\n ...(file.tokenNetworks ? { tokenNetworks: file.tokenNetworks } : {}),\n };\n\n // ── Uplink: explicit > announce (http > btp) > genesis seed ──────────────\n let proxyUrl = explicitProxyUrl;\n let btpUrl = explicitBtpUrl;\n if (!proxyUrl && !btpUrl) {\n if (announce?.info.httpEndpoint) {\n proxyUrl = proxyBaseOf(announce.info.httpEndpoint);\n } else if (announce?.info.btpEndpoint) {\n btpUrl = announce.info.btpEndpoint;\n } else if (genesisSeed?.btpEndpoint) {\n btpUrl = genesisSeed.btpEndpoint;\n } else if (inputs.requireUplink !== false) {\n // Free reads (`rig balance`, #263) tolerate a missing uplink; paid\n // commands fail here, after every source has been tried.\n throw new MissingUplinkError(configPath, relayUrl);\n }\n }\n\n // ── Destination anchor + publish/store routes ────────────────────────────\n // The channel anchors at the peer's announced ilpAddress; publish/store\n // routes come from the announce's `routes` map. Explicit values always\n // win; with neither, the publisher's `<base>.relay.store` anchor-derivation\n // convention remains as the last-resort fallback (explicit anchors only).\n const destination =\n explicitDestination ??\n announce?.info.ilpAddress ??\n genesisSeed?.ilpAddress ??\n 'g.proxy';\n const publishDestination = explicitPublish ?? announce?.routes?.publish;\n const storeDestination = explicitStore ?? announce?.routes?.store;\n\n // ── Route-price floors ───────────────────────────────────────────────────\n // The announce's `capabilities` carry a FLAT price per destination route,\n // and the connector gates every paid packet at it (a claim advancing the\n // channel by less is rejected — F06). Match the announced prices against\n // the EFFECTIVE publish/store destinations — including the publisher's\n // `<base>.relay.store` anchor-derivation fallback, so the floor applies\n // exactly when a paid write would actually target the priced route. An\n // unmatched destination gets no floor (behavior unchanged).\n let routePrices: NetworkTopology['routePrices'];\n const capabilities = announce?.capabilities;\n if (capabilities && capabilities.length > 0) {\n const derived = deriveRouteDestinations(destination);\n const priceOf = (address: string): string | undefined =>\n capabilities.find((c) => c.address === address)?.price;\n const publishPrice = priceOf(publishDestination ?? derived.publish);\n const storePrice = priceOf(storeDestination ?? derived.store);\n if (publishPrice !== undefined || storePrice !== undefined) {\n routePrices = {\n ...(publishPrice !== undefined ? { publish: publishPrice } : {}),\n ...(storePrice !== undefined ? { store: storePrice } : {}),\n };\n }\n }\n\n // ── Known peer for the embedded client's own bootstrap ───────────────────\n // The client re-queries the peer's announce itself (its internal core) and\n // negotiates the settlement chain; rig just tells it WHO to bootstrap with.\n const knownPeers = announce\n ? [\n {\n pubkey: announce.pubkey,\n relayUrl,\n btpEndpoint: announce.info.btpEndpoint ?? '',\n },\n ]\n : genesisSeed\n ? [\n {\n pubkey: genesisSeed.pubkey,\n relayUrl: genesisSeed.relayUrl,\n btpEndpoint: genesisSeed.btpEndpoint,\n },\n ]\n : [];\n\n // ── Settlement chain + per-chain parameters ──────────────────────────────\n // NOTE: the `network` preset field is deliberately NOT forwarded to the\n // embedded client. `applyNetworkPresets` puts preset chains FIRST in\n // `supportedChains`, which is what steered devnet negotiation to the\n // unfunded public Solana preset (#260 root cause 4). The announce + the\n // rule below define the chain; presets only serve as per-chain parameter\n // fallbacks inside `resolveChainSettlement`.\n const announcedChains = announce?.info.supportedChains ?? [];\n const resolveSettlement = (chain: string) =>\n resolveChainSettlement(chain, explicitMaps, announce);\n\n let selection: ChainSelection | undefined;\n let supportedChains: string[] | undefined;\n let preferredTokens: Record<string, string> | undefined;\n let tokenNetworks: Record<string, string> | undefined;\n let chainRpcUrls: Record<string, string> | undefined;\n let solanaChannel: NetworkTopology['solanaChannel'];\n let minaChannel: NetworkTopology['minaChannel'];\n\n if (file.supportedChains?.length) {\n // Explicit chain list: pass through in order (its order IS the\n // negotiation preference), filling per-chain parameter gaps from\n // announce/presets.\n const listed = file.supportedChains;\n const kept: string[] = [];\n let firstDropError: unknown;\n preferredTokens = { ...file.preferredTokens };\n tokenNetworks = { ...file.tokenNetworks };\n chainRpcUrls = { ...file.chainRpcUrls };\n for (const chain of listed) {\n const s = resolveSettlement(chain);\n // Chain negotiation is an EXACT string intersection of the two chain\n // lists — a spelling difference for the same chain (config\n // `evm:base:31337` vs announced `evm:31337`) silently strands the\n // chain and negotiation falls through to whatever else matches. When\n // a listed EVM chain names the same numeric chain id as an announced\n // one, advertise the ANNOUNCED spelling (parameters resolved from the\n // configured spelling carry over).\n let key = chain;\n if (s.family === 'evm' && !announcedChains.includes(chain)) {\n const chainId = evmChainIdOf(chain);\n const equivalent =\n chainId !== undefined\n ? announcedChains.find((c) => evmChainIdOf(c) === chainId)\n : undefined;\n if (equivalent) {\n warn(\n `rig: aligning configured settlement chain \"${chain}\" to the ` +\n `announced spelling \"${equivalent}\" (same EVM chain id ` +\n `${chainId}) — chain negotiation matches identifiers exactly`\n );\n key = equivalent;\n }\n }\n if (kept.includes(key)) continue;\n if (s.tokenAddress && !preferredTokens[key]) {\n preferredTokens[key] = s.tokenAddress;\n }\n if (s.tokenNetwork && !tokenNetworks[key]) {\n tokenNetworks[key] = s.tokenNetwork;\n }\n if (s.rpcUrl && !chainRpcUrls[key]) {\n chainRpcUrls[key] = s.rpcUrl;\n }\n // Same fail-fast guarantee as the announce/selection path below: an\n // explicitly configured EVM chain whose TokenNetwork/RPC cannot be\n // derived must fail HERE with an actionable error, not later as the\n // embedded client's generic \"tokenNetwork address is required\".\n if (s.family === 'evm') {\n if (!tokenNetworks[key]) {\n throw new TokenNetworkUnderivableError(chain, announce, relayUrl);\n }\n if (!chainRpcUrls[key]) {\n throw new Error(\n `no RPC URL is derivable for settlement chain \"${chain}\"` +\n ` — add chainRpcUrls[\"${chain}\"] to ${configPath}`\n );\n }\n }\n // A listed Solana chain is advertised to negotiation, which may pick\n // it — so its channel parameters must be derivable NOW. An explicit\n // solanaChannel config object covers it (rides through to the embedded\n // client verbatim); otherwise the first derivable Solana chain\n // provides the channel params, and an UNDERIVABLE one is dropped from\n // the advertised list (warned, with the remedy) instead of leaving a\n // chain in play that the on-chain open is guaranteed to die on.\n if (s.family === 'solana' && !file.solanaChannel) {\n if (solanaChannel) {\n // The embedded client carries ONE solanaChannel — a second Solana\n // chain cannot be honored with distinct params.\n kept.push(key);\n continue;\n }\n try {\n solanaChannel = deriveSolanaChannel(s, announce, relayUrl);\n } catch (err) {\n firstDropError ??= err;\n warn(\n `rig: dropping settlement chain ${chain} from the configured ` +\n `supportedChains — ${err instanceof Error ? err.message : String(err)}`\n );\n continue;\n }\n }\n // A listed Mina chain: same fail-fast contract as Solana. An explicit\n // minaChannel config covers it (rides through verbatim); otherwise the\n // first derivable Mina chain provides the channel params, and an\n // UNDERIVABLE one is dropped from the advertised list (warned) instead of\n // leaving a chain in play the on-chain open is guaranteed to die on.\n if (s.family === 'mina' && !file.minaChannel) {\n if (minaChannel) {\n kept.push(key);\n continue;\n }\n try {\n minaChannel = deriveMinaChannel(s, announce, relayUrl);\n } catch (err) {\n firstDropError ??= err;\n warn(\n `rig: dropping settlement chain ${chain} from the configured ` +\n `supportedChains — ${err instanceof Error ? err.message : String(err)}`\n );\n continue;\n }\n }\n kept.push(key);\n }\n if (kept.length === 0) {\n // Every listed chain was dropped — surface the first underivable\n // error instead of negotiating with an empty chain list.\n throw firstDropError;\n }\n supportedChains = kept;\n // The embedded client validates chainRpcUrls keys ⊆ supportedChains, so\n // entries for dropped/re-spelled chains must not survive; prune all\n // three chain-keyed maps to the advertised list for coherence.\n const advertised = new Set(kept);\n const prune = (map: Record<string, string>): Record<string, string> =>\n Object.fromEntries(\n Object.entries(map).filter(([chain]) => advertised.has(chain))\n );\n preferredTokens = prune(preferredTokens);\n tokenNetworks = prune(tokenNetworks);\n chainRpcUrls = prune(chainRpcUrls);\n selection = {\n chain: supportedChains[0] as string,\n reason: 'explicit',\n detail: 'supportedChains set by config',\n };\n } else if (explicitChain || announcedChains.length > 0) {\n // Selection rule: explicit > persisted channel > funded > first EVM.\n const { secretKey } = deriveNostrKeyFromMnemonic(\n inputs.identity.mnemonic,\n inputs.identity.accountIndex\n );\n const evmAddress = new EvmSigner(secretKey).address;\n // The Solana funded-chain probe needs the identity's base58 Solana\n // address, derived pre-client-start via the client's own SLIP-0010\n // helper (same paths the embedded client uses, so the probed address IS\n // the one the client will settle with). Only derived when a `solana:*`\n // chain is actually announced and no explicit chain pins selection\n // anyway; `deriveFullIdentity` degrades to an empty publicKey when the\n // Ed25519 deps are unavailable — then Solana chains are not probed.\n let solanaAddress: string | undefined;\n if (\n !explicitChain &&\n announcedChains.some((c) => c.startsWith('solana:'))\n ) {\n const fullIdentity = await deriveFullIdentity(\n inputs.identity.mnemonic,\n inputs.identity.accountIndex\n );\n solanaAddress = fullIdentity.solana.publicKey || undefined;\n // Only the address is needed here — drop the derived key material.\n fullIdentity.nostr.secretKey.fill(0);\n fullIdentity.evm.privateKey.fill(0);\n fullIdentity.solana.secretKey.fill(0);\n }\n selection = await selectSettlementChain({\n ...(explicitChain ? { explicitChain } : {}),\n announcedChains,\n records: inputs.channelRecords(),\n evmAddress,\n ...(solanaAddress ? { solanaAddress } : {}),\n resolveSettlement,\n probeBalance: inputs.probeBalance ?? evmTokenBalance,\n probeSolanaBalance: inputs.probeSolanaBalance ?? solanaTokenBalance,\n });\n const settlement = resolveSettlement(selection.chain);\n if (settlement.family === 'evm') {\n if (!settlement.tokenNetwork) {\n throw new TokenNetworkUnderivableError(\n selection.chain,\n announce,\n relayUrl\n );\n }\n if (!settlement.rpcUrl) {\n throw new Error(\n `no RPC URL is derivable for settlement chain \"${selection.chain}\"` +\n ` — add chainRpcUrls[\"${selection.chain}\"] to ${configPath}`\n );\n }\n }\n // Same fail-fast for a selected Solana chain: its channel parameters\n // must be derivable now (an explicit solanaChannel config covers it).\n if (settlement.family === 'solana' && !file.solanaChannel) {\n solanaChannel = deriveSolanaChannel(settlement, announce, relayUrl);\n }\n // Same fail-fast for a selected Mina chain (an explicit minaChannel covers it).\n if (settlement.family === 'mina' && !file.minaChannel) {\n minaChannel = deriveMinaChannel(settlement, announce, relayUrl);\n }\n supportedChains = [selection.chain];\n preferredTokens = settlement.tokenAddress\n ? { [selection.chain]: settlement.tokenAddress }\n : undefined;\n tokenNetworks = settlement.tokenNetwork\n ? { [selection.chain]: settlement.tokenNetwork }\n : undefined;\n chainRpcUrls = settlement.rpcUrl\n ? { [selection.chain]: settlement.rpcUrl }\n : undefined;\n if (selection.reason !== 'explicit') {\n warn(\n `rig: settlement chain ${selection.chain} selected — ` +\n `${selection.detail}; set TOON_CLIENT_CHAIN (or supportedChains ` +\n 'in the client config) to override'\n );\n } else if (explicitChain && selection.chain !== explicitChain) {\n // The explicit pin was re-spelled to the announced chain id (chain\n // negotiation matches identifiers exactly) — say so, like the\n // configured-supportedChains path does.\n warn(\n `rig: aligning configured settlement chain \"${explicitChain}\" to ` +\n `the announced spelling \"${selection.chain}\" — ${selection.detail}`\n );\n }\n } else {\n warn(\n 'rig: no settlement chains are configured or announced — paid writes ' +\n 'will fail until a chain is configured (supportedChains) or the ' +\n `payment peer announces its chains on ${relayUrl}`\n );\n }\n\n const network = env['TOON_CLIENT_NETWORK'] ?? file.network;\n if (network && network !== 'custom' && !file.supportedChains?.length) {\n warn(\n `rig: ignoring the \"${network}\" network preset for settlement — the ` +\n \"settlement chain comes from the payment peer's announce and your \" +\n 'config, because preset chains can point at networks your wallet ' +\n 'has no funds on; set supportedChains explicitly to use preset chains'\n );\n }\n\n return {\n ...(proxyUrl ? { proxyUrl } : {}),\n ...(btpUrl ? { btpUrl } : {}),\n destination,\n ...(publishDestination ? { publishDestination } : {}),\n ...(storeDestination ? { storeDestination } : {}),\n ...(routePrices ? { routePrices } : {}),\n knownPeers,\n ...(selection ? { selection } : {}),\n ...(supportedChains ? { supportedChains } : {}),\n ...(preferredTokens && Object.keys(preferredTokens).length > 0\n ? { preferredTokens }\n : {}),\n ...(tokenNetworks && Object.keys(tokenNetworks).length > 0\n ? { tokenNetworks }\n : {}),\n ...(chainRpcUrls && Object.keys(chainRpcUrls).length > 0\n ? { chainRpcUrls }\n : {}),\n ...(solanaChannel ? { solanaChannel } : {}),\n ...(minaChannel ? { minaChannel } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Cached-topology recovery (#279)\n// ---------------------------------------------------------------------------\n\n/** Structural check for a cached {@link NetworkTopology} document. */\nfunction isNetworkTopology(value: unknown): value is NetworkTopology {\n if (typeof value !== 'object' || value === null) return false;\n const t = value as Record<string, unknown>;\n return typeof t['destination'] === 'string' && Array.isArray(t['knownPeers']);\n}\n\n/**\n * Start failures that must NEVER trigger a cache-invalidation retry: they\n * are concurrency/state guards, not topology staleness — retrying would\n * bypass exactly what they protect.\n */\nconst NON_RECOVERABLE_START_ERRORS: ReadonlySet<string> = new Set([\n 'DaemonIdentityConflictError',\n 'StandaloneLockError',\n 'ChannelMapCorruptError',\n // An unfunded Mina fee payer is a wallet-funding problem, not topology\n // staleness — re-resolving the topology cannot fix it, so retrying would\n // just re-run the (already fail-fast) preflight for nothing.\n 'MinaFeePayerUnfundedError',\n]);\n\n/**\n * Publisher wrapper implementing the #279 cache-invalidation contract: when\n * the inner publisher was built from a CACHED topology and its bootstrap\n * (`start`/`startClientOnly`) fails, the cache entry is invalidated, the\n * topology is re-resolved LIVE, a fresh publisher replaces the inner one,\n * and the operation proceeds — one retry, never more. Bootstrap failures\n * are pre-payment by construction (start() completes before any claim is\n * signed), so the retry can never double-pay. Publishers built from a live\n * resolution get no recovery hook and fail through unchanged.\n */\nclass TopologyRecoveringPublisher implements Publisher {\n private inner: StandalonePublisher;\n private rebuild: (() => Promise<StandalonePublisher>) | undefined;\n private readonly warn: (line: string) => void;\n\n constructor(\n inner: StandalonePublisher,\n rebuild: (() => Promise<StandalonePublisher>) | undefined,\n warn: (line: string) => void\n ) {\n this.inner = inner;\n this.rebuild = rebuild;\n this.warn = warn;\n }\n\n getPublicKey(): string {\n return this.inner.getPublicKey();\n }\n\n getFeeRates(): Promise<FeeRates> {\n return this.inner.getFeeRates();\n }\n\n /**\n * Run the bootstrap step, recovering ONCE from a stale cached topology.\n * A rebuild failure surfaces the LIVE resolution's error — that is the\n * network's real state, strictly more actionable than the cached failure.\n */\n private async ensure(\n start: (p: StandalonePublisher) => Promise<void>\n ): Promise<StandalonePublisher> {\n try {\n await start(this.inner);\n return this.inner;\n } catch (err) {\n const rebuild = this.rebuild;\n this.rebuild = undefined;\n const name = err instanceof Error ? err.name : '';\n if (!rebuild || NON_RECOVERABLE_START_ERRORS.has(name)) throw err;\n this.warn(\n 'rig: bootstrap with the cached network topology failed ' +\n `(${err instanceof Error ? err.message : String(err)}) — ` +\n 'invalidating the cache and re-resolving live'\n );\n const fresh = await rebuild();\n try {\n // Failed starts release their own lock/state; stop() is idempotent.\n await this.inner.stop();\n } catch {\n // best-effort teardown of the abandoned publisher\n }\n this.inner = fresh;\n await start(this.inner);\n return this.inner;\n }\n }\n\n async publishEvent(\n event: UnsignedEvent,\n relayUrls: string[]\n ): Promise<PublishReceipt> {\n const p = await this.ensure((x) => x.start());\n return p.publishEvent(event, relayUrls);\n }\n\n async uploadGitObject(upload: GitObjectUpload): Promise<UploadReceipt> {\n const p = await this.ensure((x) => x.start());\n return p.uploadGitObject(upload);\n }\n\n async uploadBlob(upload: BlobUpload): Promise<UploadReceipt> {\n const p = await this.ensure((x) => x.start());\n return p.uploadBlob(upload);\n }\n\n // ── money lifecycle passthroughs (#263) — same recovery contract ─────────\n\n async openChannelExplicit(\n opts?: Parameters<StandalonePublisher['openChannelExplicit']>[0]\n ): ReturnType<StandalonePublisher['openChannelExplicit']> {\n const p = await this.ensure((x) => x.start());\n return p.openChannelExplicit(opts);\n }\n\n async closeRecordedChannel(\n record: ChannelMapRecord\n ): ReturnType<StandalonePublisher['closeRecordedChannel']> {\n const p = await this.ensure((x) => x.startClientOnly());\n return p.closeRecordedChannel(record);\n }\n\n async settleRecordedChannel(\n record: ChannelMapRecord\n ): ReturnType<StandalonePublisher['settleRecordedChannel']> {\n const p = await this.ensure((x) => x.startClientOnly());\n return p.settleRecordedChannel(record);\n }\n\n /** Free read on the unstarted client — no bootstrap, no recovery needed. */\n readWalletChainBalances(\n ...args: Parameters<StandalonePublisher['readWalletChainBalances']>\n ): ReturnType<StandalonePublisher['readWalletChainBalances']> {\n return this.inner.readWalletChainBalances(...args);\n }\n\n async stop(): Promise<void> {\n await this.inner.stop();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Context factory\n// ---------------------------------------------------------------------------\n\n/**\n * #262 channel-map records for this identity, reduced to the slice chain\n * selection consumes (`closed` folds in the claim-watermark timers).\n */\nfunction chainRecordsFor(\n map: ChannelMapStore,\n identity: string\n): ChannelRecordLike[] {\n return map\n .list()\n .filter((r) => r.identity === identity)\n .map((r) => {\n const watermark = map.readWatermark(r.channelId);\n return {\n chain: r.chain,\n lastUsedAt: r.lastUsedAt,\n closed:\n watermark?.closedAt !== undefined ||\n watermark?.settledAt !== undefined,\n };\n });\n}\n\n/**\n * Assemble an embedded-client standalone context: resolved identity + config\n * → network bootstrap (announce discovery / genesis seed) → ToonClientConfig\n * → nonce-guarded StandalonePublisher (guard + client start + channel open\n * happen lazily on the first paid call, or eagerly via the publisher's own\n * `start`).\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\n // Force-standalone override (RIG_STANDALONE / `rig --standalone`): when set,\n // the embedded publisher skips the same-identity daemon refusal so a running\n // toon-clientd never blocks the standalone path. The per-identity NonceLock\n // still guards against two standalone processes racing the claim watermark.\n const skipDaemonCheck = standaloneForced(env);\n if (skipDaemonCheck) {\n warn(\n 'rig: force-standalone (RIG_STANDALONE) — bypassing any running ' +\n 'toon-clientd; this rig process signs its own claims (stop the daemon ' +\n 'if it is also writing on this identity)'\n );\n }\n\n const genesisSeed = loadGenesisSeed();\n\n // ── Relay-origin ──────────────────────────────────────────────────────────\n // The relay the paid command resolved via `rig remote` (passed by the\n // command) is the user's clearest network statement; env/file follow, and\n // the genesis seed's relay is the out-of-the-box fallback.\n const relayUrl =\n options.relayUrl ??\n env['TOON_CLIENT_RELAY_URL'] ??\n file.relayUrl ??\n genesisSeed?.relayUrl ??\n // Last resort: the genesis seed normally supplies the relay, so this is\n // only reached when no seed/config/env relay is present. Point at the\n // shared public devnet relay rather than a dead local default.\n 'wss://relay-ws.devnet.toonprotocol.dev';\n\n // ── Peer→channel persistence (#262) ──────────────────────────────────────\n const channelStorePath = file.channelStorePath ?? join(dir, 'channels.json');\n const channelMap = new ChannelMapStore({\n mapPath: join(dir, RIG_CHANNEL_MAP_FILENAME),\n watermarkPath: channelStorePath,\n });\n\n // ── Topology cache (#279): keyed by relay-origin + identity + explicit\n // config; a hit skips discovery AND the pure-but-probing resolution below.\n const cache = new TopologyCache<NetworkTopology>({\n path: join(dir, TOPOLOGY_CACHE_FILENAME),\n ttlMs: topologyCacheTtlMs(env),\n validate: isNetworkTopology,\n });\n const cacheKey = topologyCacheKey({\n relayUrl,\n identity: identity.pubkey,\n fingerprint: explicitConfigFingerprint(\n env,\n file as Record<string, unknown>\n ),\n });\n\n const resolveLiveTopology = async (): Promise<NetworkTopology> => {\n // ── Live announce discovery ────────────────────────────────────────────\n // Skipped when explicit config already pins the whole payment topology\n // (fully-configured setups keep their zero-roundtrip start and their\n // exact pre-#264 behavior). A listed `solana:*` chain WITHOUT explicit\n // channel parameters is not fully pinned: the announce is a needed\n // parameter source (program id / mint), and skipping discovery would\n // also skip the announced-spelling alignment that keeps the chain list\n // negotiable. Discovery failure is non-fatal: warn + genesis seed.\n const solanaExplicitlyComplete = (file.supportedChains ?? []).every(\n (chain) =>\n !chain.startsWith('solana:') ||\n Boolean(file.solanaChannel) ||\n Boolean(\n file.tokenNetworks?.[chain] &&\n file.preferredTokens?.[chain] &&\n file.chainRpcUrls?.[chain]\n )\n );\n // A listed `mina:*` chain WITHOUT an explicit `minaChannel` is not fully\n // pinned either: the announce is the authoritative source for the\n // deployment's zkApp (and token id), so discovery must run. Only an\n // explicit `minaChannel` config makes a Mina chain announce-independent.\n const minaExplicitlyComplete = (file.supportedChains ?? []).every(\n (chain) => !chain.startsWith('mina:') || Boolean(file.minaChannel)\n );\n const fullyExplicit =\n Boolean(\n (env['TOON_CLIENT_PROXY_URL'] ?? file.proxyUrl) ||\n (env['TOON_CLIENT_BTP_URL'] ?? file.btpUrl)\n ) &&\n Boolean(env['TOON_CLIENT_DESTINATION'] ?? file.destination) &&\n Boolean(file.supportedChains?.length) &&\n solanaExplicitlyComplete &&\n minaExplicitlyComplete;\n let announce: AnnouncedPeer | undefined;\n if (!fullyExplicit) {\n try {\n const peers = await discoverAnnouncedPeers(relayUrl, {\n timeoutMs: DISCOVERY_TIMEOUT_MS,\n });\n announce = pickPaymentPeer(peers, genesisSeedPubkeys());\n if (!announce) {\n warn(\n `rig: no payment-peer announce (kind:10032) found on ${relayUrl} — ` +\n 'falling back to the genesis peer seed'\n );\n }\n } catch (err) {\n warn(\n `rig: announce discovery on ${relayUrl} failed ` +\n `(${err instanceof Error ? err.message : String(err)}) — falling ` +\n 'back to the genesis peer seed'\n );\n }\n }\n\n // ── Topology resolution (pure; explicit > announce > genesis) ──────────\n const resolved = await resolveNetworkTopology({\n env,\n file,\n configPath,\n relayUrl,\n announce,\n genesisSeed,\n identity: {\n mnemonic: identity.mnemonic,\n accountIndex: identity.accountIndex,\n pubkey: identity.pubkey,\n },\n channelRecords: () => chainRecordsFor(channelMap, identity.pubkey),\n ...(options.requireUplink !== undefined\n ? { requireUplink: options.requireUplink }\n : {}),\n warn,\n });\n // Cache only paid-path resolutions: a `requireUplink: false` free read\n // may resolve WITHOUT an uplink, and caching that would let a later paid\n // command skip past MissingUplinkError with a broken topology.\n if (options.requireUplink !== false) cache.write(cacheKey, resolved);\n return resolved;\n };\n\n const cached = cache.read(cacheKey);\n if (cached) {\n warn(\n `rig: network topology from cache (${Math.round(cached.ageMs / 1000)}s ` +\n `old; ${TOPOLOGY_TTL_ENV}=0 disables) — skipping announce discovery`\n );\n }\n const topology = cached?.topology ?? (await resolveLiveTopology());\n\n const eventFee = BigInt(file.feePerEvent ?? '1');\n\n const buildPublisher = (topo: NetworkTopology): StandalonePublisher => {\n const clientConfig: ToonClientConfig = {\n // validateConfig requires connectorUrl OR proxyUrl; with BTP-only\n // config a dummy connectorUrl satisfies it (unused at runtime — same\n // convention as the daemon).\n ...(topo.proxyUrl\n ? { proxyUrl: topo.proxyUrl }\n : { connectorUrl: 'http://127.0.0.1:1' }),\n mnemonic: identity.mnemonic,\n mnemonicAccountIndex: identity.accountIndex,\n ilpInfo: {\n pubkey: '00'.repeat(32),\n ilpAddress: 'g.toon.client',\n btpEndpoint: topo.btpUrl ?? '',\n assetCode: 'USD',\n assetScale: 6,\n },\n toonEncoder: encodeEventToToon,\n toonDecoder: decodeEventFromToon,\n ...(topo.btpUrl ? { btpUrl: topo.btpUrl, btpAuthToken: '' } : {}),\n destinationAddress: topo.destination,\n // The embedded client bootstraps against the known peer above; its\n // `relayUrl` config only seeds ArDrive-merged peers, so it stays unset.\n relayUrl: '',\n knownPeers: topo.knownPeers,\n channelStorePath,\n ...(topo.supportedChains\n ? { supportedChains: topo.supportedChains }\n : {}),\n ...(file.settlementAddresses\n ? { settlementAddresses: file.settlementAddresses }\n : {}),\n ...(topo.preferredTokens\n ? { preferredTokens: topo.preferredTokens }\n : {}),\n ...(topo.tokenNetworks ? { tokenNetworks: topo.tokenNetworks } : {}),\n ...(topo.chainRpcUrls ? { chainRpcUrls: topo.chainRpcUrls } : {}),\n // Solana channel params: an explicit config object wins verbatim; else\n // the topology's derivation (announce/presets — see resolveNetworkTopology).\n ...(file.solanaChannel\n ? { solanaChannel: file.solanaChannel }\n : topo.solanaChannel\n ? { solanaChannel: topo.solanaChannel }\n : {}),\n // Mina channel params: an explicit config object wins verbatim; else the\n // topology's derivation (announce zkApp/tokenId + preset graphqlUrl —\n // see resolveNetworkTopology / deriveMinaChannel).\n // An explicit config object keeps today's pin-a-zkApp semantics\n // verbatim. The DERIVED path additionally gets `autoDeploy`: the\n // announce/preset zkApp is shared and single-pair, so a fresh identity\n // can never open on it — the client checks on-chain ownership and\n // deploys a dedicated per-pair zkApp when needed, persisting the key\n // through the rig zkApp store (losing it would strand ~1.1 MINA).\n ...(file.minaChannel\n ? { minaChannel: file.minaChannel }\n : topo.minaChannel\n ? {\n minaChannel: {\n ...topo.minaChannel,\n autoDeploy: buildMinaAutoDeploy(\n dir,\n identity.pubkey,\n `mina:${topo.minaChannel.networkId ?? 'devnet'}`,\n warn\n ),\n },\n }\n : {}),\n };\n\n return new StandalonePublisher({\n clientConfig,\n eventFee,\n channelMap,\n warn,\n ...(skipDaemonCheck ? { skipDaemonCheck: true } : {}),\n ...(topo.publishDestination\n ? { publishDestination: topo.publishDestination }\n : {}),\n ...(topo.storeDestination\n ? { storeDestination: topo.storeDestination }\n : {}),\n // Route-price floors (F06): every per-packet claim must advance the\n // channel by at least the destination route's announced price.\n ...(topo.routePrices\n ? {\n routePrices: {\n ...(topo.routePrices.publish !== undefined\n ? { publish: BigInt(topo.routePrices.publish) }\n : {}),\n ...(topo.routePrices.store !== undefined\n ? { store: BigInt(topo.routePrices.store) }\n : {}),\n },\n }\n : {}),\n // `rig channel open --peer` (#263): anchor the channel (and its map\n // key) to an explicit peer destination instead of the configured\n // default.\n ...(options.channelDestination\n ? { channelDestination: options.channelDestination }\n : {}),\n // The peer's announce does not carry TokenNetwork/token parameters, so\n // the client's negotiation leaves them empty (#260 root cause 3) — the\n // publisher back-fills them from the derived per-chain maps before the\n // channel opens.\n ...(topo.tokenNetworks || topo.preferredTokens\n ? {\n negotiationFallbacks: {\n ...(topo.tokenNetworks\n ? { tokenNetworks: topo.tokenNetworks }\n : {}),\n ...(topo.preferredTokens\n ? { preferredTokens: topo.preferredTokens }\n : {}),\n },\n }\n : {}),\n });\n };\n\n // Cache-sourced publishers get the #279 recovery hook: a failed bootstrap\n // invalidates the entry, re-resolves live (which re-writes the cache), and\n // retries once. Live-resolved publishers fail through unchanged.\n const publisher = new TopologyRecoveringPublisher(\n buildPublisher(topology),\n cached\n ? async () => {\n cache.invalidate(cacheKey);\n return buildPublisher(await resolveLiveTopology());\n }\n : undefined,\n warn\n );\n\n // Wallet-view preset defaults (#299): when the identity has no configured\n // Solana/Mina channel (the common single-EVM-chain case), `rig balance` still\n // shows those chains — 0 for an account not yet on-chain — by reading the\n // named network's public RPC/GraphQL. Resolved HERE and passed only to the\n // wallet-view read, NOT merged into the client's settlement config: setting\n // `network` there would expand `supportedChains` via applyNetworkPresets and\n // change chain negotiation, which this deliberately avoids.\n const walletViewNetwork = env['TOON_CLIENT_NETWORK'] ?? file.network;\n let walletViewFallback: WalletViewFallback | undefined;\n if (\n walletViewNetwork === 'devnet' ||\n walletViewNetwork === 'testnet' ||\n walletViewNetwork === 'mainnet'\n ) {\n const presets = resolveClientNetwork(walletViewNetwork);\n walletViewFallback = {\n ...(presets.solanaChannel\n ? { solanaChannel: presets.solanaChannel }\n : {}),\n ...(presets.minaChannel ? { minaChannel: presets.minaChannel } : {}),\n };\n }\n\n return {\n ownerPubkey: publisher.getPublicKey(),\n identitySource: identity.source,\n identitySourceLabel: identity.sourceLabel,\n publisher,\n defaultRelayUrls: [relayUrl],\n fetchRemote: (args) => fetchRemoteState(args),\n // Money lifecycle (#263): same guard/start/channel-map machinery as the\n // paid-write path, surfaced for fund/balance/channel open|close|settle.\n money: {\n openChannel: (opts) => publisher.openChannelExplicit(opts),\n closeChannel: (record) => publisher.closeRecordedChannel(record),\n settleChannel: (record) => publisher.settleRecordedChannel(record),\n walletChainBalances: () =>\n publisher.readWalletChainBalances(walletViewFallback),\n },\n stop: () => publisher.stop(),\n };\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toon-protocol/rig",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.14.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
|
"@toon-format/toon": "^1.0.0",
|
|
46
46
|
"@toon-protocol/core": "^3.1.2",
|
|
47
47
|
"nostr-tools": "^2.20.0",
|
|
48
|
-
"@toon-protocol/client": "^0.21.
|
|
48
|
+
"@toon-protocol/client": "^0.21.2"
|
|
49
49
|
},
|
|
50
50
|
"optionalDependencies": {
|
|
51
51
|
"@ar.io/sdk": "^4.0.3",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/standalone/standalone-publisher.ts"],"sourcesContent":["/**\n * StandalonePublisher — impl 2 of the {@link Publisher} seam (#228): an\n * EMBEDDED ToonClient constructed from the caller's config (mnemonic +\n * account index, the exact `packages/client/src/config.ts` shape) instead of\n * routing through a running toon-clientd. Made for CI jobs, servers, and\n * one-shot CLI runs where no daemon exists.\n *\n * Paid-write mechanics mirror the production daemon path\n * (`client-mcp/src/daemon/client-runner.ts`) and the proven seed pipeline\n * (`rig/tests/e2e/seed/lib/{publish,git-builder}.ts`):\n *\n * - one signed balance-proof CLAIM per write (`signBalanceProof(channelId,\n * fee)` — the ChannelManager accumulates the cumulative watermark),\n * - publishes route to the relay write destination with a flat per-event\n * fee (the daemon's `feePerEvent` convention),\n * - git objects upload as kind:5094 store writes tagged\n * Git-SHA/Git-Type/Repo, priced at bytes × per-byte rate (the seed\n * pipeline's bid), routed to the store destination via `proxyPath:\n * '/store'`, with the Arweave txId decoded from the FULFILL data.\n *\n * Because the embedded client signs claims on the SAME channels a daemon on\n * the same identity would, every paid operation is preceded by the nonce\n * guard (./nonce-guard.ts): refuse if a toon-clientd holds this identity,\n * and hold an exclusive per-pubkey lockfile against other standalone\n * processes for the lifetime of this publisher.\n *\n * Channel REUSE (#262): with a `channelMap` configured, start() resumes the\n * channel recorded for (identity, channel anchor) — `trackChannel` rehydrates\n * the cumulative-claim watermark from the client's channels.json — and\n * records any fresh lazy open, so sequential CLI invocations share ONE\n * on-chain channel instead of stranding a deposit per run (./channel-map.ts).\n */\n\nimport type {\n PublishEventResult,\n SignedBalanceProof,\n ToonClientConfig,\n} from '@toon-protocol/client';\nimport { ToonClient, parseFulfillHttp } from '@toon-protocol/client';\nimport { MAX_OBJECT_SIZE } from '../objects.js';\nimport { contentTypeForPath, DEFAULT_CONTENT_TYPE } from '../mime.js';\nimport type { UnsignedEvent } from '../nip34-events.js';\nimport {\n flooredUploadFee,\n type BlobUpload,\n type FeeRates,\n type GitObjectUpload,\n type PublishReceipt,\n type Publisher,\n type UploadReceipt,\n} from '../publisher.js';\nimport {\n recordKey,\n type ChannelMapRecord,\n type ChannelMapStore,\n type PersistedChannelContext,\n} from './channel-map.js';\nimport type {\n ChannelCloseOutcome,\n ChannelOpenOutcome,\n ChannelSettleOutcome,\n WalletChainBalanceInfo,\n} from './money.js';\nimport { checkDaemonIdentity, NonceLock } from './nonce-guard.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** A fully-signed Nostr event (structural subset of nostr-tools' NostrEvent). */\nexport interface SignedNostrEvent extends UnsignedEvent {\n id: string;\n pubkey: string;\n sig: string;\n}\n\n/**\n * The slice of `ToonClient` the publisher drives. Kept structural so tests\n * inject a mock and alternative client builds stay compatible.\n */\nexport interface ToonClientLike {\n start(): Promise<unknown>;\n stop(): Promise<void>;\n isStarted?(): boolean;\n getPublicKey(): string;\n signEvent(template: UnsignedEvent): SignedNostrEvent;\n openChannel(destination?: string): Promise<string>;\n signBalanceProof(\n channelId: string,\n amount: bigint\n ): Promise<SignedBalanceProof>;\n publishEvent(\n event: SignedNostrEvent,\n options?: {\n destination?: string;\n claim?: SignedBalanceProof;\n ilpAmount?: bigint;\n proxyPath?: string;\n }\n ): Promise<PublishEventResult>;\n /** On-chain deposit total for a tracked channel (channel-map bookkeeping). */\n getChannelDepositTotal?(channelId: string): bigint;\n /** Re-read a RESUMED channel's on-chain deposit (persisted state omits it). */\n rehydrateChannelDeposit?(\n channelId: string,\n opts: { chain: string; tokenNetworkAddress: string }\n ): Promise<bigint | undefined>;\n // ── money lifecycle (#263) — optional, matches ToonClient's surface ──────\n /** Deposit extra collateral (base-unit delta) into an open channel. */\n depositToChannel?(\n channelId: string,\n amount: string | bigint\n ): Promise<{ channelId: string; txHash?: string; depositTotal: string }>;\n /** Close a channel — starts the settlement challenge window (on-chain). */\n closeChannel?(channelId: string): Promise<{\n channelId: string;\n txHash?: string;\n /** Unix SECONDS, string-encoded. */\n closedAt: string;\n settleableAt: string;\n }>;\n /** Settle a closed channel after its window — releases funds (on-chain). */\n settleChannel?(\n channelId: string\n ): Promise<{ channelId: string; txHash?: string }>;\n /**\n * Free FULL multi-chain wallet view (#299) — native coin + configured tokens\n * per chain — works on an UNSTARTED client (Solana/Mina addresses are derived\n * from the mnemonic on demand).\n */\n getWalletBalances?(): Promise<WalletChainBalanceInfo[]>;\n}\n\nexport interface StandalonePublisherOptions {\n /**\n * ToonClient config (mnemonic + mnemonicAccountIndex + proxy/BTP uplink +\n * settlement fields — see `packages/client/src/config.ts`). Exactly one of\n * `clientConfig` | `client` is required.\n */\n clientConfig?: ToonClientConfig;\n /** Pre-built client (tests / advanced callers). */\n client?: ToonClientLike;\n /**\n * ILP route for event publishes (relay `/write`). Default: derived from the\n * config's `destinationAddress` anchor (`<base>.relay.store` → `<base>.relay`,\n * matching the daemon's route derivation), else the anchor itself.\n */\n publishDestination?: string;\n /**\n * ILP route for git-object uploads (store `/store` → Arweave). Default:\n * derived like `publishDestination` (`<base>.relay.store` → `<base>.store`).\n */\n storeDestination?: string;\n /**\n * ILP destination the payment channel anchors to. Default: the client\n * config's `destinationAddress`.\n */\n channelDestination?: string;\n /** Flat fee per published event (daemon `feePerEvent` convention). Default 1n. */\n eventFee?: bigint;\n /** Upload fee per object body byte (seed pipeline's bid rate). Default 10n. */\n uploadFeePerByte?: bigint;\n /**\n * FLAT per-packet route prices the payment peer announces for the\n * publish/store destinations (kind:10032 `capabilities` — e.g.\n * `{capability:'os.publish', address:'g.proxy.relay', price:'1000'}`).\n * The connector gates every paid packet at the destination route's price:\n * a balance-proof claim advancing the channel by less is rejected (F06).\n * Applied as a FLOOR on the computed per-packet fee — `publish` floors\n * event claims, `store` floors upload claims. A destination without a\n * known price keeps the computed fee unchanged (pre-floor behavior).\n */\n routePrices?: { publish?: bigint; store?: bigint };\n /** Daemon control API port probed by the nonce guard. */\n daemonPort?: number;\n /** Directory for the per-identity advisory lockfile. */\n lockDir?: string;\n /** Fetch impl for the daemon probe (tests). */\n fetchImpl?: typeof fetch;\n /**\n * Peer→channel map store (#262): start() resumes the channel recorded for\n * (identity, channel anchor) instead of opening a fresh one, and records\n * any fresh lazy open for the next invocation. Absent (embedded callers\n * managing their own channel lifecycle): the historical behaviour — open\n * lazily every run, record nothing.\n */\n channelMap?: ChannelMapStore;\n /**\n * Per-chain settlement parameters to BACK-FILL into the client's peer\n * negotiations after start (#264/#260): peers whose kind:10032 announce\n * carries no `tokenNetworks`/`preferredTokens` negotiate with those fields\n * empty, and the on-chain channel open then fails (\"tokenNetwork address\n * is required\"). Values here never override what a peer DID announce —\n * they only fill gaps, keyed by the negotiated chain id.\n */\n negotiationFallbacks?: {\n tokenNetworks?: Record<string, string>;\n preferredTokens?: Record<string, string>;\n };\n /** Sink for non-fatal channel-persistence warnings (default: stderr). */\n warn?: (line: string) => void;\n}\n\n/** A relay/store rejected a paid write (fee NOT spent iff the claim failed too). */\nexport class StandalonePublishError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'StandalonePublishError';\n }\n}\n\n// ---------------------------------------------------------------------------\n// Route derivation (duplicated from client-mcp daemon/config.ts — that\n// package depends on this one for #227, so importing it back would be\n// circular; keep in sync)\n// ---------------------------------------------------------------------------\n\n/**\n * Derive publish/store routes from the channel anchor. Behind the devnet\n * proxy the anchor is `<base>.relay.store` (e.g. `g.proxy.relay.store`):\n * publishes terminate at `<base>.relay`, uploads at `<base>.store`. Anchors\n * not matching the convention pass through unchanged.\n */\nexport function deriveRouteDestinations(anchor: string): {\n publish: string;\n store: string;\n} {\n const segs = anchor.split('.');\n if (segs.at(-1) === 'store' && segs.at(-2) === 'relay') {\n const base = segs.slice(0, -2).join('.');\n return { publish: `${base}.relay`, store: `${base}.store` };\n }\n return { publish: anchor, store: anchor };\n}\n\n// ---------------------------------------------------------------------------\n// FULFILL → Arweave txId (mirrors @toon-protocol/client blob-storage.ts,\n// whose extractor is not exported; uses the exported parseFulfillHttp)\n// ---------------------------------------------------------------------------\n\n/** Arweave tx IDs are base64url-encoded 32-byte values (43 chars). */\nconst ARWEAVE_TX_ID_REGEX = /^[A-Za-z0-9_-]{43}$/;\n\n/**\n * Decode the Arweave txId from a store-write FULFILL. The deployed payment\n * proxy returns the store's verbatim HTTP/1.1 response\n * (`{\"accept\":true,\"txId\":…}` body); legacy non-proxy providers return bare\n * `base64(utf8(txId))`.\n *\n * @throws {StandalonePublishError} when no valid txId can be extracted.\n */\nexport function extractArweaveTxId(base64Data: string): string {\n const http = parseFulfillHttp(base64Data);\n\n if (!http.isHttp) {\n const legacy = Buffer.from(base64Data, 'base64').toString('utf8');\n if (!ARWEAVE_TX_ID_REGEX.test(legacy)) {\n throw new StandalonePublishError(\n `FULFILL data is not a valid Arweave tx ID: \"${legacy}\"`\n );\n }\n return legacy;\n }\n\n if (http.status < 200 || http.status >= 300) {\n throw new StandalonePublishError(\n `git-object upload failed: store returned HTTP ${http.status}` +\n (http.body ? ` - ${http.body}` : '')\n );\n }\n\n let parsed: { accept?: boolean; txId?: unknown; data?: unknown; error?: unknown };\n try {\n parsed = JSON.parse(http.body) as typeof parsed;\n } catch {\n throw new StandalonePublishError(\n `git-object upload response body was not valid JSON: \"${http.body}\"`\n );\n }\n\n if (parsed.accept === false) {\n const reason = typeof parsed.error === 'string' ? `: ${parsed.error}` : '';\n throw new StandalonePublishError(\n `git-object upload rejected by store (accept:false)${reason}`\n );\n }\n\n if (typeof parsed.txId === 'string' && ARWEAVE_TX_ID_REGEX.test(parsed.txId)) {\n return parsed.txId;\n }\n if (typeof parsed.data === 'string' && parsed.data.length > 0) {\n const decoded = Buffer.from(parsed.data, 'base64').toString('utf8');\n if (ARWEAVE_TX_ID_REGEX.test(decoded)) return decoded;\n }\n\n throw new StandalonePublishError(\n `git-object upload response did not contain a valid Arweave tx ID: \"${http.body}\"`\n );\n}\n\n// ---------------------------------------------------------------------------\n// Channel-resume introspection (#262)\n//\n// The peer negotiation table and the ChannelManager's peer→channel map are\n// PRIVATE on ToonClient, so resuming a channel reaches into them through a\n// structural runtime cast — the exact pattern the daemon uses\n// (client-mcp/src/daemon/client-runner.ts, openOrResumeApexChannel /\n// routeChildPeersThroughApexChannel). Keep the shapes in sync with\n// @toon-protocol/client's ToonClient/ChannelManager.\n// ---------------------------------------------------------------------------\n\n/** The slice of a `PeerNegotiation` the channel map records. */\ninterface NegotiationLike {\n chain: string;\n chainType: string;\n chainId: number | string;\n settlementAddress: string;\n tokenAddress?: string;\n tokenNetwork?: string;\n}\n\ninterface ChannelInternals {\n peerNegotiations?: Map<string, NegotiationLike>;\n channelManager?: {\n trackChannel?: (\n channelId: string,\n context: PersistedChannelContext\n ) => void;\n peerChannels?: Map<string, string>;\n };\n /**\n * The client's on-chain channel client caches per-channel chain context at\n * OPEN time only; close/settle/deposit on a channel resumed from the map\n * would throw \"No on-chain context\" without re-seeding it (#263). Same\n * structural-cast contract as the rest of this block — keep in sync with\n * `@toon-protocol/client`'s OnChainChannelClient.\n */\n onChainChannelClient?: {\n channelContext?: Map<\n string,\n { chain: string; tokenNetworkAddress: string; tokenAddress?: string }\n >;\n };\n}\n\n/** Best-effort access to the client's private negotiation/channel state. */\nfunction channelInternals(client: ToonClientLike): ChannelInternals {\n const c = client as unknown as ChannelInternals;\n const negotiations =\n c.peerNegotiations instanceof Map ? c.peerNegotiations : undefined;\n const cm =\n c.channelManager &&\n typeof c.channelManager.trackChannel === 'function' &&\n c.channelManager.peerChannels instanceof Map\n ? c.channelManager\n : undefined;\n const onChain =\n c.onChainChannelClient &&\n c.onChainChannelClient.channelContext instanceof Map\n ? c.onChainChannelClient\n : undefined;\n return {\n ...(negotiations ? { peerNegotiations: negotiations } : {}),\n ...(cm ? { channelManager: cm } : {}),\n ...(onChain ? { onChainChannelClient: onChain } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// StandalonePublisher\n// ---------------------------------------------------------------------------\n\nexport class StandalonePublisher implements Publisher {\n private readonly client: ToonClientLike;\n private readonly ownsClient: boolean;\n private readonly publishDestination: string | undefined;\n private readonly storeDestination: string | undefined;\n private readonly channelDestination: string | undefined;\n private readonly eventFee: bigint;\n private readonly uploadFeePerByte: bigint;\n private readonly routePrices:\n | StandalonePublisherOptions['routePrices']\n | undefined;\n private readonly daemonPort: number | undefined;\n private readonly lockDir: string | undefined;\n private readonly fetchImpl: typeof fetch | undefined;\n private readonly channelMap: ChannelMapStore | undefined;\n /** ILP anchor the channel is keyed by in the map (peer/apex destination). */\n private readonly channelAnchor: string | undefined;\n private readonly negotiationFallbacks:\n | StandalonePublisherOptions['negotiationFallbacks']\n | undefined;\n private readonly warn: (line: string) => void;\n\n private lock: NonceLock | undefined;\n private channelId: string | undefined;\n private readyPromise: Promise<void> | undefined;\n /** Guard + client start only (no channel) — see {@link startClientOnly}. */\n private clientReadyPromise: Promise<void> | undefined;\n /** True when the last start() RESUMED the recorded channel (#263). */\n private lastOpenResumed = false;\n\n constructor(options: StandalonePublisherOptions) {\n if (options.client && options.clientConfig) {\n throw new Error(\n 'StandalonePublisher: provide either `clientConfig` or `client`, not both'\n );\n }\n if (options.client) {\n this.client = options.client;\n this.ownsClient = false;\n } else if (options.clientConfig) {\n this.client = new ToonClient(options.clientConfig);\n this.ownsClient = true;\n } else {\n throw new Error(\n 'StandalonePublisher: one of `clientConfig` (mnemonic-based ToonClient config) or `client` is required'\n );\n }\n\n // Routes: explicit option → derived from the channel anchor (same\n // `<base>.relay.store` convention the daemon resolves).\n const anchor =\n options.channelDestination ?? options.clientConfig?.destinationAddress;\n const routes = anchor ? deriveRouteDestinations(anchor) : undefined;\n this.publishDestination = options.publishDestination ?? routes?.publish;\n this.storeDestination = options.storeDestination ?? routes?.store;\n this.channelDestination = options.channelDestination;\n\n this.eventFee = options.eventFee ?? 1n;\n this.uploadFeePerByte = options.uploadFeePerByte ?? 10n;\n this.routePrices = options.routePrices;\n this.daemonPort = options.daemonPort;\n this.lockDir = options.lockDir;\n this.fetchImpl = options.fetchImpl;\n this.channelMap = options.channelMap;\n this.channelAnchor = anchor;\n this.negotiationFallbacks = options.negotiationFallbacks;\n this.warn =\n options.warn ?? ((line) => process.stderr.write(`${line}\\n`));\n }\n\n /** Hex Nostr pubkey of the embedded identity (available before start). */\n getPublicKey(): string {\n return this.client.getPublicKey();\n }\n\n /**\n * Run the nonce guard, start the embedded client, and open (or resume) the\n * payment channel. Called lazily by the first paid operation; safe to call\n * eagerly to fail fast. Idempotent.\n */\n start(): Promise<void> {\n this.readyPromise ??= (async () => {\n await this.startClientOnly();\n try {\n this.channelId = await this.openOrResumeChannel();\n } catch (err) {\n // Release the identity lock on a failed channel open (pre-#263\n // behaviour): nothing holds claims yet, so another process may go.\n this.lock?.release();\n this.lock = undefined;\n this.clientReadyPromise = undefined;\n throw err;\n }\n })().catch((err: unknown) => {\n // Let a later call retry (e.g. after the conflicting daemon stops).\n this.readyPromise = undefined;\n throw err;\n });\n return this.readyPromise;\n }\n\n /**\n * Run the nonce guard and start the embedded client WITHOUT touching the\n * payment channel (#263): `channel close`/`settle` operate on a RECORDED\n * channel and must never open a fresh one as a side effect of starting.\n * Idempotent; `start()` layers the channel open/resume on top of this.\n */\n startClientOnly(): Promise<void> {\n this.clientReadyPromise ??= this.doStartClient().catch((err: unknown) => {\n this.clientReadyPromise = undefined;\n throw err;\n });\n return this.clientReadyPromise;\n }\n\n private async doStartClient(): Promise<void> {\n const pubkey = this.client.getPublicKey();\n\n // Guard 1: refuse while a toon-clientd holds this identity.\n await checkDaemonIdentity(pubkey, {\n ...(this.daemonPort !== undefined ? { port: this.daemonPort } : {}),\n ...(this.fetchImpl ? { fetchImpl: this.fetchImpl } : {}),\n });\n\n // Guard 2: exclusive advisory lock against other standalone processes.\n this.lock = await NonceLock.acquire(pubkey, {\n ...(this.lockDir !== undefined ? { dir: this.lockDir } : {}),\n });\n\n try {\n if (this.client.isStarted?.() !== true) {\n await this.client.start();\n }\n // #264: back-fill negotiation gaps as soon as the client's bootstrap\n // negotiation exists — before any channel open (start()) or recorded-\n // channel operation reads them.\n this.applyNegotiationFallbacks();\n } catch (err) {\n this.lock.release();\n this.lock = undefined;\n throw err;\n }\n }\n\n /**\n * Back-fill negotiated peer metadata the announce did not carry\n * (#264/#260 root cause 3): after the client's bootstrap negotiation, any\n * peer negotiation missing `tokenNetwork`/`tokenAddress` gets the derived\n * per-chain fallback for its negotiated chain, BEFORE the lazy channel\n * open reads them. Announced values are never overridden.\n */\n private applyNegotiationFallbacks(): void {\n const fallbacks = this.negotiationFallbacks;\n if (!fallbacks) return;\n const negotiations = channelInternals(this.client).peerNegotiations;\n if (!negotiations) {\n this.warn(\n 'rig: settlement fallbacks configured but the client does not ' +\n 'expose negotiation internals — on-chain channel opening may fail ' +\n 'if the peer announced no TokenNetwork'\n );\n return;\n }\n for (const negotiation of negotiations.values()) {\n if (!negotiation.tokenNetwork) {\n const tokenNetwork = fallbacks.tokenNetworks?.[negotiation.chain];\n if (tokenNetwork) negotiation.tokenNetwork = tokenNetwork;\n }\n if (!negotiation.tokenAddress) {\n const tokenAddress = fallbacks.preferredTokens?.[negotiation.chain];\n if (tokenAddress) negotiation.tokenAddress = tokenAddress;\n }\n }\n }\n\n /**\n * Open the payment channel — or, when the channel map has a record for\n * (identity, anchor), RESUME the recorded on-chain channel (#262).\n *\n * Resume seeds the client's ChannelManager (`trackChannel` rehydrates the\n * cumulative-claim watermark from channels.json; `peerChannels` makes the\n * subsequent `openChannel` return the same id instead of opening on-chain).\n * A fresh open is RECORDED so the next invocation resumes it. A corrupt map\n * file throws BEFORE anything is opened — never a silent duplicate open.\n */\n private async openOrResumeChannel(): Promise<string> {\n const map = this.channelMap;\n if (!map) {\n // No persistence configured: historical lazy open, nothing recorded.\n return this.client.openChannel(this.channelDestination);\n }\n if (!this.channelAnchor) {\n this.warn(\n 'rig: no channel anchor destination configured — the peer→channel ' +\n 'mapping cannot be persisted, so this run may open a fresh channel'\n );\n return this.client.openChannel(this.channelDestination);\n }\n\n const anchor = this.channelAnchor;\n const identity = this.client.getPublicKey();\n // Corruption check happens HERE, before any on-chain open (throws).\n const candidates = map.listFor(identity, anchor);\n const internals = channelInternals(this.client);\n const resumed = await this.resumeRecordedChannel(map, candidates, internals);\n\n // Idempotent — returns the (resumed or existing) channel for the peer if\n // one is tracked, else opens lazily on-chain.\n const channelId = await this.client.openChannel(this.channelDestination);\n\n if (resumed && channelId === resumed.channelId) {\n this.lastOpenResumed = true;\n map.touch(recordKey(resumed));\n } else {\n this.lastOpenResumed = false;\n this.recordOpenedChannel(map, internals, identity, anchor, channelId);\n }\n return channelId;\n }\n\n /**\n * Try to resume one recorded channel: the first candidate whose peer is\n * still negotiated on the SAME chain + tokenNetwork and whose watermark\n * does not show it closed/settled. Returns the resumed record, if any.\n */\n private async resumeRecordedChannel(\n map: ChannelMapStore,\n candidates: ChannelMapRecord[],\n internals: ChannelInternals\n ): Promise<ChannelMapRecord | undefined> {\n if (candidates.length === 0) return undefined;\n const cm = internals.channelManager;\n if (!cm?.trackChannel || !cm.peerChannels) {\n this.warn(\n 'rig: a recorded channel exists but the client does not expose ' +\n 'channel internals to resume it — a fresh channel may be opened'\n );\n return undefined;\n }\n\n for (const record of candidates) {\n // The peer must still be negotiated on the recorded chain/tokenNetwork;\n // a rotated peer identity or re-negotiated settlement gets a fresh\n // channel (recorded under its own key) instead of stale claims.\n const negotiation = internals.peerNegotiations?.get(record.peerId);\n if (\n !negotiation ||\n negotiation.chain !== record.chain ||\n (negotiation.tokenNetwork ?? '') !== record.tokenNetwork\n ) {\n continue;\n }\n\n // Never resume a channel the withdraw flow already closed/settled.\n const watermark = map.readWatermark(record.channelId);\n if (\n watermark?.closedAt !== undefined ||\n watermark?.settledAt !== undefined\n ) {\n continue;\n }\n if (!watermark) {\n // Fresh channels are seeded at record time, so a missing entry means\n // the watermark store was lost. Resuming from nonce 0 fails SAFE: a\n // regressed cumulative claim is rejected by the peer (no double\n // spend), but warn so the failure is diagnosable.\n this.warn(\n `rig: resuming channel ${record.channelId} with no local claim ` +\n `watermark (${map.watermarkPath}) — if this channel was claimed ` +\n 'against before, the peer will reject the stale claims; remove ' +\n `its entry from ${map.mapPath} to open a fresh channel instead`\n );\n }\n\n // trackChannel rehydrates nonce/cumulative from the watermark store;\n // seeding peerChannels makes ensureChannel/openChannel reuse the id.\n cm.trackChannel(record.channelId, record.context);\n cm.peerChannels.set(record.peerId, record.channelId);\n\n // Persisted channel state omits the on-chain deposit — re-read it so\n // fee/balance accounting is right (EVM only; mirrors the daemon).\n // #279 latency trim: skipped when the record already carries a\n // depositTotal — deposits only change through rig's own flows (open\n // --deposit / a later resume), which update the record; this is\n // display/accounting state, not the claim watermark, so a stale value\n // can never double-spend (the peer enforces the cumulative claim).\n if (\n record.context.chainType === 'evm' &&\n record.depositTotal === undefined &&\n this.client.rehydrateChannelDeposit\n ) {\n try {\n const deposit = await this.client.rehydrateChannelDeposit(\n record.channelId,\n {\n chain: record.chain,\n tokenNetworkAddress: record.context.tokenNetworkAddress,\n }\n );\n if (deposit !== undefined) {\n map.touch(recordKey(record), {\n depositTotal: deposit.toString(),\n });\n }\n } catch (err) {\n this.warn(\n `rig: deposit re-read for resumed channel ${record.channelId} ` +\n `failed: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n }\n return record;\n }\n return undefined;\n }\n\n /** Record a freshly opened channel (+ seed its claim watermark at 0/0). */\n private recordOpenedChannel(\n map: ChannelMapStore,\n internals: ChannelInternals,\n identity: string,\n destination: string,\n channelId: string\n ): void {\n let peerId: string | undefined;\n for (const [peer, channel] of internals.channelManager?.peerChannels ??\n []) {\n if (channel === channelId) {\n peerId = peer;\n break;\n }\n }\n const negotiation =\n peerId !== undefined\n ? internals.peerNegotiations?.get(peerId)\n : undefined;\n if (peerId === undefined || !negotiation) {\n this.warn(\n `rig: opened channel ${channelId} but could not record the ` +\n 'peer→channel mapping (client does not expose negotiation ' +\n 'internals) — the NEXT invocation may open another channel'\n );\n return;\n }\n\n let depositTotal: bigint | undefined;\n try {\n depositTotal = this.client.getChannelDepositTotal?.(channelId);\n } catch {\n // deposit unknown — recorded without it; a later resume re-reads it\n }\n\n map.record({\n channelId,\n peerId,\n identity,\n destination,\n chain: negotiation.chain,\n tokenNetwork: negotiation.tokenNetwork ?? '',\n // Context mirrors the daemon's persistApexChannel shape — exactly what\n // trackChannel needs on resume.\n context: {\n chainType: negotiation.chainType,\n chainId:\n typeof negotiation.chainId === 'number' ? negotiation.chainId : 0,\n tokenNetworkAddress: negotiation.tokenNetwork ?? '',\n ...(negotiation.tokenAddress\n ? { tokenAddress: negotiation.tokenAddress }\n : {}),\n recipient: negotiation.settlementAddress,\n },\n ...(depositTotal !== undefined && depositTotal > 0n\n ? { depositTotal: depositTotal.toString() }\n : {}),\n });\n // Seed nonce 0 / cumulative 0 so a later resume can tell \"never claimed\n // against\" apart from \"watermark lost\" (which only fails claim-rejected).\n map.seedWatermark(channelId);\n }\n\n /** Release the identity lock and stop the embedded client (if we own it). */\n async stop(): Promise<void> {\n this.lock?.release();\n this.lock = undefined;\n this.readyPromise = undefined;\n this.clientReadyPromise = undefined;\n this.channelId = undefined;\n // Never stop a client that was never started (`ToonClient.stop()` throws\n // INVALID_STATE) — e.g. after a free `rig balance` read (#263).\n if (this.ownsClient && this.client.isStarted?.() !== false) {\n await this.client.stop();\n }\n }\n\n // ── Money lifecycle (#263) ──────────────────────────────────────────────────\n\n /**\n * Explicit `rig channel open`: the SAME resume-or-open path the lazy paid\n * writes use (guard → start → resume the recorded channel or open + record\n * a fresh one), surfaced with a receipt — plus an optional extra collateral\n * deposit on top of the open/resume.\n */\n async openChannelExplicit(opts?: {\n /** Extra collateral to deposit AFTER the open/resume (base units). */\n deposit?: bigint;\n }): Promise<ChannelOpenOutcome> {\n await this.start();\n const channelId = this.requireChannel();\n const identity = this.client.getPublicKey();\n const destination = this.channelAnchor ?? '';\n const record =\n this.channelMap && this.channelAnchor\n ? this.channelMap\n .listFor(identity, this.channelAnchor)\n .find((r) => r.channelId === channelId)\n : undefined;\n\n const outcome: ChannelOpenOutcome = {\n channelId,\n resumed: this.lastOpenResumed,\n destination,\n ...(record ? { chain: record.chain, peerId: record.peerId } : {}),\n ...(record?.depositTotal !== undefined\n ? { depositTotal: record.depositTotal }\n : {}),\n };\n\n if (opts?.deposit !== undefined && opts.deposit > 0n) {\n if (!this.client.depositToChannel) {\n throw new StandalonePublishError(\n 'this client build does not support channel deposits ' +\n '(depositToChannel is unavailable)'\n );\n }\n const deposited = await this.client.depositToChannel(\n channelId,\n opts.deposit\n );\n outcome.depositAdded = opts.deposit.toString();\n outcome.depositTotal = deposited.depositTotal;\n if (deposited.txHash) outcome.depositTxHash = deposited.txHash;\n if (record) {\n this.channelMap?.touch(recordKey(record), {\n depositTotal: deposited.depositTotal,\n });\n }\n }\n return outcome;\n }\n\n /**\n * Adopt a RECORDED channel into the running client so on-chain close/\n * settle/deposit can act on it: `trackChannel` rehydrates the claim\n * watermark + withdraw timers from channels.json, `peerChannels` binds the\n * peer, and the on-chain client's context cache is re-seeded (it only\n * learns context at open time — a resumed channel would otherwise throw\n * \"No on-chain context\").\n */\n private adoptRecordedChannel(record: ChannelMapRecord): void {\n const internals = channelInternals(this.client);\n const cm = internals.channelManager;\n if (cm?.trackChannel && cm.peerChannels) {\n cm.trackChannel(record.channelId, record.context);\n cm.peerChannels.set(record.peerId, record.channelId);\n }\n const contextCache = internals.onChainChannelClient?.channelContext;\n if (contextCache && !contextCache.has(record.channelId)) {\n contextCache.set(record.channelId, {\n chain: record.chain,\n tokenNetworkAddress: record.context.tokenNetworkAddress,\n ...(record.context.tokenAddress\n ? { tokenAddress: record.context.tokenAddress }\n : {}),\n });\n }\n }\n\n /**\n * Close a recorded channel: starts the on-chain settlement challenge\n * window. The client persists `closedAt`/`settleableAt` into the claim\n * watermark store, which is exactly where `rig channel list`/`balance`\n * derive the closing/settleable/settled status from. Guard + client start,\n * but NEVER a channel open ({@link startClientOnly}).\n */\n async closeRecordedChannel(\n record: ChannelMapRecord\n ): Promise<ChannelCloseOutcome> {\n await this.startClientOnly();\n if (!this.client.closeChannel) {\n throw new StandalonePublishError(\n 'this client build does not support closing channels ' +\n '(closeChannel is unavailable)'\n );\n }\n this.adoptRecordedChannel(record);\n const result = await this.client.closeChannel(record.channelId);\n this.channelMap?.touch(recordKey(record));\n return result;\n }\n\n /**\n * Settle a recorded channel after its challenge window — releases the\n * remaining collateral. The client enforces the `now >= settleableAt` time\n * guard BEFORE spending gas (a too-early call throws its retryable\n * SettleTooEarlyError) and persists `settledAt` into the watermark store.\n */\n async settleRecordedChannel(\n record: ChannelMapRecord\n ): Promise<ChannelSettleOutcome> {\n await this.startClientOnly();\n if (!this.client.settleChannel) {\n throw new StandalonePublishError(\n 'this client build does not support settling channels ' +\n '(settleChannel is unavailable)'\n );\n }\n this.adoptRecordedChannel(record);\n const result = await this.client.settleChannel(record.channelId);\n this.channelMap?.touch(recordKey(record));\n return result;\n }\n\n /**\n * The full multi-chain wallet view (#299) for the embedded identity — native\n * coin + configured tokens (USDC) per chain — a FREE read on the UNSTARTED\n * client (no nonce guard, no uplink, no channel). The client derives the\n * Solana/Mina addresses from the mnemonic on demand, so ALL configured chains\n * appear even before a start. Best-effort per chain (an unreachable RPC yields\n * an `unreadable` chain, not a failure).\n */\n async readWalletChainBalances(): Promise<WalletChainBalanceInfo[]> {\n if (!this.client.getWalletBalances) return [];\n return await this.client.getWalletBalances();\n }\n\n // ── Publisher ─────────────────────────────────────────────────────────────\n\n /**\n * Fee rates for `planPush` estimation: the flat per-event fee and the\n * per-byte upload rate this publisher pays (daemon `feePerEvent` and seed\n * bid-rate conventions; override via options), with the announced route\n * prices folded in so the ESTIMATE always equals the CLAIMS: the flat\n * `eventFee` is returned pre-floored at the publish route price, and the\n * store route price rides as `minUploadFee` (the per-upload floor —\n * per-byte pricing can't fold a flat floor into the rate).\n */\n getFeeRates(): Promise<FeeRates> {\n const minUploadFee = this.routePrices?.store;\n return Promise.resolve({\n uploadFeePerByte: this.uploadFeePerByte,\n eventFee: this.effectiveEventFee(),\n ...(minUploadFee !== undefined ? { minUploadFee } : {}),\n });\n }\n\n /**\n * The flat per-event claim: `eventFee`, floored at the publish route's\n * announced price (the connector rejects a claim below it — F06).\n */\n private effectiveEventFee(): bigint {\n const min = this.routePrices?.publish ?? 0n;\n return this.eventFee > min ? this.eventFee : min;\n }\n\n /**\n * The per-upload claim: `bytes × uploadFeePerByte`, floored at the store\n * route's announced price (the connector rejects a claim below it — F06).\n */\n private uploadFee(bytes: number): bigint {\n return flooredUploadFee(\n bytes,\n this.uploadFeePerByte,\n this.routePrices?.store\n );\n }\n\n /**\n * Upload one git object as a kind:5094 store write (Git-SHA/Git-Type/Repo\n * tagged — the proven seed-pipeline shape), signing one balance-proof claim\n * for `body.length × uploadFeePerByte`, floored at the store route's\n * announced price ({@link StandalonePublisherOptions.routePrices}).\n *\n * #368: a blob's `Content-Type` is derived from its path extension (else\n * octet-stream) and sent in the `output` tag, which the store forwards onto\n * the Arweave upload's Content-Type — so a gateway serves `index.html` as\n * `text/html`, not `application/octet-stream`. Non-blob objects (trees,\n * commits, tags) are never served as files, so they stay octet-stream.\n */\n async uploadGitObject(upload: GitObjectUpload): Promise<UploadReceipt> {\n if (upload.body.length > MAX_OBJECT_SIZE) {\n throw new StandalonePublishError(\n `git object ${upload.sha} exceeds the ${MAX_OBJECT_SIZE}-byte limit: ${upload.body.length} bytes`\n );\n }\n await this.start();\n const channelId = this.requireChannel();\n\n const contentType =\n upload.type === 'blob'\n ? contentTypeForPath(upload.path)\n : DEFAULT_CONTENT_TYPE;\n\n const fee = this.uploadFee(upload.body.length);\n const event = this.client.signEvent({\n kind: 5094,\n content: '',\n created_at: nowSeconds(),\n tags: [\n ['i', upload.body.toString('base64'), 'blob'],\n ['bid', fee.toString(), 'usdc'],\n ['output', contentType],\n ['Git-SHA', upload.sha],\n ['Git-Type', upload.type],\n ['Repo', upload.repoId],\n ],\n });\n\n const claim = await this.client.signBalanceProof(channelId, fee);\n const result = await this.client.publishEvent(event, {\n ...(this.storeDestination ? { destination: this.storeDestination } : {}),\n claim,\n ilpAmount: fee,\n // The store backend serves POST /store (not the relay's /write).\n proxyPath: '/store',\n });\n if (!result.success) {\n throw new StandalonePublishError(\n `git-object upload rejected (${upload.sha}): ${result.error ?? 'store rejected the write'}`\n );\n }\n if (!result.data) {\n throw new StandalonePublishError(\n `git-object upload FULFILL carried no data (${upload.sha}); expected the Arweave tx ID`\n );\n }\n return { txId: extractArweaveTxId(result.data), feePaid: fee };\n }\n\n /**\n * Upload one raw blob (no git envelope) with an explicit `Content-Type`\n * (#368) — a kind:5094 store write carrying just the `i`/`bid`/`output`\n * tags (and an optional `Repo` provenance tag), NOT the Git-SHA/Git-Type\n * tags. Without those the store keeps the bytes verbatim instead of\n * re-deriving a git envelope, which is exactly what the ar.io path manifest\n * needs. One balance-proof claim for `body.length × uploadFeePerByte`,\n * floored at the store route's announced price.\n */\n async uploadBlob(upload: BlobUpload): Promise<UploadReceipt> {\n if (upload.body.length > MAX_OBJECT_SIZE) {\n throw new StandalonePublishError(\n `blob exceeds the ${MAX_OBJECT_SIZE}-byte limit: ${upload.body.length} bytes`\n );\n }\n await this.start();\n const channelId = this.requireChannel();\n\n const fee = this.uploadFee(upload.body.length);\n const event = this.client.signEvent({\n kind: 5094,\n content: '',\n created_at: nowSeconds(),\n tags: [\n ['i', upload.body.toString('base64'), 'blob'],\n ['bid', fee.toString(), 'usdc'],\n ['output', upload.contentType],\n ...(upload.repoId ? [['Repo', upload.repoId]] : []),\n ],\n });\n\n const claim = await this.client.signBalanceProof(channelId, fee);\n const result = await this.client.publishEvent(event, {\n ...(this.storeDestination ? { destination: this.storeDestination } : {}),\n claim,\n ilpAmount: fee,\n proxyPath: '/store',\n });\n if (!result.success) {\n throw new StandalonePublishError(\n `blob upload rejected: ${result.error ?? 'store rejected the write'}`\n );\n }\n if (!result.data) {\n throw new StandalonePublishError(\n 'blob upload FULFILL carried no data; expected the Arweave tx ID'\n );\n }\n return { txId: extractArweaveTxId(result.data), feePaid: fee };\n }\n\n /**\n * Sign the event with the embedded identity and pay-to-publish it through\n * the relay write route, one claim for the flat per-event fee.\n *\n * `relayUrls` is the interface's plural forward-compat surface (parked\n * #84): the standalone impl routes over ILP to its single configured\n * publish destination, so more than one relay is refused rather than\n * silently half-published.\n */\n async publishEvent(\n event: UnsignedEvent,\n relayUrls: string[]\n ): Promise<PublishReceipt> {\n if (relayUrls.length > 1) {\n throw new StandalonePublishError(\n `multi-relay publish is not supported yet (got ${relayUrls.length} relays) — the standalone publisher routes to a single relay destination (#84 parked)`\n );\n }\n await this.start();\n const channelId = this.requireChannel();\n\n const signed = this.client.signEvent(event);\n const fee = this.effectiveEventFee();\n const claim = await this.client.signBalanceProof(channelId, fee);\n const result = await this.client.publishEvent(signed, {\n ...(this.publishDestination\n ? { destination: this.publishDestination }\n : {}),\n claim,\n ilpAmount: fee,\n });\n if (!result.success) {\n throw new StandalonePublishError(\n `publish rejected (kind ${event.kind}): ${result.error ?? 'relay rejected the event'}`\n );\n }\n return { eventId: result.eventId ?? signed.id, feePaid: fee };\n }\n\n private requireChannel(): string {\n if (!this.channelId) {\n throw new StandalonePublishError(\n 'no payment channel open — start() did not complete'\n );\n }\n return this.channelId;\n }\n}\n\nfunction nowSeconds(): number {\n return Math.floor(Date.now() / 1000);\n}\n"],"mappings":"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","names":[]}
|