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

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 (74) hide show
  1. package/README.md +2 -0
  2. package/dist/{chunk-PLG4XER6.js → chunk-26W6GPCD.js} +48 -6
  3. package/dist/{chunk-FI4CST2D.js → chunk-2OM2QPBE.js} +6 -8
  4. package/dist/{chunk-IWQHUSMO.js → chunk-62FI3NJF.js} +78 -2
  5. package/dist/{chunk-VHNPVGSA.js → chunk-6STC5TCP.js} +3 -3
  6. package/dist/{chunk-N5RZ5HVB.js → chunk-72V5DTEC.js} +2 -2
  7. package/dist/chunk-A262WVYX.js +6 -0
  8. package/dist/{chunk-HHBDBMEO.js → chunk-A6CXKAQV.js} +186 -93
  9. package/dist/{chunk-QR2EF2NI.js → chunk-AQ7OX5NX.js} +2 -2
  10. package/dist/{chunk-RLTSCKRI.js → chunk-BPYMEMQX.js} +50 -3
  11. package/dist/{chunk-BYCEKO65.js → chunk-CLIAFTUR.js} +102 -13
  12. package/dist/{chunk-GAOAYHZA.js → chunk-DNG4KGSC.js} +3 -3
  13. package/dist/{chunk-Z4ZT5LNT.js → chunk-GSLS4464.js} +3 -3
  14. package/dist/{chunk-7ZUKL4IV.js → chunk-H5FVVEEN.js} +17 -6
  15. package/dist/{chunk-IXR5RFK7.js → chunk-HCE4MPZ5.js} +1 -1
  16. package/dist/{chunk-FYRR2UEZ.js → chunk-I555L2WK.js} +1 -1
  17. package/dist/{chunk-EQLA4FFY.js → chunk-IIZQNDQU.js} +3 -3
  18. package/dist/{chunk-FUJ36YDV.js → chunk-KXWROQGK.js} +24 -1
  19. package/dist/{chunk-ETXDTUJO.js → chunk-LAKP6FUI.js} +55 -0
  20. package/dist/{chunk-X7EZ5CLI.js → chunk-MON76YBJ.js} +3 -3
  21. package/dist/chunk-OFSOLHRX.js +120 -0
  22. package/dist/{chunk-IV6Q7NRW.js → chunk-RAGX6VG7.js} +2 -2
  23. package/dist/{chunk-KA6VN5FK.js → chunk-RXPHMS4C.js} +3 -3
  24. package/dist/{chunk-JH3SX3LY.js → chunk-SPYEJXI7.js} +6 -6
  25. package/dist/{chunk-P6QON7ER.js → chunk-TRNMTTPX.js} +1 -1
  26. package/dist/{chunk-RCM3P2HE.js → chunk-UKD3R2KU.js} +55 -4
  27. package/dist/chunk-USL7ZEXN.js +17 -0
  28. package/dist/{chunk-OQA7IHI4.js → chunk-VB5OLOZH.js} +403 -235
  29. package/dist/{content-retention-BTGSQRfP.d.ts → content-retention-CbnouEnh.d.ts} +1 -1
  30. package/dist/{credit-ledger-MI2H7NEC.js → credit-ledger-7Z7LOCTN.js} +4 -5
  31. package/dist/{credit-menu-BAsrRu3z.d.ts → credit-menu-BO8yJppy.d.ts} +4 -4
  32. package/dist/{fx-BgDmyTdd.d.ts → fx-DSOZQ2z7.d.ts} +1 -1
  33. package/dist/index.d.ts +5 -5
  34. package/dist/index.js +236 -16
  35. package/dist/internal/caller.d.ts +4495 -7305
  36. package/dist/internal/caller.js +4415 -8122
  37. package/dist/internal/index.js +1 -1
  38. package/dist/internal/server.d.ts +7 -7
  39. package/dist/internal/server.js +27 -32
  40. package/dist/{job-store-BVeF5_Jq.d.ts → job-store-BJRlmq2Q.d.ts} +1 -1
  41. package/dist/{lightning-backend-40fIzz1W.d.ts → lightning-backend-f16-fsYK.d.ts} +4 -2
  42. package/dist/memory-credit-ledger-3WOS74UH.js +11 -0
  43. package/dist/{mpp-setup-RF5FXGGV.js → mpp-setup-XB4KATMU.js} +2 -2
  44. package/dist/{payout-reporter-3Y2U55ZX.js → payout-reporter-WT7XYW42.js} +2 -2
  45. package/dist/{postgres-consumed-credential-store-BUVCAPLS.js → postgres-consumed-credential-store-W7UP33AH.js} +3 -3
  46. package/dist/{postgres-job-store-VHLQBJZC.js → postgres-job-store-3KGUGFRD.js} +2 -2
  47. package/dist/{postgres-replay-store-ARZSQ45X.js → postgres-replay-store-CVNJJOQG.js} +2 -2
  48. package/dist/{pricing-XT2IAPNE.js → pricing-QAVZUTZR.js} +12 -12
  49. package/dist/{revenue-reporter-MAERPYGK.js → revenue-reporter-7UR3W2SD.js} +2 -2
  50. package/dist/server/index.d.ts +10 -10
  51. package/dist/server/index.js +46 -53
  52. package/dist/{step-cache-CPdH1hei.d.ts → step-cache-CJFiZU3n.d.ts} +1 -1
  53. package/dist/{tempo-charge-store-Q3D7KYMH.js → tempo-charge-store-GA6CED6O.js} +2 -2
  54. package/dist/{tempo-session-store-ZRNCOX7P.js → tempo-session-store-WZWQRITB.js} +3 -3
  55. package/dist/testing/index.d.ts +3 -3
  56. package/dist/testing/index.js +6 -7
  57. package/dist/{usd-AWlrwlM5.d.ts → usd-DmHHg0_4.d.ts} +1 -1
  58. package/dist/{x402-R6HPBY5D.js → x402-ZU4GML7Z.js} +9 -30
  59. package/package.json +3 -2
  60. package/dist/chunk-66HGCPBU.js +0 -25
  61. package/dist/chunk-AT6V3SY7.js +0 -102
  62. package/dist/chunk-B2IROPQF.js +0 -1844
  63. package/dist/chunk-CZULM42S.js +0 -143
  64. package/dist/chunk-GFY5FRX3.js +0 -218
  65. package/dist/chunk-HCLVYZUZ.js +0 -244
  66. package/dist/chunk-LE74UEV7.js +0 -1071
  67. package/dist/chunk-LIRAHP7B.js +0 -53
  68. package/dist/chunk-MZRJR2CW.js +0 -776
  69. package/dist/chunk-OFVV37XC.js +0 -57
  70. package/dist/chunk-PAIM66YA.js +0 -347
  71. package/dist/chunk-UOCZXO2Z.js +0 -1663
  72. package/dist/memory-credit-ledger-W2BAUYJP.js +0 -12
  73. package/dist/tempo-lifecycle-ZBBLFBMH.js +0 -15
  74. package/dist/tempo-wallet-B5XXV3YH.js +0 -40
