@jinn-network/client 0.1.7-canary.d4e4e183 → 0.1.7-canary.f3c8e0db

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/README.md +67 -1
  2. package/dist/adapters/mech/adapter.js +27 -3
  3. package/dist/adapters/mech/adapter.js.map +1 -1
  4. package/dist/adapters/mech/safe.d.ts +1 -1
  5. package/dist/adapters/mech/safe.js +5 -3
  6. package/dist/adapters/mech/safe.js.map +1 -1
  7. package/dist/adapters/mech/types.d.ts +6 -1
  8. package/dist/adapters/mech/types.js.map +1 -1
  9. package/dist/api/activity-events-endpoint.d.ts +14 -0
  10. package/dist/api/activity-events-endpoint.js +59 -0
  11. package/dist/api/activity-events-endpoint.js.map +1 -0
  12. package/dist/api/gather-status.d.ts +3 -0
  13. package/dist/api/gather-status.js +84 -4
  14. package/dist/api/gather-status.js.map +1 -1
  15. package/dist/api/server.js +4 -0
  16. package/dist/api/server.js.map +1 -1
  17. package/dist/api/status-build.d.ts +40 -0
  18. package/dist/api/status-build.js +25 -0
  19. package/dist/api/status-build.js.map +1 -1
  20. package/dist/build-info.json +4 -4
  21. package/dist/build-meta.json +1 -1
  22. package/dist/config.d.ts +89 -39
  23. package/dist/config.js +97 -30
  24. package/dist/config.js.map +1 -1
  25. package/dist/daemon/daemon.d.ts +3 -0
  26. package/dist/daemon/daemon.js +88 -22
  27. package/dist/daemon/daemon.js.map +1 -1
  28. package/dist/daemon/gate-logger.d.ts +9 -0
  29. package/dist/daemon/gate-logger.js +2 -0
  30. package/dist/daemon/gate-logger.js.map +1 -0
  31. package/dist/daemon/readiness-gate.d.ts +1 -4
  32. package/dist/daemon/readiness-gate.js.map +1 -1
  33. package/dist/daemon/spend-cap-gate.d.ts +40 -0
  34. package/dist/daemon/spend-cap-gate.js +46 -0
  35. package/dist/daemon/spend-cap-gate.js.map +1 -0
  36. package/dist/dashboard/assets/index-CKTc0D37.css +32 -0
  37. package/dist/dashboard/assets/index-nbNLLn9_.js +345 -0
  38. package/dist/dashboard/index.html +2 -2
  39. package/dist/earning/safe-adapter.js +23 -11
  40. package/dist/earning/safe-adapter.js.map +1 -1
  41. package/dist/earning/types.d.ts +6 -6
  42. package/dist/earning/viem-clients.d.ts +11 -4
  43. package/dist/earning/viem-clients.js +14 -5
  44. package/dist/earning/viem-clients.js.map +1 -1
  45. package/dist/events/types.d.ts +2 -2
  46. package/dist/harnesses/engine/engine.js +13 -0
  47. package/dist/harnesses/engine/engine.js.map +1 -1
  48. package/dist/main.js +24 -11
  49. package/dist/main.js.map +1 -1
  50. package/dist/observability/emit-event.d.ts +3 -2
  51. package/dist/observability/emit-event.js +22 -1
  52. package/dist/observability/emit-event.js.map +1 -1
  53. package/dist/preflight/rpc-network.d.ts +40 -0
  54. package/dist/preflight/rpc-network.js +67 -1
  55. package/dist/preflight/rpc-network.js.map +1 -1
  56. package/dist/rpc/transport.d.ts +109 -0
  57. package/dist/rpc/transport.js +220 -0
  58. package/dist/rpc/transport.js.map +1 -0
  59. package/dist/spend/credential.d.ts +8 -0
  60. package/dist/spend/credential.js +30 -0
  61. package/dist/spend/credential.js.map +1 -0
  62. package/dist/spend/daemon-config.d.ts +13 -0
  63. package/dist/spend/daemon-config.js +24 -0
  64. package/dist/spend/daemon-config.js.map +1 -0
  65. package/dist/spend/pricing.d.ts +16 -0
  66. package/dist/spend/pricing.js +26 -0
  67. package/dist/spend/pricing.js.map +1 -0
  68. package/dist/spend/record.d.ts +13 -0
  69. package/dist/spend/record.js +30 -0
  70. package/dist/spend/record.js.map +1 -0
  71. package/dist/spend/usage.d.ts +27 -0
  72. package/dist/spend/usage.js +113 -0
  73. package/dist/spend/usage.js.map +1 -0
  74. package/dist/store/store.d.ts +28 -0
  75. package/dist/store/store.js +118 -4
  76. package/dist/store/store.js.map +1 -1
  77. package/dist/trajectory/transcript-parsers/types.d.ts +8 -8
  78. package/dist/tx-retry.d.ts +15 -0
  79. package/dist/tx-retry.js +30 -6
  80. package/dist/tx-retry.js.map +1 -1
  81. package/package.json +2 -1
  82. package/dist/dashboard/assets/index-BUlE8F3Y.js +0 -330
  83. package/dist/dashboard/assets/index-blqc7eqq.css +0 -32
