@dvmkit/sdk 0.6.0-rc.1 → 0.7.0-rc.10

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 (73) hide show
  1. package/README.md +39 -2
  2. package/dist/{chunk-PLG4XER6.js → chunk-2NUFE2PG.js} +47 -5
  3. package/dist/{chunk-7ZUKL4IV.js → chunk-3E4C6W7W.js} +16 -5
  4. package/dist/{chunk-HHBDBMEO.js → chunk-53SNRGQQ.js} +375 -158
  5. package/dist/{chunk-IWQHUSMO.js → chunk-62FI3NJF.js} +78 -2
  6. package/dist/chunk-6HJEVCVI.js +17 -0
  7. package/dist/chunk-A262WVYX.js +6 -0
  8. package/dist/{chunk-RLTSCKRI.js → chunk-BPYMEMQX.js} +50 -3
  9. package/dist/{chunk-IV6Q7NRW.js → chunk-C5RIQWG4.js} +1 -1
  10. package/dist/{chunk-GAOAYHZA.js → chunk-FBQVOOYF.js} +37 -6
  11. package/dist/{chunk-Z4ZT5LNT.js → chunk-GEI4MAXE.js} +3 -3
  12. package/dist/{chunk-FYRR2UEZ.js → chunk-GLIAR7RN.js} +1 -1
  13. package/dist/{chunk-X7EZ5CLI.js → chunk-HCATFJDD.js} +3 -3
  14. package/dist/{chunk-BYCEKO65.js → chunk-HPCXF3DV.js} +220 -32
  15. package/dist/{chunk-JH3SX3LY.js → chunk-IRXIH775.js} +6 -6
  16. package/dist/{chunk-QR2EF2NI.js → chunk-KCQDXIJU.js} +1 -42
  17. package/dist/chunk-MDW7ZR7V.js +47 -0
  18. package/dist/{chunk-KA6VN5FK.js → chunk-MQYM4T66.js} +3 -3
  19. package/dist/{chunk-P6QON7ER.js → chunk-NV7Z7377.js} +1 -1
  20. package/dist/{chunk-OQA7IHI4.js → chunk-OBBVDKLE.js} +484 -234
  21. package/dist/{chunk-EQLA4FFY.js → chunk-OHUXCAOM.js} +17 -6
  22. package/dist/{chunk-FI4CST2D.js → chunk-PZ5IAWFD.js} +5 -7
  23. package/dist/{chunk-IXR5RFK7.js → chunk-R3TQSXWT.js} +191 -5
  24. package/dist/{chunk-VHNPVGSA.js → chunk-T5DIXOLR.js} +3 -3
  25. package/dist/{chunk-RCM3P2HE.js → chunk-UKD3R2KU.js} +55 -4
  26. package/dist/chunk-WMGXJQI3.js +120 -0
  27. package/dist/{chunk-N5RZ5HVB.js → chunk-X6S2CHFM.js} +2 -2
  28. package/dist/{chunk-ETXDTUJO.js → chunk-ZCNPIILZ.js} +112 -1
  29. package/dist/{content-retention-BTGSQRfP.d.ts → content-retention-D2K4MKNO.d.ts} +1 -1
  30. package/dist/{credit-ledger-MI2H7NEC.js → credit-ledger-MFIIOSZH.js} +4 -5
  31. package/dist/{credit-menu-BAsrRu3z.d.ts → credit-menu-D2t1-Tf6.d.ts} +13 -6
  32. package/dist/{fx-BgDmyTdd.d.ts → fx-Ql4nvdbh.d.ts} +10 -3
  33. package/dist/index.d.ts +5 -5
  34. package/dist/index.js +248 -16
  35. package/dist/internal/caller.d.ts +4586 -7318
  36. package/dist/internal/caller.js +4431 -8127
  37. package/dist/internal/index.js +1 -1
  38. package/dist/internal/server.d.ts +7 -7
  39. package/dist/internal/server.js +30 -33
  40. package/dist/{job-store-BVeF5_Jq.d.ts → job-store-DCvJQUX0.d.ts} +1 -1
  41. package/dist/{lightning-backend-40fIzz1W.d.ts → lightning-backend-CY5ML3km.d.ts} +4 -2
  42. package/dist/memory-credit-ledger-44NE4PZC.js +11 -0
  43. package/dist/{mpp-setup-RF5FXGGV.js → mpp-setup-HRWUBULX.js} +4 -2
  44. package/dist/{payout-reporter-3Y2U55ZX.js → payout-reporter-7P66T77I.js} +6 -4
  45. package/dist/{postgres-consumed-credential-store-BUVCAPLS.js → postgres-consumed-credential-store-IDGPFKIL.js} +3 -3
  46. package/dist/{postgres-job-store-VHLQBJZC.js → postgres-job-store-UKNJ2F6Z.js} +2 -2
  47. package/dist/{postgres-replay-store-ARZSQ45X.js → postgres-replay-store-PMA4ADXN.js} +2 -2
  48. package/dist/{pricing-XT2IAPNE.js → pricing-52XJHJ6K.js} +12 -12
  49. package/dist/{revenue-reporter-MAERPYGK.js → revenue-reporter-HV7RMS54.js} +2 -2
  50. package/dist/server/index.d.ts +12 -11
  51. package/dist/server/index.js +49 -53
  52. package/dist/{step-cache-CPdH1hei.d.ts → step-cache-BOWAn_BH.d.ts} +84 -7
  53. package/dist/{tempo-charge-store-Q3D7KYMH.js → tempo-charge-store-P2Z4RMU7.js} +2 -2
  54. package/dist/{tempo-session-store-ZRNCOX7P.js → tempo-session-store-VSP7KLBS.js} +3 -3
  55. package/dist/testing/index.d.ts +3 -3
  56. package/dist/testing/index.js +7 -6
  57. package/dist/{usd-AWlrwlM5.d.ts → usd-MAtMp3DB.d.ts} +1 -1
  58. package/dist/{x402-R6HPBY5D.js → x402-3IQ3HXSG.js} +9 -29
  59. package/package.json +3 -2
  60. package/dist/chunk-AT6V3SY7.js +0 -102
  61. package/dist/chunk-B2IROPQF.js +0 -1844
  62. package/dist/chunk-CZULM42S.js +0 -143
  63. package/dist/chunk-GFY5FRX3.js +0 -218
  64. package/dist/chunk-HCLVYZUZ.js +0 -244
  65. package/dist/chunk-LE74UEV7.js +0 -1071
  66. package/dist/chunk-LIRAHP7B.js +0 -53
  67. package/dist/chunk-MZRJR2CW.js +0 -776
  68. package/dist/chunk-OFVV37XC.js +0 -57
  69. package/dist/chunk-PAIM66YA.js +0 -347
  70. package/dist/chunk-UOCZXO2Z.js +0 -1663
  71. package/dist/memory-credit-ledger-W2BAUYJP.js +0 -12
  72. package/dist/tempo-lifecycle-ZBBLFBMH.js +0 -15
  73. package/dist/tempo-wallet-B5XXV3YH.js +0 -40
