@vapi-network/core 0.5.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/README.md +406 -5
  2. package/dist/account-add.d.ts +101 -0
  3. package/dist/account-add.d.ts.map +1 -0
  4. package/dist/account-movement-lock.d.ts +10 -0
  5. package/dist/account-movement-lock.d.ts.map +1 -0
  6. package/dist/agent-link.d.ts +16 -1
  7. package/dist/agent-link.d.ts.map +1 -1
  8. package/dist/agent-link.js +131 -54
  9. package/dist/agent-link.js.map +3 -3
  10. package/dist/agent-profile.d.ts +47 -6
  11. package/dist/agent-profile.d.ts.map +1 -1
  12. package/dist/agent-run.d.ts +12 -0
  13. package/dist/agent-run.d.ts.map +1 -0
  14. package/dist/allocation.d.ts +64 -0
  15. package/dist/allocation.d.ts.map +1 -0
  16. package/dist/api-key.js +4 -2
  17. package/dist/api-key.js.map +1 -1
  18. package/dist/atomic-file.d.ts +23 -0
  19. package/dist/atomic-file.d.ts.map +1 -0
  20. package/dist/audit.d.ts +4 -1
  21. package/dist/audit.d.ts.map +1 -1
  22. package/dist/backup.d.ts +307 -0
  23. package/dist/backup.d.ts.map +1 -0
  24. package/dist/backup.js +49938 -0
  25. package/dist/backup.js.map +6 -0
  26. package/dist/call-contracts-wire.d.ts +12 -12
  27. package/dist/ceiling.d.ts +98 -0
  28. package/dist/ceiling.d.ts.map +1 -0
  29. package/dist/cloud-backup.d.ts +123 -0
  30. package/dist/cloud-backup.d.ts.map +1 -0
  31. package/dist/cloud-backup.js +50827 -0
  32. package/dist/cloud-backup.js.map +6 -0
  33. package/dist/config.d.ts +5 -0
  34. package/dist/config.d.ts.map +1 -1
  35. package/dist/device.d.ts +15 -0
  36. package/dist/device.d.ts.map +1 -0
  37. package/dist/distribute.d.ts +68 -0
  38. package/dist/distribute.d.ts.map +1 -0
  39. package/dist/index.d.ts +17 -0
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +10131 -1228
  42. package/dist/index.js.map +3 -3
  43. package/dist/keystore.d.ts +24 -1
  44. package/dist/keystore.d.ts.map +1 -1
  45. package/dist/marketplace-contracts.d.ts +14 -14
  46. package/dist/member-bundle.d.ts +110 -0
  47. package/dist/member-bundle.d.ts.map +1 -0
  48. package/dist/movement-open.d.ts +10 -0
  49. package/dist/movement-open.d.ts.map +1 -0
  50. package/dist/movement.d.ts +172 -0
  51. package/dist/movement.d.ts.map +1 -0
  52. package/dist/networks.d.ts +1 -0
  53. package/dist/networks.d.ts.map +1 -1
  54. package/dist/receipts.d.ts +33 -0
  55. package/dist/receipts.d.ts.map +1 -1
  56. package/dist/router-client.d.ts +9 -1
  57. package/dist/router-client.d.ts.map +1 -1
  58. package/dist/router-client.js +36500 -16308
  59. package/dist/router-client.js.map +3 -3
  60. package/dist/run-budget.d.ts +17 -0
  61. package/dist/run-budget.d.ts.map +1 -0
  62. package/dist/secret-store.d.ts +23 -5
  63. package/dist/secret-store.d.ts.map +1 -1
  64. package/dist/secrets.d.ts +2 -0
  65. package/dist/secrets.d.ts.map +1 -1
  66. package/dist/secrets.js +1439 -56
  67. package/dist/secrets.js.map +3 -3
  68. package/dist/siblings.d.ts +41 -0
  69. package/dist/siblings.d.ts.map +1 -0
  70. package/dist/spend-policy.d.ts +12 -0
  71. package/dist/spend-policy.d.ts.map +1 -1
  72. package/dist/status.d.ts +73 -0
  73. package/dist/status.d.ts.map +1 -0
  74. package/dist/support-report.d.ts +1 -1
  75. package/dist/swarm-capital.d.ts +147 -0
  76. package/dist/swarm-capital.d.ts.map +1 -0
  77. package/dist/swarm.d.ts +224 -0
  78. package/dist/swarm.d.ts.map +1 -0
  79. package/dist/transfer-journal.d.ts +11 -0
  80. package/dist/transfer-journal.d.ts.map +1 -0
  81. package/dist/transfer.d.ts +65 -0
  82. package/dist/transfer.d.ts.map +1 -0
  83. package/dist/vault-key.d.ts +41 -0
  84. package/dist/vault-key.d.ts.map +1 -0
  85. package/dist/vault-migrate.d.ts +29 -0
  86. package/dist/vault-migrate.d.ts.map +1 -0
  87. package/dist/vault.d.ts +104 -0
  88. package/dist/vault.d.ts.map +1 -0
  89. package/dist/wallet-store.d.ts +135 -39
  90. package/dist/wallet-store.d.ts.map +1 -1
  91. package/dist/x402-pay.d.ts +12 -0
  92. package/dist/x402-pay.d.ts.map +1 -1
  93. package/dist/x402.d.ts +3 -0
  94. package/dist/x402.d.ts.map +1 -1
  95. package/package.json +9 -1
