@dvmkit/sdk 0.1.0-rc.2 → 0.1.0-rc.3

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 (42) hide show
  1. package/README.md +10 -2
  2. package/dist/{revenue-reporter-GB4WKLDC.js → chunk-2ABMGUDS.js} +1 -0
  3. package/dist/chunk-5SO7ZVOH.js +375 -0
  4. package/dist/chunk-66HGCPBU.js +25 -0
  5. package/dist/{chunk-KQAJVVZT.js → chunk-AZBXSXQT.js} +27 -287
  6. package/dist/chunk-C2DC4FKR.js +21424 -0
  7. package/dist/{chunk-H25M54MI.js → chunk-C3MTFLC6.js} +16 -0
  8. package/dist/{chunk-OJ5WFIB2.js → chunk-EXHBXA4U.js} +1 -1
  9. package/dist/chunk-FT7IM66W.js +1557 -0
  10. package/dist/{chunk-KXWROQGK.js → chunk-FUJ36YDV.js} +1 -24
  11. package/dist/{tempo-charge-store-6GJEMNUU.js → chunk-JZWELPFH.js} +1 -0
  12. package/dist/{chunk-365P52XQ.js → chunk-LWUR4CGG.js} +2 -1
  13. package/dist/chunk-RHP3BRTH.js +1090 -0
  14. package/dist/{tempo-session-store-FTEEGZXA.js → chunk-RU7SXHLO.js} +2 -1
  15. package/dist/{chunk-7IH5SG2A.js → chunk-TKA6ZP4M.js} +62 -41
  16. package/dist/chunk-TVI4V7GF.js +283 -0
  17. package/dist/{payout-reporter-4TNWRS5F.js → chunk-X3IKFWJA.js} +3 -2
  18. package/dist/{chunk-DCNT4PJS.js → chunk-XY5Y5REG.js} +6 -258
  19. package/dist/chunk-XYTSDAPH.js +232 -0
  20. package/dist/chunk-YD3TZNXV.js +1042 -0
  21. package/dist/{credit-ledger-RO4FGSHG.js → credit-ledger-ED6JXKVD.js} +2 -2
  22. package/dist/credit-menu-DONAtGVf.d.ts +5076 -0
  23. package/dist/{ssrf-DZi-xJyn.d.ts → fx-Bq4cvn16.d.ts} +37 -119
  24. package/dist/index.d.ts +8 -66
  25. package/dist/index.js +11 -219
  26. package/dist/internal/index.d.ts +5745 -0
  27. package/dist/internal/index.js +6379 -0
  28. package/dist/{job-store-6gR4pZRP.d.ts → job-store-m2pYmvbr.d.ts} +1493 -12
  29. package/dist/{memory-credit-ledger-I2G64DDK.js → memory-credit-ledger-XJ5VQEVP.js} +3 -3
  30. package/dist/payout-reporter-3UB5WRCV.js +13 -0
  31. package/dist/revenue-reporter-JIKUPXOK.js +7 -0
  32. package/dist/server/index.d.ts +14 -3590
  33. package/dist/server/index.js +309 -20796
  34. package/dist/ssrf-DbFkpDv0.d.ts +118 -0
  35. package/dist/tempo-charge-store-RIFTALZK.js +8 -0
  36. package/dist/tempo-session-store-DALMRIWN.js +11 -0
  37. package/dist/testing/index.d.ts +3 -2
  38. package/dist/testing/index.js +3 -2
  39. package/dist/usd-DjVAPMlf.d.ts +97 -0
  40. package/dist/x402-5EVIUSEP.js +81 -0
  41. package/package.json +6 -2
  42. package/dist/x402-35VLYFKZ.js +0 -1272
@@ -1,4 +1,4 @@
1
- import { C as Currency } from './job-store-6gR4pZRP.js';
1
+ import { C as Currency } from './job-store-m2pYmvbr.js';
2
2
 
3
3
  /**
4
4
  * Quote-time fx snapshot. Embedded in scribe's `lockedQuote` for within-job
@@ -60,6 +60,19 @@ interface PlatformFxSource {
60
60
  url: string;
61
61
  token: string;
62
62
  }
63
+ /**
64
+ * In-process cache window for the fx snapshot. 5 minutes amortises rate-limit
65
+ * budget across concurrent quotes and gives a within-window quote → job pair a
66
+ * consistent rate without an explicit lock. Bumped from 60s in internal-review; the
67
+ * per-call drift remains sub-cent at typical DVM amounts (BTC's intra-5-min
68
+ * volatility is well under 1%). Within-job consistency is unaffected — scribe's
69
+ * `lockedQuote.fx` is captured once at quote-time and reused at settlement
70
+ * regardless of the TTL. Hosted-DVM upstream fan-out is handled by internal-review
71
+ * (platform-served fx endpoint) rather than this TTL.
72
+ */
73
+ declare const FX_CACHE_TTL_MS = 300000;
74
+ /** Default retry budget per `fetch()` call before raising `fx_rate_unavailable`. */
75
+ declare const FX_RETRY_COUNT = 2;
63
76
  /** Agent-relayable copy for an {@link FxRateUnavailableError}. */