@@ -1,143 +0,0 @@
1
- // src/defaults.ts
2
- import { homedir } from "os";
3
- import { join } from "path";
4
- var CONFIG_DIR = process.env.DVM_CONFIG_DIR ?? join(homedir(), ".dvm");
5
- var CONFIG_FILE = join(CONFIG_DIR, "config.json");
6
- var CONFIG_BACKUP_FILE = join(CONFIG_DIR, "config.json.bak");
7
- var CONFIG_AUDIT_FILE = join(CONFIG_DIR, "config-audit.jsonl");
8
- var CONFIG_PINNED_FILE = join(CONFIG_DIR, "config.json.pinned");
9
- var JOBS_FILE = join(CONFIG_DIR, "jobs.json");
10
- var REQUEST_INTENTS_DIR = join(CONFIG_DIR, "request-intents");
11
- var FAVORITES_FILE = join(CONFIG_DIR, "favorites.json");
12
- var MESSAGES_DIR = join(CONFIG_DIR, "messages");
13
- var RECEIPTS_FILE = join(CONFIG_DIR, "receipts.jsonl");
14
- var CREDITS_FILE = join(CONFIG_DIR, "credits.json");
15
- var X402_CHANNELS_FILE = join(CONFIG_DIR, "x402-channels.json");
16
- var TEMPO_CHANNELS_FILE = join(CONFIG_DIR, "tempo-channels.json");
17
- var FEEDBACK_NUDGES_FILE = join(CONFIG_DIR, "feedback-nudges.json");
18
- var RECOVERY_MNEMONIC_FILE = join(CONFIG_DIR, "recovery.mnemonic");
19
- var WALLET_FILE = join(CONFIG_DIR, "wallet.json");
20
- var AGENT_MNEMONIC_FILE = join(CONFIG_DIR, "wallet.mnemonic");
21
- var IDENTITIES_FILE = join(CONFIG_DIR, "identities.json");
22
- var PAIRINGS_DIR = join(CONFIG_DIR, "pairings");
23
- var BUDGET_FILE = join(CONFIG_DIR, "budget.json");
24
- var BTC_RATE_FILE = join(CONFIG_DIR, "btc-rate.json");
25
- var ADMIN_DIR = join(CONFIG_DIR, "admin");
26
- var PROXY_STATE_FILE = join(ADMIN_DIR, "proxy.json");
27
- var BUILDER_CONFIG_DIR = process.env.DVMKIT_CONFIG_DIR ?? join(homedir(), ".dvmkit");
28
- var BUILDER_FILE = join(BUILDER_CONFIG_DIR, "builder.json");
29
- var BUILDER_MNEMONIC_FILE = join(BUILDER_CONFIG_DIR, "recovery.mnemonic");
30
- var BUILDER_KEY_FILE = join(BUILDER_CONFIG_DIR, "builder.key");
31
- var MELT_PENDING_LOG_FILE = join(BUILDER_CONFIG_DIR, "melt-pending.log");
32
- var DRAIN_PARK_LOG_FILE = join(BUILDER_CONFIG_DIR, "drain-park.log");
33
- var DRAIN_HOLD_STATE_FILE = join(BUILDER_CONFIG_DIR, "drain-hold.json");
34
- var MELT_SUCCESS_FILE = join(BUILDER_CONFIG_DIR, "melt-success.json");
35
-
36
- // src/lib/config-dir.ts
37
- import { chmodSync, mkdirSync, statSync } from "fs";
38
- var CONFIG_DIR_MODE = 448;
39
- function ensureConfigDir() {
40
- ensureConfigSubdir(CONFIG_DIR);
41
- }
42
- function ensureConfigSubdir(path) {
43
- mkdirSync(path, { recursive: true, mode: CONFIG_DIR_MODE });
44
- }
45
- function repairConfigDirMode() {
46
- const base = {
47
- path: CONFIG_DIR,
48
- exists: false,
49
- modeOctal: null,
50
- repaired: false,
51
- skippedNonPosix: process.platform === "win32"
52
- };
53
- if (base.skippedNonPosix) return base;
54
- let mode;
55
- try {
56
- mode = statSync(CONFIG_DIR).mode & 511;
57
- } catch (err) {
58
- const message = err instanceof Error ? err.message : String(err);
59
- const missing = err.code === "ENOENT";
60
- return missing ? base : { ...base, error: message };
61
- }
62
- if ((mode & 63) === 0) {
63
- return { ...base, exists: true, modeOctal: toOctal(mode) };
64
- }
65
- try {
66
- chmodSync(CONFIG_DIR, CONFIG_DIR_MODE);
67
- } catch (err) {
68
- return {
69
- ...base,
70
- exists: true,
71
- modeOctal: toOctal(mode),
72
- error: err instanceof Error ? err.message : String(err)
73
- };
74
- }
75
- return {
76
- ...base,
77
- exists: true,
78
- modeOctal: toOctal(CONFIG_DIR_MODE),
79
- previousModeOctal: toOctal(mode),
80
- repaired: true
81
- };
82
- }
83
- function toOctal(mode) {
84
- return "0" + mode.toString(8).padStart(3, "0");
85
- }
86
-
87
- // src/lib/payment-rails/rpc.ts
88
- function resolveRpcOverride(envValue, bundled) {
89
- const override = envValue?.trim() ?? "";
90
- return override.length > 0 ? { url: override, source: "override" } : { url: bundled, source: "bundled" };
91
- }
92
- function resolveRpcHttpTransportOptions(source, envValue) {
93
- if (source !== "override" || !envValue?.trim()) return void 0;
94
- let parsed;
95
- try {
96
- parsed = JSON.parse(envValue);
97
- } catch {
98
- throw new Error("RPC headers must be a JSON object of non-empty string values.");
99
- }
100
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
101
- throw new Error("RPC headers must be a JSON object of non-empty string values.");
102
- const headers = {};
103
- for (const [name, value] of Object.entries(parsed)) {
104
- if (!/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/.test(name) || typeof value !== "string" || value.length === 0 || /[\r\n]/.test(value))
105
- throw new Error("RPC headers must be a JSON object of non-empty string values.");
106
- headers[name] = value;
107
- }
108
- if (Object.keys(headers).length === 0)
109
- throw new Error("RPC headers must be a JSON object of non-empty string values.");
110
- return { fetchOptions: { headers } };
111
- }
112
-
113
- export {
114
- CONFIG_DIR,
115
- CONFIG_FILE,
116
- CONFIG_BACKUP_FILE,
117
- CONFIG_AUDIT_FILE,
118
- CONFIG_PINNED_FILE,
119
- JOBS_FILE,
120
- REQUEST_INTENTS_DIR,
121
- FAVORITES_FILE,
122
- MESSAGES_DIR,
123
- RECEIPTS_FILE,
124
- CREDITS_FILE,
125
- X402_CHANNELS_FILE,
126
- TEMPO_CHANNELS_FILE,
127
- FEEDBACK_NUDGES_FILE,
128
- RECOVERY_MNEMONIC_FILE,
129
- WALLET_FILE,
130
- AGENT_MNEMONIC_FILE,
131
- IDENTITIES_FILE,
132
- PAIRINGS_DIR,
133
- BUDGET_FILE,
134
- BTC_RATE_FILE,
135
- BUILDER_CONFIG_DIR,
136
- BUILDER_FILE,
137
- BUILDER_KEY_FILE,
138
- ensureConfigDir,
139
- ensureConfigSubdir,
140
- repairConfigDirMode,
141
- resolveRpcOverride,
142
- resolveRpcHttpTransportOptions
143
- };
@@ -1,218 +0,0 @@
1
- import {
2
- isValidEvmPrivateKey,
3
- loadConfig,
4
- updateConfig
5
- } from "./chunk-MZRJR2CW.js";
6
- import {
7
- resolveRpcHttpTransportOptions,
8
- resolveRpcOverride
9
- } from "./chunk-CZULM42S.js";
10
- import {
11
- redactUrl,
12
- redactUrlsInText
13
- } from "./chunk-FUJ36YDV.js";
14
- import {
15
- DvmError
16
- } from "./chunk-LIRAHP7B.js";
17
-
18
- // src/lib/payment-rails/tempo-wallet.ts
19
- var TEMPO_USDC_MAINNET = "0x20C000000000000000000000b9537d11c60E8b50";
20
- var TEMPO_CHAIN_ID = 4217;
21
- var TEMPO_MODERATO_CHAIN_ID = 42431;
22
- var TEMPO_PATH_USD_MODERATO = "0x20c0000000000000000000000000000000000000";
23
- var TEMPO_MODERATO_RPC = "https://rpc.moderato.tempo.xyz";
24
- function resolveTempoNetwork(network = "mainnet") {
25
- return network === "moderato" ? {
26
- network,
27
- chainId: TEMPO_MODERATO_CHAIN_ID,
28
- rpcUrl: TEMPO_MODERATO_RPC,
29
- defaultToken: TEMPO_PATH_USD_MODERATO
30
- } : {
31
- network,
32
- chainId: TEMPO_CHAIN_ID,
33
- rpcUrl: "https://rpc.tempo.xyz",
34
- defaultToken: TEMPO_USDC_MAINNET
35
- };
36
- }
37
- function resolveTempoAsset(env = process.env, network = "mainnet") {
38
- const address = env.DVM_TEMPO_CURRENCY ?? resolveTempoNetwork(network).defaultToken;
39
- if (!HEX_ADDRESS_RE.test(address)) {
40
- throw new DvmError(
41
- "tempo_config_invalid",
42
- `DVM_TEMPO_CURRENCY must be a 0x-prefixed 40-char hex address; got '${address}'.`,
43
- "Unset DVM_TEMPO_CURRENCY to fall back to the bundled USDC.e mainnet default."
44
- );
45
- }
46
- const symbol = tempoTokenSymbol(address);
47
- return {
48
- address,
49
- symbol,
50
- label: tempoTokenLabel(address),
51
- unit: symbol ?? "tokens"
52
- };
53
- }
54
- function tempoTokenLabel(address) {
55
- return tempoTokenSymbol(address) ?? `the token at ${shortAddress(address)}`;
56
- }
57
- function tempoTokenReference(address) {
58
- const symbol = tempoTokenSymbol(address);
59
- return symbol ? `${symbol} (${address})` : address;
60
- }
61
- function tempoBalanceCheckHint(token) {
62
- const configured = tryResolveTempoAsset();
63
- if (configured?.address.toLowerCase() === token.toLowerCase()) {
64
- return "This channel's token is the rail's configured settlement asset, so 'dvm wallet balance' reads it.";
65
- }
66
- return `'dvm wallet balance' reads the rail's configured settlement asset (${configured?.address ?? "unresolvable in this environment"}) rather than this channel's token (${token}), so query that contract directly for the balance that matters here.`;
67
- }
68
- function tryResolveTempoAsset(env = process.env) {
69
- try {
70
- return resolveTempoAsset(env);
71
- } catch {
72
- return null;
73
- }
74
- }
75
- function tempoWrongAssetHint(asset) {
76
- return `This rail settles only in ${asset.label} (${asset.address}) on Tempo. Another Tempo dollar \u2014 USDT0 or any other stablecoin \u2014 is a different contract and will never show up here, however much of it your wallet says you hold. If you funded with the wrong one, swap it to ${asset.label} on Tempo (an on-chain swap, fees are sub-cent) or send ${asset.label} to this address.`;
77
- }
78
- function tempoTokenSymbol(address) {
79
- if (address.toLowerCase() === TEMPO_USDC_MAINNET.toLowerCase()) return "USDC.e";
80
- if (address.toLowerCase() === TEMPO_PATH_USD_MODERATO.toLowerCase()) return "pathUSD";
81
- return null;
82
- }
83
- var HEX_ADDRESS_RE = /^0x[0-9a-fA-F]{40}$/;
84
- async function connectedTempoAccount() {
85
- const config = loadConfig();
86
- return tempoAccountFromConfig(config);
87
- }
88
- async function generateTempoKey(opts = {}) {
89
- const config = loadConfig();
90
- if (!config) {
91
- throw new DvmError(
92
- "no_config",
93
- "No config found.",
94
- "Run `dvm init` before generating a Tempo wallet."
95
- );
96
- }
97
- const existing = await tempoAccountFromConfig(config);
98
- if (existing && !opts.force) {
99
- throw new DvmError(
100
- "tempo_wallet_exists",
101
- `Tempo wallet already configured (address ${existing.address}).`,
102
- "Pass `--force` to replace, or run `dvm wallet tempo-disconnect` to clear it first."
103
- );
104
- }
105
- if (existing && opts.force) {
106
- const { assertTempoWalletIdle } = await import("./tempo-lifecycle-ZBBLFBMH.js");
107
- await assertTempoWalletIdle("replace");
108
- }
109
- const { privateKeyToAccount, generatePrivateKey } = await import("viem/accounts");
110
- const privateKey = generatePrivateKey();
111
- const account = privateKeyToAccount(privateKey);
112
- const generated = {
113
- privateKey,
114
- address: account.address.toLowerCase(),
115
- network: opts.network ?? "mainnet"
116
- };
117
- await opts.beforePersist?.(generated);
118
- await updateConfig({
119
- reason: "tempo_wallet_generate",
120
- patch: () => ({
121
- set: {
122
- tempo: { method: "tempo", apiKey: privateKey, network: generated.network }
123
- }
124
- })
125
- });
126
- return generated;
127
- }
128
- async function getTempoUsdcBalance(account) {
129
- const accountNetwork = account.network ?? "mainnet";
130
- const network = resolveTempoNetwork(accountNetwork);
131
- const tokenAddress = resolveTempoAsset(process.env, accountNetwork).address;
132
- const { createPublicClient, http, erc20Abi } = await import("viem");
133
- const { tempo, tempoModerato } = await import("viem/chains");
134
- const chain = accountNetwork === "moderato" ? tempoModerato : tempo;
135
- const { url: rpcUrl, source: rpcSource } = resolveRpcOverride(
136
- process.env.DVM_TEMPO_RPC_URL,
137
- network.rpcUrl
138
- );
139
- try {
140
- const transportOptions = resolveRpcHttpTransportOptions(
141
- rpcSource,
142
- process.env.DVM_TEMPO_RPC_HEADERS_JSON
143
- );
144
- const client = createPublicClient({
145
- chain,
146
- transport: transportOptions ? http(rpcUrl, transportOptions) : http(rpcUrl)
147
- });
148
- const raw = await client.readContract({
149
- address: tokenAddress,
150
- abi: erc20Abi,
151
- functionName: "balanceOf",
152
- args: [account.address]
153
- });
154
- return {
155
- address: account.address,
156
- rawBaseUnits: raw,
157
- usdc: Number(raw) / 1e6,
158
- tokenAddress,
159
- chainId: network.chainId,
160
- // redacted here rather than at a display surface, so the clear
161
- // endpoint never leaves this function on the success path either.
162
- rpcUrlRedacted: redactUrl(rpcUrl),
163
- rpcSource,
164
- fetchedAtMs: Date.now()
165
- };
166
- } catch (err) {
167
- const cause = redactUrlsInText(err instanceof Error ? err.message : String(err));
168
- throw new DvmError(
169
- "tempo_rpc_error",
170
- `Tempo USDC balance lookup failed: ${cause}`,
171
- // A caller who set the override doesn't need to be told to set it. Which
172
- // knob is theirs to turn depends on whose endpoint this was.
173
- rpcSource === "override" ? `DVM_TEMPO_RPC_URL sent this read to ${redactUrl(rpcUrl)}. Correct it, or unset it to fall back to Tempo's bundled endpoint.` : `Verify connectivity to Tempo's bundled endpoint (${redactUrl(rpcUrl)}), or send the read somewhere else with DVM_TEMPO_RPC_URL.`
174
- );
175
- }
176
- }
177
- async function deriveTempoAddress(privateKey) {
178
- if (!isValidEvmPrivateKey(privateKey)) throw invalidTempoPrivateKey();
179
- const { privateKeyToAccount } = await import("viem/accounts");
180
- return privateKeyToAccount(privateKey).address.toLowerCase();
181
- }
182
- function shortAddress(addr) {
183
- return `${addr.slice(0, 6)}\u2026${addr.slice(-4)}`;
184
- }
185
- async function tempoAccountFromConfig(config) {
186
- if (config?.tempo?.method !== "tempo") return null;
187
- const apiKey = config.tempo.apiKey;
188
- if (!apiKey || !isValidEvmPrivateKey(apiKey)) return null;
189
- const privateKey = apiKey;
190
- const address = await deriveTempoAddress(privateKey);
191
- return { privateKey, address, network: config.tempo.network ?? "mainnet" };
192
- }
193
- function invalidTempoPrivateKey() {
194
- return new DvmError(
195
- "invalid_api_key",
196
- "A Tempo private key must be a valid 0x-prefixed 32-byte hex secp256k1 private key.",
197
- "Generate one with `cast wallet new` or `openssl rand -hex 32` (and prepend 0x)."
198
- );
199
- }
200
-
201
- export {
202
- TEMPO_USDC_MAINNET,
203
- TEMPO_CHAIN_ID,
204
- TEMPO_MODERATO_CHAIN_ID,
205
- TEMPO_PATH_USD_MODERATO,
206
- TEMPO_MODERATO_RPC,
207
- resolveTempoNetwork,
208
- resolveTempoAsset,
209
- tempoTokenLabel,
210
- tempoTokenReference,
211
- tempoBalanceCheckHint,
212
- tryResolveTempoAsset,
213
- tempoWrongAssetHint,
214
- connectedTempoAccount,
215
- generateTempoKey,
216
- getTempoUsdcBalance,
217
- deriveTempoAddress
218
- };
@@ -1,244 +0,0 @@
1
- import {
2
- DEFAULT_CREDIT_MAX,
3
- DEFAULT_CREDIT_MIN,
4
- DEFAULT_CREDIT_TTL_SECONDS,
5
- DEFAULT_JOB_RETENTION_DAYS,
6
- isZodSchema
7
- } from "./chunk-RCM3P2HE.js";
8
- import {
9
- InvalidCurrencyError,
10
- validateCurrency
11
- } from "./chunk-BYCEKO65.js";
12
- import {
13
- parseUsdPrice
14
- } from "./chunk-AT6V3SY7.js";
15
-
16
- // src/lib/wire-schema.ts
17
- var CAPABILITY_NAME_RE = /^[a-z0-9][a-z0-9-]*$/;
18
-
19
- // src/sdk/configure.ts
20
- function configureDVM(config) {
21
- const tags = collectTags(config);
22
- const currency = resolvePricingCurrency(config.currency);
23
- const capabilities = buildCapabilityMap(config, currency);
24
- const jobRetentionDays = resolveJobRetentionDays(config.jobRetentionDays);
25
- const onlyCapability = singleCapabilityEntry(capabilities);
26
- const descriptor = {
27
- name: config.name,
28
- description: config.description ?? "",
29
- tags,
30
- currency,
31
- price: onlyCapability?.price,
32
- inputSchema: onlyCapability?.input,
33
- state: onlyCapability?.state ?? {},
34
- idleTimeout: config.idleTimeout ?? 3600,
35
- processingWatchdog: config.processingWatchdog,
36
- jobRetentionDays,
37
- config,
38
- capabilities,
39
- paymentMethods: config.paymentMethods,
40
- x402: config.x402,
41
- mpp: config.mpp,
42
- credit: resolveCreditConfig(config.credit),
43
- auth: config.auth
44
- };
45
- CONFIGURED_DVM_DESCRIPTORS.add(descriptor);
46
- return Object.freeze(descriptor);
47
- }
48
- function resolveJobRetentionDays(raw) {
49
- const days = raw ?? DEFAULT_JOB_RETENTION_DAYS;
50
- if (!Number.isSafeInteger(days) || days < 0 || days > Math.floor((Number.MAX_SAFE_INTEGER - Date.now()) / 864e5)) {
51
- throw new Error(
52
- `configureDVM: jobRetentionDays must be a non-negative whole number of days; received ${JSON.stringify(raw)}`
53
- );
54
- }
55
- return days;
56
- }
57
- function isConfiguredDVMDescriptor(value) {
58
- return typeof value === "object" && value !== null && CONFIGURED_DVM_DESCRIPTORS.has(value);
59
- }
60
- var CONFIGURED_DVM_DESCRIPTORS = /* @__PURE__ */ new WeakSet();
61
- function resolvePricingCurrency(raw) {
62
- if (raw === void 0) return "usd";
63
- try {
64
- return validateCurrency(raw);
65
- } catch (err) {
66
- if (err instanceof InvalidCurrencyError) {
67
- throw new Error(
68
- `configureDVM: currency ${JSON.stringify(raw)} is not an ISO 4217 lowercase code like "usd" or "eur". Omit the field to price in USD.`,
69
- { cause: err }
70
- );
71
- }
72
- throw err;
73
- }
74
- }
75
- function resolveCreditConfig(credit) {
76
- if (!credit) return void 0;
77
- const min = credit.min ?? DEFAULT_CREDIT_MIN;
78
- const max = credit.max ?? DEFAULT_CREDIT_MAX;
79
- const ttlSeconds = credit.ttl ?? DEFAULT_CREDIT_TTL_SECONDS;
80
- const minAmount = validatePriceLiteral(min, "credit.min");
81
- const maxAmount = validatePriceLiteral(max, "credit.max");
82
- if (minAmount > maxAmount) {
83
- throw new Error(
84
- `credit.min (${JSON.stringify(min)}) exceeds credit.max (${JSON.stringify(max)}) \u2014 no funding amount could satisfy both.`
85
- );
86
- }
87
- if (!Number.isFinite(ttlSeconds) || ttlSeconds <= 0) {
88
- throw new Error(`credit.ttl must be a positive number of seconds (got ${ttlSeconds}).`);
89
- }
90
- const allowOneShotStablecoin = credit.allowOneShotStablecoin === true;
91
- return { min, max, ttlSeconds, allowOneShotStablecoin };
92
- }
93
- function validatePriceLiteral(value, field, key = field) {
94
- if (typeof value === "number") {
95
- throw new Error(
96
- `${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.`
97
- );
98
- }
99
- if (typeof value !== "string") {
100
- throw new Error(`${field} must be a USD literal like "$0.05" (got ${JSON.stringify(value)}).`);
101
- }
102
- return parseUsdPrice(value, field);
103
- }
104
- function validateCapabilityPrice(price, capabilityName, currency) {
105
- if (price === void 0) return;
106
- if (currency !== "usd") {
107
- throw new Error(
108
- `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.`
109
- );
110
- }
111
- validatePriceLiteral(price, `configureDVM: capability "${capabilityName}" price`, "price");
112
- }
113
- function collectTags(config) {
114
- const tagSet = /* @__PURE__ */ new Set();
115
- if (config.tag) tagSet.add(config.tag);
116
- if (config.tags) {
117
- for (const t of config.tags) tagSet.add(t);
118
- }
119
- return [...tagSet];
120
- }
121
- function buildCapabilityMap(config, currency) {
122
- const hasExplicitBlock = config.capabilities !== void 0;
123
- const hasFlatCapabilityName = typeof config.capability === "string";
124
- const hasFlatHandler = typeof config.onJob === "function";
125
- if (hasExplicitBlock && (hasFlatCapabilityName || hasFlatHandler || flatPerCapFieldsSet(config))) {
126
- throw new Error(
127
- "configureDVM: pass either the flat single-capability shape (capability + onJob + ...) or the explicit `capabilities` block, not both."
128
- );
129
- }
130
- if (hasExplicitBlock) {
131
- return buildExplicitCapabilities(config.capabilities ?? {}, currency);
132
- }
133
- if (!hasFlatHandler) {
134
- throw new Error("configureDVM: onJob handler is required");
135
- }
136
- const name = typeof config.capability === "string" ? config.capability : slugifyCapabilityName(config.name);
137
- validateCapabilityName(name);
138
- validateCapabilityPrice(config.price, name, currency);
139
- validateExampleAgainstInput(name, config.input, config.example);
140
- const entry = {
141
- name,
142
- description: config.description ?? "",
143
- input: config.input,
144
- example: config.example,
145
- state: config.state ?? {},
146
- price: config.price,
147
- onQuote: config.onQuote,
148
- onJob: config.onJob,
149
- onResponse: config.onResponse,
150
- onPayment: config.onPayment,
151
- onApproval: config.onApproval,
152
- onCancel: config.onCancel,
153
- onMessage: config.onMessage
154
- };
155
- return { [name]: entry };
156
- }
157
- function buildExplicitCapabilities(capabilities, currency) {
158
- const keys = Object.keys(capabilities);
159
- if (keys.length === 0) {
160
- throw new Error("configureDVM: `capabilities` block must declare at least one capability");
161
- }
162
- const out = {};
163
- for (const name of keys) {
164
- validateCapabilityName(name);
165
- const cap = capabilities[name];
166
- if (typeof cap.onJob !== "function") {
167
- throw new Error(`configureDVM: capability "${name}" is missing required onJob handler`);
168
- }
169
- validateCapabilityPrice(cap.price, name, currency);
170
- validateExampleAgainstInput(name, cap.input, cap.example);
171
- const entry = {
172
- name,
173
- description: cap.description ?? "",
174
- // Per-capability `input` is the builder's Zod schema; the `any` widening
175
- // through `Record<string, CapabilityConfig<any, any>>` is unavoidable
176
- // here.
177
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
178
- input: cap.input,
179
- example: cap.example,
180
- state: cap.state !== void 0 ? structuredClone(cap.state) : {},
181
- price: cap.price,
182
- onQuote: cap.onQuote,
183
- onJob: cap.onJob,
184
- onResponse: cap.onResponse,
185
- onPayment: cap.onPayment,
186
- onApproval: cap.onApproval,
187
- onCancel: cap.onCancel,
188
- onMessage: cap.onMessage
189
- };
190
- out[name] = entry;
191
- }
192
- return out;
193
- }
194
- function flatPerCapFieldsSet(config) {
195
- 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";
196
- }
197
- function validateExampleAgainstInput(capName, input, example) {
198
- if (example === void 0) return;
199
- if (!isZodSchema(input)) return;
200
- const result = input.safeParse(example);
201
- if (result.success) return;
202
- throw new Error(
203
- `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)}`
204
- );
205
- }
206
- function formatZodIssues(err) {
207
- if (err && typeof err === "object" && "issues" in err && Array.isArray(err.issues)) {
208
- const issues = err.issues;
209
- if (issues.length === 0) return "(no issues reported)";
210
- return issues.map((issue) => {
211
- const path = issue.path.length > 0 ? issue.path.join(".") : "(root)";
212
- return `${path}: ${issue.message}`;
213
- }).join("; ");
214
- }
215
- if (err && typeof err === "object" && "message" in err && typeof err.message === "string") {
216
- return err.message.replace(/\s+/g, " ").trim();
217
- }
218
- try {
219
- return JSON.stringify(err);
220
- } catch {
221
- return String(err);
222
- }
223
- }
224
- function validateCapabilityName(name) {
225
- if (!CAPABILITY_NAME_RE.test(name)) {
226
- throw new Error(
227
- `configureDVM: invalid capability name "${name}" \u2014 must be lowercase letters/digits/hyphens, must start with a letter or digit, must not contain slashes.`
228
- );
229
- }
230
- }
231
- function slugifyCapabilityName(name) {
232
- return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
233
- }
234
- function singleCapabilityEntry(capabilities) {
235
- const keys = Object.keys(capabilities);
236
- if (keys.length !== 1) return void 0;
237
- return capabilities[keys[0]];
238
- }
239
-
240
- export {
241
- CAPABILITY_NAME_RE,
242
- configureDVM,
243
- isConfiguredDVMDescriptor
244
- };