package/dist/secrets.js CHANGED
@@ -2212,7 +2212,7 @@ function weierstrass(curveDef) {
2212
2212
  function prepSig(msgHash, privateKey, opts = defaultSigOpts) {
2213
2213
  if (["recovered", "canonical"].some((k) => k in opts))
2214
2214
  throw new Error("sign() legacy options not supported");
2215
- const { hash: hash2, randomBytes: randomBytes3 } = CURVE;
2215
+ const { hash: hash2, randomBytes: randomBytes5 } = CURVE;
2216
2216
  let { lowS, prehash, extraEntropy: ent } = opts;
2217
2217
  if (lowS == null)
2218
2218
  lowS = true;
@@ -2224,7 +2224,7 @@ function weierstrass(curveDef) {
2224
2224
  const d = normPrivateKeyToScalar(privateKey);
2225
2225
  const seedArgs = [int2octets(d), int2octets(h1int)];
2226
2226
  if (ent != null && ent !== false) {
2227
- const e2 = ent === true ? randomBytes3(Fp.BYTES) : ent;
2227
+ const e2 = ent === true ? randomBytes5(Fp.BYTES) : ent;
2228
2228
  seedArgs.push(ensureBytes("extraEntropy", e2));
2229
2229
  }
2230
2230
  const seed = concatBytes2(...seedArgs);
@@ -2793,16 +2793,16 @@ function hexToBytes3(hex_, opts = {}) {
2793
2793
  assertSize(hex3, { size: opts.size });
2794
2794
  hex3 = pad(hex3, { dir: "right", size: opts.size });
2795
2795
  }
2796
- let hexString = hex3.slice(2);
2797
- if (hexString.length % 2)
2798
- hexString = `0${hexString}`;
2799
- const length = hexString.length / 2;
2796
+ let hexString2 = hex3.slice(2);
2797
+ if (hexString2.length % 2)
2798
+ hexString2 = `0${hexString2}`;
2799
+ const length = hexString2.length / 2;
2800
2800
  const bytes = new Uint8Array(length);
2801
2801
  for (let index = 0, j = 0; index < length; index++) {
2802
- const nibbleLeft = charCodeToBase16(hexString.charCodeAt(j++));
2803
- const nibbleRight = charCodeToBase16(hexString.charCodeAt(j++));
2802
+ const nibbleLeft = charCodeToBase16(hexString2.charCodeAt(j++));
2803
+ const nibbleRight = charCodeToBase16(hexString2.charCodeAt(j++));
2804
2804
  if (nibbleLeft === void 0 || nibbleRight === void 0) {
2805
- throw new BaseError(`Invalid byte sequence ("${hexString[j - 2]}${hexString[j - 1]}" in "${hexString}").`);
2805
+ throw new BaseError(`Invalid byte sequence ("${hexString2[j - 2]}${hexString2[j - 1]}" in "${hexString2}").`);
2806
2806
  }
2807
2807
  bytes[index] = nibbleLeft * 16 + nibbleRight;
2808
2808
  }
@@ -4273,9 +4273,9 @@ var init_stringify = __esm({
4273
4273
  });
4274
4274
 
4275
4275
  // src/keystore.ts
4276
- import { createCipheriv, createDecipheriv, randomBytes as randomBytes2, scrypt } from "node:crypto";
4277
- import { mkdir as mkdir2, readFile as readFile2, rename as rename2, stat as stat2, writeFile as writeFile2 } from "node:fs/promises";
4278
- import { dirname as dirname2 } from "node:path";
4276
+ import { createCipheriv, createDecipheriv, randomBytes as randomBytes3, scrypt } from "node:crypto";
4277
+ import { mkdir as mkdir3, readFile as readFile3, rename as rename3, stat as stat3, writeFile as writeFile2 } from "node:fs/promises";
4278
+ import { dirname as dirname3 } from "node:path";
4279
4279
 
4280
4280
  // ../../node_modules/.pnpm/@solana+errors@5.5.1_typescript@6.0.3/node_modules/@solana/errors/dist/index.node.mjs
4281
4281
  var SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED = 1;
@@ -46542,13 +46542,232 @@ function date4(params) {
46542
46542
 
46543
46543
  // src/config.ts
46544
46544
  import { constants } from "node:fs";
46545
- import { copyFile, mkdir, readFile, rename, stat, writeFile } from "node:fs/promises";
46545
+ import { copyFile, mkdir as mkdir2, readFile as readFile2, rename as rename2, stat as stat2, writeFile } from "node:fs/promises";
46546
46546
  import { homedir } from "node:os";
46547
- import { dirname, join as join2 } from "node:path";
46547
+ import { dirname as dirname2, join as join3 } from "node:path";
46548
46548
 
46549
46549
  // ../../node_modules/.pnpm/viem@2.56.1_typescript@6.0.3_zod@4.5.4/node_modules/viem/_esm/index.js
46550
46550
  init_getAddress();
46551
46551
 
46552
+ // src/atomic-file.ts
46553
+ import { randomBytes as randomBytes2 } from "node:crypto";
46554
+ import { link, mkdir, open as open2, readdir, readFile, rename, stat, unlink } from "node:fs/promises";
46555
+ import { basename, dirname, join as join2 } from "node:path";
46556
+ var DEFAULT_LOCK_TIMEOUT_MS = 2e3;
46557
+ var LOCK_RETRY_MS = 20;
46558
+ var LOCK_STALE_AFTER_MS = 12e4;
46559
+ var LOCK_HEARTBEAT_MS = 3e4;
46560
+ var atomicFileTestHooks;
46561
+ var FileLockedError = class extends Error {
46562
+ constructor(message = "Timed out waiting for the file lock.") {
46563
+ super(message);
46564
+ this.name = "FileLockedError";
46565
+ }
46566
+ };
46567
+ async function withFileLock(lockPath, fn, options = {}) {
46568
+ await mkdir(dirname(lockPath), { recursive: true, mode: 448 });
46569
+ const deadline = Date.now() + (options.timeoutMs ?? DEFAULT_LOCK_TIMEOUT_MS);
46570
+ const ownerRecord = {
46571
+ pid: process.pid,
46572
+ token: randomBytes2(16).toString("hex")
46573
+ };
46574
+ const owner = JSON.stringify(ownerRecord);
46575
+ const releasedOwner = JSON.stringify({ ...ownerRecord, released: true });
46576
+ let handle;
46577
+ while (handle === void 0) {
46578
+ await atomicFileTestHooks?.beforeLockAttempt?.(lockPath);
46579
+ try {
46580
+ const candidate = await open2(lockPath, "wx", 384);
46581
+ try {
46582
+ await candidate.writeFile(owner, "utf8");
46583
+ await candidate.sync();
46584
+ await atomicFileTestHooks?.afterLockCreate?.(lockPath);
46585
+ if (await lockTakeoverInProgress(lockPath)) {
46586
+ await candidate.close();
46587
+ await removeOwnedLock(lockPath, [owner]);
46588
+ await removeAbandonedLockTombs(lockPath);
46589
+ if (Date.now() >= deadline) {
46590
+ throw new FileLockedError(options.lockedMessage);
46591
+ }
46592
+ await new Promise((resolve) => setTimeout(resolve, LOCK_RETRY_MS));
46593
+ continue;
46594
+ }
46595
+ handle = candidate;
46596
+ } catch (error61) {
46597
+ await candidate.close().catch(() => void 0);
46598
+ await removeOwnedLock(lockPath, [owner]).catch(() => void 0);
46599
+ throw error61;
46600
+ }
46601
+ } catch (error61) {
46602
+ if (!hasCode(error61, "EEXIST")) throw error61;
46603
+ await removeStaleLock(lockPath);
46604
+ if (Date.now() >= deadline) {
46605
+ throw new FileLockedError(options.lockedMessage);
46606
+ }
46607
+ await new Promise((resolve) => setTimeout(resolve, LOCK_RETRY_MS));
46608
+ }
46609
+ }
46610
+ const ownedHandle = handle;
46611
+ const heartbeat = setInterval(() => {
46612
+ const time3 = /* @__PURE__ */ new Date();
46613
+ void ownedHandle.utimes(time3, time3).catch(() => void 0);
46614
+ }, LOCK_HEARTBEAT_MS);
46615
+ heartbeat.unref();
46616
+ try {
46617
+ return await fn();
46618
+ } finally {
46619
+ clearInterval(heartbeat);
46620
+ try {
46621
+ await markLockReleased(ownedHandle, releasedOwner);
46622
+ } finally {
46623
+ try {
46624
+ await ownedHandle.close();
46625
+ } finally {
46626
+ await removeOwnedLock(lockPath, [owner, releasedOwner]);
46627
+ }
46628
+ }
46629
+ }
46630
+ }
46631
+ async function removeStaleLock(path) {
46632
+ try {
46633
+ const owner = await readFile(path, "utf8");
46634
+ if (lockOwnerReleased(owner)) {
46635
+ await removeOwnedLock(path, [owner]);
46636
+ return;
46637
+ }
46638
+ const metadata = await stat(path);
46639
+ if (Date.now() - metadata.mtimeMs <= LOCK_STALE_AFTER_MS) return;
46640
+ const pid = lockOwnerPid(owner);
46641
+ if (pid !== void 0 && processIsAlive(pid)) return;
46642
+ await atomicFileTestHooks?.beforeStaleLockBreak?.(path);
46643
+ await removeOwnedLock(path, [owner]);
46644
+ } catch (error61) {
46645
+ if (!hasCode(error61, "ENOENT")) throw error61;
46646
+ }
46647
+ }
46648
+ async function removeOwnedLock(path, owners) {
46649
+ const tombPath = `${path}.stale.${randomBytes2(16).toString("hex")}`;
46650
+ try {
46651
+ await rename(path, tombPath);
46652
+ } catch (error61) {
46653
+ if (hasCode(error61, "ENOENT")) {
46654
+ await removeOwnedTombs(path, owners);
46655
+ const currentOwner = await readFile(path, "utf8").catch(() => void 0);
46656
+ if (currentOwner !== void 0 && owners.includes(currentOwner)) {
46657
+ await removeOwnedLock(path, owners);
46658
+ }
46659
+ return;
46660
+ }
46661
+ throw error61;
46662
+ }
46663
+ let pendingError;
46664
+ try {
46665
+ const renamedOwner = await readFile(tombPath, "utf8");
46666
+ if (!owners.includes(renamedOwner) && !lockOwnerReleased(renamedOwner)) {
46667
+ await atomicFileTestHooks?.afterMismatchedLockRename?.(path);
46668
+ const currentOwner = await readFile(tombPath, "utf8");
46669
+ if (!lockOwnerReleased(currentOwner)) await restoreRenamedLock(path, tombPath);
46670
+ await atomicFileTestHooks?.afterMismatchedLockRestore?.(path);
46671
+ }
46672
+ } catch (error61) {
46673
+ pendingError = error61;
46674
+ }
46675
+ try {
46676
+ await unlink(tombPath);
46677
+ } catch (error61) {
46678
+ if (!hasCode(error61, "ENOENT") && pendingError === void 0) {
46679
+ pendingError = error61;
46680
+ }
46681
+ }
46682
+ if (pendingError !== void 0) throw pendingError;
46683
+ }
46684
+ async function lockTakeoverInProgress(path) {
46685
+ const prefix = `${basename(path)}.stale.`;
46686
+ return (await readdir(dirname(path))).some((entry) => entry.startsWith(prefix));
46687
+ }
46688
+ async function removeAbandonedLockTombs(path) {
46689
+ const directory = dirname(path);
46690
+ const prefix = `${basename(path)}.stale.`;
46691
+ for (const entry of await readdir(directory)) {
46692
+ if (!entry.startsWith(prefix)) continue;
46693
+ const tombPath = join2(directory, entry);
46694
+ const [metadata, owner] = await Promise.all([
46695
+ stat(tombPath).catch(() => void 0),
46696
+ readFile(tombPath, "utf8").catch(() => void 0)
46697
+ ]);
46698
+ if (metadata === void 0 || owner === void 0) continue;
46699
+ const pid = lockOwnerPid(owner);
46700
+ const abandoned = lockOwnerReleased(owner) || Date.now() - metadata.mtimeMs > LOCK_STALE_AFTER_MS && (pid === void 0 || !processIsAlive(pid));
46701
+ if (!abandoned) continue;
46702
+ await unlink(tombPath).catch((error61) => {
46703
+ if (!hasCode(error61, "ENOENT")) throw error61;
46704
+ });
46705
+ }
46706
+ }
46707
+ async function removeOwnedTombs(path, owners) {
46708
+ const directory = dirname(path);
46709
+ const prefix = `${basename(path)}.stale.`;
46710
+ for (const entry of await readdir(directory)) {
46711
+ if (!entry.startsWith(prefix)) continue;
46712
+ const tombPath = join2(directory, entry);
46713
+ const owner = await readFile(tombPath, "utf8").catch(() => void 0);
46714
+ if (owner === void 0 || !owners.includes(owner)) continue;
46715
+ await unlink(tombPath).catch((error61) => {
46716
+ if (!hasCode(error61, "ENOENT")) throw error61;
46717
+ });
46718
+ }
46719
+ }
46720
+ async function restoreRenamedLock(path, tombPath) {
46721
+ while (true) {
46722
+ try {
46723
+ await link(tombPath, path);
46724
+ return;
46725
+ } catch (error61) {
46726
+ if (hasCode(error61, "ENOENT")) return;
46727
+ if (!hasCode(error61, "EEXIST")) throw error61;
46728
+ await new Promise((resolve) => setTimeout(resolve, LOCK_RETRY_MS));
46729
+ }
46730
+ }
46731
+ }
46732
+ async function markLockReleased(handle, owner) {
46733
+ const contents = Buffer.from(owner, "utf8");
46734
+ await handle.write(contents, 0, contents.length, 0);
46735
+ await handle.truncate(contents.length);
46736
+ await handle.sync();
46737
+ }
46738
+ function lockOwnerPid(owner) {
46739
+ try {
46740
+ const value = JSON.parse(owner);
46741
+ if (typeof value !== "object" || value === null) return void 0;
46742
+ const pid = Reflect.get(value, "pid");
46743
+ return typeof pid === "number" && Number.isSafeInteger(pid) && pid > 0 ? pid : void 0;
46744
+ } catch {
46745
+ return void 0;
46746
+ }
46747
+ }
46748
+ function lockOwnerReleased(owner) {
46749
+ try {
46750
+ const value = JSON.parse(owner);
46751
+ return typeof value === "object" && value !== null && Reflect.get(value, "released") === true;
46752
+ } catch {
46753
+ return false;
46754
+ }
46755
+ }
46756
+ function processIsAlive(pid) {
46757
+ if (atomicFileTestHooks?.isProcessAlive !== void 0) {
46758
+ return atomicFileTestHooks.isProcessAlive(pid);
46759
+ }
46760
+ try {
46761
+ process.kill(pid, 0);
46762
+ return true;
46763
+ } catch (error61) {
46764
+ return hasCode(error61, "EPERM");
46765
+ }
46766
+ }
46767
+ function hasCode(error61, code) {
46768
+ return error61 instanceof Error && "code" in error61 && error61.code === code;
46769
+ }
46770
+
46552
46771
  // src/x402-networks.ts
46553
46772
  var BASE_MAINNET_CAIP2 = "eip155:8453";
46554
46773
  var ARC_MAINNET_CAIP2 = "eip155:5042";
@@ -46592,6 +46811,205 @@ var BROWSER_ENABLED_X402_NETWORK_CONFIG = Object.freeze({
46592
46811
  [ARC_TESTNET_CAIP2]: browserNetwork(arcTestnetUsdc)
46593
46812
  });
46594
46813
 
46814
+ // src/net-guard.ts
46815
+ import { lookup as dnsLookup } from "node:dns/promises";
46816
+ import { request as httpRequest } from "node:http";
46817
+ import { request as httpsRequest } from "node:https";
46818
+ import { isIP } from "node:net";
46819
+ import { Readable } from "node:stream";
46820
+ var defaultLookup = async (hostname3) => {
46821
+ const addresses = await dnsLookup(hostname3, { all: true });
46822
+ return addresses.map(({ address }) => address);
46823
+ };
46824
+ async function assertPublicUrl(url2, options) {
46825
+ if (url2.username || url2.password) {
46826
+ throw guardError(url2);
46827
+ }
46828
+ if (url2.protocol !== "http:" && url2.protocol !== "https:") {
46829
+ throw guardError(url2);
46830
+ }
46831
+ if (options.allowPrivateNetwork) {
46832
+ return;
46833
+ }
46834
+ const hostname3 = normalizeHostname(url2.hostname);
46835
+ const lowercaseHostname = hostname3.toLowerCase();
46836
+ if (!lowercaseHostname || lowercaseHostname === "localhost" || lowercaseHostname.endsWith(".localhost") || lowercaseHostname.endsWith(".local") || lowercaseHostname.endsWith(".internal")) {
46837
+ throw guardError(url2);
46838
+ }
46839
+ if (isIP(hostname3)) {
46840
+ if (isNonPublicAddress(hostname3)) {
46841
+ throw guardError(url2);
46842
+ }
46843
+ return;
46844
+ }
46845
+ let addresses;
46846
+ try {
46847
+ addresses = await withTimeout(
46848
+ (options.lookup ?? defaultLookup)(hostname3),
46849
+ options.timeoutMs ?? 5e3
46850
+ );
46851
+ } catch {
46852
+ throw guardError(url2);
46853
+ }
46854
+ if (addresses.length === 0 || addresses.some(isNonPublicAddress)) {
46855
+ throw guardError(url2);
46856
+ }
46857
+ }
46858
+ function createPublicFetch(options) {
46859
+ const lookupFn = options.lookup ?? defaultLookup;
46860
+ return (async (input2, init) => {
46861
+ const url2 = new URL(input2 instanceof Request ? input2.url : input2);
46862
+ await assertPublicUrl(url2, {
46863
+ allowPrivateNetwork: options.allowPrivateNetwork,
46864
+ lookup: lookupFn,
46865
+ timeoutMs: 5e3
46866
+ });
46867
+ const normalized = new Request(input2, init);
46868
+ const bytes = normalized.body ? Buffer.from(await normalized.arrayBuffer()) : null;
46869
+ return await new Promise((resolve, reject) => {
46870
+ const requestOptions = {
46871
+ method: normalized.method,
46872
+ headers: Object.fromEntries(normalized.headers),
46873
+ signal: normalized.signal,
46874
+ ...options.allowPrivateNetwork ? {} : { lookup: pinnedLookup(lookupFn) }
46875
+ };
46876
+ const onResponse = (incoming) => {
46877
+ try {
46878
+ const headers = new Headers();
46879
+ for (const [name, value] of Object.entries(incoming.headers)) {
46880
+ if (Array.isArray(value)) {
46881
+ for (const item of value) headers.append(name, item);
46882
+ } else if (value !== void 0) {
46883
+ headers.set(name, value);
46884
+ }
46885
+ }
46886
+ const status = incoming.statusCode ?? 500;
46887
+ const bodyForbidden = normalized.method === "HEAD" || [204, 205, 304].includes(status);
46888
+ if (bodyForbidden) incoming.resume();
46889
+ const response = new Response(
46890
+ bodyForbidden ? null : Readable.toWeb(incoming),
46891
+ { status, statusText: incoming.statusMessage, headers }
46892
+ );
46893
+ Object.defineProperty(response, "url", { value: url2.toString() });
46894
+ resolve(response);
46895
+ } catch (error61) {
46896
+ incoming.destroy();
46897
+ reject(error61);
46898
+ }
46899
+ };
46900
+ const request = url2.protocol === "https:" ? httpsRequest(url2, requestOptions, onResponse) : httpRequest(url2, requestOptions, onResponse);
46901
+ request.on("error", reject);
46902
+ if (bytes) request.write(bytes);
46903
+ request.end();
46904
+ });
46905
+ });
46906
+ }
46907
+ function pinnedLookup(lookup) {
46908
+ return (hostname3, dnsOptions, callback) => {
46909
+ void lookup(normalizeHostname(hostname3)).then((addresses) => {
46910
+ if (addresses.length === 0 || addresses.some(isNonPublicAddress)) {
46911
+ callback(guardError(new URL(`https://${hostname3}`)), "");
46912
+ return;
46913
+ }
46914
+ const address = addresses[0];
46915
+ const family = isIP(address);
46916
+ callback(
46917
+ null,
46918
+ dnsOptions.all ? [{ address, family }] : address,
46919
+ dnsOptions.all ? void 0 : family
46920
+ );
46921
+ }).catch(
46922
+ (error61) => callback(error61 instanceof Error ? error61 : new Error("DNS lookup failed."), "")
46923
+ );
46924
+ };
46925
+ }
46926
+ async function withTimeout(operation, timeoutMs) {
46927
+ let timeout;
46928
+ try {
46929
+ return await Promise.race([
46930
+ operation,
46931
+ new Promise((_resolve, reject) => {
46932
+ timeout = setTimeout(() => reject(new Error("DNS lookup timed out.")), timeoutMs);
46933
+ })
46934
+ ]);
46935
+ } finally {
46936
+ if (timeout) clearTimeout(timeout);
46937
+ }
46938
+ }
46939
+ function normalizeHostname(hostname3) {
46940
+ const unbracketed = hostname3.startsWith("[") && hostname3.endsWith("]") ? hostname3.slice(1, -1) : hostname3;
46941
+ return unbracketed.endsWith(".") ? unbracketed.slice(0, -1) : unbracketed;
46942
+ }
46943
+ function isNonPublicAddress(address) {
46944
+ const normalized = normalizeHostname(address);
46945
+ const version3 = isIP(normalized);
46946
+ if (version3 === 4) {
46947
+ return isNonPublicIpv4(parseIpv4(normalized));
46948
+ }
46949
+ if (version3 === 6) {
46950
+ return isNonPublicIpv6(parseIpv6(normalized));
46951
+ }
46952
+ return true;
46953
+ }
46954
+ function parseIpv4(address) {
46955
+ const octets = address.split(".").map(Number);
46956
+ return [octets[0], octets[1], octets[2], octets[3]];
46957
+ }
46958
+ function isNonPublicIpv4([first, second, third]) {
46959
+ return first === 0 || first === 10 || first === 100 && second >= 64 && second <= 127 || first === 127 || first === 169 && second === 254 || first === 172 && second >= 16 && second <= 31 || first === 192 && second === 0 && (third === 0 || third === 2) || first === 192 && second === 168 || first === 198 && second >= 18 && second <= 19 || first === 198 && second === 51 && third === 100 || first === 203 && second === 0 && third === 113 || first >= 224;
46960
+ }
46961
+ function parseIpv6(address) {
46962
+ let normalized = address.toLowerCase();
46963
+ const lastColon = normalized.lastIndexOf(":");
46964
+ const dottedTail = normalized.slice(lastColon + 1);
46965
+ if (dottedTail.includes(".")) {
46966
+ const [first, second, third, fourth] = parseIpv4(dottedTail);
46967
+ normalized = `${normalized.slice(0, lastColon + 1)}${(first << 8 | second).toString(16)}:${(third << 8 | fourth).toString(16)}`;
46968
+ }
46969
+ const halves = normalized.split("::");
46970
+ const left = halves[0] ? halves[0].split(":").map(parseHex) : [];
46971
+ const right = halves[1] ? halves[1].split(":").map(parseHex) : [];
46972
+ const omittedCount = 8 - left.length - right.length;
46973
+ return halves.length === 2 ? [...left, ...Array.from({ length: omittedCount }, () => 0), ...right] : left;
46974
+ }
46975
+ function parseHex(value) {
46976
+ return Number.parseInt(value, 16);
46977
+ }
46978
+ function isNonPublicIpv6(hextets) {
46979
+ if (hextets.slice(0, 5).every((hextet) => hextet === 0) && hextets[5] === 65535) {
46980
+ const embeddedIpv4 = [
46981
+ hextets[6] >> 8,
46982
+ hextets[6] & 255,
46983
+ hextets[7] >> 8,
46984
+ hextets[7] & 255
46985
+ ];
46986
+ return isNonPublicIpv4(embeddedIpv4);
46987
+ }
46988
+ if (hextets.slice(0, 6).every((hextet) => hextet === 0)) {
46989
+ const compatibleIpv4 = [
46990
+ hextets[6] >> 8,
46991
+ hextets[6] & 255,
46992
+ hextets[7] >> 8,
46993
+ hextets[7] & 255
46994
+ ];
46995
+ if (isNonPublicIpv4(compatibleIpv4)) return true;
46996
+ }
46997
+ const isUnspecified = hextets.every((hextet) => hextet === 0);
46998
+ const isLoopback = hextets.slice(0, 7).every((hextet) => hextet === 0) && hextets[7] === 1;
46999
+ const first = hextets[0];
47000
+ const isUniqueLocal = first >= 64512 && first <= 65023;
47001
+ const isLinkLocal = first >= 65152 && first <= 65215;
47002
+ const isSiteLocal = first >= 65216 && first <= 65279;
47003
+ const isMulticast = first >= 65280;
47004
+ const isDocumentation = first === 8193 && hextets[1] === 3512;
47005
+ return isUnspecified || isLoopback || isUniqueLocal || isLinkLocal || isSiteLocal || isMulticast || isDocumentation;
47006
+ }
47007
+ function guardError(url2) {
47008
+ return new Error(
47009
+ `URL hostname ${JSON.stringify(url2.hostname)} is not allowed; private or internal destinations require allowPrivateNetwork true in the vAPI config.`
47010
+ );
47011
+ }
47012
+
46595
47013
  // src/networks.ts
46596
47014
  var NETWORKS = {
46597
47015
  [BASE_MAINNET_CAIP2]: {
@@ -46601,7 +47019,8 @@ var NETWORKS = {
46601
47019
  usdc: CANONICAL_X402_USDC_NETWORKS[BASE_MAINNET_CAIP2].usdc,
46602
47020
  gasToken: "ETH",
46603
47021
  rpcEnv: "BASE_RPC_URL",
46604
- publicRpcUrl: "https://mainnet.base.org"
47022
+ publicRpcUrl: "https://mainnet.base.org",
47023
+ explorerUrl: "https://basescan.org"
46605
47024
  },
46606
47025
  [ARC_MAINNET_CAIP2]: {
46607
47026
  family: "evm",
@@ -46666,6 +47085,10 @@ function isSupportedPaymentNetwork(network) {
46666
47085
  var DEFAULT_REGISTRY_URL = "https://api.vapinetwork.ai";
46667
47086
  var DEFAULT_DISCOVERY_URL = `${DEFAULT_REGISTRY_URL}/api/call/services`;
46668
47087
  var DEFAULT_MARKETPLACE_DISCOVERY_URL = `${DEFAULT_REGISTRY_URL}/api/call/discovery`;
47088
+ var DEFAULT_SPEND_CAPS = {
47089
+ perCallAtomic: "100000",
47090
+ perDayAtomic: "1000000"
47091
+ };
46669
47092
  var atomicString = external_exports.string().regex(/^\d+$/, "Expected non-negative atomic units.");
46670
47093
  var spendCapsSchema = external_exports.object({
46671
47094
  perCallAtomic: atomicString,
@@ -46696,7 +47119,8 @@ var configSchema = external_exports.object({
46696
47119
  }).optional()
46697
47120
  })
46698
47121
  ),
46699
- spendCaps: spendCapsSchema
47122
+ spendCaps: spendCapsSchema,
47123
+ device: external_exports.string().regex(/^[a-z0-9][a-z0-9-]{0,31}$/).optional()
46700
47124
  }).superRefine((config2, context) => {
46701
47125
  for (const [network, configured] of Object.entries(config2.networks)) {
46702
47126
  if (isSolanaNetwork(network)) {
@@ -46727,22 +47151,22 @@ var configSchema = external_exports.object({
46727
47151
  }
46728
47152
  }
46729
47153
  });
46730
- function getVapiPaths(directory = process.env.VAPI_HOME?.trim() || join2(homedir(), ".vapi")) {
47154
+ function getVapiPaths(directory = process.env.VAPI_HOME?.trim() || join3(homedir(), ".vapi")) {
46731
47155
  return {
46732
47156
  directory,
46733
- agentsDir: join2(directory, "agents"),
46734
- config: join2(directory, "config.json"),
46735
- keystore: join2(directory, "keystore.json"),
46736
- receipts: join2(directory, "receipts.jsonl"),
46737
- searches: join2(directory, "searches.jsonl"),
46738
- ledger: join2(directory, "spend-ledger.json")
47157
+ agentsDir: join3(directory, "agents"),
47158
+ config: join3(directory, "config.json"),
47159
+ keystore: join3(directory, "keystore.json"),
47160
+ receipts: join3(directory, "receipts.jsonl"),
47161
+ searches: join3(directory, "searches.jsonl"),
47162
+ ledger: join3(directory, "spend-ledger.json")
46739
47163
  };
46740
47164
  }
46741
47165
  async function migrateLegacyVapiHome(options = {}) {
46742
47166
  const target = getVapiPaths(options.targetDirectory);
46743
- const legacyDirectory = options.legacyDirectory ?? join2(homedir(), ".vapi", "agent-cash");
47167
+ const legacyDirectory = options.legacyDirectory ?? join3(homedir(), ".vapi", "agent-cash");
46744
47168
  if (legacyDirectory === target.directory || !await pathExists(legacyDirectory)) return [];
46745
- await mkdir(target.directory, { recursive: true, mode: 448 });
47169
+ await mkdir2(target.directory, { recursive: true, mode: 448 });
46746
47170
  const copied = [];
46747
47171
  for (const filename of [
46748
47172
  "config.json",
@@ -46751,10 +47175,17 @@ async function migrateLegacyVapiHome(options = {}) {
46751
47175
  "searches.jsonl",
46752
47176
  "spend-ledger.json"
46753
47177
  ]) {
46754
- const source = join2(legacyDirectory, filename);
46755
- const destination = join2(target.directory, filename);
47178
+ const source = join3(legacyDirectory, filename);
47179
+ const destination = join3(target.directory, filename);
46756
47180
  try {
46757
- await copyFile(source, destination, constants.COPYFILE_EXCL);
47181
+ const copy = async () => await copyFile(source, destination, constants.COPYFILE_EXCL);
47182
+ if (filename === "receipts.jsonl") {
47183
+ await withFileLock(`${destination}.lock`, copy, {
47184
+ lockedMessage: "The receipt journal is busy. Wait for the other vapi command to finish, then retry."
47185
+ });
47186
+ } else {
47187
+ await copy();
47188
+ }
46758
47189
  copied.push(destination);
46759
47190
  } catch (error61) {
46760
47191
  if (isMissingFile(error61) || isAlreadyExists(error61)) continue;
@@ -46770,7 +47201,7 @@ async function migrateLegacyVapiHome(options = {}) {
46770
47201
  }
46771
47202
  async function pathExists(path) {
46772
47203
  try {
46773
- await stat(path);
47204
+ await stat2(path);
46774
47205
  return true;
46775
47206
  } catch (error61) {
46776
47207
  if (isMissingFile(error61)) return false;
@@ -46937,7 +47368,34 @@ var KeystoreError = class extends Error {
46937
47368
  this.name = "KeystoreError";
46938
47369
  }
46939
47370
  };
46940
- async function deriveKey(passphrase, salt) {
47371
+ function encryptWithKey(key, plaintext) {
47372
+ if (key.byteLength !== KEY_LENGTH) {
47373
+ throw new Error(`Expected a ${KEY_LENGTH}-byte AES-256-GCM key.`);
47374
+ }
47375
+ const nonce = randomBytes3(12);
47376
+ const cipher = createCipheriv("aes-256-gcm", key, nonce, {
47377
+ authTagLength: AUTH_TAG_LENGTH
47378
+ });
47379
+ const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
47380
+ return { nonce, ciphertext, tag: cipher.getAuthTag() };
47381
+ }
47382
+ function decryptWithKey(key, sealed) {
47383
+ if (key.byteLength !== KEY_LENGTH) {
47384
+ throw new Error(`Expected a ${KEY_LENGTH}-byte AES-256-GCM key.`);
47385
+ }
47386
+ if (sealed.nonce.byteLength !== 12) {
47387
+ throw new Error("Expected a 12-byte AES-GCM nonce.");
47388
+ }
47389
+ if (sealed.tag.byteLength !== AUTH_TAG_LENGTH) {
47390
+ throw new Error(`Expected a ${AUTH_TAG_LENGTH}-byte AES-GCM authentication tag.`);
47391
+ }
47392
+ const decipher = createDecipheriv("aes-256-gcm", key, sealed.nonce, {
47393
+ authTagLength: AUTH_TAG_LENGTH
47394
+ });
47395
+ decipher.setAuthTag(sealed.tag);
47396
+ return Buffer.concat([decipher.update(sealed.ciphertext), decipher.final()]);
47397
+ }
47398
+ async function deriveScryptKey(passphrase, salt) {
46941
47399
  return await new Promise((resolve, reject) => {
46942
47400
  scrypt(
46943
47401
  passphrase,
@@ -46998,17 +47456,15 @@ async function sealPlaintext(plaintext, passphrase) {
46998
47456
  if (passphrase.length === 0) {
46999
47457
  throw new KeystoreError("Keystore passphrase cannot be empty.");
47000
47458
  }
47001
- const salt = randomBytes2(32);
47002
- const iv = randomBytes2(12);
47003
- const key = await deriveKey(passphrase, salt);
47459
+ const salt = randomBytes3(32);
47460
+ const key = await deriveScryptKey(passphrase, salt);
47004
47461
  try {
47005
- const cipher = createCipheriv("aes-256-gcm", key, iv, { authTagLength: AUTH_TAG_LENGTH });
47006
- const ciphertext = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
47462
+ const sealed = encryptWithKey(key, Buffer.from(plaintext, "utf8"));
47007
47463
  return {
47008
47464
  cipher: "aes-256-gcm",
47009
- ciphertext: ciphertext.toString("base64"),
47010
- iv: iv.toString("base64"),
47011
- authTag: cipher.getAuthTag().toString("base64"),
47465
+ ciphertext: sealed.ciphertext.toString("base64"),
47466
+ iv: sealed.nonce.toString("base64"),
47467
+ authTag: sealed.tag.toString("base64"),
47012
47468
  kdf: "scrypt",
47013
47469
  salt: salt.toString("base64"),
47014
47470
  kdfParams: {
@@ -47036,19 +47492,14 @@ async function decryptSecret(keystore, passphrase) {
47036
47492
  const iv = Buffer.from(parsed.crypto.iv, "base64");
47037
47493
  const authTag = Buffer.from(parsed.crypto.authTag, "base64");
47038
47494
  const ciphertext = Buffer.from(parsed.crypto.ciphertext, "base64");
47039
- const key = await deriveKey(passphrase, salt);
47495
+ const key = await deriveScryptKey(passphrase, salt);
47040
47496
  let unlocked;
47041
47497
  try {
47042
- if (authTag.length !== AUTH_TAG_LENGTH) {
47043
- throw new Error(`Expected a ${AUTH_TAG_LENGTH}-byte AES-GCM authentication tag.`);
47044
- }
47045
- const decipher = createDecipheriv("aes-256-gcm", key, iv, {
47046
- authTagLength: AUTH_TAG_LENGTH
47047
- });
47048
- decipher.setAuthTag(authTag);
47049
- const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString(
47050
- "utf8"
47051
- );
47498
+ const plaintext = decryptWithKey(key, {
47499
+ nonce: iv,
47500
+ ciphertext,
47501
+ tag: authTag
47502
+ }).toString("utf8");
47052
47503
  unlocked = parsed.version === 3 ? keysFromEntropy(parsed, plaintext) : {
47053
47504
  keys: parsed.version === 1 ? { evm: `0x${plaintext}` } : encryptedKeysSchema.parse(JSON.parse(plaintext)).keys
47054
47505
  };
@@ -47123,7 +47574,7 @@ async function refuseExistingKeystore(path) {
47123
47574
  await migrateLegacyVapiHome({ targetDirectory: getVapiPaths().directory });
47124
47575
  }
47125
47576
  try {
47126
- await stat2(path);
47577
+ await stat3(path);
47127
47578
  throw new KeystoreError(
47128
47579
  `Keystore already exists at ${path}. Refusing to replace the local payment key.`
47129
47580
  );
@@ -47133,10 +47584,19 @@ async function refuseExistingKeystore(path) {
47133
47584
  }
47134
47585
  }
47135
47586
  }
47587
+ function validatePrivateKey(value) {
47588
+ const normalized = value.trim().toLowerCase();
47589
+ if (!/^0x[0-9a-f]{64}$/u.test(normalized)) {
47590
+ throw new KeystoreError(
47591
+ "A private key is 0x followed by 64 hexadecimal characters. Check for a missing character."
47592
+ );
47593
+ }
47594
+ return normalized;
47595
+ }
47136
47596
  async function readStoredKeystore(path) {
47137
47597
  let raw;
47138
47598
  try {
47139
- raw = await readFile2(path, "utf8");
47599
+ raw = await readFile3(path, "utf8");
47140
47600
  } catch (error61) {
47141
47601
  if (isMissingFile(error61)) {
47142
47602
  throw new KeystoreError(`No vAPI keystore found at ${path}. Run vapi init.`);
@@ -47209,8 +47669,8 @@ async function createSolanaSigner(seed) {
47209
47669
  }
47210
47670
  }
47211
47671
  async function writeKeystore(path, keystore, exclusive) {
47212
- await mkdir2(dirname2(path), { recursive: true, mode: 448 });
47213
- const temporaryPath = `${path}.${process.pid}.${randomBytes2(6).toString("hex")}.tmp`;
47672
+ await mkdir3(dirname3(path), { recursive: true, mode: 448 });
47673
+ const temporaryPath = `${path}.${process.pid}.${randomBytes3(6).toString("hex")}.tmp`;
47214
47674
  await writeFile2(temporaryPath, `${JSON.stringify(keystore, null, 2)}
47215
47675
  `, {
47216
47676
  encoding: "utf8",
@@ -47219,7 +47679,7 @@ async function writeKeystore(path, keystore, exclusive) {
47219
47679
  });
47220
47680
  if (exclusive) {
47221
47681
  try {
47222
- await stat2(path);
47682
+ await stat3(path);
47223
47683
  throw new KeystoreError(
47224
47684
  `Keystore already exists at ${path}. Refusing to replace the local payment key.`
47225
47685
  );
@@ -47227,12 +47687,935 @@ async function writeKeystore(path, keystore, exclusive) {
47227
47687
  if (!isMissingFile(error61)) throw error61;
47228
47688
  }
47229
47689
  }
47230
- await rename2(temporaryPath, path);
47690
+ await rename3(temporaryPath, path);
47691
+ }
47692
+
47693
+ // src/vault.ts
47694
+ import { randomBytes as randomBytes4 } from "node:crypto";
47695
+ import { mkdir as mkdir4, open as open3, readFile as readFile4, rename as rename4, rm, stat as stat4, unlink as unlink2, writeFile as writeFile3 } from "node:fs/promises";
47696
+ import { dirname as dirname4 } from "node:path";
47697
+
47698
+ // src/wallet-name.ts
47699
+ var WALLET_NAME_PATTERN = /^[a-z0-9][a-z0-9-]{0,31}$/;
47700
+ var walletNameSchema = external_exports.string().regex(
47701
+ WALLET_NAME_PATTERN,
47702
+ "A wallet name is 1 to 32 characters of lowercase letters, digits and dashes, starting with a letter or digit."
47703
+ );
47704
+ function isWalletName(value) {
47705
+ return WALLET_NAME_PATTERN.test(value);
47706
+ }
47707
+
47708
+ // src/vault.ts
47709
+ var VaultError = class extends Error {
47710
+ code;
47711
+ constructor(code, message, options) {
47712
+ super(message, options);
47713
+ this.name = "VaultError";
47714
+ this.code = code;
47715
+ }
47716
+ };
47717
+ var SEALED_FIELD_PATTERN = /^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/u;
47718
+ var WRAPPED_KEY_PATTERN = /^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/u;
47719
+ var ADDRESS_PATTERN = /^0x[0-9a-fA-F]{40}$/u;
47720
+ var VAULT_LOCK_TIMEOUT_MS = 2e3;
47721
+ var STALE_VAULT_LOCK_MS = 3e4;
47722
+ var sealedFieldSchema = external_exports.string().regex(SEALED_FIELD_PATTERN);
47723
+ var addressSchema = external_exports.string().regex(ADDRESS_PATTERN);
47724
+ var accountFields = {
47725
+ name: walletNameSchema,
47726
+ address: addressSchema,
47727
+ createdAt: external_exports.string()
47728
+ };
47729
+ var storedAccountSchema = external_exports.discriminatedUnion("kind", [
47730
+ external_exports.object({
47731
+ ...accountFields,
47732
+ kind: external_exports.literal("derived"),
47733
+ index: external_exports.number().int().nonnegative().refine(Number.isSafeInteger, "Derived account index must be a safe integer.")
47734
+ }).strict(),
47735
+ external_exports.object({
47736
+ ...accountFields,
47737
+ kind: external_exports.literal("imported"),
47738
+ key: sealedFieldSchema
47739
+ }).strict()
47740
+ ]);
47741
+ var vaultFileSchema = external_exports.object({
47742
+ version: external_exports.literal(2),
47743
+ kdf: external_exports.literal("device-key"),
47744
+ cipher: external_exports.literal("aes-256-gcm"),
47745
+ phrase: sealedFieldSchema,
47746
+ accounts: external_exports.array(storedAccountSchema),
47747
+ nextDerivedIndex: external_exports.number().int().nonnegative().refine(Number.isSafeInteger, "Next derived account index must be a safe integer.").optional(),
47748
+ protected: external_exports.boolean(),
47749
+ wrappedKey: external_exports.string().regex(WRAPPED_KEY_PATTERN).optional()
47750
+ }).strict();
47751
+ var DeviceVault = class {
47752
+ file;
47753
+ path;
47754
+ key;
47755
+ balanceOf;
47756
+ now;
47757
+ constructor(options, file2) {
47758
+ this.path = options.path;
47759
+ this.key = options.key;
47760
+ this.balanceOf = options.balanceOf;
47761
+ this.now = options.now ?? (() => /* @__PURE__ */ new Date());
47762
+ this.file = file2;
47763
+ }
47764
+ accounts() {
47765
+ return this.file.accounts.map(copyAccount);
47766
+ }
47767
+ async deriveAccount(name) {
47768
+ try {
47769
+ return await this.update(async (file2) => {
47770
+ assertAvailableName(file2, name);
47771
+ const index = nextDerivedIndex(file2);
47772
+ const plaintext = decryptField(this.key, file2.phrase);
47773
+ let seed;
47774
+ let address;
47775
+ try {
47776
+ seed = phraseToSeed(plaintext.toString("utf8"));
47777
+ address = privateKeyToAccount(
47778
+ deriveEvmPrivateKey(seed, `m/44'/60'/0'/0/${index}`)
47779
+ ).address;
47780
+ } finally {
47781
+ seed?.fill(0);
47782
+ plaintext.fill(0);
47783
+ }
47784
+ const account = {
47785
+ name,
47786
+ kind: "derived",
47787
+ index,
47788
+ address,
47789
+ createdAt: this.now().toISOString()
47790
+ };
47791
+ file2.accounts.push(account);
47792
+ file2.nextDerivedIndex = index + 1;
47793
+ return copyAccount(account);
47794
+ });
47795
+ } catch (error61) {
47796
+ throw asVaultError(error61, "corrupt", "Unable to derive a vault account.");
47797
+ }
47798
+ }
47799
+ async importAccount(name, privateKey) {
47800
+ try {
47801
+ return await this.update(async (file2) => {
47802
+ assertAvailableName(file2, name);
47803
+ let validated;
47804
+ let address;
47805
+ try {
47806
+ validated = validatePrivateKey(privateKey);
47807
+ address = privateKeyToAccount(validated).address;
47808
+ } catch (error61) {
47809
+ throw new VaultError("bad_key", "The imported private key is not valid.", {
47810
+ cause: error61
47811
+ });
47812
+ }
47813
+ const plaintext = Buffer.from(validated.slice(2), "hex");
47814
+ let account;
47815
+ try {
47816
+ account = {
47817
+ name,
47818
+ kind: "imported",
47819
+ address,
47820
+ key: sealField(this.key, plaintext),
47821
+ createdAt: this.now().toISOString()
47822
+ };
47823
+ } finally {
47824
+ plaintext.fill(0);
47825
+ }
47826
+ file2.accounts.push(account);
47827
+ return copyAccount(account);
47828
+ });
47829
+ } catch (error61) {
47830
+ throw asVaultError(error61, "corrupt", "Unable to import a vault account.");
47831
+ }
47832
+ }
47833
+ async removeAccount(name) {
47834
+ try {
47835
+ await this.update(async (file2) => {
47836
+ const account = file2.accounts.find((candidate) => candidate.name === name);
47837
+ if (!account) {
47838
+ throw new VaultError("not_found", "No vault account has that name.");
47839
+ }
47840
+ if (!this.balanceOf) {
47841
+ throw new VaultError(
47842
+ "has_balance",
47843
+ "The account balance could not be verified, so the key was not removed."
47844
+ );
47845
+ }
47846
+ if (await this.balanceOf(account.address) > 0n) {
47847
+ throw new VaultError(
47848
+ "has_balance",
47849
+ "This account still has a balance. Run vapi sweep before removing it."
47850
+ );
47851
+ }
47852
+ file2.nextDerivedIndex = nextDerivedIndex(file2);
47853
+ const currentIndex = file2.accounts.findIndex((candidate) => candidate.name === name);
47854
+ if (currentIndex === -1) {
47855
+ throw new VaultError("not_found", "No vault account has that name.");
47856
+ }
47857
+ file2.accounts.splice(currentIndex, 1);
47858
+ });
47859
+ } catch (error61) {
47860
+ throw asVaultError(error61, "corrupt", "Unable to remove the vault account.");
47861
+ }
47862
+ }
47863
+ async rename(from, to) {
47864
+ try {
47865
+ await this.update(async (file2) => {
47866
+ const account = file2.accounts.find((candidate) => candidate.name === from);
47867
+ if (!account) {
47868
+ throw new VaultError("not_found", "No vault account has that name.");
47869
+ }
47870
+ if (from === to) return;
47871
+ assertAccountName(to);
47872
+ if (file2.accounts.some((candidate) => candidate.name === to)) {
47873
+ throw new VaultError("name_taken", "A vault account already has that name.");
47874
+ }
47875
+ account.name = to;
47876
+ });
47877
+ } catch (error61) {
47878
+ throw asVaultError(error61, "corrupt", "Unable to rename the vault account.");
47879
+ }
47880
+ }
47881
+ async signer(name) {
47882
+ try {
47883
+ const latest = await readVaultFile(this.path, this.key);
47884
+ this.file = latest;
47885
+ const stored = latest.accounts.find((account2) => account2.name === name);
47886
+ if (!stored) {
47887
+ throw new VaultError("not_found", "No vault account has that name.");
47888
+ }
47889
+ let account;
47890
+ if (stored.kind === "derived") {
47891
+ const plaintext = decryptField(this.key, latest.phrase);
47892
+ let seed;
47893
+ try {
47894
+ seed = phraseToSeed(plaintext.toString("utf8"));
47895
+ account = privateKeyToAccount(
47896
+ deriveEvmPrivateKey(seed, `m/44'/60'/0'/0/${stored.index}`)
47897
+ );
47898
+ } finally {
47899
+ seed?.fill(0);
47900
+ plaintext.fill(0);
47901
+ }
47902
+ } else {
47903
+ const plaintext = decryptField(this.key, stored.key);
47904
+ try {
47905
+ if (plaintext.byteLength !== 32) {
47906
+ throw new VaultError("corrupt", "An imported vault key has the wrong length.");
47907
+ }
47908
+ const privateKey = `0x${plaintext.toString("hex")}`;
47909
+ account = privateKeyToAccount(privateKey);
47910
+ } finally {
47911
+ plaintext.fill(0);
47912
+ }
47913
+ }
47914
+ if (account.address.toLowerCase() !== stored.address.toLowerCase()) {
47915
+ throw new VaultError("corrupt", "A vault key does not match its stored address.");
47916
+ }
47917
+ return account;
47918
+ } catch (error61) {
47919
+ throw asVaultError(error61, "corrupt", "Unable to open the vault account signer.");
47920
+ }
47921
+ }
47922
+ async phrase() {
47923
+ const latest = await readVaultFile(this.path, this.key);
47924
+ this.file = latest;
47925
+ const plaintext = decryptField(this.key, latest.phrase);
47926
+ try {
47927
+ try {
47928
+ return validateRecoveryPhrase(plaintext.toString("utf8"));
47929
+ } catch (error61) {
47930
+ throw new VaultError("corrupt", "The vault recovery phrase is not valid.", {
47931
+ cause: error61
47932
+ });
47933
+ }
47934
+ } finally {
47935
+ plaintext.fill(0);
47936
+ }
47937
+ }
47938
+ async save() {
47939
+ try {
47940
+ await this.update(async () => void 0);
47941
+ } catch (error61) {
47942
+ throw asVaultError(error61, "corrupt", "Unable to save the vault.");
47943
+ }
47944
+ }
47945
+ resealSecrets(file2) {
47946
+ const phrase = decryptField(this.key, file2.phrase);
47947
+ try {
47948
+ const accounts = file2.accounts.map((account) => {
47949
+ if (account.kind === "derived") return { ...account };
47950
+ const privateKey = decryptField(this.key, account.key);
47951
+ try {
47952
+ if (privateKey.byteLength !== 32) {
47953
+ throw new VaultError("corrupt", "An imported vault key has the wrong length.");
47954
+ }
47955
+ return { ...account, key: sealField(this.key, privateKey) };
47956
+ } finally {
47957
+ privateKey.fill(0);
47958
+ }
47959
+ });
47960
+ return {
47961
+ ...file2,
47962
+ phrase: sealField(this.key, phrase),
47963
+ accounts
47964
+ };
47965
+ } finally {
47966
+ phrase.fill(0);
47967
+ }
47968
+ }
47969
+ async update(operation) {
47970
+ return await withVaultLock(this.path, async () => {
47971
+ const latest = await readVaultFile(this.path, this.key);
47972
+ const result = await operation(latest);
47973
+ const rewritten = this.resealSecrets(latest);
47974
+ await writeVaultFile(this.path, rewritten);
47975
+ this.file = rewritten;
47976
+ return result;
47977
+ });
47978
+ }
47979
+ };
47980
+ async function openVault(args) {
47981
+ assertVaultKey(args.key);
47982
+ return new DeviceVault(args, await readVaultFile(args.path, args.key));
47983
+ }
47984
+ async function exportVaultPhrase(args) {
47985
+ return await (await openVault(args)).phrase();
47986
+ }
47987
+ async function exportVaultAccountKey(args) {
47988
+ assertVaultKey(args.key);
47989
+ try {
47990
+ const file2 = await readVaultFile(args.path, args.key);
47991
+ const account = file2.accounts.find((candidate) => candidate.name === args.name);
47992
+ if (!account) {
47993
+ throw new VaultError("not_found", "No vault account has that name.");
47994
+ }
47995
+ let privateKey;
47996
+ if (account.kind === "derived") {
47997
+ const plaintext = decryptField(args.key, file2.phrase);
47998
+ let seed;
47999
+ try {
48000
+ seed = phraseToSeed(plaintext.toString("utf8"));
48001
+ privateKey = deriveEvmPrivateKey(seed, `m/44'/60'/0'/0/${account.index}`);
48002
+ } finally {
48003
+ seed?.fill(0);
48004
+ plaintext.fill(0);
48005
+ }
48006
+ } else {
48007
+ const plaintext = decryptField(args.key, account.key);
48008
+ try {
48009
+ if (plaintext.byteLength !== 32) {
48010
+ throw new VaultError("corrupt", "An imported vault key has the wrong length.");
48011
+ }
48012
+ privateKey = `0x${plaintext.toString("hex")}`;
48013
+ } finally {
48014
+ plaintext.fill(0);
48015
+ }
48016
+ }
48017
+ if (privateKeyToAccount(privateKey).address.toLowerCase() !== account.address.toLowerCase()) {
48018
+ throw new VaultError("corrupt", "A vault key does not match its stored address.");
48019
+ }
48020
+ return privateKey;
48021
+ } catch (error61) {
48022
+ throw asVaultError(error61, "corrupt", "Unable to export the vault account key.");
48023
+ }
48024
+ }
48025
+ async function exportMemberKey(args) {
48026
+ return await exportVaultAccountKey(args);
48027
+ }
48028
+ function assertVaultKey(key) {
48029
+ if (key.byteLength !== 32) {
48030
+ throw new VaultError("bad_key", "A device vault key must contain exactly 32 bytes.");
48031
+ }
48032
+ }
48033
+ function assertAccountName(name) {
48034
+ const parsed = walletNameSchema.safeParse(name);
48035
+ if (!parsed.success) {
48036
+ throw new VaultError("name_taken", "The vault account name is not valid.", {
48037
+ cause: parsed.error
48038
+ });
48039
+ }
48040
+ }
48041
+ function assertAvailableName(file2, name) {
48042
+ assertAccountName(name);
48043
+ if (file2.accounts.some((account) => account.name === name)) {
48044
+ throw new VaultError("name_taken", "A vault account already has that name.");
48045
+ }
48046
+ }
48047
+ function nextDerivedIndex(file2) {
48048
+ let next = file2.nextDerivedIndex ?? 0;
48049
+ for (const account of file2.accounts) {
48050
+ if (account.kind === "derived") next = Math.max(next, account.index + 1);
48051
+ }
48052
+ return next;
48053
+ }
48054
+ function copyAccount(account) {
48055
+ if (account.kind === "derived") return { ...account };
48056
+ return {
48057
+ name: account.name,
48058
+ kind: account.kind,
48059
+ address: account.address,
48060
+ createdAt: account.createdAt
48061
+ };
48062
+ }
48063
+ function sealField(key, plaintext) {
48064
+ const { nonce, ciphertext, tag } = encryptWithKey(key, plaintext);
48065
+ return [nonce, ciphertext, tag].map((part) => part.toString("base64url")).join(".");
48066
+ }
48067
+ function openField(key, value) {
48068
+ const [nonce = "", ciphertext = "", tag = ""] = value.split(".");
48069
+ return decryptWithKey(key, {
48070
+ nonce: Buffer.from(nonce, "base64url"),
48071
+ ciphertext: Buffer.from(ciphertext, "base64url"),
48072
+ tag: Buffer.from(tag, "base64url")
48073
+ });
48074
+ }
48075
+ function decryptField(key, value) {
48076
+ try {
48077
+ return openField(key, value);
48078
+ } catch (error61) {
48079
+ throw new VaultError("bad_key", "Unable to decrypt the vault with this device key.", {
48080
+ cause: error61
48081
+ });
48082
+ }
48083
+ }
48084
+ function decryptPhraseForOpen(key, phrase) {
48085
+ try {
48086
+ return openField(key, phrase);
48087
+ } catch (error61) {
48088
+ throw new VaultError("bad_key", "Unable to decrypt the vault with this device key.", {
48089
+ cause: error61
48090
+ });
48091
+ }
48092
+ }
48093
+ function parseVaultFile(raw) {
48094
+ let value;
48095
+ try {
48096
+ value = JSON.parse(raw);
48097
+ } catch (error61) {
48098
+ throw new VaultError("corrupt", "The vault file is not valid JSON.", { cause: error61 });
48099
+ }
48100
+ const parsed = vaultFileSchema.safeParse(value);
48101
+ if (!parsed.success) {
48102
+ throw new VaultError("corrupt", "The vault file has an invalid structure.", {
48103
+ cause: parsed.error
48104
+ });
48105
+ }
48106
+ if (parsed.data.protected !== (parsed.data.wrappedKey !== void 0)) {
48107
+ throw new VaultError("corrupt", "The vault protection metadata is inconsistent.");
48108
+ }
48109
+ const names = /* @__PURE__ */ new Set();
48110
+ const indexes = /* @__PURE__ */ new Set();
48111
+ for (const account of parsed.data.accounts) {
48112
+ if (names.has(account.name)) {
48113
+ throw new VaultError("corrupt", "The vault file contains duplicate account names.");
48114
+ }
48115
+ names.add(account.name);
48116
+ if (account.kind === "derived") {
48117
+ if (indexes.has(account.index)) {
48118
+ throw new VaultError("corrupt", "The vault file contains duplicate derived indexes.");
48119
+ }
48120
+ indexes.add(account.index);
48121
+ }
48122
+ }
48123
+ return parsed.data;
48124
+ }
48125
+ async function readVaultFile(path, key) {
48126
+ const file2 = await readVaultFileUnlocked(path);
48127
+ const plaintext = decryptPhraseForOpen(key, file2.phrase);
48128
+ try {
48129
+ try {
48130
+ validateRecoveryPhrase(plaintext.toString("utf8"));
48131
+ } catch (error61) {
48132
+ throw new VaultError("corrupt", "The vault recovery phrase is not valid.", {
48133
+ cause: error61
48134
+ });
48135
+ }
48136
+ } finally {
48137
+ plaintext.fill(0);
48138
+ }
48139
+ return file2;
48140
+ }
48141
+ async function readVaultFileUnlocked(path) {
48142
+ let raw;
48143
+ try {
48144
+ raw = await readFile4(path, "utf8");
48145
+ } catch (error61) {
48146
+ if (isMissingFile2(error61)) {
48147
+ throw new VaultError("not_found", "No vault file was found at the requested path.", {
48148
+ cause: error61
48149
+ });
48150
+ }
48151
+ throw new VaultError("corrupt", "Unable to read the vault file.", { cause: error61 });
48152
+ }
48153
+ return parseVaultFile(raw);
48154
+ }
48155
+ async function writeVaultFile(path, file2) {
48156
+ await mkdir4(dirname4(path), { recursive: true, mode: 448 });
48157
+ const temporaryPath = `${path}.${process.pid}.${randomBytes4(6).toString("hex")}.tmp`;
48158
+ try {
48159
+ await writeFile3(temporaryPath, `${JSON.stringify(file2, null, 2)}
48160
+ `, {
48161
+ encoding: "utf8",
48162
+ mode: 384,
48163
+ flag: "wx"
48164
+ });
48165
+ await rename4(temporaryPath, path);
48166
+ } catch (error61) {
48167
+ await rm(temporaryPath, { force: true }).catch(() => void 0);
48168
+ throw error61;
48169
+ }
48170
+ }
48171
+ async function withVaultLock(path, operation) {
48172
+ await mkdir4(dirname4(path), { recursive: true, mode: 448 });
48173
+ const lockPath = `${path}.lock`;
48174
+ const deadline = Date.now() + VAULT_LOCK_TIMEOUT_MS;
48175
+ let handle;
48176
+ while (!handle) {
48177
+ try {
48178
+ handle = await open3(lockPath, "wx", 384);
48179
+ } catch (error61) {
48180
+ if (!isExistingFile(error61)) throw error61;
48181
+ await removeStaleVaultLock(lockPath);
48182
+ if (Date.now() >= deadline) {
48183
+ throw new VaultError("corrupt", "Timed out waiting for the vault lock.");
48184
+ }
48185
+ await new Promise((resolve) => setTimeout(resolve, 20));
48186
+ }
48187
+ }
48188
+ try {
48189
+ return await operation();
48190
+ } finally {
48191
+ await handle.close();
48192
+ await unlink2(lockPath).catch(() => void 0);
48193
+ }
48194
+ }
48195
+ async function removeStaleVaultLock(path) {
48196
+ try {
48197
+ const metadata = await stat4(path);
48198
+ if (Date.now() - metadata.mtimeMs > STALE_VAULT_LOCK_MS) await unlink2(path);
48199
+ } catch (error61) {
48200
+ if (!isMissingFile2(error61)) throw error61;
48201
+ }
48202
+ }
48203
+ function isMissingFile2(error61) {
48204
+ return typeof error61 === "object" && error61 !== null && "code" in error61 && Reflect.get(error61, "code") === "ENOENT";
48205
+ }
48206
+ function isExistingFile(error61) {
48207
+ return typeof error61 === "object" && error61 !== null && "code" in error61 && Reflect.get(error61, "code") === "EEXIST";
48208
+ }
48209
+ function asVaultError(error61, code, message) {
48210
+ return error61 instanceof VaultError ? error61 : new VaultError(code, message, { cause: error61 });
48211
+ }
48212
+
48213
+ // src/device.ts
48214
+ var DEVICE_NAME_PATTERN = /^[a-z0-9][a-z0-9-]{0,31}$/;
48215
+
48216
+ // src/agent-link.ts
48217
+ var DEFAULT_AGENT_SCOPES = ["mcp:call", "router.use"];
48218
+ var ALLOWED_AGENT_SCOPES = /* @__PURE__ */ new Set([...DEFAULT_AGENT_SCOPES, "call.publish"]);
48219
+ var publicFetch = createPublicFetch({ allowPrivateNetwork: false });
48220
+ var agentSecretAccounts = (wallet) => ({
48221
+ tokens: `vapi.agent.${wallet}.tokens`,
48222
+ routerStake: `vapi.agent.${wallet}.router.stake`,
48223
+ routerBalance: `vapi.agent.${wallet}.router.balance`
48224
+ });
48225
+
48226
+ // src/agent-profile.ts
48227
+ var DEFAULT_AGENT_TOOLS = ["call.search", "call.inspect", "call.pay"];
48228
+ var AGENT_GRANTS = ["read", "delegate", "allocate"];
48229
+ var agentProfileShape = {
48230
+ version: external_exports.literal(1),
48231
+ name: walletNameSchema,
48232
+ wallet: external_exports.string(),
48233
+ model: external_exports.string().min(1),
48234
+ instructions: external_exports.string().max(2e4),
48235
+ verifiedOnly: external_exports.boolean().default(true),
48236
+ approveAboveUsd: external_exports.number().min(0).default(0.5),
48237
+ maxSteps: external_exports.number().int().min(1).max(50).default(12),
48238
+ paused: external_exports.boolean().default(false),
48239
+ grants: external_exports.array(external_exports.enum(AGENT_GRANTS)).default([]),
48240
+ createdAt: external_exports.string()
48241
+ };
48242
+ function createAgentProfileSchema(toolNames) {
48243
+ if (toolNames.length === 0) {
48244
+ throw new Error("Agent profiles require at least one registered tool.");
48245
+ }
48246
+ const tools = [...toolNames];
48247
+ return external_exports.object({ ...agentProfileShape, tools: external_exports.array(external_exports.enum(tools)).default(tools) });
48248
+ }
48249
+ var agentProfileSchema = createAgentProfileSchema(DEFAULT_AGENT_TOOLS);
48250
+
48251
+ // src/agent-run.ts
48252
+ var RUN_ID_PATTERN = /^run_[0-9a-f]{24}$/;
48253
+ var runRefSchema = external_exports.strictObject({
48254
+ id: external_exports.string().regex(RUN_ID_PATTERN),
48255
+ swarm: external_exports.string().optional(),
48256
+ member: external_exports.string().optional(),
48257
+ parentRunId: external_exports.string().optional()
48258
+ });
48259
+
48260
+ // src/swarm.ts
48261
+ var SWARM_NAME_PATTERN = /^[a-z][a-z0-9-]{0,15}$/;
48262
+ var SWARM_ROLE_PATTERN = /^[a-z][a-z0-9]{0,7}$/;
48263
+ var atomicSchema = external_exports.string().regex(/^\d+$/);
48264
+ var addressSchema2 = external_exports.string().regex(/^0x[0-9a-fA-F]{40}$/);
48265
+ var capsSchema = external_exports.strictObject({ perCallAtomic: atomicSchema, perDayAtomic: atomicSchema });
48266
+ var movementIdSchema = external_exports.string().regex(/^mv_[A-Za-z0-9_-]{8,128}$/u);
48267
+ var amountUsdSchema = external_exports.string().regex(/^\d+\.\d{2}$/u);
48268
+ var setupFundingStatusSchema = external_exports.enum(["planned", "sent", "incomplete"]);
48269
+ var blockedFundingLegSchema = external_exports.strictObject({
48270
+ to: external_exports.string().trim().min(1),
48271
+ amountUsd: amountUsdSchema,
48272
+ reason: external_exports.string().trim().min(1)
48273
+ });
48274
+ var setupFundingSchema = external_exports.strictObject({
48275
+ amountUsd: amountUsdSchema,
48276
+ from: walletNameSchema,
48277
+ movementId: movementIdSchema.optional(),
48278
+ status: setupFundingStatusSchema,
48279
+ policy: external_exports.strictObject({
48280
+ strategy: external_exports.enum(["targets", "even", "weights"]),
48281
+ movementId: movementIdSchema.optional(),
48282
+ status: setupFundingStatusSchema,
48283
+ blocked: external_exports.array(blockedFundingLegSchema).optional()
48284
+ }).optional()
48285
+ });
48286
+ var accountSteps = {
48287
+ creating: external_exports.boolean().default(false),
48288
+ created: external_exports.boolean(),
48289
+ capped: external_exports.boolean(),
48290
+ linked: external_exports.boolean()
48291
+ };
48292
+ var treasurySchema = external_exports.strictObject({
48293
+ account: walletNameSchema,
48294
+ // A planned account has no address until its vault derivation succeeds.
48295
+ address: addressSchema2.optional(),
48296
+ steps: external_exports.strictObject(accountSteps)
48297
+ }).refine(
48298
+ (value) => !value.steps.created || value.address !== void 0,
48299
+ "Created accounts require an address."
48300
+ );
48301
+ var memberSchema = external_exports.strictObject({
48302
+ account: walletNameSchema,
48303
+ address: addressSchema2.optional(),
48304
+ role: external_exports.string().regex(SWARM_ROLE_PATTERN),
48305
+ weight: external_exports.number().int().positive(),
48306
+ targetAtomic: atomicSchema,
48307
+ ceilingAtomic: atomicSchema.nullable(),
48308
+ steps: external_exports.strictObject({ ...accountSteps, profiled: external_exports.boolean() })
48309
+ }).refine(
48310
+ (value) => !value.steps.created || value.address !== void 0,
48311
+ "Created accounts require an address."
48312
+ );
48313
+ var swarmFileSchema = external_exports.strictObject({
48314
+ v: external_exports.literal(1),
48315
+ name: external_exports.string().regex(SWARM_NAME_PATTERN),
48316
+ device: external_exports.string().regex(DEVICE_NAME_PATTERN),
48317
+ network: external_exports.enum([BASE_MAINNET_CAIP2, ARC_MAINNET_CAIP2]),
48318
+ createdAt: external_exports.iso.datetime(),
48319
+ treasury: treasurySchema,
48320
+ members: external_exports.array(memberSchema),
48321
+ setupFunding: setupFundingSchema.optional(),
48322
+ policy: external_exports.strictObject({
48323
+ strategy: external_exports.enum(["targets", "even", "weights"]),
48324
+ treasuryCaps: capsSchema
48325
+ })
48326
+ }).refine(
48327
+ (value) => (/* @__PURE__ */ new Set([value.treasury.account, ...value.members.map((member) => member.account)])).size === value.members.length + 1,
48328
+ "Swarm accounts must be unique."
48329
+ );
48330
+
48331
+ // src/member-bundle.ts
48332
+ var MEMBER_BUNDLE_MAX_BYTES = 32 * 1024;
48333
+ var REMOTE_BLOCKED_GRANTS = /* @__PURE__ */ new Set([
48334
+ "delegate",
48335
+ "allocate"
48336
+ ]);
48337
+ var BASE64URL_PATTERN = /^[A-Za-z0-9_-]+$/;
48338
+ var ADDRESS_PATTERN2 = /^0x[0-9a-fA-F]{40}$/;
48339
+ var PRIVATE_KEY_PATTERN = /^0x[0-9a-fA-F]{64}$/;
48340
+ var ATOMIC_PATTERN = /^\d+$/;
48341
+ var FORBIDDEN_CONFIG_KEYS = /* @__PURE__ */ new Set(["rpcUrl", "apiKey"]);
48342
+ var MemberBundleError = class extends Error {
48343
+ constructor(code, message) {
48344
+ super(message);
48345
+ this.code = code;
48346
+ this.name = "MemberBundleError";
48347
+ }
48348
+ code;
48349
+ };
48350
+ var hexString = (pattern, message) => external_exports.custom((value) => typeof value === "string" && pattern.test(value), message);
48351
+ var linkSchema = external_exports.strictObject({
48352
+ apiBase: external_exports.string(),
48353
+ clientId: external_exports.string(),
48354
+ owner: external_exports.custom((value) => typeof value === "string" && value.startsWith("0x")),
48355
+ label: external_exports.string(),
48356
+ scopes: external_exports.array(external_exports.string()),
48357
+ linkedAt: external_exports.iso.datetime(),
48358
+ routerBaseUrl: external_exports.string().optional()
48359
+ });
48360
+ var remoteTokensSchema = external_exports.strictObject({
48361
+ accessToken: external_exports.string().min(1),
48362
+ expiresAt: external_exports.number().finite(),
48363
+ scopes: external_exports.array(external_exports.string()),
48364
+ refreshable: external_exports.literal(false)
48365
+ });
48366
+ var storedTokensSchema = external_exports.object({
48367
+ accessToken: external_exports.string().min(1),
48368
+ refreshToken: external_exports.string().min(1),
48369
+ expiresAt: external_exports.number().finite(),
48370
+ scopes: external_exports.array(external_exports.string())
48371
+ });
48372
+ var credentialsSchema = external_exports.strictObject({
48373
+ tokens: external_exports.string().min(1).refine(isRemoteTokens, "A member bundle carries a non-refreshable access token only."),
48374
+ routerStake: external_exports.string().min(1).optional(),
48375
+ routerBalance: external_exports.string().min(1).optional()
48376
+ });
48377
+ var remoteProfileSchema = agentProfileSchema.strict().refine((profile) => profile.grants.every((grant) => !REMOTE_BLOCKED_GRANTS.has(grant)), {
48378
+ message: "A remote member never holds the delegate or allocate grant.",
48379
+ path: ["grants"]
48380
+ });
48381
+ var credentialFreeUrl = external_exports.url().refine((value) => unsafeUrlPart(value) === void 0, {
48382
+ message: "A member bundle URL never carries a user name, password, query or fragment."
48383
+ });
48384
+ var memberBundleConfigSchema = external_exports.unknown().refine((value) => !hasForbiddenConfigKey(value), {
48385
+ message: "A member bundle never carries RPC URLs or API keys."
48386
+ }).pipe(
48387
+ external_exports.strictObject({
48388
+ discoveryUrl: credentialFreeUrl,
48389
+ marketplaceDiscoveryUrl: credentialFreeUrl,
48390
+ networks: external_exports.record(
48391
+ external_exports.string().refine(isMemberBundleNetwork, {
48392
+ message: "Expected a network with a public RPC default."
48393
+ }),
48394
+ external_exports.strictObject({ usdc: external_exports.string() })
48395
+ ).refine(
48396
+ (networks) => Object.entries(networks).every(
48397
+ ([id, network]) => isMemberBundleNetwork(id) && network.usdc === NETWORKS[id].usdc
48398
+ ),
48399
+ { message: "Expected the canonical USDC token of each network." }
48400
+ ).refine((networks) => Object.keys(networks).length === 1, {
48401
+ message: "A member bundle carries exactly one network: the swarm's."
48402
+ })
48403
+ })
48404
+ );
48405
+ var memberBundleSchema = external_exports.strictObject({
48406
+ v: external_exports.literal(1),
48407
+ account: walletNameSchema,
48408
+ address: hexString(ADDRESS_PATTERN2, "Expected a 20-byte 0x address."),
48409
+ privateKey: hexString(PRIVATE_KEY_PATTERN, "Expected a 32-byte 0x private key."),
48410
+ swarm: external_exports.string().regex(SWARM_NAME_PATTERN),
48411
+ profile: remoteProfileSchema,
48412
+ link: linkSchema,
48413
+ credentials: credentialsSchema,
48414
+ caps: external_exports.strictObject(spendCapsSchema.shape),
48415
+ allowanceExpiresAt: external_exports.iso.datetime(),
48416
+ ceilingAtomic: external_exports.string().regex(ATOMIC_PATTERN, "Expected non-negative atomic units."),
48417
+ config: memberBundleConfigSchema,
48418
+ runId: external_exports.string().regex(RUN_ID_PATTERN),
48419
+ parentRunId: external_exports.string().regex(RUN_ID_PATTERN).optional()
48420
+ });
48421
+ function memberBundleConfig(owner, network) {
48422
+ const discoveryUrl = safeDiscoveryUrl("discoveryUrl", owner.discoveryUrl);
48423
+ const marketplaceDiscoveryUrl = safeDiscoveryUrl(
48424
+ "marketplaceDiscoveryUrl",
48425
+ owner.marketplaceDiscoveryUrl
48426
+ );
48427
+ if (!Object.hasOwn(owner.networks, network) || !isMemberBundleNetwork(network)) {
48428
+ throw new MemberBundleError(
48429
+ "unsupported_network",
48430
+ `A member bundle runs on the swarm network ${network}, which must be enabled in this config and have a public RPC default.`
48431
+ );
48432
+ }
48433
+ return {
48434
+ discoveryUrl,
48435
+ marketplaceDiscoveryUrl,
48436
+ networks: { [network]: { usdc: NETWORKS[network].usdc } }
48437
+ };
48438
+ }
48439
+ function headlessConfigFromBundle(config2) {
48440
+ const networks = {};
48441
+ for (const [id, network] of Object.entries(config2.networks)) {
48442
+ if (!isMemberBundleNetwork(id) || network === void 0) continue;
48443
+ networks[id] = { rpcUrl: NETWORKS[id].publicRpcUrl, usdc: network.usdc };
48444
+ }
48445
+ return configSchema.parse({
48446
+ discoveryUrl: config2.discoveryUrl,
48447
+ marketplaceDiscoveryUrl: config2.marketplaceDiscoveryUrl,
48448
+ networks,
48449
+ spendCaps: { ...DEFAULT_SPEND_CAPS }
48450
+ });
48451
+ }
48452
+ function createMemberBundle(input2) {
48453
+ const bundle = parseBundle({ ...input2, profile: withoutRemoteGrants(input2.profile), v: 1 });
48454
+ assertKeyMatchesAddress(bundle);
48455
+ const encoded = Buffer.from(JSON.stringify(bundle), "utf8").toString("base64url");
48456
+ if (encoded.length > MEMBER_BUNDLE_MAX_BYTES) {
48457
+ throw new MemberBundleError(
48458
+ "too_large",
48459
+ `The member bundle is larger than ${MEMBER_BUNDLE_MAX_BYTES} bytes.`
48460
+ );
48461
+ }
48462
+ return encoded;
48463
+ }
48464
+ function openMemberBundle(text) {
48465
+ if (typeof text !== "string") {
48466
+ throw new MemberBundleError("invalid", "The member bundle is not a string.");
48467
+ }
48468
+ if (text.length > MEMBER_BUNDLE_MAX_BYTES) {
48469
+ throw new MemberBundleError(
48470
+ "too_large",
48471
+ `The member bundle is larger than ${MEMBER_BUNDLE_MAX_BYTES} bytes.`
48472
+ );
48473
+ }
48474
+ if (!BASE64URL_PATTERN.test(text)) {
48475
+ throw new MemberBundleError("invalid", "The member bundle is not base64url text.");
48476
+ }
48477
+ let decoded;
48478
+ try {
48479
+ decoded = JSON.parse(Buffer.from(text, "base64url").toString("utf8"));
48480
+ } catch {
48481
+ throw new MemberBundleError("invalid", "The member bundle does not decode to JSON.");
48482
+ }
48483
+ const bundle = parseBundle(decoded);
48484
+ assertKeyMatchesAddress(bundle);
48485
+ return bundle;
48486
+ }
48487
+ async function readMemberCredentials(args) {
48488
+ if (!isWalletName(args.account)) {
48489
+ throw new MemberBundleError("invalid", "The member account name is not valid.");
48490
+ }
48491
+ const names = agentSecretAccounts(args.account);
48492
+ const storedTokens = await args.secrets.get(names.tokens);
48493
+ if (storedTokens === void 0 || storedTokens === "") {
48494
+ throw new MemberBundleError(
48495
+ "not_linked",
48496
+ `The account ${args.account} is not linked to vAPI. Link it with vapi login first.`
48497
+ );
48498
+ }
48499
+ const routerStake = await args.secrets.get(names.routerStake);
48500
+ const routerBalance = await args.secrets.get(names.routerBalance);
48501
+ const parsed = parseStoredTokens(storedTokens);
48502
+ if (parsed === void 0) {
48503
+ throw new MemberBundleError(
48504
+ "not_linked",
48505
+ `The account ${args.account}'s vAPI credentials cannot be exported safely. Run vapi login --account ${args.account} again.`
48506
+ );
48507
+ }
48508
+ const tokens = JSON.stringify({
48509
+ accessToken: parsed.accessToken,
48510
+ expiresAt: parsed.expiresAt,
48511
+ scopes: parsed.scopes,
48512
+ refreshable: false
48513
+ });
48514
+ return {
48515
+ tokens,
48516
+ ...routerStake === void 0 || routerStake === "" ? {} : { routerStake },
48517
+ ...routerBalance === void 0 || routerBalance === "" ? {} : { routerBalance }
48518
+ };
48519
+ }
48520
+ function isRemoteTokens(value) {
48521
+ try {
48522
+ return remoteTokensSchema.safeParse(JSON.parse(value)).success;
48523
+ } catch {
48524
+ return false;
48525
+ }
48526
+ }
48527
+ function parseStoredTokens(value) {
48528
+ try {
48529
+ const parsed = storedTokensSchema.safeParse(JSON.parse(value));
48530
+ return parsed.success ? parsed.data : void 0;
48531
+ } catch {
48532
+ return void 0;
48533
+ }
48534
+ }
48535
+ function isMemberBundleNetwork(id) {
48536
+ return Object.hasOwn(NETWORKS, id) && typeof NETWORKS[id].publicRpcUrl === "string";
48537
+ }
48538
+ function hasForbiddenConfigKey(value) {
48539
+ if (Array.isArray(value)) return value.some(hasForbiddenConfigKey);
48540
+ if (typeof value !== "object" || value === null) return false;
48541
+ return Object.entries(value).some(
48542
+ ([key, item]) => FORBIDDEN_CONFIG_KEYS.has(key) || hasForbiddenConfigKey(item)
48543
+ );
48544
+ }
48545
+ function unsafeUrlPart(value) {
48546
+ let url2;
48547
+ try {
48548
+ url2 = new URL(value);
48549
+ } catch {
48550
+ return "an unparseable value";
48551
+ }
48552
+ if (url2.username !== "" || url2.password !== "") return "a user name or password";
48553
+ if (url2.search !== "" || value.includes("?")) return "a query string";
48554
+ if (url2.hash !== "" || value.includes("#")) return "a fragment";
48555
+ return void 0;
48556
+ }
48557
+ function safeDiscoveryUrl(field, value) {
48558
+ const part = unsafeUrlPart(value);
48559
+ if (part !== void 0) {
48560
+ throw new MemberBundleError(
48561
+ "unsafe_config",
48562
+ `The owner config's ${field} has ${part}, which can carry a credential, so it never goes into a member bundle. Point ${field} at a plain URL (check VAPI_DISCOVERY_URL, VAPI_MARKETPLACE_DISCOVERY_URL and config.json).`
48563
+ );
48564
+ }
48565
+ return value;
48566
+ }
48567
+ function withoutRemoteGrants(profile) {
48568
+ if (typeof profile !== "object" || profile === null || !Array.isArray(profile.grants)) {
48569
+ return profile;
48570
+ }
48571
+ return {
48572
+ ...profile,
48573
+ grants: profile.grants.filter((grant) => !REMOTE_BLOCKED_GRANTS.has(grant))
48574
+ };
48575
+ }
48576
+ function parseBundle(value) {
48577
+ const parsed = memberBundleSchema.safeParse(value);
48578
+ if (!parsed.success) {
48579
+ const fields = [
48580
+ ...new Set(parsed.error.issues.map((issue2) => issue2.path.map(String).join(".") || "(root)"))
48581
+ ];
48582
+ throw new MemberBundleError(
48583
+ "invalid",
48584
+ `The member bundle is not valid (fields: ${fields.join(", ")}).`
48585
+ );
48586
+ }
48587
+ return parsed.data;
48588
+ }
48589
+ function assertKeyMatchesAddress(bundle) {
48590
+ let derived;
48591
+ try {
48592
+ derived = privateKeyToAccount(bundle.privateKey).address;
48593
+ } catch {
48594
+ throw new MemberBundleError("key_mismatch", "The member bundle's key is not a valid key.");
48595
+ }
48596
+ if (derived.toLowerCase() !== bundle.address.toLowerCase()) {
48597
+ throw new MemberBundleError(
48598
+ "key_mismatch",
48599
+ "The member bundle's key does not match its address."
48600
+ );
48601
+ }
47231
48602
  }
47232
48603
  export {
48604
+ MEMBER_BUNDLE_MAX_BYTES,
48605
+ MemberBundleError,
47233
48606
  createKeystoreWithPhrase,
48607
+ createMemberBundle,
47234
48608
  decryptPrivateKey,
47235
48609
  exportKeystoreKeys,
47236
- exportRecoveryPhrase
48610
+ exportMemberKey,
48611
+ exportRecoveryPhrase,
48612
+ exportVaultAccountKey,
48613
+ exportVaultPhrase,
48614
+ headlessConfigFromBundle,
48615
+ memberBundleConfig,
48616
+ memberBundleConfigSchema,
48617
+ memberBundleSchema,
48618
+ openMemberBundle,
48619
+ readMemberCredentials
47237
48620
  };
47238
48621
  //# sourceMappingURL=secrets.js.map