@@ -1,34 +1,15 @@
1
- import {
2
- computeResultHash,
3
- costAnchor,
4
- formatUsd,
5
- isDrainReceipt,
6
- isFundingReceipt,
7
- msatsToUsd,
8
- verifyDrainReceipt,
9
- verifyFundingReceipt,
10
- verifyReceipt
11
- } from "./chunk-PAIM66YA.js";
12
- import {
13
- AGENT_MNEMONIC_FILE,
14
- BUILDER_CONFIG_DIR,
15
- BUILDER_FILE,
16
- BUILDER_KEY_FILE,
17
- WALLET_FILE,
18
- ensureConfigDir
19
- } from "./chunk-CZULM42S.js";
20
1
  import {
21
2
  canonicaliseForSigning
22
- } from "./chunk-OFVV37XC.js";
3
+ } from "./chunk-UKD3R2KU.js";
23
4
  import {
24
5
  callerLoggers
25
- } from "./chunk-66HGCPBU.js";
6
+ } from "./chunk-KXWROQGK.js";
26
7
  import {
27
8
  DvmError
28
- } from "./chunk-LIRAHP7B.js";
9
+ } from "./chunk-BPYMEMQX.js";
29
10
  import {
30
11
  diagnosticConsole
31
- } from "./chunk-ETXDTUJO.js";
12
+ } from "./chunk-LAKP6FUI.js";
32
13
 
33
14
  // src/lib/cashu/lock-pubkey.ts