@@ -1,5 +1,6 @@
1
- import { createPublicClient, http } from 'viem';
1
+ import { createPublicClient, http, HttpRequestError } from 'viem';
2
2
  import { base, baseSepolia } from 'viem/chains';
3
+ import { describeFallbackChain, maskRpcHost } from '../rpc/transport.js';
3
4
  export function expectedChainIdForNetwork(network) {
4
5
  return network === 'testnet' ? 84532 : 8453;
5
6
  }
@@ -118,4 +119,69 @@ export function rpcNetworkFailureHint(result) {
118
119
  }
119
120
  return 'Set rpcUrl to a Base mainnet endpoint such as https://mainnet.base.org, or set BASE_RPC_URL.';
120
121
  }
122
+ function classifyProbeError(host, err) {
123
+ const message = err instanceof Error ? err.message : String(err);
124
+ if (err instanceof HttpRequestError && typeof err.status === 'number') {
125
+ return { ok: false, host, code: err.status, message };
126
+ }
127
+ // viem wraps HTTP errors inside other shapes; walk the cause chain.
128
+ let cur = err?.cause;
129
+ while (cur) {
130
+ if (cur instanceof HttpRequestError && typeof cur.status === 'number') {
131
+ return { ok: false, host, code: cur.status, message };
132
+ }
133
+ cur = cur.cause;
134
+ }
135
+ return { ok: false, host, reason: 'unreachable', message };
136
+ }
137
+ /**
138
+ * Probe each URL in a fallback chain with a per-slot `eth_blockNumber` call.
139
+ * Records ok/latency or a structured failure (HTTP status / unreachable).
140
+ *
141
+ * The probe is **log-only** — it never throws on per-slot failures. Startup
142
+ * gating remains the job of {@link checkRpcNetwork}, which fails loud on
143
+ * chain-id mismatch.
144
+ *
145
+ * Per AC9 (issue #592): emits one log line per slot
146
+ * `[rpc] <layer> <host> ok latency=Nms`
147
+ * `[rpc] <layer> <host> warn <http-status>` (e.g. 429, 503)
148
+ * `[rpc] <layer> <host> warn unreachable: <message>`
149
+ */
150
+ export async function probeFallbackChain(urls, network, layer, options = {}) {
151
+ const log = options.log ?? ((m) => process.stderr.write(`${m}\n`));
152
+ // Chain is informational only — eth_blockNumber doesn't depend on chain
153
+ // identity. Both L1 and L2 layers reuse the same Base/Base-Sepolia pair
154
+ // since this is just used to satisfy viem's typing.
155
+ const chain = network === 'testnet' ? baseSepolia : base;
156
+ const results = [];
157
+ for (const url of urls) {
158
+ const host = maskRpcHost(url);
159
+ const client = createPublicClient({ chain, transport: http(url, { retryCount: 0 }) });
160
+ const t0 = performance.now();
161
+ try {
162
+ await client.getBlockNumber();
163
+ const latencyMs = Math.max(0, Math.round(performance.now() - t0));
164
+ log(`[rpc] ${layer} ${host} ok latency=${latencyMs}ms`);
165
+ results.push({ ok: true, host, latencyMs });
166
+ }
167
+ catch (err) {
168
+ const failure = classifyProbeError(host, err);
169
+ if (failure.code !== undefined) {
170
+ log(`[rpc] ${layer} ${host} warn ${failure.code}`);
171
+ }
172
+ else {
173
+ log(`[rpc] ${layer} ${host} warn unreachable: ${failure.message.split('\n')[0]}`);
174
+ }
175
+ results.push(failure);
176
+ }
177
+ }
178
+ return results;
179
+ }
180
+ /**
181
+ * Boot-log summary line for a fallback chain, formatted per AC7:
182
+ * `[rpc] L2 transport: fallback chain (N providers) — primary=<host>`
183
+ */
184
+ export function summarizeFallbackChain(layer, urls) {
185
+ return `[rpc] ${layer} transport: ${describeFallbackChain(urls)}`;
186
+ }
121
187
  //# sourceMappingURL=rpc-network.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"rpc-network.js","sourceRoot":"","sources":["../../src/preflight/rpc-network.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"rpc-network.js","sourceRoot":"","sources":["../../src/preflight/rpc-network.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,109 @@
