@routedock/routedock 0.1.2 → 0.3.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/LICENSE +21 -0
- package/README.md +532 -4
- package/dist/MppSessionHandler-1EL8QBAn.d.cts +215 -0
- package/dist/MppSessionHandler-Bq4S-JTE.d.ts +215 -0
- package/dist/ProviderRegistry-CA_TdREG.d.ts +142 -0
- package/dist/ProviderRegistry-D-0rqK0N.d.cts +142 -0
- package/dist/RouteDockClient-Bh86bVly.d.ts +469 -0
- package/dist/RouteDockClient-go9aopfF.d.cts +469 -0
- package/dist/SessionReconciler-Bw-PoBVl.d.ts +134 -0
- package/dist/SessionReconciler-Cz2nuOlJ.d.cts +134 -0
- package/dist/SessionStore-B4-Hi4H8.d.cts +143 -0
- package/dist/SessionStore-BaYiegP0.d.ts +143 -0
- package/dist/client.cjs +2746 -217
- package/dist/client.d.cts +51 -81
- package/dist/client.d.ts +51 -81
- package/dist/client.js +2690 -218
- package/dist/index.cjs +3634 -420
- package/dist/index.d.cts +12 -3
- package/dist/index.d.ts +12 -3
- package/dist/index.js +3569 -419
- package/dist/logger-D8_Q2ytp.d.cts +467 -0
- package/dist/logger-D8_Q2ytp.d.ts +467 -0
- package/dist/provider/fastify.cjs +1099 -0
- package/dist/provider/fastify.d.cts +40 -0
- package/dist/provider/fastify.d.ts +40 -0
- package/dist/provider/fastify.js +1097 -0
- package/dist/provider/hono.cjs +1152 -0
- package/dist/provider/hono.d.cts +101 -0
- package/dist/provider/hono.d.ts +101 -0
- package/dist/provider/hono.js +1142 -0
- package/dist/provider.cjs +1105 -147
- package/dist/provider.d.cts +38 -27
- package/dist/provider.d.ts +38 -27
- package/dist/provider.js +1079 -148
- package/dist/react.cjs +2931 -0
- package/dist/react.d.cts +107 -0
- package/dist/react.d.ts +107 -0
- package/dist/react.js +2924 -0
- package/dist/routedockMiddleware-57gUaEMf.d.ts +64 -0
- package/dist/routedockMiddleware-DlyLrxp7.d.cts +64 -0
- package/dist/schema.cjs +404 -0
- package/dist/schema.d.cts +3 -0
- package/dist/schema.d.ts +3 -0
- package/dist/schema.js +399 -0
- package/dist/sign-AKH67sft.d.cts +53 -0
- package/dist/sign-BEq-OsSq.d.ts +53 -0
- package/dist/testing.cjs +59 -0
- package/dist/testing.d.cts +114 -0
- package/dist/testing.d.ts +114 -0
- package/dist/testing.js +56 -0
- package/package.json +143 -23
- package/PUBLISH_CHECKLIST.md +0 -70
- package/dist/SessionStore-CCVO0PHM.d.cts +0 -152
- package/dist/SessionStore-CCVO0PHM.d.ts +0 -152
- package/dist/routedockMiddleware-B7WTB3LT.d.cts +0 -53
- package/dist/routedockMiddleware-DRgdrycG.d.ts +0 -53
- package/src/client/ModeRouter.ts +0 -104
- package/src/client/MppChargeClient.ts +0 -44
- package/src/client/MppSessionClient.ts +0 -144
- package/src/client/RouteDockClient.ts +0 -124
- package/src/client/index.ts +0 -8
- package/src/client/x402Client.ts +0 -84
- package/src/index.ts +0 -9
- package/src/provider/MppChargeHandler.ts +0 -90
- package/src/provider/MppSessionHandler.ts +0 -157
- package/src/provider/index.ts +0 -7
- package/src/provider/routedockMiddleware.ts +0 -154
- package/src/provider/x402Handler.ts +0 -164
- package/src/schemas/routedock.schema.json +0 -159
- package/src/store/SessionStore.ts +0 -90
- package/src/tests/smoke.test.ts +0 -228
- package/src/types.ts +0 -165
- package/tsconfig.json +0 -14
- package/tsup.config.ts +0 -15
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { usdcToStroops } from '@routedock/nulth-sdk';
|
|
2
|
+
import { l as SpendStore, a as DailySpend, R as RetryPolicy } from './RouteDockClient-go9aopfF.cjs';
|
|
3
|
+
import { ClientStellarSigner } from '@x402/stellar';
|
|
4
|
+
import { h as RouteDockManifest, c as PaymentResult } from './logger-D8_Q2ytp.cjs';
|
|
5
|
+
import { Keypair } from '@stellar/stellar-sdk';
|
|
6
|
+
import { SupabaseClient } from '@supabase/supabase-js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* USDC conversion — canonical implementation lives in @routedock/nulth-sdk and is
|
|
10
|
+
* re-exported here so SDK consumers (client, provider) share one converter.
|
|
11
|
+
*
|
|
12
|
+
* @routedock/routedock already depends on @routedock/nulth-sdk, so re-exporting
|
|
13
|
+
* (rather than defining a parallel helper) keeps a single source of truth.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @deprecated Use {@link usdcToStroops} — the same validated bigint converter.
|
|
18
|
+
* Kept as a thin alias so existing call sites keep working during migration.
|
|
19
|
+
*/
|
|
20
|
+
declare const usdcToUnits: typeof usdcToStroops;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* File-backed durable {@link SpendStore}.
|
|
24
|
+
*
|
|
25
|
+
* Persists the daily spend accumulator to a JSON file on disk, preserving
|
|
26
|
+
* cumulative spending totals across process restarts, crashes, and redeploys.
|
|
27
|
+
*
|
|
28
|
+
* ### Error semantics
|
|
29
|
+
* - File absent (ENOENT): returns null — legitimately no spend recorded yet.
|
|
30
|
+
* - File present but unreadable or malformed: throws — a safety control must
|
|
31
|
+
* fail closed, not assume nothing was spent.
|
|
32
|
+
*
|
|
33
|
+
* ### Write atomicity
|
|
34
|
+
* Writes go to `<filePath>.tmp` first, then `fs.rename` onto the target.
|
|
35
|
+
* `rename` is atomic within a filesystem, so there is no window where the
|
|
36
|
+
* target file is truncated but not yet fully written.
|
|
37
|
+
*/
|
|
38
|
+
declare class FileSpendStore implements SpendStore {
|
|
39
|
+
readonly filePath: string;
|
|
40
|
+
constructor(filePath: string);
|
|
41
|
+
read(): Promise<DailySpend | null>;
|
|
42
|
+
write(state: DailySpend): Promise<void>;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
type Network = 'testnet' | 'mainnet';
|
|
46
|
+
declare class X402Client {
|
|
47
|
+
private readonly network;
|
|
48
|
+
private readonly retryPolicy?;
|
|
49
|
+
private readonly httpClient;
|
|
50
|
+
private readonly signer;
|
|
51
|
+
constructor(secretKeyOrSigner: string | ClientStellarSigner, network: Network, retryPolicy?: RetryPolicy | undefined);
|
|
52
|
+
/** Replace signer (e.g. swap to Nulth ZK account payer before pay) */
|
|
53
|
+
withSigner(signer: ClientStellarSigner): X402Client;
|
|
54
|
+
pay(url: string, manifest: RouteDockManifest): Promise<PaymentResult>;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
declare class MppChargeClient {
|
|
58
|
+
private readonly keypair;
|
|
59
|
+
private readonly network;
|
|
60
|
+
private readonly retryPolicy?;
|
|
61
|
+
constructor(keypair: Keypair, network: 'testnet' | 'mainnet', retryPolicy?: RetryPolicy | undefined);
|
|
62
|
+
pay(url: string, manifest: RouteDockManifest): Promise<PaymentResult>;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
interface OnChainProviderInfo {
|
|
66
|
+
account: string;
|
|
67
|
+
endpoint: string;
|
|
68
|
+
tags: string[];
|
|
69
|
+
}
|
|
70
|
+
declare class OnChainRegistry {
|
|
71
|
+
private readonly horizon;
|
|
72
|
+
private readonly knownAccounts;
|
|
73
|
+
private readonly timeoutMs;
|
|
74
|
+
constructor(options: {
|
|
75
|
+
horizonUrl: string;
|
|
76
|
+
knownAccounts: string[];
|
|
77
|
+
timeoutMs?: number;
|
|
78
|
+
});
|
|
79
|
+
/**
|
|
80
|
+
* stellar-sdk 14.6.1 has no way to cancel the underlying Horizon request, so
|
|
81
|
+
* we stop waiting on it instead: the promise returned here rejects once
|
|
82
|
+
* `timeoutMs` elapses, even though the request may still be in flight. The
|
|
83
|
+
* timer is always cleared, including on the error path.
|
|
84
|
+
*/
|
|
85
|
+
private loadAccountWithTimeout;
|
|
86
|
+
listProviders(): Promise<OnChainProviderInfo[]>;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
interface ProviderRecord {
|
|
90
|
+
name: string;
|
|
91
|
+
description: string | undefined;
|
|
92
|
+
base_url: string;
|
|
93
|
+
modes: string[];
|
|
94
|
+
tags: string[];
|
|
95
|
+
network: string;
|
|
96
|
+
payee: string;
|
|
97
|
+
source: 'supabase' | 'onchain';
|
|
98
|
+
/**
|
|
99
|
+
* True only when the provider was verified out-of-band — for on-chain
|
|
100
|
+
* records the payee is the account that published the endpoint, for
|
|
101
|
+
* supabase records only rows flagged `verified` are returned.
|
|
102
|
+
*/
|
|
103
|
+
verified: boolean;
|
|
104
|
+
}
|
|
105
|
+
interface ProviderRegistryConfig {
|
|
106
|
+
supabase?: SupabaseClient;
|
|
107
|
+
onChain: {
|
|
108
|
+
horizonUrl: string;
|
|
109
|
+
knownAccounts: string[];
|
|
110
|
+
/**
|
|
111
|
+
* Optional explicit network override. When omitted it is derived from the
|
|
112
|
+
* Horizon URL: URLs containing `testnet` map to 'testnet', everything else
|
|
113
|
+
* defaults to 'mainnet'.
|
|
114
|
+
*/
|
|
115
|
+
network?: 'testnet' | 'mainnet';
|
|
116
|
+
/**
|
|
117
|
+
* Per-account timeout for on-chain account loads, in milliseconds.
|
|
118
|
+
* Defaults to 10000 ms.
|
|
119
|
+
*/
|
|
120
|
+
timeoutMs?: number;
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
declare class ProviderRegistry {
|
|
124
|
+
private readonly supabase;
|
|
125
|
+
private readonly onChain;
|
|
126
|
+
private readonly network;
|
|
127
|
+
constructor(config: ProviderRegistryConfig);
|
|
128
|
+
listProviders(): Promise<ProviderRecord[]>;
|
|
129
|
+
private trySupabase;
|
|
130
|
+
private tryOnChain;
|
|
131
|
+
/**
|
|
132
|
+
* Fetch a provider's manifest bound to the record's payee. The record's
|
|
133
|
+
* `payee` (from supabase `verified` rows or the on-chain registry account)
|
|
134
|
+
* is the out-of-band trust anchor: the fetched manifest's `payee` must
|
|
135
|
+
* equal it, otherwise `RouteDockSignatureError` is thrown. This closes the
|
|
136
|
+
* gap where the registry never cross-checked the on-chain payee against
|
|
137
|
+
* the manifest payee.
|
|
138
|
+
*/
|
|
139
|
+
fetchProviderManifest(record: ProviderRecord): Promise<RouteDockManifest>;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export { FileSpendStore as F, MppChargeClient as M, type OnChainProviderInfo as O, type ProviderRecord as P, X402Client as X, OnChainRegistry as a, ProviderRegistry as b, type ProviderRegistryConfig as c, usdcToUnits as u };
|
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
import { Keypair } from '@stellar/stellar-sdk';
|
|
2
|
+
import { g as RouteDockLogger, P as PaymentMode, h as RouteDockManifest, d as PreflightResult, c as PaymentResult, b as EstimateCostResult, n as SessionOptions, m as SessionHandle } from './logger-D8_Q2ytp.js';
|
|
3
|
+
import { ClientStellarSigner } from '@x402/stellar';
|
|
4
|
+
import { SignAuthEntry } from '@stellar/stellar-sdk/contract';
|
|
5
|
+
import { usdcToStroops } from '@routedock/nulth-sdk';
|
|
6
|
+
|
|
7
|
+
interface RetryPolicy {
|
|
8
|
+
/** Total attempts including the first call. Default: 4 (3 retries). */
|
|
9
|
+
maxAttempts?: number;
|
|
10
|
+
/** Base delay for exponential backoff in ms. Default: 250. */
|
|
11
|
+
baseDelayMs?: number;
|
|
12
|
+
/** Maximum jittered backoff delay in ms. Default: 30000. */
|
|
13
|
+
maxDelayMs?: number;
|
|
14
|
+
/** Called before each retry with the attempt number, error, and upcoming delay. */
|
|
15
|
+
/**
|
|
16
|
+
* Observability hook fired before each retry. Awaited, so a returned promise
|
|
17
|
+
* is honoured; throwing or rejecting is caught and logged rather than
|
|
18
|
+
* aborting the retry loop.
|
|
19
|
+
*/
|
|
20
|
+
onRetry?: (attempt: number, error: Error, nextDelayMs: number) => void | Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* Log sink for internal diagnostics (an `onRetry` hook that throws).
|
|
23
|
+
* Defaults to a console-backed logger.
|
|
24
|
+
*/
|
|
25
|
+
logger?: RouteDockLogger;
|
|
26
|
+
}
|
|
27
|
+
declare const DEFAULT_RETRY_POLICY: Required<Omit<RetryPolicy, 'onRetry' | 'logger'>>;
|
|
28
|
+
/**
|
|
29
|
+
* Full jitter exponential backoff delay.
|
|
30
|
+
* Delay range: 0..min(maxDelayMs, baseDelayMs * 2 ** attempt).
|
|
31
|
+
*/
|
|
32
|
+
declare function backoffDelayMs(attempt: number, baseDelayMs: number, maxDelayMs?: number): number;
|
|
33
|
+
/**
|
|
34
|
+
* Execute `fn` with exponential backoff on retryable RouteDockErrors.
|
|
35
|
+
* Honors `retryAfterMs` from facilitator 429/503 responses when present.
|
|
36
|
+
*/
|
|
37
|
+
declare function withRetry<T>(fn: () => Promise<T>, policy?: RetryPolicy): Promise<T>;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Enforce the per-endpoint `deprecated` and `sunset_at` metadata for the URL
|
|
41
|
+
* being paid. `pay()` never learns the HTTP method, so a path shared by more
|
|
42
|
+
* than one descriptor (e.g. GET and POST on `/infer`) is refused only when
|
|
43
|
+
* every matching descriptor has sunset. Paths the manifest does not list keep
|
|
44
|
+
* today's behavior. Query strings and hashes are ignored because only
|
|
45
|
+
* `URL.pathname` is compared.
|
|
46
|
+
*/
|
|
47
|
+
declare function assertEndpointActive(manifest: RouteDockManifest, url: string, logger?: RouteDockLogger): void;
|
|
48
|
+
/**
|
|
49
|
+
* Validate semantic constraints for manifest fields beyond JSON schema syntax.
|
|
50
|
+
* Enforces:
|
|
51
|
+
* - All keys in `latency_hints` must be a subset of declared `regions`.
|
|
52
|
+
* - If `assets` is defined, it must be a non-empty array whose first entry
|
|
53
|
+
* (`assets[0]`) matches the root-level `asset` and `asset_contract`.
|
|
54
|
+
*/
|
|
55
|
+
declare function assertManifestValid(manifest: RouteDockManifest, baseUrl?: string): void;
|
|
56
|
+
/**
|
|
57
|
+
* Override the manifest cache's max size (default 512). Affects the shared,
|
|
58
|
+
* process-wide cache used by all RouteDockClient instances.
|
|
59
|
+
*/
|
|
60
|
+
declare function configureManifestCache(maxSize: number): void;
|
|
61
|
+
/**
|
|
62
|
+
* Evict a provider's cached manifest immediately, forcing the next
|
|
63
|
+
* `fetchManifest(baseUrl)` to re-fetch and re-validate it. Use when a
|
|
64
|
+
* provider signals that their manifest changed (e.g. after a config update
|
|
65
|
+
* or a failed payment due to a mode change) so the next request does not
|
|
66
|
+
* keep routing on a stale cached copy for the remainder of the TTL.
|
|
67
|
+
*
|
|
68
|
+
* The cache is keyed by base URL as passed to `fetchManifest` — callers that
|
|
69
|
+
* pass a full URL (e.g. `RouteDockClient.invalidateManifest`) must normalize
|
|
70
|
+
* to the origin before calling this.
|
|
71
|
+
*/
|
|
72
|
+
declare function invalidateManifest(baseUrl: string): void;
|
|
73
|
+
|
|
74
|
+
interface ModeSelectOptions {
|
|
75
|
+
/** Force mpp-session if the provider supports it */
|
|
76
|
+
sustained?: boolean;
|
|
77
|
+
session?: boolean;
|
|
78
|
+
/**
|
|
79
|
+
* Preferred streaming transport for sustained sessions. When 'websocket',
|
|
80
|
+
* the mpp-session-ws variant is preferred when the provider advertises it;
|
|
81
|
+
* when 'sse' (or omitted), the classic mpp-session mode is preferred.
|
|
82
|
+
* Either transport falls back to the other session variant when the
|
|
83
|
+
* preferred one is not advertised.
|
|
84
|
+
*/
|
|
85
|
+
transport?: 'sse' | 'websocket';
|
|
86
|
+
/** Prefer the lowest-cost supported per-request mode when set to 'cost'. */
|
|
87
|
+
optimize?: 'cost';
|
|
88
|
+
/** Optional maximum acceptable per-request amount for cost-based selection. */
|
|
89
|
+
budget_per_request?: string;
|
|
90
|
+
/**
|
|
91
|
+
* Override mode selection and use this specific mode.
|
|
92
|
+
* Throws RouteDockNoSupportedModeError if the provider does not support it.
|
|
93
|
+
*/
|
|
94
|
+
forceMode?: PaymentMode;
|
|
95
|
+
/** Structured logger for mode selection events. Defaults to no-op. */
|
|
96
|
+
logger?: RouteDockLogger;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Fetch, validate, and cache a RouteDock manifest from `baseUrl`.
|
|
100
|
+
*
|
|
101
|
+
* `expectedPayee`, when provided, is the out-of-band trust anchor for the
|
|
102
|
+
* manifest (e.g. the account this provider registered on-chain): the fetched
|
|
103
|
+
* manifest's `payee` must equal it or `RouteDockSignatureError` is thrown.
|
|
104
|
+
* Callers that obtained a payee from a registry/pinned allowlist MUST pass it;
|
|
105
|
+
* without it the self-signed signature alone proves nothing about who served
|
|
106
|
+
* the manifest.
|
|
107
|
+
*/
|
|
108
|
+
declare function fetchManifest(baseUrl: string, retryPolicy?: RetryPolicy, manifestTimeoutMs?: number, expectedPayee?: string): Promise<RouteDockManifest>;
|
|
109
|
+
/**
|
|
110
|
+
* Deterministic mode selection per Section 6.3 of ROUTEDOCK_MASTER.md.
|
|
111
|
+
*
|
|
112
|
+
* Active modes are always considered before modes listed in `deprecated_modes`.
|
|
113
|
+
* Deprecated modes remain usable as a compatibility fallback and produce a
|
|
114
|
+
* logger warning when selected. An explicit forceMode remains authoritative,
|
|
115
|
+
* but also warns when the forced mode is deprecated.
|
|
116
|
+
*/
|
|
117
|
+
declare function selectMode(manifest: RouteDockManifest, options?: ModeSelectOptions): PaymentMode;
|
|
118
|
+
/**
|
|
119
|
+
* Rank a list of provider manifests by estimated p50 round-trip latency for a given target region.
|
|
120
|
+
* Manifests declaring latency_hints for the target region are sorted ascending by latency (lowest first).
|
|
121
|
+
* Manifests that list the region in `regions` without an explicit latency hint come next.
|
|
122
|
+
* Manifests not advertising the target region come last.
|
|
123
|
+
*/
|
|
124
|
+
declare function rankProvidersByLatency(manifests: RouteDockManifest[], targetRegion: string): RouteDockManifest[];
|
|
125
|
+
|
|
126
|
+
interface NulthPolicyState {
|
|
127
|
+
dailyCapStroops: bigint;
|
|
128
|
+
allowlistCommitment: string;
|
|
129
|
+
allowedPayees: readonly string[];
|
|
130
|
+
dailySpendStroops: bigint;
|
|
131
|
+
dayBucket: number;
|
|
132
|
+
witnessSecret: string;
|
|
133
|
+
expiryLedger?: number;
|
|
134
|
+
}
|
|
135
|
+
interface PaymentAuthContext {
|
|
136
|
+
authEntry: string;
|
|
137
|
+
payee: string;
|
|
138
|
+
amountStroops: bigint;
|
|
139
|
+
assetContract: string;
|
|
140
|
+
ledgerSequence: number;
|
|
141
|
+
}
|
|
142
|
+
interface NulthProof {
|
|
143
|
+
version: 1;
|
|
144
|
+
proof: string;
|
|
145
|
+
publicInputs: {
|
|
146
|
+
authDigest: string;
|
|
147
|
+
allowlistCommitment: string;
|
|
148
|
+
capCommitment: string;
|
|
149
|
+
payeeHash: string;
|
|
150
|
+
amountStroops: string;
|
|
151
|
+
dayBucket: string;
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
interface NulthAuthSignature {
|
|
155
|
+
nulth: 'zk-v1';
|
|
156
|
+
proof: NulthProof;
|
|
157
|
+
}
|
|
158
|
+
interface NulthClientConfig {
|
|
159
|
+
nulthAccount: string;
|
|
160
|
+
network: 'testnet' | 'mainnet';
|
|
161
|
+
prover?: 'mock';
|
|
162
|
+
policy: NulthPolicyState;
|
|
163
|
+
verifierContract?: string;
|
|
164
|
+
/** Log sink for the mock-prover warning. Defaults to a console-backed logger. */
|
|
165
|
+
logger?: RouteDockLogger;
|
|
166
|
+
}
|
|
167
|
+
declare class NulthPolicyError extends Error {
|
|
168
|
+
readonly code: 'payee_not_allowed' | 'daily_cap_exceeded' | 'session_expired' | 'auth_entry_mismatch';
|
|
169
|
+
constructor(code: NulthPolicyError['code'], message: string);
|
|
170
|
+
}
|
|
171
|
+
interface NulthStellarSigner {
|
|
172
|
+
address: string;
|
|
173
|
+
signAuthEntry: SignAuthEntry;
|
|
174
|
+
}
|
|
175
|
+
interface NulthSignerConfig extends NulthClientConfig {
|
|
176
|
+
paymentContext?: Omit<PaymentAuthContext, 'authEntry'>;
|
|
177
|
+
}
|
|
178
|
+
declare function createNulthSigner(config: NulthSignerConfig): NulthStellarSigner;
|
|
179
|
+
declare function setNulthPaymentContext(config: NulthSignerConfig, context: Omit<PaymentAuthContext, 'authEntry'>): void;
|
|
180
|
+
declare function paymentContextFromManifest(manifest: {
|
|
181
|
+
payee: string;
|
|
182
|
+
asset_contract: string;
|
|
183
|
+
pricing: {
|
|
184
|
+
x402?: {
|
|
185
|
+
amount: string;
|
|
186
|
+
};
|
|
187
|
+
'mpp-charge'?: {
|
|
188
|
+
amount: string;
|
|
189
|
+
};
|
|
190
|
+
};
|
|
191
|
+
}, mode: 'x402' | 'mpp-charge', ledgerSequence: number): Omit<PaymentAuthContext, 'authEntry'>;
|
|
192
|
+
declare function createPolicyState(input: {
|
|
193
|
+
dailyCapUsdc: string;
|
|
194
|
+
allowedPayees: readonly string[];
|
|
195
|
+
witnessSecret: string;
|
|
196
|
+
expiryLedger?: number;
|
|
197
|
+
ledgerSequence?: number;
|
|
198
|
+
}): NulthPolicyState;
|
|
199
|
+
interface NulthVaultConfig {
|
|
200
|
+
mode: 'nulth';
|
|
201
|
+
nulthAccount: string;
|
|
202
|
+
prover?: 'mock';
|
|
203
|
+
witnessSecret: string;
|
|
204
|
+
allowedPayees: readonly string[];
|
|
205
|
+
dailyCapUsdc: string;
|
|
206
|
+
expiryLedger?: number;
|
|
207
|
+
verifierContract?: string;
|
|
208
|
+
}
|
|
209
|
+
declare function assertNulthVaultManifest(manifest: RouteDockManifest, nulthAccount: string): void;
|
|
210
|
+
declare function fetchLedgerSequence(network: 'testnet' | 'mainnet'): Promise<number>;
|
|
211
|
+
declare function prepareNulthSigner(vault: NulthVaultConfig, manifest: RouteDockManifest, mode: PaymentMode, network: 'testnet' | 'mainnet', ledgerSequenceOverride?: number): Promise<{
|
|
212
|
+
signer: ClientStellarSigner;
|
|
213
|
+
config: NulthSignerConfig;
|
|
214
|
+
}>;
|
|
215
|
+
|
|
216
|
+
declare function decodeAuthSignature(encoded: string): NulthAuthSignature;
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Daily spend accumulator, keyed by an ISO date (YYYY-MM-DD).
|
|
220
|
+
*
|
|
221
|
+
* Totals are decimal strings of integer microUSDC (1 USDC = 10^7 units) so
|
|
222
|
+
* the record stays JSON-serializable with no floating point precision loss.
|
|
223
|
+
*/
|
|
224
|
+
interface DailySpend {
|
|
225
|
+
date: string;
|
|
226
|
+
/** Global accumulated spend for the day, in microUSDC (e.g. "5000000") */
|
|
227
|
+
totalMicros: string;
|
|
228
|
+
/** Per-endpoint accumulated spend for the day, keyed by origin URL, in microUSDC */
|
|
229
|
+
endpoints: Record<string, string>;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Durable backing store for the client's local daily spend cap.
|
|
233
|
+
*
|
|
234
|
+
* The spend cap is a financial safety control: if its accumulator lives only in
|
|
235
|
+
* heap memory, any crash, OOM kill, or container restart silently resets it to
|
|
236
|
+
* zero and the cap can be bypassed. Inject a persistent implementation
|
|
237
|
+
* (Redis, SQL, Supabase, a file, …) so the counter survives restarts.
|
|
238
|
+
*/
|
|
239
|
+
interface SpendStore {
|
|
240
|
+
/** Return the persisted accumulator, or null if none has been written yet. */
|
|
241
|
+
read(): Promise<DailySpend | null>;
|
|
242
|
+
/** Persist the accumulator. */
|
|
243
|
+
write(state: DailySpend): Promise<void>;
|
|
244
|
+
}
|
|
245
|
+
interface InMemorySpendStoreOptions {
|
|
246
|
+
/** Log a startup warning about non-durability. Defaults to true. */
|
|
247
|
+
warn?: boolean;
|
|
248
|
+
/** Log sink for the non-durability warning. Defaults to a console-backed logger. */
|
|
249
|
+
logger?: RouteDockLogger;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Default, non-durable {@link SpendStore}. Holds the accumulator on the heap, so
|
|
253
|
+
* it resets on every process restart. Logs a startup warning unless silenced.
|
|
254
|
+
* Use this only for development; inject a persistent store in production.
|
|
255
|
+
*/
|
|
256
|
+
declare class InMemorySpendStore implements SpendStore {
|
|
257
|
+
private state;
|
|
258
|
+
constructor(options?: InMemorySpendStoreOptions);
|
|
259
|
+
read(): Promise<DailySpend | null>;
|
|
260
|
+
write(state: DailySpend): Promise<void>;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
interface SpendCap {
|
|
264
|
+
/** Maximum total USDC spend per day (decimal string, e.g. "1.00") */
|
|
265
|
+
daily: string;
|
|
266
|
+
asset: 'USDC';
|
|
267
|
+
/**
|
|
268
|
+
* Optional per-endpoint daily spend caps, keyed by endpoint origin URL
|
|
269
|
+
* (e.g. "https://api.openai.com"). Checked before the global `daily` cap.
|
|
270
|
+
* An endpoint not listed here is only subject to the global cap.
|
|
271
|
+
* Both limits are enforced independently — hitting an endpoint cap does
|
|
272
|
+
* not prevent spend on other endpoints, but all spend still counts toward
|
|
273
|
+
* the global cap.
|
|
274
|
+
*
|
|
275
|
+
* Keys are normalized to `new URL(key).origin` when the client is
|
|
276
|
+
* constructed (lowercased host, no trailing slash, no default port), so
|
|
277
|
+
* "https://API.example.com/", "https://api.example.com" and
|
|
278
|
+
* "https://api.example.com:443" are all equivalent. A key that isn't a
|
|
279
|
+
* valid URL, that includes a path/query/hash, or that normalizes to the
|
|
280
|
+
* same origin as another key throws at construction time.
|
|
281
|
+
*/
|
|
282
|
+
endpointCaps?: Record<string, string>;
|
|
283
|
+
}
|
|
284
|
+
type VaultConfig = NulthVaultConfig;
|
|
285
|
+
interface RouteDockClientConfig {
|
|
286
|
+
/** Stellar keypair or raw secret key (S...) — fee payer / fallback signer */
|
|
287
|
+
wallet: Keypair | string;
|
|
288
|
+
network: 'testnet' | 'mainnet';
|
|
289
|
+
/** Optional local daily spend cap — checked before every payment, nulth vault payments included */
|
|
290
|
+
spendCap?: SpendCap;
|
|
291
|
+
/**
|
|
292
|
+
* Ed25519 secret key (S...) for signing channel commitments. Required for mpp-session.
|
|
293
|
+
*
|
|
294
|
+
* WARNING: Do not log or serialize the config object — it contains this secret in plaintext.
|
|
295
|
+
* The RouteDockClient stores it outside the instance to prevent leakage via JSON.stringify
|
|
296
|
+
* or console.log, but the raw config object is not protected.
|
|
297
|
+
*/
|
|
298
|
+
commitmentSecret?: string | undefined;
|
|
299
|
+
/** Retry policy for transient failures (network, facilitator 5xx). */
|
|
300
|
+
retryPolicy?: RetryPolicy;
|
|
301
|
+
/**
|
|
302
|
+
* Durable backing store for the daily spend cap accumulator. Defaults to a
|
|
303
|
+
* non-durable in-memory store that resets on restart (with a startup warning).
|
|
304
|
+
* Inject a persistent implementation for production safety.
|
|
305
|
+
*/
|
|
306
|
+
spendStore?: SpendStore;
|
|
307
|
+
/** Structured logger for SDK events. Defaults to no-op (silent). */
|
|
308
|
+
logger?: RouteDockLogger;
|
|
309
|
+
/**
|
|
310
|
+
* Timeout in milliseconds for manifest fetches. A provider that accepts the TCP
|
|
311
|
+
* connection but never sends a response body will be aborted after this delay.
|
|
312
|
+
* Default: 5000 ms.
|
|
313
|
+
*/
|
|
314
|
+
manifestTimeoutMs?: number;
|
|
315
|
+
/**
|
|
316
|
+
* Out-of-band trust anchor for manifests: the Stellar payee (G...) the client
|
|
317
|
+
* expects the provider to be (e.g. the account registered in the on-chain
|
|
318
|
+
* provider registry, or a pinned/allowlisted key). When set, every fetched
|
|
319
|
+
* manifest's `payee` must equal this value or payment is refused with
|
|
320
|
+
* `RouteDockSignatureError`. Without it, a self-signed manifest proves nothing
|
|
321
|
+
* about who served it, so set this whenever you obtained a provider address
|
|
322
|
+
* from a registry.
|
|
323
|
+
*/
|
|
324
|
+
expectedPayee?: string;
|
|
325
|
+
/**
|
|
326
|
+
* Vault custody mode. When `nulth`, payments use a Nulth account as payer
|
|
327
|
+
* with off-chain ZK proofs attached as auth signatures.
|
|
328
|
+
*/
|
|
329
|
+
vault?: VaultConfig;
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* @deprecated Use {@link usdcToStroops} — the same validated bigint converter,
|
|
333
|
+
* shared across the SDK and nulth-sdk. Kept as a thin alias so existing spend
|
|
334
|
+
* cap code keeps working during migration.
|
|
335
|
+
*/
|
|
336
|
+
declare const usdcToMicros: typeof usdcToStroops;
|
|
337
|
+
declare class RouteDockClient {
|
|
338
|
+
/** Per-(account,asset) trustline existence cache keyed by `${network}:${pubkey}:${asset}` */
|
|
339
|
+
private static _trustlineCache;
|
|
340
|
+
private readonly keypair;
|
|
341
|
+
private readonly network;
|
|
342
|
+
private readonly spendCap;
|
|
343
|
+
private readonly retryPolicy;
|
|
344
|
+
private readonly logger;
|
|
345
|
+
private readonly manifestTimeoutMs;
|
|
346
|
+
private readonly expectedPayee;
|
|
347
|
+
private readonly vault;
|
|
348
|
+
/**
|
|
349
|
+
* Durable backing store for the local daily spend accumulator (keyed by
|
|
350
|
+
* YYYY-MM-DD). Totals are persisted as decimal strings of microUSDC
|
|
351
|
+
* (1 USDC = 10^7) so stores stay JSON-safe with no precision loss.
|
|
352
|
+
*/
|
|
353
|
+
private readonly spendStore;
|
|
354
|
+
private x402;
|
|
355
|
+
private readonly charge;
|
|
356
|
+
private readonly session;
|
|
357
|
+
/**
|
|
358
|
+
* Promise-chain mutex for serializing read-modify-write on the spend
|
|
359
|
+
* accumulator. Each pay() call awaits the previous one, preventing
|
|
360
|
+
* concurrent read-modify-write races that could overrun the cap.
|
|
361
|
+
*/
|
|
362
|
+
private _spendMutex;
|
|
363
|
+
/**
|
|
364
|
+
* Pending spend reservations keyed by reserveId. Used by _rollbackSpend
|
|
365
|
+
* to revert the accumulator when a payment fails after reservation.
|
|
366
|
+
*/
|
|
367
|
+
private _pendingReserves;
|
|
368
|
+
constructor(config: RouteDockClientConfig);
|
|
369
|
+
/** Fetch manifest and select mode — shared by pay() and estimateCost(). */
|
|
370
|
+
private _resolveManifest;
|
|
371
|
+
/**
|
|
372
|
+
* Serialize async work through the per-instance spend mutex so concurrent
|
|
373
|
+
* pay() calls cannot interleave read-modify-write on the accumulator.
|
|
374
|
+
*/
|
|
375
|
+
private _withSpendMutex;
|
|
376
|
+
/**
|
|
377
|
+
* Check that the payer has a trustline for the payment asset and surface
|
|
378
|
+
* manifest metadata (regions, latency hints, capabilities). Safe to
|
|
379
|
+
* call before committing to a payment — for approval gates and manual
|
|
380
|
+
* trustline remediation.
|
|
381
|
+
*/
|
|
382
|
+
preflight(manifest: RouteDockManifest, mode?: PaymentMode, endpoint?: string): Promise<PreflightResult>;
|
|
383
|
+
/**
|
|
384
|
+
* Verify via Horizon that the payer account has a trustline for the
|
|
385
|
+
* payment asset declared in the manifest. Throws RouteDockTrustlineError
|
|
386
|
+
* with an exact Stellar CLI change-trust remediation command when the
|
|
387
|
+
* trustline is missing. Results are cached per (account, asset) so the
|
|
388
|
+
* per-payment overhead is one RPC call amortized to zero after the first.
|
|
389
|
+
*/
|
|
390
|
+
private _checkTrustline;
|
|
391
|
+
/**
|
|
392
|
+
* Pay for one request at `url`. Fetches manifest, selects payment mode,
|
|
393
|
+
* runs trustline preflight, reserves local spend cap BEFORE executing the
|
|
394
|
+
* payment, then commits the spend record on success. Rolls back the
|
|
395
|
+
* reservation if the on-chain payment fails, and also when the call
|
|
396
|
+
* completes without paying anything (an x402 endpoint answering a plain
|
|
397
|
+
* 200 with no challenge). Concurrent pay() calls are serialized through a
|
|
398
|
+
* per-instance mutex to prevent spend-cap overrun.
|
|
399
|
+
*/
|
|
400
|
+
pay(url: string, options?: ModeSelectOptions): Promise<PaymentResult>;
|
|
401
|
+
/** Nulth ZK vault path — proof built off-chain, attached as auth signature */
|
|
402
|
+
private _payWithNulthVault;
|
|
403
|
+
/**
|
|
404
|
+
* Resolve manifest and compute the expected charge WITHOUT submitting any
|
|
405
|
+
* transaction. Safe to call before committing — for approval gates and
|
|
406
|
+
* budget-aware routing.
|
|
407
|
+
*/
|
|
408
|
+
estimateCost(url: string, options?: ModeSelectOptions): Promise<EstimateCostResult>;
|
|
409
|
+
/**
|
|
410
|
+
* Open a sustained MPP session at `url`. Verifies mpp-session is supported
|
|
411
|
+
* before opening a channel. Returns a SessionHandle for streaming + closing.
|
|
412
|
+
*
|
|
413
|
+
* By default the session auto-closes after a wall-clock timeout (1h) so an
|
|
414
|
+
* orphaned channel cannot keep collateral locked on-chain — override or
|
|
415
|
+
* disable via `options.maxDurationMs`.
|
|
416
|
+
*/
|
|
417
|
+
openSession(url: string, options?: SessionOptions): Promise<SessionHandle>;
|
|
418
|
+
/**
|
|
419
|
+
* Record spend for an MPP session voucher. Called before each voucher
|
|
420
|
+
* fetch in stream(). Enforces the same local daily and per-endpoint caps
|
|
421
|
+
* as discrete pay() calls. Returns a commit function to clean up the
|
|
422
|
+
* reservation after the voucher fetch succeeds.
|
|
423
|
+
*/
|
|
424
|
+
private _recordSessionSpend;
|
|
425
|
+
/**
|
|
426
|
+
* Remove the commitment secret from memory and make it eligible for GC.
|
|
427
|
+
* Call when this client instance is no longer needed — particularly in
|
|
428
|
+
* long-lived processes or Worker threads.
|
|
429
|
+
*/
|
|
430
|
+
dispose(): void;
|
|
431
|
+
/**
|
|
432
|
+
* Evict the cached manifest for `url` immediately, so the next pay() /
|
|
433
|
+
* estimateCost() / openSession() call re-fetches it instead of routing on
|
|
434
|
+
* the stale cached copy. Use when a provider signals their manifest
|
|
435
|
+
* changed (e.g. after a config update) and the change must take effect
|
|
436
|
+
* before the cache TTL expires.
|
|
437
|
+
*/
|
|
438
|
+
invalidateManifest(url: string): void;
|
|
439
|
+
/**
|
|
440
|
+
* Reserve spend against local daily caps BEFORE dispatching payment.
|
|
441
|
+
* Returns a reserveId that must be passed to _commitSpend() on success
|
|
442
|
+
* or _rollbackSpend() on failure.
|
|
443
|
+
*
|
|
444
|
+
* The entire read-check-write is serialized through the per-instance
|
|
445
|
+
* mutex (_withSpendMutex) so concurrent pay() calls cannot interleave
|
|
446
|
+
* and overrun the cap.
|
|
447
|
+
*
|
|
448
|
+
* Enforcement order:
|
|
449
|
+
* 1. Per-endpoint cap (if `endpointCaps[endpointKey]` is set) — checked first.
|
|
450
|
+
* 2. Global daily cap — checked second.
|
|
451
|
+
*
|
|
452
|
+
* Both limits are independent: a payment is only reserved after **both** pass.
|
|
453
|
+
* All spend (regardless of endpoint) counts toward the global accumulator.
|
|
454
|
+
*/
|
|
455
|
+
private _checkAndReserveSpend;
|
|
456
|
+
/**
|
|
457
|
+
* Commit a previously reserved spend after payment succeeds. For discrete
|
|
458
|
+
* pay() calls this is a no-op (the spend was already recorded by reserve).
|
|
459
|
+
* Reserved for future use if the committed amount differs from the reserved.
|
|
460
|
+
*/
|
|
461
|
+
private _commitSpend;
|
|
462
|
+
/**
|
|
463
|
+
* Rollback a previously reserved spend when payment fails. Reverts the
|
|
464
|
+
* tentative accumulator write done by _checkAndReserveSpend.
|
|
465
|
+
*/
|
|
466
|
+
private _rollbackSpend;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
export { selectMode as A, setNulthPaymentContext as B, usdcToMicros as C, DEFAULT_RETRY_POLICY as D, withRetry as E, InMemorySpendStore as I, type ModeSelectOptions as M, type NulthAuthSignature as N, type PaymentAuthContext as P, type RetryPolicy as R, type SpendCap as S, type VaultConfig as V, type DailySpend as a, type InMemorySpendStoreOptions as b, type NulthClientConfig as c, NulthPolicyError as d, type NulthPolicyState as e, type NulthProof as f, type NulthSignerConfig as g, type NulthStellarSigner as h, type NulthVaultConfig as i, RouteDockClient as j, type RouteDockClientConfig as k, type SpendStore as l, assertEndpointActive as m, assertManifestValid as n, assertNulthVaultManifest as o, backoffDelayMs as p, configureManifestCache as q, createNulthSigner as r, createPolicyState as s, decodeAuthSignature as t, fetchLedgerSequence as u, fetchManifest as v, invalidateManifest as w, paymentContextFromManifest as x, prepareNulthSigner as y, rankProvidersByLatency as z };
|