34
15
  function toLockPubkey(raw) {
@@ -40,45 +21,257 @@ function isX402SettlementReconciliationReason(value) {
40
21
  return value === "settlement_not_on_chain" || value === "chain_unreachable" || value === "settlement_unbookmarked" || value === "facilitator_channel_state_unavailable";
41
22
  }
42
23
 
43
- // src/lib/mints.ts
44
- var RECOMMENDED_MINTS = [
45
- {
46
- name: "Coinos",
47
- description: "Primary Cashu mint accepted by dvmkit DVMs",
48
- url: "https://mint.coinos.io"
49
- },
50
- {
51
- name: "Voltz Mint",
52
- description: "Fallback Cashu mint accepted by dvmkit DVMs",
53
- url: "https://mint.lnvoltz.com"
24
+ // src/lib/credit-implicit.ts
25
+ var IMPLICIT_CREDIT_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
26
+
27
+ // src/lib/payment-rails/tempo-wire.ts
28
+ var TEMPO_SESSION_UPDATED_REASON = "Tempo session updated; attach the next voucher to fund this credit.";
29
+
30
+ // src/defaults.ts
31
+ import { homedir } from "os";
32
+ import { join } from "path";
33
+ var CONFIG_DIR = process.env.DVM_CONFIG_DIR ?? join(homedir(), ".dvm");
34
+ var CONFIG_FILE = join(CONFIG_DIR, "config.json");
35
+ var CONFIG_BACKUP_FILE = join(CONFIG_DIR, "config.json.bak");
36
+ var CONFIG_AUDIT_FILE = join(CONFIG_DIR, "config-audit.jsonl");
37
+ var CONFIG_PINNED_FILE = join(CONFIG_DIR, "config.json.pinned");
38
+ var JOBS_FILE = join(CONFIG_DIR, "jobs.json");
39
+ var REQUEST_INTENTS_DIR = join(CONFIG_DIR, "request-intents");
40
+ var FAVORITES_FILE = join(CONFIG_DIR, "favorites.json");
41
+ var MESSAGES_DIR = join(CONFIG_DIR, "messages");
42
+ var RECEIPTS_FILE = join(CONFIG_DIR, "receipts.jsonl");
43
+ var CREDITS_FILE = join(CONFIG_DIR, "credits.json");
44
+ var X402_CHANNELS_FILE = join(CONFIG_DIR, "x402-channels.json");
45
+ var TEMPO_CHANNELS_FILE = join(CONFIG_DIR, "tempo-channels.json");
46
+ var FEEDBACK_NUDGES_FILE = join(CONFIG_DIR, "feedback-nudges.json");
47
+ var RECOVERY_MNEMONIC_FILE = join(CONFIG_DIR, "recovery.mnemonic");
48
+ var WALLET_FILE = join(CONFIG_DIR, "wallet.json");
49
+ var AGENT_MNEMONIC_FILE = join(CONFIG_DIR, "wallet.mnemonic");
50
+ var IDENTITIES_FILE = join(CONFIG_DIR, "identities.json");
51
+ var PAIRINGS_DIR = join(CONFIG_DIR, "pairings");
52
+ var BUDGET_FILE = join(CONFIG_DIR, "budget.json");
53
+ var BTC_RATE_FILE = join(CONFIG_DIR, "btc-rate.json");
54
+ var ADMIN_DIR = join(CONFIG_DIR, "admin");
55
+ var PROXY_STATE_FILE = join(ADMIN_DIR, "proxy.json");
56
+ var BUILDER_CONFIG_DIR = process.env.DVMKIT_CONFIG_DIR ?? join(homedir(), ".dvmkit");
57
+ var BUILDER_FILE = join(BUILDER_CONFIG_DIR, "builder.json");
58
+ var BUILDER_MNEMONIC_FILE = join(BUILDER_CONFIG_DIR, "recovery.mnemonic");
59
+ var BUILDER_KEY_FILE = join(BUILDER_CONFIG_DIR, "builder.key");
60
+ var MELT_PENDING_LOG_FILE = join(BUILDER_CONFIG_DIR, "melt-pending.log");
61
+ var DRAIN_PARK_LOG_FILE = join(BUILDER_CONFIG_DIR, "drain-park.log");
62
+ var DRAIN_HOLD_STATE_FILE = join(BUILDER_CONFIG_DIR, "drain-hold.json");
63
+ var MELT_SUCCESS_FILE = join(BUILDER_CONFIG_DIR, "melt-success.json");
64
+
65
+ // src/lib/config-dir.ts
66
+ import { chmodSync, mkdirSync, statSync } from "fs";
67
+ var CONFIG_DIR_MODE = 448;
68
+ function ensureConfigDir() {
69
+ ensureConfigSubdir(CONFIG_DIR);
70
+ }
71
+ function ensureConfigSubdir(path) {
72
+ mkdirSync(path, { recursive: true, mode: CONFIG_DIR_MODE });
73
+ }
74
+ function repairConfigDirMode() {
75
+ const base = {
76
+ path: CONFIG_DIR,
77
+ exists: false,
78
+ modeOctal: null,
79
+ repaired: false,
80
+ skippedNonPosix: process.platform === "win32"
81
+ };
82
+ if (base.skippedNonPosix) return base;
83
+ let mode;
84
+ try {
85
+ mode = statSync(CONFIG_DIR).mode & 511;
86
+ } catch (err) {
87
+ const message = err instanceof Error ? err.message : String(err);
88
+ const missing = err.code === "ENOENT";
89
+ return missing ? base : { ...base, error: message };
90
+ }
91
+ if ((mode & 63) === 0) {
92
+ return { ...base, exists: true, modeOctal: toOctal(mode) };
54
93
  }
55
- ];
56
- function isTestMintUrl(url) {
57
- let host;
58
94
  try {
59
- host = new URL(url).hostname.toLowerCase();
95
+ chmodSync(CONFIG_DIR, CONFIG_DIR_MODE);
96
+ } catch (err) {
97
+ return {
98
+ ...base,
99
+ exists: true,
100
+ modeOctal: toOctal(mode),
101
+ error: err instanceof Error ? err.message : String(err)
102
+ };
103
+ }
104
+ return {
105
+ ...base,
106
+ exists: true,
107
+ modeOctal: toOctal(CONFIG_DIR_MODE),
108
+ previousModeOctal: toOctal(mode),
109
+ repaired: true
110
+ };
111
+ }
112
+ function toOctal(mode) {
113
+ return "0" + mode.toString(8).padStart(3, "0");
114
+ }
115
+
116
+ // src/lib/fx-coingecko.ts
117
+ var COINGECKO_SIMPLE_PRICE_URL = "https://api.coingecko.com/api/v3/simple/price?ids=bitcoin,usd-coin&vs_currencies=usd";
118
+ var COINGECKO_BTC_USD_URL = "https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd";
119
+ var DEFAULT_TIMEOUT_MS = 5e3;
120
+ var DEFAULT_USER_AGENT = "dvmkit-platform/1.0 (+https://dvmkit.com)";
121
+ async function fetchCoinGeckoSpot(opts) {
122
+ const body = await fetchCoinGeckoBody(opts, COINGECKO_SIMPLE_PRICE_URL);
123
+ const btc = body.bitcoin?.usd;
124
+ const usdc = body["usd-coin"]?.usd;
125
+ if (!isPositiveNumber(btc)) {
126
+ throw new Error("coingecko response missing bitcoin.usd");
127
+ }
128
+ if (!isPositiveNumber(usdc)) {
129
+ throw new Error("coingecko response missing usd-coin.usd");
130
+ }
131
+ return { btc_usd: btc, usdc_usd: usdc, as_of: (/* @__PURE__ */ new Date()).toISOString() };
132
+ }
133
+ async function fetchCoinGeckoBtcUsd(opts) {
134
+ const body = await fetchCoinGeckoBody(opts, COINGECKO_BTC_USD_URL);
135
+ const btc = body.bitcoin?.usd;
136
+ if (!isPositiveNumber(btc)) {
137
+ throw new Error("coingecko response missing bitcoin.usd");
138
+ }
139
+ return { btc_usd: btc, as_of: (/* @__PURE__ */ new Date()).toISOString() };
140
+ }
141
+ async function fetchCoinGeckoBody(opts, defaultUrl) {
142
+ const url = opts?.url ?? defaultUrl;
143
+ const fetchImpl = opts?.fetchImpl ?? fetch;
144
+ const timeoutMs = opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
145
+ const userAgent = opts?.userAgent ?? DEFAULT_USER_AGENT;
146
+ const res = await fetchImpl(url, {
147
+ headers: { "User-Agent": userAgent, Accept: "application/json" },
148
+ signal: AbortSignal.timeout(timeoutMs)
149
+ });
150
+ if (!res.ok) {
151
+ throw new Error(`coingecko returned HTTP ${res.status.toString()}`);
152
+ }
153
+ return await res.json();
154
+ }
155
+ function isPositiveNumber(v) {
156
+ return typeof v === "number" && Number.isFinite(v) && v > 0;
157
+ }
158
+
159
+ // src/lib/currency.ts
160
+ import { existsSync, readFileSync, writeFileSync } from "fs";
161
+ import { dirname } from "path";
162
+ var COST_ANCHOR_BANDS = {
163
+ usd: [0.01, 0.1, 1, 10],
164
+ eur: [0.01, 0.1, 1, 10],
165
+ gbp: [0.01, 0.1, 1, 10],
166
+ jpy: [1, 10, 100, 1500]
167
+ };
168
+ function costAnchor(amount, currency = "usd") {
169
+ const bands = COST_ANCHOR_BANDS[currency] ?? COST_ANCHOR_BANDS.usd;
170
+ if (amount < bands[0]) return "fraction of a cent";
171
+ if (amount <= bands[1]) return "comparable to an API call";
172
+ if (amount <= bands[2]) return "comparable to a ChatGPT query";
173
+ if (amount <= bands[3]) return "comparable to a coffee";
174
+ return "significant purchase \u2014 review carefully";
175
+ }
176
+ function formatUsd(usd) {
177
+ if (usd === 0) return "0.00";
178
+ if (usd >= 0.01) return usd.toFixed(2);
179
+ const digits = Math.max(2, -Math.floor(Math.log10(usd)) + 1);
180
+ return usd.toFixed(digits);
181
+ }
182
+ function formatUsdcMicrounits(microunits) {
183
+ const value = typeof microunits === "bigint" ? microunits : BigInt(microunits);
184
+ const whole = value / 1000000n;
185
+ const fraction = (value % 1000000n).toString().padStart(6, "0").replace(/0+$/, "");
186
+ return `${whole}.${fraction.padEnd(2, "0") || "00"}`;
187
+ }
188
+ async function fetchBtcUsdRate() {
189
+ if (memRate && Date.now() - memTime < CACHE_TTL) {
190
+ return memRate;
191
+ }
192
+ const disk = readDiskCache();
193
+ if (disk) {
194
+ memRate = disk;
195
+ memTime = Date.now();
196
+ return disk;
197
+ }
198
+ const rate = await fetchFromApi();
199
+ memRate = rate;
200
+ memTime = Date.now();
201
+ writeDiskCache(rate);
202
+ return rate;
203
+ }
204
+ function getLastKnownBtcRate() {
205
+ if (memRate && memTime > 0) return { rate: memRate, cachedAt: memTime };
206
+ return readDiskCacheStale();
207
+ }
208
+ function formatStaleness(cachedAt, now = Date.now()) {
209
+ const ageMs = Math.max(0, now - cachedAt);
210
+ const seconds = Math.floor(ageMs / 1e3);
211
+ if (seconds < 60) return `${seconds}s ago`;
212
+ const minutes = Math.floor(seconds / 60);
213
+ if (minutes < 60) return `${minutes} min ago`;
214
+ const hours = Math.floor(minutes / 60);
215
+ if (hours < 24) return `${hours} hour${hours === 1 ? "" : "s"} ago`;
216
+ const days = Math.floor(hours / 24);
217
+ return `${days} day${days === 1 ? "" : "s"} ago`;
218
+ }
219
+ var CACHE_TTL = 5 * 60 * 1e3;
220
+ var CACHE_FILE = BTC_RATE_FILE;
221
+ var memRate = null;
222
+ var memTime = 0;
223
+ function readDiskCache() {
224
+ try {
225
+ if (!existsSync(CACHE_FILE)) return null;
226
+ const data = JSON.parse(readFileSync(CACHE_FILE, "utf-8"));
227
+ if (Date.now() - data.timestamp >= CACHE_TTL) return null;
228
+ if (!isPlausibleBtcUsdRate(data.rate)) return null;
229
+ return data.rate;
60
230
  } catch {
61
- return false;
231
+ return null;
62
232
  }
63
- if (host.startsWith("[") && host.endsWith("]")) host = host.slice(1, -1);
64
- if (LOOPBACK_HOSTS.has(host)) return true;
65
- if (host.endsWith(".localhost")) return true;
66
- return TEST_MINT_HOSTS.has(host);
67
233
  }
68
- var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "0.0.0.0", "::1"]);
69
- var TEST_MINT_HOSTS = /* @__PURE__ */ new Set(["testnut.cashu.space", "dvmkit-testmint.fly.dev"]);
234
+ function readDiskCacheStale() {
235
+ try {
236
+ if (!existsSync(CACHE_FILE)) return null;
237
+ const data = JSON.parse(readFileSync(CACHE_FILE, "utf-8"));
238
+ if (!isPlausibleBtcUsdRate(data.rate)) return null;
239
+ return { rate: data.rate, cachedAt: data.timestamp };
240
+ } catch {
241
+ return null;
242
+ }
243
+ }
244
+ function writeDiskCache(rate) {
245
+ try {
246
+ ensureConfigSubdir(dirname(CACHE_FILE));
247
+ writeFileSync(CACHE_FILE, JSON.stringify({ rate, timestamp: Date.now() }));
248
+ } catch {
249
+ }
250
+ }
251
+ var MAX_PLAUSIBLE_BTC_USD = 1e7;
252
+ function isPlausibleBtcUsdRate(rate) {
253
+ return typeof rate === "number" && Number.isFinite(rate) && rate > 0 && rate < MAX_PLAUSIBLE_BTC_USD;
254
+ }
255
+ async function fetchFromApi() {
256
+ const spot = await fetchCoinGeckoBtcUsd();
257
+ const rate = spot.btc_usd;
258
+ if (!isPlausibleBtcUsdRate(rate)) {
259
+ throw new Error(`implausible BTC/USD rate from upstream: ${String(rate)}`);
260
+ }
261
+ return rate;
262
+ }
70
263
 
71
264
  // src/lib/secret-backup.ts
72
- import { existsSync, readFileSync, writeFileSync } from "fs";
265
+ import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
73
266
  function backupSecretFile(file, nextContent) {
74
- const content = existsSync(file) ? readFileSync(file) : null;
267
+ const content = existsSync2(file) ? readFileSync2(file) : null;
75
268
  if (content === null) return null;
76
269
  if (content.toString("utf-8").trim() === nextContent?.trim()) return null;
77
270
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
78
271
  for (let n = 0; n < 100; n++) {
79
272
  const backup = n === 0 ? `${file}.${stamp}.bak` : `${file}.${stamp}-${String(n)}.bak`;
80
273
  try {
81
- writeFileSync(backup, content, { mode: 384, flag: "wx" });
274
+ writeFileSync2(backup, content, { mode: 384, flag: "wx" });
82
275
  return backup;
83
276
  } catch (err) {
84
277
  if (err.code !== "EEXIST") throw err;
@@ -89,20 +282,20 @@ function backupSecretFile(file, nextContent) {
89
282
 
90
283
  // src/lib/wallet-agent/agent-mnemonic-file.ts
91
284
  import {
92
- chmodSync,
93
- existsSync as existsSync2,
94
- readFileSync as readFileSync2,
285
+ chmodSync as chmodSync2,
286
+ existsSync as existsSync3,
287
+ readFileSync as readFileSync3,
95
288
  renameSync,
96
289
  unlinkSync,
97
- writeFileSync as writeFileSync2
290
+ writeFileSync as writeFileSync3
98
291
  } from "fs";
99
292
  function readAgentMnemonic() {
100
- if (!existsSync2(AGENT_MNEMONIC_FILE)) return null;
101
- const raw = readFileSync2(AGENT_MNEMONIC_FILE, "utf-8");
293
+ if (!existsSync3(AGENT_MNEMONIC_FILE)) return null;
294
+ const raw = readFileSync3(AGENT_MNEMONIC_FILE, "utf-8");
102
295
  return raw.trim().toLowerCase().split(/\s+/).join(" ");
103
296
  }
104
297
  function assertAgentMnemonicReplaceable(opts = {}) {
105
- if (existsSync2(AGENT_MNEMONIC_FILE) && !opts.force) {
298
+ if (existsSync3(AGENT_MNEMONIC_FILE) && !opts.force) {
106
299
  throw new DvmError(
107
300
  "agent_mnemonic_exists",
108
301
  `An agent wallet recovery mnemonic already exists at ${AGENT_MNEMONIC_FILE}.`,
@@ -116,7 +309,7 @@ function writeAgentMnemonic(mnemonic, opts = {}) {
116
309
  ensureConfigDir();
117
310
  const backupFile = backupSecretFile(AGENT_MNEMONIC_FILE, normalised);
118
311
  const tmp = `${AGENT_MNEMONIC_FILE}.tmp`;
119
- writeFileSync2(tmp, normalised + "\n", { mode: 384 });
312
+ writeFileSync3(tmp, normalised + "\n", { mode: 384 });
120
313
  try {
121
314
  renameSync(tmp, AGENT_MNEMONIC_FILE);
122
315
  } catch (err) {
@@ -127,13 +320,13 @@ function writeAgentMnemonic(mnemonic, opts = {}) {
127
320
  throw err;
128
321
  }
129
322
  try {
130
- chmodSync(AGENT_MNEMONIC_FILE, 384);
323
+ chmodSync2(AGENT_MNEMONIC_FILE, 384);
131
324
  } catch {
132
325
  }
133
326
  return { backupFile };
134
327
  }
135
328
  function deleteAgentMnemonic() {
136
- if (!existsSync2(AGENT_MNEMONIC_FILE)) return { backupFile: null };
329
+ if (!existsSync3(AGENT_MNEMONIC_FILE)) return { backupFile: null };
137
330
  const backupFile = backupSecretFile(AGENT_MNEMONIC_FILE);
138
331
  unlinkSync(AGENT_MNEMONIC_FILE);
139
332
  return { backupFile };
@@ -195,7 +388,7 @@ function mnemonicFingerprint(mnemonic) {
195
388
  }
196
389
 
197
390
  // src/lib/wallet-agent/store.ts
198
- import { existsSync as existsSync3, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "fs";
391
+ import { existsSync as existsSync4, readFileSync as readFileSync4, renameSync as renameSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "fs";
199
392
  import lockfile from "proper-lockfile";
200
393
  var LOCK_FILE = `${WALLET_FILE}.lock`;
201
394
  var DEFAULT_LOCK_STALE_MS = 3e4;
@@ -204,9 +397,12 @@ var DEFAULT_LOCK_RETRY_INTERVAL_MS = 1e3;
204
397
  var WALLET_VERSION = 9;
205
398
  var RECENT_SPENDS_CAP = 10;
206
399
  var DEFAULT_AGENT_MINT_URL = "https://mint.coinos.io";
400
+ function loadWalletConfiguredMints() {
401
+ return loadAgentWallet()?.mints ?? [];
402
+ }
207
403
  function loadAgentWallet() {
208
- if (!existsSync3(WALLET_FILE)) return null;
209
- const raw = readFileSync3(WALLET_FILE, "utf-8");
404
+ if (!existsSync4(WALLET_FILE)) return null;
405
+ const raw = readFileSync4(WALLET_FILE, "utf-8");
210
406
  let parsed;
211
407
  try {
212
408
  parsed = JSON.parse(raw);
@@ -288,7 +484,7 @@ function saveAgentWallet(wallet) {
288
484
  ensureConfigDir();
289
485
  const tmp = `${WALLET_FILE}.tmp`;
290
486
  const data = JSON.stringify(wallet, null, 2) + "\n";
291
- writeFileSync3(tmp, data, { mode: 384 });
487
+ writeFileSync4(tmp, data, { mode: 384 });
292
488
  try {
293
489
  renameSync2(tmp, WALLET_FILE);
294
490
  } catch (err) {
@@ -300,7 +496,7 @@ function saveAgentWallet(wallet) {
300
496
  }
301
497
  }
302
498
  function walletExists() {
303
- return existsSync3(WALLET_FILE);
499
+ return existsSync4(WALLET_FILE);
304
500
  }
305
501
  async function withWalletLock(fn, opts = {}) {
306
502
  ensureConfigDir();
@@ -1061,34 +1257,34 @@ function buildAdminAuthHeader(args) {
1061
1257
 
1062
1258
  // src/lib/builder-identity.ts
1063
1259
  import {
1064
- chmodSync as chmodSync3,
1065
- existsSync as existsSync5,
1066
- mkdirSync as mkdirSync2,
1067
- readFileSync as readFileSync5,
1260
+ chmodSync as chmodSync4,
1261
+ existsSync as existsSync6,
1262
+ mkdirSync as mkdirSync3,
1263
+ readFileSync as readFileSync6,
1068
1264
  renameSync as renameSync4,
1069
- statSync,
1265
+ statSync as statSync2,
1070
1266
  unlinkSync as unlinkSync4,
1071
- writeFileSync as writeFileSync5
1267
+ writeFileSync as writeFileSync6
1072
1268
  } from "fs";
1073
1269
  import { schnorr as schnorr3 } from "@noble/curves/secp256k1.js";
1074
1270
  import { sha256 as sha2563 } from "@noble/hashes/sha2.js";
1075
1271
 
1076
1272
  // src/lib/builder-store.ts
1077
1273
  import {
1078
- chmodSync as chmodSync2,
1079
- existsSync as existsSync4,
1080
- mkdirSync,
1081
- readFileSync as readFileSync4,
1274
+ chmodSync as chmodSync3,
1275
+ existsSync as existsSync5,
1276
+ mkdirSync as mkdirSync2,
1277
+ readFileSync as readFileSync5,
1082
1278
  renameSync as renameSync3,
1083
1279
  unlinkSync as unlinkSync3,
1084
- writeFileSync as writeFileSync4
1280
+ writeFileSync as writeFileSync5
1085
1281
  } from "fs";
1086
1282
  var CURRENT_VERSION = 2;
1087
1283
  function loadBuilderStore() {
1088
- if (!existsSync4(BUILDER_FILE)) {
1284
+ if (!existsSync5(BUILDER_FILE)) {
1089
1285
  return { version: CURRENT_VERSION, dvms: {} };
1090
1286
  }
1091
- const raw = readFileSync4(BUILDER_FILE, "utf-8");
1287
+ const raw = readFileSync5(BUILDER_FILE, "utf-8");
1092
1288
  let parsed;
1093
1289
  try {
1094
1290
  parsed = JSON.parse(raw);
@@ -1124,7 +1320,7 @@ function loadIdentityPubkey() {
1124
1320
  return loadBuilderStore().identity_pubkey ?? null;
1125
1321
  }
1126
1322
  function setIdentityPubkey(pubkey) {
1127
- mkdirSync(BUILDER_CONFIG_DIR, { recursive: true, mode: 448 });
1323
+ mkdirSync2(BUILDER_CONFIG_DIR, { recursive: true, mode: 448 });
1128
1324
  const store = loadBuilderStore();
1129
1325
  store.identity_pubkey = pubkey;
1130
1326
  writeBuilderStore(store);
@@ -1134,7 +1330,7 @@ function writeBuilderStore(store) {
1134
1330
  backupSecretFile(BUILDER_FILE, content);
1135
1331
  const tmp = `${BUILDER_FILE}.tmp`;
1136
1332
  try {
1137
- writeFileSync4(tmp, content, { mode: 384 });
1333
+ writeFileSync5(tmp, content, { mode: 384 });
1138
1334
  renameSync3(tmp, BUILDER_FILE);
1139
1335
  } catch (err) {
1140
1336
  try {
@@ -1144,7 +1340,7 @@ function writeBuilderStore(store) {
1144
1340
  throw err;
1145
1341
  }
1146
1342
  try {
1147
- chmodSync2(BUILDER_FILE, 384);
1343
+ chmodSync3(BUILDER_FILE, 384);
1148
1344
  } catch {
1149
1345
  }
1150
1346
  }
@@ -1180,9 +1376,9 @@ function generateIdentity() {
1180
1376
  return { pubkey: bytesToHex4(publicKey), secret: bytesToHex4(secretKey) };
1181
1377
  }
1182
1378
  function loadIdentitySecret() {
1183
- if (!existsSync5(BUILDER_KEY_FILE)) return null;
1379
+ if (!existsSync6(BUILDER_KEY_FILE)) return null;
1184
1380
  warnAndTightenLooseKeyMode();
1185
- const raw = readFileSync5(BUILDER_KEY_FILE, "utf-8").trim();
1381
+ const raw = readFileSync6(BUILDER_KEY_FILE, "utf-8").trim();
1186
1382
  if (!/^[0-9a-f]{64}$/.test(raw)) {
1187
1383
  throw new DvmError(
1188
1384
  "identity_secret_malformed",
@@ -1201,9 +1397,9 @@ function writeIdentity(keypair, opts = {}) {
1201
1397
  "Pass --force to overwrite. Any DVMs deployed under the old key will need to be re-deployed to refresh their /v1/info attestation."
1202
1398
  );
1203
1399
  }
1204
- mkdirSync2(BUILDER_CONFIG_DIR, { recursive: true, mode: 448 });
1400
+ mkdirSync3(BUILDER_CONFIG_DIR, { recursive: true, mode: 448 });
1205
1401
  const tmp = `${BUILDER_KEY_FILE}.tmp`;
1206
- writeFileSync5(tmp, keypair.secret + "\n", { mode: 384 });
1402
+ writeFileSync6(tmp, keypair.secret + "\n", { mode: 384 });
1207
1403
  try {
1208
1404
  renameSync4(tmp, BUILDER_KEY_FILE);
1209
1405
  } catch (err) {
@@ -1214,7 +1410,7 @@ function writeIdentity(keypair, opts = {}) {
1214
1410
  throw err;
1215
1411
  }
1216
1412
  try {
1217
- chmodSync3(BUILDER_KEY_FILE, 384);
1413
+ chmodSync4(BUILDER_KEY_FILE, 384);
1218
1414
  } catch {
1219
1415
  }
1220
1416
  setIdentityPubkey(keypair.pubkey);
@@ -1248,7 +1444,7 @@ function verifyAttestation(payload, signatureHex, pubkeyHex) {
1248
1444
  function warnAndTightenLooseKeyMode() {
1249
1445
  if (process.platform === "win32") return;
1250
1446
  try {
1251
- const mode = statSync(BUILDER_KEY_FILE).mode & 511;
1447
+ const mode = statSync2(BUILDER_KEY_FILE).mode & 511;
1252
1448
  if ((mode & 63) === 0) return;
1253
1449
  diagnosticConsole.warn(
1254
1450
  {
@@ -1259,7 +1455,7 @@ function warnAndTightenLooseKeyMode() {
1259
1455
  mode: mode.toString(8).padStart(3, "0")
1260
1456
  }
1261
1457
  );
1262
- chmodSync3(BUILDER_KEY_FILE, 384);
1458
+ chmodSync4(BUILDER_KEY_FILE, 384);
1263
1459
  } catch {
1264
1460
  }
1265
1461
  }
@@ -1279,6 +1475,82 @@ function hexToBytes4(hex) {
1279
1475
  return out;
1280
1476
  }
1281
1477
 
1478
+ // src/lib/receipt.ts
1479
+ import { schnorr as schnorr4 } from "@noble/curves/secp256k1.js";
1480
+ import { sha256 as sha2564 } from "@noble/hashes/sha2.js";
1481
+ import { bytesToHex as bytesToHex5, hexToBytes as hexToBytes5 } from "@noble/hashes/utils.js";
1482
+ function computeResultHash(summary, artifactContents) {
1483
+ const artifactHashes = artifactContents.map(
1484
+ (content) => bytesToHex5(sha2564(canonicaliseForSigning(content)))
1485
+ );
1486
+ return bytesToHex5(
1487
+ sha2564(
1488
+ canonicaliseForSigning({
1489
+ summary: summary ?? null,
1490
+ artifact_hashes: artifactHashes
1491
+ })
1492
+ )
1493
+ );
1494
+ }
1495
+ function signReceipt(unsigned, secretHex) {
1496
+ const bytes = canonicaliseForSigning(unsigned);
1497
+ const signature = bytesToHex5(schnorr4.sign(bytes, hexToBytes5(secretHex)));
1498
+ return { ...unsigned, signature };
1499
+ }
1500
+ function verifyReceipt(receipt) {
1501
+ try {
1502
+ const { signature, ...unsigned } = receipt;
1503
+ const bytes = canonicaliseForSigning(unsigned);
1504
+ return schnorr4.verify(hexToBytes5(signature), bytes, hexToBytes5(receipt.receipt_pubkey));
1505
+ } catch {
1506
+ return false;
1507
+ }
1508
+ }
1509
+ function isSignedJobReceipt(value) {
1510
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
1511
+ const r = value;
1512
+ const paid = r.paid;
1513
+ return r.v === 1 && typeof r.job_id === "string" && typeof r.dvm_id === "string" && typeof r.dvm === "string" && typeof r.capability === "string" && (r.outcome === "completed" || r.outcome === "failed" || r.outcome === "cancelled") && typeof r.seq === "number" && typeof r.issued_at === "number" && typeof r.receipt_pubkey === "string" && typeof r.signature === "string" && !!paid && typeof paid === "object" && !Array.isArray(paid) && typeof paid.msats === "number";
1514
+ }
1515
+ function signDrainReceipt(unsigned, secretHex) {
1516
+ const bytes = canonicaliseForSigning(unsigned);
1517
+ const signature = bytesToHex5(schnorr4.sign(bytes, hexToBytes5(secretHex)));
1518
+ return { ...unsigned, signature };
1519
+ }
1520
+ function signFundingReceipt(unsigned, secretHex) {
1521
+ const bytes = canonicaliseForSigning(unsigned);
1522
+ const signature = bytesToHex5(schnorr4.sign(bytes, hexToBytes5(secretHex)));
1523
+ return { ...unsigned, signature };
1524
+ }
1525
+ function isFundingReceipt(value) {
1526
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
1527
+ const r = value;
1528
+ return r.v === 1 && r.kind === "credit_funding" && typeof r.credit_id === "string" && typeof r.fund_id === "string" && typeof r.funded_micro === "number" && typeof r.balance_after_micro === "number" && typeof r.ledger_seq === "number" && typeof r.issued_at === "number" && typeof r.caller_pubkey === "string" && typeof r.dvm_id === "string" && typeof r.dvm === "string" && typeof r.receipt_pubkey === "string" && typeof r.signature === "string";
1529
+ }
1530
+ function verifyFundingReceipt(receipt) {
1531
+ try {
1532
+ const { signature, ...unsigned } = receipt;
1533
+ const bytes = canonicaliseForSigning(unsigned);
1534
+ return schnorr4.verify(hexToBytes5(signature), bytes, hexToBytes5(receipt.receipt_pubkey));
1535
+ } catch {
1536
+ return false;
1537
+ }
1538
+ }
1539
+ function isDrainReceipt(value) {
1540
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
1541
+ const r = value;
1542
+ return r.v === 1 && r.kind === "credit_drain" && typeof r.credit_id === "string" && typeof r.drain_id === "string" && (r.event === "requested" || r.event === "parked" || r.event === "picked_up" || r.event === "sent" || r.event === "released") && typeof r.method === "string" && typeof r.amount_micro === "number" && typeof r.issued_at === "number" && typeof r.caller_pubkey === "string" && typeof r.dvm_id === "string" && typeof r.dvm === "string" && typeof r.receipt_pubkey === "string" && typeof r.signature === "string";
1543
+ }
1544
+ function verifyDrainReceipt(receipt) {
1545
+ try {
1546
+ const { signature, ...unsigned } = receipt;
1547
+ const bytes = canonicaliseForSigning(unsigned);
1548
+ return schnorr4.verify(hexToBytes5(signature), bytes, hexToBytes5(receipt.receipt_pubkey));
1549
+ } catch {
1550
+ return false;
1551
+ }
1552
+ }
1553
+
1282
1554
  // src/lib/receipt-verify.ts
1283
1555
  function attestedDvmIdentity(builder) {
1284
1556
  const builderPubkey = authenticatedBuilderPubkey(builder);
@@ -1424,90 +1696,6 @@ function weakestReceiptVerdict(statuses) {
1424
1696
  if (statuses.includes("verified_unattested")) return "verified_unattested";
1425
1697
  return "verified";
1426
1698
  }
1427
- function drainReceiptsDisplay(status, sound, total) {
1428
- const tally = `${String(sound)} of ${String(total)}`;
1429
- switch (status) {
1430
- case "verified":
1431
- return `Verified reclaim receipts (${tally}): signed by the key this DVM's builder identity attests.`;
1432
- case "verified_unattested":
1433
- return `Reclaim receipts, signed but unattested (${tally}): the signature holds, but this DVM publishes no builder identity to vouch for the signing key.`;
1434
- case "invalid":
1435
- return `UNVERIFIED reclaim receipts: only ${tally} hold up \u2014 the refund is still owed, but this evidence is not sound.`;
1436
- case "absent":
1437
- return "No reclaim receipts: this DVM returned no signed evidence of the drain.";
1438
- }
1439
- }
1440
- function drainReceiptsHint(status) {
1441
- switch (status) {
1442
- case "verified":
1443
- return "The refund evidence chains to this DVM's published builder identity, and is stored locally with that identity pinned beside it \u2014 so an unhonoured refund stays provable against the builder, not against an anonymous key.";
1444
- case "verified_unattested":
1445
- return "The receipts are signed, but nothing vouches for the signing key \u2014 expected for a locally-run or development DVM, not for a deployed one. Stored either way; the refund is owed regardless.";
1446
- case "invalid":
1447
- return "The refund is unaffected \u2014 this is a problem with the proof, not the money. Keep this output, and treat the reclaim as unattested when deciding how much to leave on deposit at this DVM.";
1448
- case "absent":
1449
- return "This DVM countersigned nothing, so the only artifact behind this reclaim is the drain request the caller signed. Weaker evidence than a countersigned drain, and worth weighing before funding here again.";
1450
- }
1451
- }
1452
- function receiptDisplay(receipt, status, rate = 0) {
1453
- const what = `${receipt.dvm}/${receipt.capability} (DVM ID ${receipt.dvm_id})`;
1454
- const when = receiptIssuedAtIso(receipt)?.slice(0, 10);
1455
- return `${VERDICT_PREFIX[status]}: paid ${formatPaid(receipt.paid.msats, rate)} for ${what}, ${receipt.outcome}${when ? ` ${when}` : ""}.`;
1456
- }
1457
- function receiptIssuedAtIso(receipt) {
1458
- const seconds = receipt.issued_at;
1459
- return typeof seconds === "number" ? epochMsToIso(seconds * 1e3) : null;
1460
- }
1461
- function epochMsToIso(ms) {
1462
- if (typeof ms !== "number" || !Number.isFinite(ms) || Math.abs(ms) > MAX_TIMESTAMP_MS) {
1463
- return null;
1464
- }
1465
- return new Date(ms).toISOString();
1466
- }
1467
- function receiptHint(status, jobId) {
1468
- const verifyCmd = jobId ? `'dvm receipts verify ${jobId}'` : "'dvm receipts verify <job-id>'";
1469
- switch (status) {
1470
- case "verified":
1471
- return `Proof of purchase is stored locally. Re-check it any time with ${verifyCmd}, or hand over the raw bundle with 'dvm receipts export'.`;
1472
- case "verified_unattested":
1473
- return `The receipt's signature is valid, but this DVM publishes no builder identity to vouch for the signing key \u2014 expected for a locally-run or development DVM, not for a deployed one. Stored either way; inspect it with ${verifyCmd}.`;
1474
- case "invalid":
1475
- return `The job itself is unaffected \u2014 this is a problem with the proof, not the result. Run ${verifyCmd} to see which check failed before relying on this receipt as evidence.`;
1476
- case "absent":
1477
- return "This DVM does not issue signed receipts, so there is no proof of purchase to store. Nothing went wrong.";
1478
- }
1479
- }
1480
- function receiptFailureDetail(reason) {
1481
- switch (reason) {
1482
- case "job_id_mismatch":
1483
- return "The receipt names a different job than the one it was collected for \u2014 it is not proof of this job, whoever signed it.";
1484
- case "drain_id_mismatch":
1485
- return "The receipt names a different reclaim than the one it is filed under \u2014 it is not evidence of this drain, whoever signed it.";
1486
- case "credit_id_mismatch":
1487
- return "The receipt names a different credit than the one this reclaim was against \u2014 it is not evidence of this refund, whoever signed it.";
1488
- case "fund_id_mismatch":
1489
- return "The receipt names a different funding operation than the caller submitted.";
1490
- case "funding_amount_mismatch":
1491
- return "The signed funded amount differs from the provider's funding response.";
1492
- case "funding_balance_mismatch":
1493
- return "The signed post-funding balance differs from the provider's fresh balance response.";
1494
- case "funding_ledger_seq_mismatch":
1495
- return "The signed funding sequence differs from the provider's fresh balance response.";
1496
- case "caller_pubkey_mismatch":
1497
- return "The funding receipt names a different caller than the key that submitted the funding.";
1498
- case "signature_invalid":
1499
- return "The receipt's signature does not verify under the key it names \u2014 the bytes were altered, or it was never signed by that key.";
1500
- case "attestation_invalid":
1501
- return "The DVM's published builder attestation does not verify under its own builder pubkey.";
1502
- case "dvm_identity_mismatch":
1503
- return "The receipt names a different immutable DVM ID than the builder attestation, so it is not evidence for this provider.";
1504
- case "receipt_key_mismatch":
1505
- return "The receipt was signed by a different key than the one the DVM's builder attested.";
1506
- case "result_hash_mismatch":
1507
- return "The receipt attests a different result than the one delivered.";
1508
- }
1509
- }
1510
- var MAX_TIMESTAMP_MS = 864e13;
1511
1699
  function checkJobId(receipt, expectedJobId) {
1512
1700
  if (expectedJobId === void 0) return "unchecked";
1513
1701
  return receipt.job_id === expectedJobId ? "ok" : "mismatch";
@@ -1516,12 +1704,6 @@ function checkDrainField(actual, expected) {
1516
1704
  if (expected === void 0 || actual === void 0) return "unchecked";
1517
1705
  return actual === expected ? "ok" : "mismatch";
1518
1706
  }
1519
- var VERDICT_PREFIX = {
1520
- verified: "Verified receipt",
1521
- verified_unattested: "Receipt (signed, unattested)",
1522
- invalid: "UNVERIFIED receipt",
1523
- absent: "No receipt"
1524
- };
1525
1707
  function checkResultHash(receipt, delivered) {
1526
1708
  if (!delivered) return "unchecked";
1527
1709
  if (receipt.result_hash === null) return "unchecked";
@@ -1539,14 +1721,6 @@ function isTrustAnchor(value) {
1539
1721
  const anchor = value;
1540
1722
  return typeof anchor.pubkey === "string" && typeof anchor.signature === "string" && !!anchor.attestation && typeof anchor.attestation === "object" && !Array.isArray(anchor.attestation);
1541
1723
  }
1542
- function formatPaid(msats, rate) {
1543
- if (msats === 0) return "nothing (free job)";
1544
- const sats = msats % 1e3 === 0 ? msats / 1e3 : Math.round(msats / 1e3 * 100) / 100;
1545
- const satsLabel = `${sats.toLocaleString("en-US")} sats`;
1546
- if (rate <= 0) return satsLabel;
1547
- const usd = msatsToUsd(msats, rate);
1548
- return `~$${formatUsd(usd)} (${satsLabel}, ${costAnchor(usd)})`;
1549
- }
1550
1724
 
1551
1725
  // src/lib/request-id.ts
1552
1726
  var MAX_REQUEST_ID_LENGTH = 128;
@@ -1583,54 +1757,44 @@ function createMonotonicClock() {
1583
1757
  };
1584
1758
  }
1585
1759
 
1586
- // src/lib/cashu/verify.ts
1587
- import { getTokenMetadata } from "@cashu/cashu-ts";
1588
- function verifyCashuToken(token, acceptedMints) {
1589
- let mint, unit, totalMsats;
1590
- try {
1591
- const meta = getTokenMetadata(token);
1592
- mint = meta.mint;
1593
- unit = meta.unit;
1594
- totalMsats = meta.amount.toNumber() * 1e3;
1595
- } catch {
1596
- throw new DvmError(
1597
- "cashu_invalid_token",
1598
- "The Cashu token could not be decoded.",
1599
- "Ensure the token is a valid cashuA... or cashuB... string."
1600
- );
1601
- }
1602
- const normalised = acceptedMints.map(normaliseMintUrl);
1603
- if (!normalised.includes(normaliseMintUrl(mint))) {
1604
- throw new DvmError(
1605
- "cashu_mint_not_accepted",
1606
- `Mint ${mint} is not accepted by this provider.`,
1607
- `Accepted mints: ${acceptedMints.join(", ")}`
1608
- );
1609
- }
1610
- return { mint, totalMsats, unit };
1611
- }
1612
- function normaliseMintUrl(url) {
1613
- try {
1614
- const u = new URL(url);
1615
- u.hostname = u.hostname.toLowerCase();
1616
- u.pathname = u.pathname.replace(/\/+/g, "/").replace(/\/+$/, "") || "/";
1617
- return u.toString().replace(/\/$/, "");
1618
- } catch {
1619
- return url.toLowerCase().replace(/\/+$/, "");
1620
- }
1621
- }
1622
-
1623
- // src/lib/payment-rails/tempo-wire.ts
1624
- var TEMPO_SESSION_UPDATED_REASON = "Tempo session updated; attach the next voucher to fund this credit.";
1625
-
1626
1760
  export {
1627
1761
  toLockPubkey,
1628
1762
  isX402SettlementReconciliationReason,
1629
- verifyCashuToken,
1630
- normaliseMintUrl,
1763
+ IMPLICIT_CREDIT_TTL_MS,
1631
1764
  TEMPO_SESSION_UPDATED_REASON,
1632
- RECOMMENDED_MINTS,
1633
- isTestMintUrl,
1765
+ CONFIG_DIR,
1766
+ CONFIG_FILE,
1767
+ CONFIG_BACKUP_FILE,
1768
+ CONFIG_AUDIT_FILE,
1769
+ CONFIG_PINNED_FILE,
1770
+ JOBS_FILE,
1771
+ REQUEST_INTENTS_DIR,
1772
+ FAVORITES_FILE,
1773
+ MESSAGES_DIR,
1774
+ RECEIPTS_FILE,
1775
+ CREDITS_FILE,
1776
+ X402_CHANNELS_FILE,
1777
+ TEMPO_CHANNELS_FILE,
1778
+ FEEDBACK_NUDGES_FILE,
1779
+ RECOVERY_MNEMONIC_FILE,
1780
+ WALLET_FILE,
1781
+ AGENT_MNEMONIC_FILE,
1782
+ IDENTITIES_FILE,
1783
+ PAIRINGS_DIR,
1784
+ BUDGET_FILE,
1785
+ BTC_RATE_FILE,
1786
+ ensureConfigDir,
1787
+ ensureConfigSubdir,
1788
+ repairConfigDirMode,
1789
+ COINGECKO_SIMPLE_PRICE_URL,
1790
+ fetchCoinGeckoSpot,
1791
+ fetchCoinGeckoBtcUsd,
1792
+ costAnchor,
1793
+ formatUsd,
1794
+ formatUsdcMicrounits,
1795
+ fetchBtcUsdRate,
1796
+ getLastKnownBtcRate,
1797
+ formatStaleness,
1634
1798
  backupSecretFile,
1635
1799
  readAgentMnemonic,
1636
1800
  assertAgentMnemonicReplaceable,
@@ -1644,6 +1808,7 @@ export {
1644
1808
  WALLET_VERSION,
1645
1809
  RECENT_SPENDS_CAP,
1646
1810
  DEFAULT_AGENT_MINT_URL,
1811
+ loadWalletConfiguredMints,
1647
1812
  loadAgentWallet,
1648
1813
  saveAgentWallet,
1649
1814
  walletExists,
@@ -1677,6 +1842,16 @@ export {
1677
1842
  buildAttestation,
1678
1843
  signAttestation,
1679
1844
  verifyAttestation,
1845
+ computeResultHash,
1846
+ signReceipt,
1847
+ verifyReceipt,
1848
+ isSignedJobReceipt,
1849
+ signDrainReceipt,
1850
+ signFundingReceipt,
1851
+ isFundingReceipt,
1852
+ verifyFundingReceipt,
1853
+ isDrainReceipt,
1854
+ verifyDrainReceipt,
1680
1855
  attestedDvmIdentity,
1681
1856
  authenticatedBuilderPubkey,
1682
1857
  verifyJobReceipt,
@@ -1684,13 +1859,6 @@ export {
1684
1859
  verifyDrainReceiptChain,
1685
1860
  verifyFundingReceiptChain,
1686
1861
  weakestReceiptVerdict,
1687
- drainReceiptsDisplay,
1688
- drainReceiptsHint,
1689
- receiptDisplay,
1690
- receiptIssuedAtIso,
1691
- epochMsToIso,
1692
- receiptHint,
1693
- receiptFailureDetail,
1694
1862
  isPublicRequestId,
1695
1863
  parseTraceparent,
1696
1864
  createMonotonicClock