1
+ /**
2
+ * RPC transport helper — builds a viem `fallback()` transport from a single
3
+ * URL, an array of URLs, or a comma-separated string. The substrate for issue
4
+ * #592 (multi-RPC fallback chain across daemon / relayer / indexer / CI).
5
+ *
6
+ * Design:
7
+ * - `parseRpcUrls` normalises `string | readonly string[]` to a deduplicated
8
+ * non-empty array, splits comma-strings, trims, drops empties, and caps the
9
+ * chain at {@link MAX_RPC_CHAIN_LENGTH} providers (extras are dropped with a
10
+ * warning). Throws when no URLs remain.
11
+ * - `buildFallbackTransport` wraps `http(url)` per slot inside viem's
12
+ * `fallback([...], { rank: false, retryCount: 0 })`. `rank: false` is
13
+ * explicit — the issue's "Tenderly stays in slot 3" constraint requires
14
+ * order preservation (no latency-based reshuffling). `retryCount: 0` keeps
15
+ * the helper from doubling retries on top of the existing
16
+ * `withRecoverableRetry` wrapper used by callers like the mech adapter.
17
+ * - On exhausted fall-through the helper rejects with `AllRpcsFailedError`,
18
+ * which carries a structured `providers: readonly string[]` list of masked
19
+ * hosts so callers and tests can assert on it.
20
+ * - `describeFallbackChain` formats the canonical AC7 boot-log summary line:
21
+ * `fallback chain (N providers) — primary=<host>`.
22
+ *
23
+ * Do not conflate with `discovery.fallbackToOnchain` — that's a separate layer
24
+ * at the read-API level (Ponder → eth_getLogs floor). This helper sits beneath
25
+ * both layers at the JSON-RPC transport level.
26
+ */
27
+ import { type FallbackTransport, type Transport } from 'viem';
28
+ /**
29
+ * Hard cap on the number of providers in a single fallback chain. Four covers
30
+ * the "operator paid primary + public publicnode + public sepolia.base.org +
31
+ * Tenderly slot-3" shape from the issue; beyond that the boot probe takes too
32
+ * long and slot 5+ is almost always copy-paste noise.
33
+ */
34
+ export declare const MAX_RPC_CHAIN_LENGTH = 4;
35
+ export interface ParseRpcUrlsOptions {
36
+ /** Logger used to emit the "capped" warning. Defaults to `console.error`. */
37
+ log?: (message: string) => void;
38
+ }
39
+ /**
40
+ * Normalise `string | readonly string[]` into a non-empty, deduplicated,
41
+ * capped list of RPC URLs. Comma-separated strings are split (operator
42
+ * convention, see `peers` in `config.ts`). Duplicates are removed before the
43
+ * cap is applied so the effective chain length matches operator intent (an
44
+ * operator who prepends their paid primary to the existing fallback list
45
+ * shouldn't burn a slot on the duplicate).
46
+ *
47
+ * @throws if the input yields zero non-empty URLs.
48
+ */
49
+ export declare function parseRpcUrls(input: string | readonly string[], options?: ParseRpcUrlsOptions): string[];
50
+ /**
51
+ * Error thrown when every provider in a fallback chain has failed. Carries
52
+ * the masked host list so callers can surface a useful operator-facing message
53
+ * without leaking secret query strings (api-key paths).
54
+ */
55
+ export declare class AllRpcsFailedError extends Error {
56
+ readonly providers: readonly string[];
57
+ readonly cause?: unknown;
58
+ constructor(providers: readonly string[], cause?: unknown);
59
+ }
60
+ /**
61
+ * Detect errors that viem's `fallback()` short-circuits on (via its
62
+ * `shouldThrow(err)` fast-exit) — `ExecutionRevertedError`,
63
+ * `TransactionRejectedRpcError`, `UserRejectedRequestError`,
64
+ * `WalletConnectSessionSettlementError`, and CAIP user-rejected (code
65
+ * `5000`). These are EVM- or wallet-level signals, NOT transport-level
66
+ * failures, so they must propagate unchanged. Wrapping them as
67
+ * `AllRpcsFailedError` would give the operator-app a false-positive
68
+ * "all RPCs failed" message when the actual problem is e.g. a contract
69
+ * revert on a view function.
70
+ *
71
+ * Mirrors viem's own predicate at
72
+ * `node_modules/viem/clients/transports/fallback.ts` (`shouldThrow`),
73
+ * dispatching by **numeric JSON-RPC code** (the path that catches real
74
+ * `RpcRequestError` instances coming out of the HTTP transport) plus the
75
+ * `ExecutionRevertedError.nodeMessage` regex (the path that catches reverts
76
+ * surfaced as plain `Error` with no numeric code). The cause-chain walk by
77
+ * class `name` remains as a third path for wallet-provider stacks that
78
+ * deliver proper class instances (and for already-normalised errors after
79
+ * viem's `buildRequest` re-wrap pass).
80
+ */
81
+ export declare function isViemShouldThrowError(err: unknown): boolean;
82
+ /**
83
+ * Mask an RPC URL down to its hostname for display / error reporting. Drops
84
+ * the path so api-key segments in the URL don't leak into logs.
85
+ */
86
+ export declare function maskRpcHost(url: string): string;
87
+ export interface BuildFallbackTransportOptions {
88
+ /**
89
+ * Set to `true` to let viem rank providers by latency. Default `false` —
90
+ * order matters for operator-configured paid primaries and the
91
+ * "Tenderly stays in slot 3" constraint from the issue.
92
+ */
93
+ rank?: boolean;
94
+ }
95
+ /**
96
+ * Build a viem fallback transport over the given URLs. Returns a callable
97
+ * transport suitable for `createPublicClient({ transport })`.
98
+ *
99
+ * Errors that exhaust the chain are wrapped in `AllRpcsFailedError`.
100
+ */
101
+ export declare function buildFallbackTransport(urls: readonly string[], options?: BuildFallbackTransportOptions): FallbackTransport;
102
+ export declare namespace buildFallbackTransport {
103
+ var buildFromTransports: (transports: readonly Transport[], urls: readonly string[], options?: BuildFallbackTransportOptions) => FallbackTransport;
104
+ }
105
+ /**
106
+ * Boot-log summary line for a fallback chain. Matches the canonical AC7
107
+ * format: `fallback chain (N providers) — primary=<host>`.
108
+ */
109
+ export declare function describeFallbackChain(urls: readonly string[]): string;
@@ -0,0 +1,220 @@
1
+ /**
2
+ * RPC transport helper — builds a viem `fallback()` transport from a single
3
+ * URL, an array of URLs, or a comma-separated string. The substrate for issue
4
+ * #592 (multi-RPC fallback chain across daemon / relayer / indexer / CI).
5
+ *
6
+ * Design:
7
+ * - `parseRpcUrls` normalises `string | readonly string[]` to a deduplicated
8
+ * non-empty array, splits comma-strings, trims, drops empties, and caps the
9
+ * chain at {@link MAX_RPC_CHAIN_LENGTH} providers (extras are dropped with a
10
+ * warning). Throws when no URLs remain.
11
+ * - `buildFallbackTransport` wraps `http(url)` per slot inside viem's
12
+ * `fallback([...], { rank: false, retryCount: 0 })`. `rank: false` is
13
+ * explicit — the issue's "Tenderly stays in slot 3" constraint requires
14
+ * order preservation (no latency-based reshuffling). `retryCount: 0` keeps
15
+ * the helper from doubling retries on top of the existing
16
+ * `withRecoverableRetry` wrapper used by callers like the mech adapter.
17
+ * - On exhausted fall-through the helper rejects with `AllRpcsFailedError`,
18
+ * which carries a structured `providers: readonly string[]` list of masked
19
+ * hosts so callers and tests can assert on it.
20
+ * - `describeFallbackChain` formats the canonical AC7 boot-log summary line:
21
+ * `fallback chain (N providers) — primary=<host>`.
22
+ *
23
+ * Do not conflate with `discovery.fallbackToOnchain` — that's a separate layer
24
+ * at the read-API level (Ponder → eth_getLogs floor). This helper sits beneath
25
+ * both layers at the JSON-RPC transport level.
26
+ */
27
+ import { ExecutionRevertedError, fallback, http, TransactionRejectedRpcError, UserRejectedRequestError, } from 'viem';
28
+ /**
29
+ * Hard cap on the number of providers in a single fallback chain. Four covers
30
+ * the "operator paid primary + public publicnode + public sepolia.base.org +
31
+ * Tenderly slot-3" shape from the issue; beyond that the boot probe takes too
32
+ * long and slot 5+ is almost always copy-paste noise.
33
+ */
34
+ export const MAX_RPC_CHAIN_LENGTH = 4;
35
+ /**
36
+ * Normalise `string | readonly string[]` into a non-empty, deduplicated,
37
+ * capped list of RPC URLs. Comma-separated strings are split (operator
38
+ * convention, see `peers` in `config.ts`). Duplicates are removed before the
39
+ * cap is applied so the effective chain length matches operator intent (an
40
+ * operator who prepends their paid primary to the existing fallback list
41
+ * shouldn't burn a slot on the duplicate).
42
+ *
43
+ * @throws if the input yields zero non-empty URLs.
44
+ */
45
+ export function parseRpcUrls(input, options = {}) {
46
+ const log = options.log ?? ((m) => process.stderr.write(`${m}\n`));
47
+ const raw = typeof input === 'string' ? input.split(',') : [...input];
48
+ const cleaned = raw.map((u) => u.trim()).filter((u) => u.length > 0);
49
+ if (cleaned.length === 0) {
50
+ throw new Error('parseRpcUrls: at least one RPC URL is required');
51
+ }
52
+ // Dedup before applying the cap so repeated URLs (easy to introduce when an
53
+ // operator prepends a paid primary that already exists in the fallback list)
54
+ // don't burn slots of the 4-slot chain. `Set` preserves first-seen insertion
55
+ // order, which matches the "primary stays in slot 0" constraint.
56
+ const deduped = [...new Set(cleaned)];
57
+ if (deduped.length > MAX_RPC_CHAIN_LENGTH) {
58
+ log(`[rpc] capped fallback chain to ${MAX_RPC_CHAIN_LENGTH} providers ` +
59
+ `(dropped ${deduped.length - MAX_RPC_CHAIN_LENGTH} extra slots)`);
60
+ return deduped.slice(0, MAX_RPC_CHAIN_LENGTH);
61
+ }
62
+ return deduped;
63
+ }
64
+ /**
65
+ * Error thrown when every provider in a fallback chain has failed. Carries
66
+ * the masked host list so callers can surface a useful operator-facing message
67
+ * without leaking secret query strings (api-key paths).
68
+ */
69
+ export class AllRpcsFailedError extends Error {
70
+ providers;
71
+ cause;
72
+ constructor(providers, cause) {
73
+ super(`All RPC providers in the fallback chain failed (providers=${providers.join(', ')})`);
74
+ this.name = 'AllRpcsFailedError';
75
+ this.providers = providers;
76
+ this.cause = cause;
77
+ }
78
+ }
79
+ /**
80
+ * Detect errors that viem's `fallback()` short-circuits on (via its
81
+ * `shouldThrow(err)` fast-exit) — `ExecutionRevertedError`,
82
+ * `TransactionRejectedRpcError`, `UserRejectedRequestError`,
83
+ * `WalletConnectSessionSettlementError`, and CAIP user-rejected (code
84
+ * `5000`). These are EVM- or wallet-level signals, NOT transport-level
85
+ * failures, so they must propagate unchanged. Wrapping them as
86
+ * `AllRpcsFailedError` would give the operator-app a false-positive
87
+ * "all RPCs failed" message when the actual problem is e.g. a contract
88
+ * revert on a view function.
89
+ *
90
+ * Mirrors viem's own predicate at
91
+ * `node_modules/viem/clients/transports/fallback.ts` (`shouldThrow`),
92
+ * dispatching by **numeric JSON-RPC code** (the path that catches real
93
+ * `RpcRequestError` instances coming out of the HTTP transport) plus the
94
+ * `ExecutionRevertedError.nodeMessage` regex (the path that catches reverts
95
+ * surfaced as plain `Error` with no numeric code). The cause-chain walk by
96
+ * class `name` remains as a third path for wallet-provider stacks that
97
+ * deliver proper class instances (and for already-normalised errors after
98
+ * viem's `buildRequest` re-wrap pass).
99
+ */
100
+ export function isViemShouldThrowError(err) {
101
+ // 1. Code-based dispatch — matches what viem's own `shouldThrow` does,
102
+ // so a raw `RpcRequestError { code: 3 | -32003 | 4001 | 7000 | 5000 }`
103
+ // from the HTTP transport is caught here regardless of class name.
104
+ // Codes are pulled from the viem error classes (no magic numbers) where
105
+ // available; 7000 and 5000 are inline because
106
+ // `WalletConnectSessionSettlementError` is not re-exported from the
107
+ // top-level `viem` package and 5000 is the CAIP user-rejected code
108
+ // viem hard-codes alongside it.
109
+ if (err instanceof Error && 'code' in err && typeof err.code === 'number') {
110
+ const code = err.code;
111
+ if (code === ExecutionRevertedError.code || // 3 — contract revert
112
+ code === TransactionRejectedRpcError.code || // -32003 — tx rejected
113
+ code === UserRejectedRequestError.code || // 4001 — user-rejected
114
+ code === 7000 || // WalletConnectSessionSettlementError
115
+ code === 5000 // CAIP user-rejected
116
+ )
117
+ return true;
118
+ // 2. nodeMessage regex — catches reverts that arrive as a plain `Error`
119
+ // (no numeric code, no recognised class name). Viem's own
120
+ // `shouldThrow` runs this check too.
121
+ if (ExecutionRevertedError.nodeMessage.test(err.message))
122
+ return true;
123
+ }
124
+ // 3. Cause-chain walk — viem wraps low-level RPC errors in higher-level
125
+ // error classes (e.g. `ContractFunctionExecutionError` →
126
+ // `RpcRequestError` → `ExecutionRevertedError`), and wallet providers
127
+ // can deliver proper class instances directly. Walk with cycle
128
+ // detection so a self-referential `.cause` cannot infinite-loop.
129
+ const seen = new Set();
130
+ let cursor = err;
131
+ while (cursor instanceof Error && !seen.has(cursor)) {
132
+ seen.add(cursor);
133
+ if (cursor.name === 'ExecutionRevertedError')
134
+ return true;
135
+ if (cursor.name === 'TransactionRejectedRpcError')
136
+ return true;
137
+ if (cursor.name === 'UserRejectedRequestError')
138
+ return true;
139
+ if (cursor.name === 'WalletConnectSessionSettlementError')
140
+ return true;
141
+ cursor = cursor.cause;
142
+ }
143
+ return false;
144
+ }
145
+ /**
146
+ * Mask an RPC URL down to its hostname for display / error reporting. Drops
147
+ * the path so api-key segments in the URL don't leak into logs.
148
+ */
149
+ export function maskRpcHost(url) {
150
+ try {
151
+ const parsed = new URL(url);
152
+ return parsed.hostname || '(unknown host)';
153
+ }
154
+ catch {
155
+ return '(invalid rpc url)';
156
+ }
157
+ }
158
+ /**
159
+ * Build a viem fallback transport over the given URLs. Returns a callable
160
+ * transport suitable for `createPublicClient({ transport })`.
161
+ *
162
+ * Errors that exhaust the chain are wrapped in `AllRpcsFailedError`.
163
+ */
164
+ export function buildFallbackTransport(urls, options = {}) {
165
+ const transports = urls.map((url) => http(url));
166
+ return buildFallbackTransport.buildFromTransports(transports, urls, options);
167
+ }
168
+ /**
169
+ * Internal helper exposed for tests: build a fallback transport from
170
+ * pre-constructed viem transports (e.g. `custom()` mocks) so tests can drive
171
+ * each slot deterministically without an actual HTTP fetch.
172
+ */
173
+ buildFallbackTransport.buildFromTransports = function buildFromTransports(transports, urls, options = {}) {
174
+ const maskedProviders = urls.map(maskRpcHost);
175
+ const inner = fallback(transports, {
176
+ rank: options.rank ?? false,
177
+ retryCount: 0,
178
+ });
179
+ // Wrap the transport so that the final exhausted-chain rejection surfaces
180
+ // as our structured `AllRpcsFailedError` (carrying the masked host list).
181
+ // We do the wrap at the transport-factory level rather than swapping the
182
+ // returned function's `request` so the FallbackTransport's typed shape
183
+ // (`onResponse`, `transports`, etc.) is preserved.
184
+ const wrapped = ((config) => {
185
+ const t = inner(config);
186
+ const originalRequest = t.request.bind(t);
187
+ t.request = (async (args) => {
188
+ try {
189
+ return await originalRequest(args);
190
+ }
191
+ catch (err) {
192
+ // viem's fallback() short-circuits on shouldThrow-class errors
193
+ // (contract revert, user-rejected, CAIP 5000) — those are
194
+ // EVM/wallet-level, not transport-level failures. Propagate them
195
+ // unchanged so callers (and the operator-app's `rpc_all_failed`
196
+ // state message) don't misread a single-slot revert as an
197
+ // exhausted-chain outage.
198
+ if (isViemShouldThrowError(err))
199
+ throw err;
200
+ // Otherwise: viem doesn't expose a distinct "all transports failed"
201
+ // error class — when fallback exhausts the chain it throws the last
202
+ // underlying error. Wrap so the caller gets a stable structural
203
+ // error carrying the masked provider list.
204
+ throw new AllRpcsFailedError(maskedProviders, err);
205
+ }
206
+ });
207
+ return t;
208
+ });
209
+ return wrapped;
210
+ };
211
+ /**
212
+ * Boot-log summary line for a fallback chain. Matches the canonical AC7
213
+ * format: `fallback chain (N providers) — primary=<host>`.
214
+ */
215
+ export function describeFallbackChain(urls) {
216
+ if (urls.length === 0)
217
+ return 'fallback chain (0 providers)';
218
+ return `fallback chain (${urls.length} providers) — primary=${maskRpcHost(urls[0])}`;
219
+ }
220
+ //# sourceMappingURL=transport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.js","sourceRoot":"","sources":["../../src/rpc/transport.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /** A credential identity. Format: `{provider}:{authMethod}`, e.g. `anthropic:api-key`. Never empty. */
2
+ export type CredentialId = string;
3
+ /**
4
+ * Resolve which authentication credential a harness will bill against, from
5
+ * the presence of provider env vars. Returns null when the harness makes no
6
+ * paid LLM call (e.g. prediction harnesses) or no credential is recognisable.
7
+ */
8
+ export declare function resolveCredentialId(harness: string | undefined, env: NodeJS.ProcessEnv): CredentialId | null;
@@ -0,0 +1,30 @@
1
+ import { CLAUDE_CODE_HARNESS, CODEX_HARNESS, HERMES_AGENT_HARNESS, canonicalHarnessName, } from '../harnesses/names.js';
2
+ /**
3
+ * Resolve which authentication credential a harness will bill against, from
4
+ * the presence of provider env vars. Returns null when the harness makes no
5
+ * paid LLM call (e.g. prediction harnesses) or no credential is recognisable.
6
+ */
7
+ export function resolveCredentialId(harness, env) {
8
+ if (!harness)
9
+ return null;
10
+ switch (canonicalHarnessName(harness)) {
11
+ case CLAUDE_CODE_HARNESS:
12
+ // empty string treated as absent
13
+ if (env['CLAUDE_CODE_OAUTH_TOKEN'])
14
+ return 'anthropic:subscription';
15
+ if (env['ANTHROPIC_API_KEY'])
16
+ return 'anthropic:api-key';
17
+ return null;
18
+ case CODEX_HARNESS:
19
+ if (env['OPENAI_API_KEY'])
20
+ return 'openai:api-key';
21
+ return 'openai:subscription';
22
+ case HERMES_AGENT_HARNESS: {
23
+ const provider = (env['JINN_HERMES_PROVIDER'] ?? 'hermes').trim().toLowerCase();
24
+ return `${provider}:api-key`;
25
+ }
26
+ default:
27
+ return null;
28
+ }
29
+ }
30
+ //# sourceMappingURL=credential.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credential.js","sourceRoot":"","sources":["../../src/spend/credential.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,aAAa,EACb,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,uBAAuB,CAAC;AAK/B;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CACjC,OAA2B,EAC3B,GAAsB;IAEtB,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAC1B,QAAQ,oBAAoB,CAAC,OAAO,CAAC,EAAE,CAAC;QACtC,KAAK,mBAAmB;YACtB,iCAAiC;YACjC,IAAI,GAAG,CAAC,yBAAyB,CAAC;gBAAE,OAAO,wBAAwB,CAAC;YACpE,IAAI,GAAG,CAAC,mBAAmB,CAAC;gBAAE,OAAO,mBAAmB,CAAC;YACzD,OAAO,IAAI,CAAC;QACd,KAAK,aAAa;YAChB,IAAI,GAAG,CAAC,gBAAgB,CAAC;gBAAE,OAAO,gBAAgB,CAAC;YACnD,OAAO,qBAAqB,CAAC;QAC/B,KAAK,oBAAoB,CAAC,CAAC,CAAC;YAC1B,MAAM,QAAQ,GAAG,CAAC,GAAG,CAAC,sBAAsB,CAAC,IAAI,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YAChF,OAAO,GAAG,QAAQ,UAAU,CAAC;QAC/B,CAAC;QACD;YACE,OAAO,IAAI,CAAC;IAChB,CAAC;AACH,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { JinnConfig } from '../config.js';
2
+ import { type CredentialId } from './credential.js';
3
+ export interface SpendCapDaemonConfig {
4
+ /** credentialId -> USD/day cap. */
5
+ caps: Record<CredentialId, number>;
6
+ /** manifest CID -> the credential its harness bills against. */
7
+ manifestCredentials: Record<string, CredentialId>;
8
+ }
9
+ /**
10
+ * Assemble the daemon's spend-cap config from operator config + env. Returns
11
+ * undefined when no credential ends up with a cap (the gate then stays off).
12
+ */
13
+ export declare function buildSpendCapConfig(config: Pick<JinnConfig, 'joinedSolverNets' | 'spendCaps'>, env: NodeJS.ProcessEnv): SpendCapDaemonConfig | undefined;
@@ -0,0 +1,24 @@
1
+ import { resolveCredentialId } from './credential.js';
2
+ /**
3
+ * Assemble the daemon's spend-cap config from operator config + env. Returns
4
+ * undefined when no credential ends up with a cap (the gate then stays off).
5
+ */
6
+ export function buildSpendCapConfig(config, env) {
7
+ const blanketRaw = env['JINN_SPEND_CAP_USD'];
8
+ const blanketNum = blanketRaw != null && blanketRaw.trim() !== '' ? Number(blanketRaw) : NaN;
9
+ const blanket = Number.isFinite(blanketNum) && blanketNum > 0 ? blanketNum : undefined;
10
+ const manifestCredentials = {};
11
+ for (const [manifestCid, entry] of Object.entries(config.joinedSolverNets ?? {})) {
12
+ const credentialId = resolveCredentialId(entry.harness, env);
13
+ if (credentialId)
14
+ manifestCredentials[manifestCid] = credentialId;
15
+ }
16
+ const caps = {};
17
+ for (const credentialId of new Set(Object.values(manifestCredentials))) {
18
+ const cap = config.spendCaps?.[credentialId] ?? blanket;
19
+ if (cap != null)
20
+ caps[credentialId] = cap;
21
+ }
22
+ return Object.keys(caps).length > 0 ? { caps, manifestCredentials } : undefined;
23
+ }
24
+ //# sourceMappingURL=daemon-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon-config.js","sourceRoot":"","sources":["../../src/spend/daemon-config.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAqB,MAAM,iBAAiB,CAAC;AASzE;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CACjC,MAA0D,EAC1D,GAAsB;IAEtB,MAAM,UAAU,GAAG,GAAG,CAAC,oBAAoB,CAAC,CAAC;IAC7C,MAAM,UAAU,GAAG,UAAU,IAAI,IAAI,IAAI,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IAC7F,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;IAEvF,MAAM,mBAAmB,GAAiC,EAAE,CAAC;IAC7D,KAAK,MAAM,CAAC,WAAW,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,gBAAgB,IAAI,EAAE,CAAC,EAAE,CAAC;QACjF,MAAM,YAAY,GAAG,mBAAmB,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QAC7D,IAAI,YAAY;YAAE,mBAAmB,CAAC,WAAW,CAAC,GAAG,YAAY,CAAC;IACpE,CAAC;IAED,MAAM,IAAI,GAAiC,EAAE,CAAC;IAC9C,KAAK,MAAM,YAAY,IAAI,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC,EAAE,CAAC;QACvE,MAAM,GAAG,GAAG,MAAM,CAAC,SAAS,EAAE,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC;QACxD,IAAI,GAAG,IAAI,IAAI;YAAE,IAAI,CAAC,YAAY,CAAC,GAAG,GAAG,CAAC;IAC5C,CAAC;IAED,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,mBAAmB,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AAClF,CAAC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Price a token count in USD for the given model. Returns null when the model
3
+ * is unknown to the catalog (caller falls back to a heuristic).
4
+ *
5
+ * Uses the tokenlens bundled, offline model catalog (models.dev snapshot).
6
+ * The `tokenlens` re-export of `getTokenCosts` accepts an object with
7
+ * `{ modelId, usage }` and injects the default catalog automatically.
8
+ *
9
+ * Note: tokenlens's bundled catalog may lag current model IDs (e.g.
10
+ * `claude-haiku-4-5-20251001` is absent in v1.3.1 and returns null);
11
+ * callers must handle null gracefully.
12
+ */
13
+ export declare function priceTokens(modelId: string, tokens: {
14
+ inputTokens: number;
15
+ outputTokens: number;
16
+ }): number | null;
@@ -0,0 +1,26 @@
1
+ import { getTokenCosts } from 'tokenlens';
2
+ /**
3
+ * Price a token count in USD for the given model. Returns null when the model
4
+ * is unknown to the catalog (caller falls back to a heuristic).
5
+ *
6
+ * Uses the tokenlens bundled, offline model catalog (models.dev snapshot).
7
+ * The `tokenlens` re-export of `getTokenCosts` accepts an object with
8
+ * `{ modelId, usage }` and injects the default catalog automatically.
9
+ *
10
+ * Note: tokenlens's bundled catalog may lag current model IDs (e.g.
11
+ * `claude-haiku-4-5-20251001` is absent in v1.3.1 and returns null);
12
+ * callers must handle null gracefully.
13
+ */
14
+ export function priceTokens(modelId, tokens) {
15
+ try {
16
+ const costs = getTokenCosts({
17
+ modelId,
18
+ usage: { prompt_tokens: tokens.inputTokens, completion_tokens: tokens.outputTokens },
19
+ });
20
+ return typeof costs?.totalUSD === 'number' ? costs.totalUSD : null;
21
+ }
22
+ catch {
23
+ return null;
24
+ }
25
+ }
26
+ //# sourceMappingURL=pricing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pricing.js","sourceRoot":"","sources":["../../src/spend/pricing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE1C;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,WAAW,CACzB,OAAe,EACf,MAAqD;IAErD,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,aAAa,CAAC;YAC1B,OAAO;YACP,KAAK,EAAE,EAAE,aAAa,EAAE,MAAM,CAAC,WAAW,EAAE,iBAAiB,EAAE,MAAM,CAAC,YAAY,EAAE;SACrF,CAAC,CAAC;QACH,OAAO,OAAO,KAAK,EAAE,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;IACrE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { Store } from '../store/store.js';
2
+ /**
3
+ * Record the cost of one finished harness run as a `task_cost` activity row.
4
+ * Called once per harness run (at the POST_SNAPSHOT transition). Best-effort:
5
+ * never throws — a parse failure must not break task execution.
6
+ */
7
+ export declare function recordTaskCost(store: Store, args: {
8
+ requestId: string;
9
+ harness: string;
10
+ model: string | undefined;
11
+ workingDir: string;
12
+ solverType: string | null;
13
+ }, env?: NodeJS.ProcessEnv): void;
@@ -0,0 +1,30 @@
1
+ import { resolveCredentialId } from './credential.js';
2
+ import { harvestHarnessUsage } from './usage.js';
3
+ /**
4
+ * Record the cost of one finished harness run as a `task_cost` activity row.
5
+ * Called once per harness run (at the POST_SNAPSHOT transition). Best-effort:
6
+ * never throws — a parse failure must not break task execution.
7
+ */
8
+ export function recordTaskCost(store, args, env = process.env) {
9
+ try {
10
+ const credentialId = resolveCredentialId(args.harness, env);
11
+ if (!credentialId)
12
+ return;
13
+ const usage = harvestHarnessUsage(args.harness, args.workingDir, args.model);
14
+ store.recordActivityEvent({
15
+ ts: new Date().toISOString(),
16
+ kind: 'task_cost',
17
+ requestId: args.requestId,
18
+ solverType: args.solverType,
19
+ credentialId,
20
+ costUsdMicros: Math.round(usage.costUsd * 1_000_000),
21
+ model: usage.model,
22
+ detail: usage.estimated ? 'estimated' : 'observed',
23
+ });
24
+ }
25
+ catch (err) {
26
+ console.warn(`[spend] failed to record task cost for ${args.requestId}: ` +
27
+ `${err instanceof Error ? err.message : String(err)}`);
28
+ }
29
+ }
30
+ //# sourceMappingURL=record.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"record.js","sourceRoot":"","sources":["../../src/spend/record.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEjD;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAC5B,KAAY,EACZ,IAMC,EACD,MAAyB,OAAO,CAAC,GAAG;IAEpC,IAAI,CAAC;QACH,MAAM,YAAY,GAAG,mBAAmB,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QAC5D,IAAI,CAAC,YAAY;YAAE,OAAO;QAC1B,MAAM,KAAK,GAAG,mBAAmB,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QAC7E,KAAK,CAAC,mBAAmB,CAAC;YACxB,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YAC5B,IAAI,EAAE,WAAW;YACjB,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,YAAY;YACZ,aAAa,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,GAAG,SAAS,CAAC;YACpD,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,MAAM,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU;SACnD,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,IAAI,CACV,0CAA0C,IAAI,CAAC,SAAS,IAAI;YAC1D,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CACxD,CAAC;IACJ,CAAC;AACH,CAAC"}