64
77
  interface FxRateUnavailableCopy {
65
78
  display?: string;
@@ -163,6 +176,28 @@ interface CreateFxFetcherOpts {
163
176
  }
164
177
  /** Build the default fx fetcher backed by `DVMKIT_FX_SOURCE` (or CoinGecko). */
165
178
  declare function createFxFetcher(opts?: CreateFxFetcherOpts): FxFetcher;
179
+ /**
180
+ * Prime `fetcher`'s snapshot cache so the first priced surface to need a rate
181
+ * isn't the process's first fetch (internal-review).
182
+ *
183
+ * internal-review gave every fx hop in the payment layer a ladder down onto
184
+ * {@link FxFetcher.lastKnown}, which closes the failure for any process that
185
+ * has ever held a rate. A free-to-submit DVM never has: the job is free, so no
186
+ * quote fetches one, and the ask-time pin in `ctx.requestPayment` *is* the
187
+ * first fetch — a blip at the rate provider in that window has nothing to
188
+ * degrade onto and still costs the receipt its whole `credit` block.
189
+ *
190
+ * Fire-and-forget by construction: never awaited, so it can't slow a boot;
191
+ * never rethrown, so it can't fail one. A failure is deliberately **silent** —
192
+ * a warn on every offline `dvmctl dev` boot is worse than the gap this closes,
193
+ * and the first real ask walks the internal-review ladder and names its own failure
194
+ * there. Racing that ask costs one upstream fetch rather than two, because
195
+ * {@link createFxFetcher} dedupes in flight.
196
+ *
197
+ * Already-warm fetchers are left alone, so calling this twice — or against an
198
+ * injected fetcher a caller primed itself — is free.
199
+ */
200
+ declare function warmFxSnapshot(fetcher: FxFetcher): void;
166
201
  /**
167
202
  * Look up `currency`'s rate in `snapshot`.
168
203
  *
@@ -205,121 +240,4 @@ declare function fxRateFor(snapshot: FxRateSnapshot, currency: Currency): number
205
240
  */
206
241
  declare function resolveFxSourceFromEnv(env: Record<string, string | undefined>): string | PlatformFxSource | undefined;
207
242
 
208
- /**
209
- * Why `assertSafeUrl` refused this URL. Carried on `SSRFError` so callers can
210
- * branch without string-matching `err.message` — DNS failures (NXDOMAIN, empty
211
- * resolution), unparseable URLs, and a non-https scheme each have distinct
212
- * recovery paths and shouldn't collapse together.
213
- */
214
- type SSRFReason = "invalid_url" | "insecure_scheme" | "private_address" | "dns_failed";
215
- /**
216
- * Thrown when an input URL points at internal infrastructure we refuse to
217
- * fetch from inside the cluster. Carries a `reason` discriminator so the
218
- * caller can map cleanly onto its public taxonomy instead of inspecting the
219
- * message string.
220
- */
221
- declare class SSRFError extends Error {
222
- readonly code: "ssrf_blocked";
223
- readonly reason: SSRFReason;
224
- constructor(reason: SSRFReason, message: string);
225
- }
226
- /** Pluggable DNS resolver. Defaults to `node:dns/promises` `lookup(host, { all: true })`. */
227
- type SSRFResolver = (host: string) => Promise<{
228
- address: string;
229
- family: number;
230
- }[]>;
231
- /**
232
- * Configurable hooks for `assertSafeUrl`. Defaults preserve the dvmkit
233
- * baseline denylist; extra entries compose with (never replace) the defaults.
234
- */
235
- interface SSRFGuardOpts {
236
- /** Custom DNS resolver — typically a test stub. */
237
- resolver?: SSRFResolver;
238
- /** Additional exact hostnames to deny on top of the defaults (lowercased on input). */
239
- extraHostnamesExact?: Iterable<string>;
240
- /**
241
- * Additional hostname suffixes to deny on top of the defaults. Each entry
242
- * should include the leading dot (e.g. `.example.invalid`) — the same shape
243
- * the defaults use.
244
- */
245
- extraHostnameSuffixes?: Iterable<string>;
246
- /** Additional IPv4 predicate. Returns true for an unsafe address. */
247
- extraUnsafeIPv4?: (ipv4: string) => boolean;
248
- /** Additional IPv6 predicate. Returns true for an unsafe address. */
249
- extraUnsafeIPv6?: (ipv6: string) => boolean;
250
- /**
251
- * Underlying fetch implementation `createPinnedFetch` wraps. Defaults to
252
- * `globalThis.fetch`. Pass an instrumented wrapper (e.g. scribe's
253
- * `ctx.fetch` from `createInstrumentedFetch`) to keep `dvm.fetch` spans
254
- * around outbound HEAD / GET — the dispatcher pin for every safe redirect
255
- * hop is preserved because `init.dispatcher` flows through any thin fetch
256
- * wrapper unchanged.
257
- */
258
- baseFetch?: typeof fetch;
259
- /**
260
- * Called after a redirect response is accepted and before the target is
261
- * resolved or connected. Lets an outer transport refuse to replay an
262
- * original request once its source has demonstrably responded.
263
- */
264
- onRedirectFollowed?: () => void;
265
- }
266
- /**
267
- * Reject URLs whose host points at internal infrastructure — private /
268
- * link-local IP ranges, the AWS / GCP metadata IP, Fly's `*.internal`
269
- * service-discovery names, and a small denylist of obviously-internal
270
- * hostnames. Defence-in-depth — full mitigation belongs at the egress firewall
271
- * and at fetch-time IP pinning (`createPinnedFetch`, internal-review).
272
- *
273
- * Throws `SSRFError` with a `reason` discriminator so callers can branch onto
274
- * their own typed error taxonomy.
275
- */
276
- declare function assertSafeUrl(url: string, opts?: SSRFGuardOpts): Promise<void>;
277
- /**
278
- * A `fetch`-compatible function pinned to a single pre-validated IP address.
279
- *
280
- * Returned by {@link createPinnedFetch}. Closes the DNS-rebinding window in
281
- * which `assertSafeUrl` validates an address but the subsequent `fetch()`
282
- * re-resolves DNS and lands on a different (private) address.
283
- *
284
- * Callers MUST `await close()` when done with the pin so undici's connection
285
- * pool is released.
286
- */
287
- interface PinnedFetch {
288
- /** Fetch whose initial request and every followed redirect are pinned to their pre-validated address sets. */
289
- fetch: typeof fetch;
290
- /** Primary (first) validated address of the initial URL. */
291
- address: string;
292
- /** IP family (4 or 6) of {@link address} (the primary). */
293
- family: number;
294
- /** Release the undici connection pool backing this pinned fetch. */
295
- close(): Promise<void>;
296
- }
297
- /**
298
- * Resolve the URL's host once, validate every answer against the same
299
- * denylist `assertSafeUrl` applies, then return a `fetch` whose `connect.lookup`
300
- * replays that frozen, pre-validated address set (see {@link buildPinnedLookup}).
301
- * Redirects are followed manually: each target URL is independently required
302
- * to be HTTPS, resolved once, denylist-checked, and pinned before it can be
303
- * requested. That prevents an automatic redirect from reusing the source
304
- * host's dispatcher for a different hostname, while keeping a redirect from
305
- * opening a DNS-rebinding window.
306
- *
307
- * Throws `SSRFError` with the same `reason` discriminator as `assertSafeUrl`,
308
- * so error-mapping paths (`mapSsrfError` in cast/scribe) work unchanged.
309
- *
310
- * For IP-literal URLs no dispatcher is needed, but redirects still pass
311
- * through the same validation loop.
312
- *
313
- * Replaying the full validated set (not just `addresses[0]`) preserves Node's
314
- * happy-eyeballs fallback across the answers — a host whose first address is an
315
- * unroutable family still connects via the others — while keeping the rebind
316
- * window closed, since every replayed address was already denylist-checked.
317
- *
318
- * Pass `opts.baseFetch` to wrap an instrumented fetch (e.g. scribe's
319
- * `ctx.fetch` from `createInstrumentedFetch`) — `dvm.fetch` spans flow
320
- * through because `init.dispatcher` is forwarded to the underlying
321
- * `globalThis.fetch` unchanged.
322
- */
323
- declare function createPinnedFetch(url: string, opts?: SSRFGuardOpts): Promise<PinnedFetch>;
324
-
325
- export { type CreateFxFetcherOpts as C, DEFAULT_FX_CURRENCIES as D, type FxFetcher as F, type PinnedFetch as P, SSRFError as S, DEFAULT_FX_RATE_SOURCE as a, type FxRateSnapshot as b, FxRateUnavailableError as c, type PlatformFxSource as d, type SSRFGuardOpts as e, type SSRFReason as f, type SSRFResolver as g, assertSafeUrl as h, createFxFetcher as i, createPinnedFetch as j, fxRateFor as k, resolveFxSourceFromEnv as r };
243
+ export { type CreateFxFetcherOpts as C, DEFAULT_FX_CURRENCIES as D, type FxFetcher as F, type PlatformFxSource as P, DEFAULT_FX_RATE_SOURCE as a, type FxRateSnapshot as b, FxRateUnavailableError as c, createFxFetcher as d, FX_CACHE_TTL_MS as e, fxRateFor as f, FX_RETRY_COUNT as g, resolveFxSourceFromEnv as r, warmFxSnapshot as w };
package/dist/index.d.ts CHANGED
@@ -1,12 +1,15 @@
1
- import { C as Currency, Z as ZodLike, D as DVMConfig, a as DVMDescriptor } from './job-store-6gR4pZRP.js';
2
- export { A as ApprovalContent, b as ArtifactContent, c as CancelContent, d as CanonicalEnvelope, e as CreateSignedRequestVerifierOpts, f as CreditConfig, g as CreditView, h as DEFAULT_CREDIT_MAX, i as DEFAULT_CREDIT_MIN, j as DEFAULT_CREDIT_TTL_SECONDS, k as DVMRouteContext, I as IncomingMessage, l as InputType, m as InvalidCurrencyError, J as JobRecord, n as JobStatus, o as JobStore, K as KVStore, L as Logger, P as PaymentContent, p as PaymentMethod, q as PriceValue, r as ProgressContent, s as PromptOpts, Q as QuoteConfig, t as QuoteContext, u as QuoteResult, R as ResolvedCreditConfig, v as ResponseContent, S as SDKJobContext, w as SDKPaymentRequestOpts, x as SIGNED_REQUEST_AUTH_ID, y as SIGNED_REQUEST_STATEMENT_VERSION, z as SignedRequestAudience, B as SignedRequestDomain, E as SignedRequestError, F as SignedRequestFailure, G as SignedRequestReplayStore, H as SignedRequestSignOpts, M as SignedRequestStatementHeader, N as SignedRequestVerifier, U as UnsupportedCurrencyError, O as createSignedRequestVerifier, T as isZodSchema, V as signedRequestStatementHeader, W as validateCurrency } from './job-store-6gR4pZRP.js';
3
- export { C as CreateFxFetcherOpts, D as DEFAULT_FX_CURRENCIES, a as DEFAULT_FX_RATE_SOURCE, F as FxFetcher, b as FxRateSnapshot, c as FxRateUnavailableError, P as PinnedFetch, d as PlatformFxSource, S as SSRFError, e as SSRFGuardOpts, f as SSRFReason, g as SSRFResolver, h as assertSafeUrl, i as createFxFetcher, j as createPinnedFetch, k as fxRateFor, r as resolveFxSourceFromEnv } from './ssrf-DZi-xJyn.js';
1
+ import { C as Currency, Z as ZodLike, D as DVMConfig, a as DVMDescriptor } from './job-store-m2pYmvbr.js';
2
+ export { A as ApprovalContent, b as ArtifactContent, c as CancelContent, d as CanonicalEnvelope, e as CreateSignedRequestVerifierOpts, f as CreditConfig, g as CreditView, h as DEFAULT_CREDIT_MAX, i as DEFAULT_CREDIT_MIN, j as DEFAULT_CREDIT_TTL_SECONDS, k as DVMRouteContext, I as IncomingMessage, l as InputType, m as InvalidCurrencyError, J as JobRecord, n as JobStatus, o as JobStore, K as KVStore, L as Logger, P as PaymentContent, p as PaymentMethod, q as PriceValue, r as ProgressContent, s as PromptOpts, Q as QuoteConfig, t as QuoteContext, u as QuoteResult, R as ResolvedCreditConfig, v as ResponseContent, S as SDKJobContext, w as SDKPaymentRequestOpts, x as SIGNED_REQUEST_AUTH_ID, y as SIGNED_REQUEST_STATEMENT_VERSION, z as SignedRequestAudience, B as SignedRequestDomain, E as SignedRequestError, F as SignedRequestFailure, G as SignedRequestReplayStore, H as SignedRequestSignOpts, M as SignedRequestStatementHeader, N as SignedRequestVerifier, U as UnsupportedCurrencyError, O as createSignedRequestVerifier, T as isZodSchema, V as signedRequestStatementHeader, W as validateCurrency } from './job-store-m2pYmvbr.js';
3
+ export { C as CreateFxFetcherOpts, D as DEFAULT_FX_CURRENCIES, a as DEFAULT_FX_RATE_SOURCE, F as FxFetcher, b as FxRateSnapshot, c as FxRateUnavailableError, P as PlatformFxSource, d as createFxFetcher, f as fxRateFor, r as resolveFxSourceFromEnv } from './fx-Bq4cvn16.js';
4
+ export { I as InvalidFxRateError, f as fiatToSatsCeil, a as formatFiat, b as formatUsd, r as roundUsd, s as satsToFiat } from './usd-DjVAPMlf.js';
5
+ export { P as PinnedFetch, S as SSRFError, a as SSRFGuardOpts, b as SSRFReason, c as SSRFResolver, d as assertSafeUrl, e as createPinnedFetch } from './ssrf-DbFkpDv0.js';
4
6
  export { z } from 'zod';
5
7
  import '@cashu/cashu-ts';
6
8
  import 'mppx';
7
- import 'hono';
8
9
  import '@x402/core/server';
9
10
  import '@x402/evm/batch-settlement/server';
11
+ import 'hono';
12
+ import 'viem';
10
13
  import 'pg';
11
14
  import '@x402/core/types';
12
15
 
@@ -32,67 +35,6 @@ declare function buildUpfront(input: {
32
35
  currency: string;
33
36
  }): Upfront;
34
37
 
35
- /**
36
- * Thrown by `fiatToSatsCeil` / `satsToFiat` when the supplied `ratePerBtc` is
37
- * non-finite or non-positive. Surfaces a misconfigured fx fetcher loudly
38
- * instead of silently producing a 0-sats charge or a 0-fiat display string —
39
- * a silent 0 lost real money in internal-review slice-04. Callers obtain a valid rate
40
- * from the SDK's fx fetcher (which raises `FxRateUnavailableError` on
41
- * upstream failure); this error is the defence-in-depth backstop for any
42
- * caller that constructs a rate by other means.
43
- */
44
- declare class InvalidFxRateError extends Error {
45
- readonly code = "invalid_fx_rate";
46
- readonly ratePerBtc: unknown;
47
- constructor(ratePerBtc: unknown);
48
- }
49
- /**
50
- * Convert a fiat amount to integer sats at the supplied fiat-per-BTC rate.
51
- * Rounds **up** so the operator never overcharges the caller (caller-friendly
52
- * rounding). Negative or non-finite amounts collapse to 0 (a $0 charge is
53
- * well-defined). A non-finite or non-positive `ratePerBtc` raises
54
- * `InvalidFxRateError` — a silent 0 would mask a misconfigured fetcher into
55
- * shipping a free charge. The math is currency-agnostic — `ratePerBtc` is the
56
- * number of units of *some* fiat per 1 BTC; the caller looks the right rate
57
- * out of an `FxRateSnapshot`.
58
- */
59
- declare function fiatToSatsCeil(amount: number, ratePerBtc: number): number;
60
- /**
61
- * Convert integer sats back to a fiat amount at the supplied fiat-per-BTC
62
- * rate. A non-finite or non-positive `ratePerBtc` raises
63
- * `InvalidFxRateError` — the silent `0` would land in display strings and
64
- * mislead callers about the value at hand.
65
- */
66
- declare function satsToFiat(sats: number, ratePerBtc: number): number;
67
- /**
68
- * Round a USD value to 4 decimal places (matches scribe's stored display
69
- * precision). This is the **storage / accounting** precision — preserves
70
- * sub-cent amounts so a `$0.0150` per-minute rate doesn't collapse to
71
- * `$0.02` before it ever reaches a multiplier. See {@link formatUsd} for the
72
- * human-render precision, which deliberately truncates at the cent boundary
73
- * for amounts ≥ $0.01.
74
- */
75
- declare function roundUsd(usd: number): number;
76
- /**
77
- * Format a USD amount with adaptive precision so sub-cent amounts don't
78
- * render as `$0.00`. The 2dp/4dp boundary at $0.01 is **human-render**
79
- * precision and is intentionally coarser than {@link roundUsd}'s storage
80
- * precision: a value like `0.015` keeps four decimals through `roundUsd`
81
- * (storage / multiplier inputs) but renders as `$0.02` here (human display).
82
- * Don't try to unify the two — they serve different surfaces.
83
- */
84
- declare function formatUsd(usd: number): string;
85
- /**
86
- * Format a fiat amount with currency-aware symbol, precision, and ISO code.
87
- * Used by the CLI to render payment-request `fiat_amount` envelopes ("$0.05
88
- * USD", "€0.05 EUR", "¥5 JPY"). Precision is adaptive: JPY has no sub-yen
89
- * denomination so renders as 0 decimals, every other currency uses 2 decimals
90
- * at or above 0.01 and 4 below so sub-cent figures don't collapse to 0.00.
91
- * Unknown currencies (anything not in {@link FIAT_SYMBOLS}) render with the
92
- * uppercased ISO code and no leading symbol.
93
- */
94
- declare function formatFiat(amount: number, currency: Currency): string;
95
-
96
38
  /** Create a typed DVM descriptor from a configuration object. */
97
39
  declare function configureDVM<State = Record<string, unknown>, InputSchema extends ZodLike | undefined = undefined>(config: DVMConfig<State, InputSchema>): DVMDescriptor<State, InputSchema>;
98
40
  /** Check whether this SDK module instance created a value through `configureDVM`. */
@@ -141,4 +83,4 @@ interface ProgressHeartbeatOpts {
141
83
  */
142
84
  declare function withProgressHeartbeat<T>(opts: ProgressHeartbeatOpts, fn: () => Promise<T>): Promise<T>;
143
85
 
144
- export { Currency, DVMConfig, DVMDescriptor, InvalidFxRateError, type ProgressHeartbeatOpts, type Upfront, ZodLike, buildUpfront, configureDVM, envFlag, fiatToSatsCeil, formatFiat, formatUsd, isConfiguredDVMDescriptor, roundUsd, satsToFiat, withProgressHeartbeat };
86
+ export { Currency, DVMConfig, DVMDescriptor, type ProgressHeartbeatOpts, type Upfront, ZodLike, buildUpfront, configureDVM, envFlag, isConfiguredDVMDescriptor, withProgressHeartbeat };
package/dist/index.js CHANGED
@@ -1,18 +1,24 @@
1
+ import {
2
+ SSRFError,
3
+ assertSafeUrl,
4
+ createPinnedFetch,
5
+ envFlag
6
+ } from "./chunk-TVI4V7GF.js";
7
+ import {
8
+ configureDVM,
9
+ isConfiguredDVMDescriptor
10
+ } from "./chunk-XYTSDAPH.js";
1
11
  import {
2
12
  DEFAULT_CREDIT_MAX,
3
13
  DEFAULT_CREDIT_MIN,
4
14
  DEFAULT_CREDIT_TTL_SECONDS,
5
15
  SIGNED_REQUEST_AUTH_ID,
6
16
  SIGNED_REQUEST_STATEMENT_VERSION,
7
- SSRFError,
8
17
  SignedRequestError,
9
- assertSafeUrl,
10
- createPinnedFetch,
11
18
  createSignedRequestVerifier,
12
- envFlag,
13
19
  isZodSchema,
14
20
  signedRequestStatementHeader
15
- } from "./chunk-KQAJVVZT.js";
21
+ } from "./chunk-AZBXSXQT.js";
16
22
  import {
17
23
  DEFAULT_FX_CURRENCIES,
18
24
  DEFAULT_FX_RATE_SOURCE,
@@ -30,224 +36,10 @@ import {
30
36
  fiatToSatsCeil,
31
37
  formatFiat,
32
38
  formatUsd,
33
- parseUsdPrice,
34
39
  roundUsd,
35
40
  satsToFiat
36
41
  } from "./chunk-AT6V3SY7.js";
37
42
 
38
- // src/lib/wire-schema.ts
39
- var CAPABILITY_NAME_RE = /^[a-z0-9][a-z0-9-]*$/;
40
-
41
- // src/sdk/configure.ts
42
- function configureDVM(config) {
43
- const tags = collectTags(config);
44
- const currency = resolvePricingCurrency(config.currency);
45
- const capabilities = buildCapabilityMap(config, currency);
46
- const onlyCapability = singleCapabilityEntry(capabilities);
47
- const descriptor = {
48
- name: config.name,
49
- description: config.description ?? "",
50
- tags,
51
- currency,
52
- price: onlyCapability?.price,
53
- inputSchema: onlyCapability?.input,
54
- state: onlyCapability?.state ?? {},
55
- idleTimeout: config.idleTimeout ?? 3600,
56
- processingWatchdog: config.processingWatchdog,
57
- config,
58
- capabilities,
59
- paymentMethods: config.paymentMethods,
60
- x402: config.x402,
61
- mpp: config.mpp,
62
- credit: resolveCreditConfig(config.credit),
63
- auth: config.auth
64
- };
65
- CONFIGURED_DVM_DESCRIPTORS.add(descriptor);
66
- return Object.freeze(descriptor);
67
- }
68
- function isConfiguredDVMDescriptor(value) {
69
- return typeof value === "object" && value !== null && CONFIGURED_DVM_DESCRIPTORS.has(value);
70
- }
71
- var CONFIGURED_DVM_DESCRIPTORS = /* @__PURE__ */ new WeakSet();
72
- function resolvePricingCurrency(raw) {
73
- if (raw === void 0) return "usd";
74
- try {
75
- return validateCurrency(raw);
76
- } catch (err) {
77
- if (err instanceof InvalidCurrencyError) {
78
- throw new Error(
79
- `configureDVM: currency ${JSON.stringify(raw)} is not an ISO 4217 lowercase code like "usd" or "eur". Omit the field to price in USD.`,
80
- { cause: err }
81
- );
82
- }
83
- throw err;
84
- }
85
- }
86
- function resolveCreditConfig(credit) {
87
- if (!credit) return void 0;
88
- const min = credit.min ?? DEFAULT_CREDIT_MIN;
89
- const max = credit.max ?? DEFAULT_CREDIT_MAX;
90
- const ttlSeconds = credit.ttl ?? DEFAULT_CREDIT_TTL_SECONDS;
91
- const minAmount = validatePriceLiteral(min, "credit.min");
92
- const maxAmount = validatePriceLiteral(max, "credit.max");
93
- if (minAmount > maxAmount) {
94
- throw new Error(
95
- `credit.min (${JSON.stringify(min)}) exceeds credit.max (${JSON.stringify(max)}) \u2014 no funding amount could satisfy both.`
96
- );
97
- }
98
- if (!Number.isFinite(ttlSeconds) || ttlSeconds <= 0) {
99
- throw new Error(`credit.ttl must be a positive number of seconds (got ${ttlSeconds}).`);
100
- }
101
- const allowOneShotStablecoin = credit.allowOneShotStablecoin === true;
102
- return { min, max, ttlSeconds, allowOneShotStablecoin };
103
- }
104
- function validatePriceLiteral(value, field, key = field) {
105
- if (typeof value === "number") {
106
- throw new Error(
107
- `${field} is a number (${value}). Prices are USD literals \u2014 write ${key}: "$0.01" for one cent. Callers still pay in sats; the SDK converts at request time. Omit the field for a free capability, or use \`onQuote\` for dynamic pricing.`
108
- );
109
- }
110
- if (typeof value !== "string") {
111
- throw new Error(`${field} must be a USD literal like "$0.05" (got ${JSON.stringify(value)}).`);
112
- }
113
- return parseUsdPrice(value, field);
114
- }
115
- function validateCapabilityPrice(price, capabilityName, currency) {
116
- if (price === void 0) return;
117
- if (currency !== "usd") {
118
- throw new Error(
119
- `configureDVM: capability "${capabilityName}" declares a static price (${JSON.stringify(price)}) but this DVM prices in ${currency}. Static prices are USD literals, so they cannot express ${currency} \u2014 price this capability with \`onQuote\` returning { amount, currency: ${JSON.stringify(currency)} }, or drop the DVM-level \`currency\` to price in USD.`
120
- );
121
- }
122
- validatePriceLiteral(price, `configureDVM: capability "${capabilityName}" price`, "price");
123
- }
124
- function collectTags(config) {
125
- const tagSet = /* @__PURE__ */ new Set();
126
- if (config.tag) tagSet.add(config.tag);
127
- if (config.tags) {
128
- for (const t of config.tags) tagSet.add(t);
129
- }
130
- return [...tagSet];
131
- }
132
- function buildCapabilityMap(config, currency) {
133
- const hasExplicitBlock = config.capabilities !== void 0;
134
- const hasFlatCapabilityName = typeof config.capability === "string";
135
- const hasFlatHandler = typeof config.onJob === "function";
136
- if (hasExplicitBlock && (hasFlatCapabilityName || hasFlatHandler || flatPerCapFieldsSet(config))) {
137
- throw new Error(
138
- "configureDVM: pass either the flat single-capability shape (capability + onJob + ...) or the explicit `capabilities` block, not both."
139
- );
140
- }
141
- if (hasExplicitBlock) {
142
- return buildExplicitCapabilities(config.capabilities ?? {}, currency);
143
- }
144
- if (!hasFlatHandler) {
145
- throw new Error("configureDVM: onJob handler is required");
146
- }
147
- const name = typeof config.capability === "string" ? config.capability : slugifyCapabilityName(config.name);
148
- validateCapabilityName(name);
149
- validateCapabilityPrice(config.price, name, currency);
150
- validateExampleAgainstInput(name, config.input, config.example);
151
- const entry = {
152
- name,
153
- description: config.description ?? "",
154
- input: config.input,
155
- example: config.example,
156
- state: config.state ?? {},
157
- price: config.price,
158
- onQuote: config.onQuote,
159
- onJob: config.onJob,
160
- onResponse: config.onResponse,
161
- onPayment: config.onPayment,
162
- onApproval: config.onApproval,
163
- onCancel: config.onCancel,
164
- onMessage: config.onMessage
165
- };
166
- return { [name]: entry };
167
- }
168
- function buildExplicitCapabilities(capabilities, currency) {
169
- const keys = Object.keys(capabilities);
170
- if (keys.length === 0) {
171
- throw new Error("configureDVM: `capabilities` block must declare at least one capability");
172
- }
173
- const out = {};
174
- for (const name of keys) {
175
- validateCapabilityName(name);
176
- const cap = capabilities[name];
177
- if (typeof cap.onJob !== "function") {
178
- throw new Error(`configureDVM: capability "${name}" is missing required onJob handler`);
179
- }
180
- validateCapabilityPrice(cap.price, name, currency);
181
- validateExampleAgainstInput(name, cap.input, cap.example);
182
- const entry = {
183
- name,
184
- description: cap.description ?? "",
185
- // Per-capability `input` is the builder's Zod schema; the `any` widening
186
- // through `Record<string, CapabilityConfig<any, any>>` is unavoidable
187
- // here.
188
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
189
- input: cap.input,
190
- example: cap.example,
191
- state: cap.state !== void 0 ? structuredClone(cap.state) : {},
192
- price: cap.price,
193
- onQuote: cap.onQuote,
194
- onJob: cap.onJob,
195
- onResponse: cap.onResponse,
196
- onPayment: cap.onPayment,
197
- onApproval: cap.onApproval,
198
- onCancel: cap.onCancel,
199
- onMessage: cap.onMessage
200
- };
201
- out[name] = entry;
202
- }
203
- return out;
204
- }
205
- function flatPerCapFieldsSet(config) {
206
- return config.input !== void 0 || config.example !== void 0 || config.state !== void 0 || config.price !== void 0 || config.onQuote !== void 0 || typeof config.onResponse === "function" || typeof config.onPayment === "function" || typeof config.onApproval === "function" || typeof config.onCancel === "function" || typeof config.onMessage === "function";
207
- }
208
- function validateExampleAgainstInput(capName, input, example) {
209
- if (example === void 0) return;
210
- if (!isZodSchema(input)) return;
211
- const result = input.safeParse(example);
212
- if (result.success) return;
213
- throw new Error(
214
- `configureDVM: capability "${capName}" example does not satisfy its input schema. Update the example to match the schema (including any \`.refine()\` checks). Issues: ${formatZodIssues(result.error)}`
215
- );
216
- }
217
- function formatZodIssues(err) {
218
- if (err && typeof err === "object" && "issues" in err && Array.isArray(err.issues)) {
219
- const issues = err.issues;
220
- if (issues.length === 0) return "(no issues reported)";
221
- return issues.map((issue) => {
222
- const path = issue.path.length > 0 ? issue.path.join(".") : "(root)";
223
- return `${path}: ${issue.message}`;
224
- }).join("; ");
225
- }
226
- if (err && typeof err === "object" && "message" in err && typeof err.message === "string") {
227
- return err.message.replace(/\s+/g, " ").trim();
228
- }
229
- try {
230
- return JSON.stringify(err);
231
- } catch {
232
- return String(err);
233
- }
234
- }
235
- function validateCapabilityName(name) {
236
- if (!CAPABILITY_NAME_RE.test(name)) {
237
- throw new Error(
238
- `configureDVM: invalid capability name "${name}" \u2014 must be lowercase letters/digits/hyphens, must start with a letter or digit, must not contain slashes.`
239
- );
240
- }
241
- }
242
- function slugifyCapabilityName(name) {
243
- return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
244
- }
245
- function singleCapabilityEntry(capabilities) {
246
- const keys = Object.keys(capabilities);
247
- if (keys.length !== 1) return void 0;
248
- return capabilities[keys[0]];
249
- }
250
-
251
43
  // src/sdk/heartbeat.ts
252
44
  async function withProgressHeartbeat(opts, fn) {
253
45
  const onProgress = opts.onProgress;