@vapi-network/core 0.3.0 → 0.7.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 (90) hide show
  1. package/README.md +406 -5
  2. package/dist/account-add.d.ts +94 -0
  3. package/dist/account-add.d.ts.map +1 -0
  4. package/dist/accounts.d.ts.map +1 -1
  5. package/dist/agent-link.d.ts +112 -0
  6. package/dist/agent-link.d.ts.map +1 -0
  7. package/dist/agent-link.js +1781 -0
  8. package/dist/agent-link.js.map +6 -0
  9. package/dist/agent-profile.d.ts +56 -0
  10. package/dist/agent-profile.d.ts.map +1 -0
  11. package/dist/allocation.d.ts +33 -0
  12. package/dist/allocation.d.ts.map +1 -0
  13. package/dist/api-key.d.ts +99 -0
  14. package/dist/api-key.d.ts.map +1 -0
  15. package/dist/api-key.js +20086 -0
  16. package/dist/api-key.js.map +6 -0
  17. package/dist/audit.d.ts +5 -3
  18. package/dist/audit.d.ts.map +1 -1
  19. package/dist/authorization-state.d.ts +58 -0
  20. package/dist/authorization-state.d.ts.map +1 -0
  21. package/dist/backup.d.ts +186 -0
  22. package/dist/backup.d.ts.map +1 -0
  23. package/dist/backup.js +47542 -0
  24. package/dist/backup.js.map +6 -0
  25. package/dist/call-contracts-wire.d.ts +151 -11
  26. package/dist/call-contracts-wire.d.ts.map +1 -1
  27. package/dist/ceiling.d.ts +90 -0
  28. package/dist/ceiling.d.ts.map +1 -0
  29. package/dist/cloud-backup.d.ts +125 -0
  30. package/dist/cloud-backup.d.ts.map +1 -0
  31. package/dist/cloud-backup.js +48402 -0
  32. package/dist/cloud-backup.js.map +6 -0
  33. package/dist/config.d.ts +7 -1
  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/discovery.d.ts +20 -2
  38. package/dist/discovery.d.ts.map +1 -1
  39. package/dist/distribute.d.ts +60 -0
  40. package/dist/distribute.d.ts.map +1 -0
  41. package/dist/index.d.ts +18 -1
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +8106 -1393
  44. package/dist/index.js.map +3 -3
  45. package/dist/keystore.d.ts +24 -1
  46. package/dist/keystore.d.ts.map +1 -1
  47. package/dist/marketplace-contracts.d.ts +448 -15
  48. package/dist/marketplace-contracts.d.ts.map +1 -1
  49. package/dist/movement.d.ts +102 -0
  50. package/dist/movement.d.ts.map +1 -0
  51. package/dist/networks.d.ts +17 -2
  52. package/dist/networks.d.ts.map +1 -1
  53. package/dist/receipts.d.ts +42 -0
  54. package/dist/receipts.d.ts.map +1 -1
  55. package/dist/router-client.d.ts +117 -0
  56. package/dist/router-client.d.ts.map +1 -0
  57. package/dist/router-client.js +46147 -0
  58. package/dist/router-client.js.map +6 -0
  59. package/dist/secret-store.d.ts +17 -5
  60. package/dist/secret-store.d.ts.map +1 -1
  61. package/dist/secrets.d.ts +1 -0
  62. package/dist/secrets.d.ts.map +1 -1
  63. package/dist/secrets.js +597 -25
  64. package/dist/secrets.js.map +3 -3
  65. package/dist/siblings.d.ts +41 -0
  66. package/dist/siblings.d.ts.map +1 -0
  67. package/dist/spend-policy.d.ts +12 -0
  68. package/dist/spend-policy.d.ts.map +1 -1
  69. package/dist/status.d.ts +73 -0
  70. package/dist/status.d.ts.map +1 -0
  71. package/dist/support-report.d.ts +1 -1
  72. package/dist/sweep.d.ts +4 -0
  73. package/dist/sweep.d.ts.map +1 -1
  74. package/dist/transfer.d.ts +62 -0
  75. package/dist/transfer.d.ts.map +1 -0
  76. package/dist/vault-key.d.ts +41 -0
  77. package/dist/vault-key.d.ts.map +1 -0
  78. package/dist/vault-migrate.d.ts +29 -0
  79. package/dist/vault-migrate.d.ts.map +1 -0
  80. package/dist/vault.d.ts +93 -0
  81. package/dist/vault.d.ts.map +1 -0
  82. package/dist/wallet-store.d.ts +141 -31
  83. package/dist/wallet-store.d.ts.map +1 -1
  84. package/dist/x402-networks.d.ts +6 -9
  85. package/dist/x402-networks.d.ts.map +1 -1
  86. package/dist/x402-pay.d.ts +136 -0
  87. package/dist/x402-pay.d.ts.map +1 -0
  88. package/dist/x402.d.ts +20 -1
  89. package/dist/x402.d.ts.map +1 -1
  90. package/package.json +21 -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: randomBytes4 } = 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 ? randomBytes4(Fp.BYTES) : ent;
2228
2228
  seedArgs.push(ensureBytes("extraEntropy", e2));
2229
2229
  }
2230
2230
  const seed = concatBytes2(...seedArgs);
@@ -46551,6 +46551,7 @@ init_getAddress();
46551
46551
 
46552
46552
  // src/x402-networks.ts
46553
46553
  var BASE_MAINNET_CAIP2 = "eip155:8453";
46554
+ var ARC_MAINNET_CAIP2 = "eip155:5042";
46554
46555
  var ARC_TESTNET_CAIP2 = "eip155:5042002";
46555
46556
  var SOLANA_MAINNET_CAIP2 = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d";
46556
46557
  var X402_SOLANA_MAINNET_CAIP2 = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";
@@ -46569,8 +46570,13 @@ var arcTestnetUsdc = canonicalIdentity("0x36000000000000000000000000000000000000
46569
46570
  name: "USDC",
46570
46571
  version: "2"
46571
46572
  });
46573
+ var arcMainnetUsdc = canonicalIdentity("0x3600000000000000000000000000000000000000", {
46574
+ name: "USDC",
46575
+ version: "2"
46576
+ });
46572
46577
  var CANONICAL_X402_USDC_NETWORKS = Object.freeze({
46573
46578
  [BASE_MAINNET_CAIP2]: baseMainnetUsdc,
46579
+ [ARC_MAINNET_CAIP2]: arcMainnetUsdc,
46574
46580
  [ARC_TESTNET_CAIP2]: arcTestnetUsdc
46575
46581
  });
46576
46582
  function browserNetwork(identity) {
@@ -46582,6 +46588,7 @@ function browserNetwork(identity) {
46582
46588
  }
46583
46589
  var BROWSER_ENABLED_X402_NETWORK_CONFIG = Object.freeze({
46584
46590
  [BASE_MAINNET_CAIP2]: browserNetwork(baseMainnetUsdc),
46591
+ [ARC_MAINNET_CAIP2]: browserNetwork(arcMainnetUsdc),
46585
46592
  [ARC_TESTNET_CAIP2]: browserNetwork(arcTestnetUsdc)
46586
46593
  });
46587
46594
 
@@ -46594,7 +46601,18 @@ var NETWORKS = {
46594
46601
  usdc: CANONICAL_X402_USDC_NETWORKS[BASE_MAINNET_CAIP2].usdc,
46595
46602
  gasToken: "ETH",
46596
46603
  rpcEnv: "BASE_RPC_URL",
46597
- publicRpcUrl: "https://mainnet.base.org"
46604
+ publicRpcUrl: "https://mainnet.base.org",
46605
+ explorerUrl: "https://basescan.org"
46606
+ },
46607
+ [ARC_MAINNET_CAIP2]: {
46608
+ family: "evm",
46609
+ chainId: 5042,
46610
+ name: "Arc mainnet",
46611
+ usdc: CANONICAL_X402_USDC_NETWORKS[ARC_MAINNET_CAIP2].usdc,
46612
+ gasToken: "USDC",
46613
+ rpcEnv: "ARC_RPC_URL",
46614
+ publicRpcUrl: "https://rpc.mainnet.arc.io",
46615
+ explorerUrl: "https://explorer.arc.io"
46598
46616
  },
46599
46617
  [ARC_TESTNET_CAIP2]: {
46600
46618
  family: "evm",
@@ -46679,7 +46697,8 @@ var configSchema = external_exports.object({
46679
46697
  }).optional()
46680
46698
  })
46681
46699
  ),
46682
- spendCaps: spendCapsSchema
46700
+ spendCaps: spendCapsSchema,
46701
+ device: external_exports.string().regex(/^[a-z0-9][a-z0-9-]{0,31}$/).optional()
46683
46702
  }).superRefine((config2, context) => {
46684
46703
  for (const [network, configured] of Object.entries(config2.networks)) {
46685
46704
  if (isSolanaNetwork(network)) {
@@ -46693,7 +46712,14 @@ var configSchema = external_exports.object({
46693
46712
  continue;
46694
46713
  }
46695
46714
  try {
46696
- getAddress(configured.usdc);
46715
+ const usdc = getAddress(configured.usdc);
46716
+ if ((network === ARC_MAINNET_CAIP2 || network === ARC_TESTNET_CAIP2) && usdc !== getAddress(NETWORKS[network].usdc)) {
46717
+ context.addIssue({
46718
+ code: "custom",
46719
+ path: ["networks", network, "usdc"],
46720
+ message: "Expected the canonical Arc USDC predeploy."
46721
+ });
46722
+ }
46697
46723
  } catch {
46698
46724
  context.addIssue({
46699
46725
  code: "custom",
@@ -46706,6 +46732,7 @@ var configSchema = external_exports.object({
46706
46732
  function getVapiPaths(directory = process.env.VAPI_HOME?.trim() || join2(homedir(), ".vapi")) {
46707
46733
  return {
46708
46734
  directory,
46735
+ agentsDir: join2(directory, "agents"),
46709
46736
  config: join2(directory, "config.json"),
46710
46737
  keystore: join2(directory, "keystore.json"),
46711
46738
  receipts: join2(directory, "receipts.jsonl"),
@@ -46912,7 +46939,34 @@ var KeystoreError = class extends Error {
46912
46939
  this.name = "KeystoreError";
46913
46940
  }
46914
46941
  };
46915
- async function deriveKey(passphrase, salt) {
46942
+ function encryptWithKey(key, plaintext) {
46943
+ if (key.byteLength !== KEY_LENGTH) {
46944
+ throw new Error(`Expected a ${KEY_LENGTH}-byte AES-256-GCM key.`);
46945
+ }
46946
+ const nonce = randomBytes2(12);
46947
+ const cipher = createCipheriv("aes-256-gcm", key, nonce, {
46948
+ authTagLength: AUTH_TAG_LENGTH
46949
+ });
46950
+ const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
46951
+ return { nonce, ciphertext, tag: cipher.getAuthTag() };
46952
+ }
46953
+ function decryptWithKey(key, sealed) {
46954
+ if (key.byteLength !== KEY_LENGTH) {
46955
+ throw new Error(`Expected a ${KEY_LENGTH}-byte AES-256-GCM key.`);
46956
+ }
46957
+ if (sealed.nonce.byteLength !== 12) {
46958
+ throw new Error("Expected a 12-byte AES-GCM nonce.");
46959
+ }
46960
+ if (sealed.tag.byteLength !== AUTH_TAG_LENGTH) {
46961
+ throw new Error(`Expected a ${AUTH_TAG_LENGTH}-byte AES-GCM authentication tag.`);
46962
+ }
46963
+ const decipher = createDecipheriv("aes-256-gcm", key, sealed.nonce, {
46964
+ authTagLength: AUTH_TAG_LENGTH
46965
+ });
46966
+ decipher.setAuthTag(sealed.tag);
46967
+ return Buffer.concat([decipher.update(sealed.ciphertext), decipher.final()]);
46968
+ }
46969
+ async function deriveScryptKey(passphrase, salt) {
46916
46970
  return await new Promise((resolve, reject) => {
46917
46971
  scrypt(
46918
46972
  passphrase,
@@ -46974,16 +47028,14 @@ async function sealPlaintext(plaintext, passphrase) {
46974
47028
  throw new KeystoreError("Keystore passphrase cannot be empty.");
46975
47029
  }
46976
47030
  const salt = randomBytes2(32);
46977
- const iv = randomBytes2(12);
46978
- const key = await deriveKey(passphrase, salt);
47031
+ const key = await deriveScryptKey(passphrase, salt);
46979
47032
  try {
46980
- const cipher = createCipheriv("aes-256-gcm", key, iv, { authTagLength: AUTH_TAG_LENGTH });
46981
- const ciphertext = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
47033
+ const sealed = encryptWithKey(key, Buffer.from(plaintext, "utf8"));
46982
47034
  return {
46983
47035
  cipher: "aes-256-gcm",
46984
- ciphertext: ciphertext.toString("base64"),
46985
- iv: iv.toString("base64"),
46986
- authTag: cipher.getAuthTag().toString("base64"),
47036
+ ciphertext: sealed.ciphertext.toString("base64"),
47037
+ iv: sealed.nonce.toString("base64"),
47038
+ authTag: sealed.tag.toString("base64"),
46987
47039
  kdf: "scrypt",
46988
47040
  salt: salt.toString("base64"),
46989
47041
  kdfParams: {
@@ -47011,19 +47063,14 @@ async function decryptSecret(keystore, passphrase) {
47011
47063
  const iv = Buffer.from(parsed.crypto.iv, "base64");
47012
47064
  const authTag = Buffer.from(parsed.crypto.authTag, "base64");
47013
47065
  const ciphertext = Buffer.from(parsed.crypto.ciphertext, "base64");
47014
- const key = await deriveKey(passphrase, salt);
47066
+ const key = await deriveScryptKey(passphrase, salt);
47015
47067
  let unlocked;
47016
47068
  try {
47017
- if (authTag.length !== AUTH_TAG_LENGTH) {
47018
- throw new Error(`Expected a ${AUTH_TAG_LENGTH}-byte AES-GCM authentication tag.`);
47019
- }
47020
- const decipher = createDecipheriv("aes-256-gcm", key, iv, {
47021
- authTagLength: AUTH_TAG_LENGTH
47022
- });
47023
- decipher.setAuthTag(authTag);
47024
- const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString(
47025
- "utf8"
47026
- );
47069
+ const plaintext = decryptWithKey(key, {
47070
+ nonce: iv,
47071
+ ciphertext,
47072
+ tag: authTag
47073
+ }).toString("utf8");
47027
47074
  unlocked = parsed.version === 3 ? keysFromEntropy(parsed, plaintext) : {
47028
47075
  keys: parsed.version === 1 ? { evm: `0x${plaintext}` } : encryptedKeysSchema.parse(JSON.parse(plaintext)).keys
47029
47076
  };
@@ -47108,6 +47155,15 @@ async function refuseExistingKeystore(path) {
47108
47155
  }
47109
47156
  }
47110
47157
  }
47158
+ function validatePrivateKey(value) {
47159
+ const normalized = value.trim().toLowerCase();
47160
+ if (!/^0x[0-9a-f]{64}$/u.test(normalized)) {
47161
+ throw new KeystoreError(
47162
+ "A private key is 0x followed by 64 hexadecimal characters. Check for a missing character."
47163
+ );
47164
+ }
47165
+ return normalized;
47166
+ }
47111
47167
  async function readStoredKeystore(path) {
47112
47168
  let raw;
47113
47169
  try {
@@ -47204,10 +47260,526 @@ async function writeKeystore(path, keystore, exclusive) {
47204
47260
  }
47205
47261
  await rename2(temporaryPath, path);
47206
47262
  }
47263
+
47264
+ // src/vault.ts
47265
+ import { randomBytes as randomBytes3 } from "node:crypto";
47266
+ import { mkdir as mkdir3, open as open2, readFile as readFile3, rename as rename3, rm, stat as stat3, unlink, writeFile as writeFile3 } from "node:fs/promises";
47267
+ import { dirname as dirname3 } from "node:path";
47268
+
47269
+ // src/wallet-name.ts
47270
+ var WALLET_NAME_PATTERN = /^[a-z0-9][a-z0-9-]{0,31}$/;
47271
+ var walletNameSchema = external_exports.string().regex(
47272
+ WALLET_NAME_PATTERN,
47273
+ "A wallet name is 1 to 32 characters of lowercase letters, digits and dashes, starting with a letter or digit."
47274
+ );
47275
+
47276
+ // src/vault.ts
47277
+ var VaultError = class extends Error {
47278
+ code;
47279
+ constructor(code, message, options) {
47280
+ super(message, options);
47281
+ this.name = "VaultError";
47282
+ this.code = code;
47283
+ }
47284
+ };
47285
+ var SEALED_FIELD_PATTERN = /^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/u;
47286
+ var WRAPPED_KEY_PATTERN = /^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/u;
47287
+ var ADDRESS_PATTERN = /^0x[0-9a-fA-F]{40}$/u;
47288
+ var VAULT_LOCK_TIMEOUT_MS = 2e3;
47289
+ var STALE_VAULT_LOCK_MS = 3e4;
47290
+ var sealedFieldSchema = external_exports.string().regex(SEALED_FIELD_PATTERN);
47291
+ var addressSchema = external_exports.string().regex(ADDRESS_PATTERN);
47292
+ var accountFields = {
47293
+ name: walletNameSchema,
47294
+ address: addressSchema,
47295
+ createdAt: external_exports.string()
47296
+ };
47297
+ var storedAccountSchema = external_exports.discriminatedUnion("kind", [
47298
+ external_exports.object({
47299
+ ...accountFields,
47300
+ kind: external_exports.literal("derived"),
47301
+ index: external_exports.number().int().nonnegative().refine(Number.isSafeInteger, "Derived account index must be a safe integer.")
47302
+ }).strict(),
47303
+ external_exports.object({
47304
+ ...accountFields,
47305
+ kind: external_exports.literal("imported"),
47306
+ key: sealedFieldSchema
47307
+ }).strict()
47308
+ ]);
47309
+ var vaultFileSchema = external_exports.object({
47310
+ version: external_exports.literal(2),
47311
+ kdf: external_exports.literal("device-key"),
47312
+ cipher: external_exports.literal("aes-256-gcm"),
47313
+ phrase: sealedFieldSchema,
47314
+ accounts: external_exports.array(storedAccountSchema),
47315
+ nextDerivedIndex: external_exports.number().int().nonnegative().refine(Number.isSafeInteger, "Next derived account index must be a safe integer.").optional(),
47316
+ protected: external_exports.boolean(),
47317
+ wrappedKey: external_exports.string().regex(WRAPPED_KEY_PATTERN).optional()
47318
+ }).strict();
47319
+ var DeviceVault = class {
47320
+ file;
47321
+ path;
47322
+ key;
47323
+ balanceOf;
47324
+ now;
47325
+ constructor(options, file2) {
47326
+ this.path = options.path;
47327
+ this.key = options.key;
47328
+ this.balanceOf = options.balanceOf;
47329
+ this.now = options.now ?? (() => /* @__PURE__ */ new Date());
47330
+ this.file = file2;
47331
+ }
47332
+ accounts() {
47333
+ return this.file.accounts.map(copyAccount);
47334
+ }
47335
+ async deriveAccount(name) {
47336
+ try {
47337
+ return await this.update(async (file2) => {
47338
+ assertAvailableName(file2, name);
47339
+ const index = nextDerivedIndex(file2);
47340
+ const plaintext = decryptField(this.key, file2.phrase);
47341
+ let seed;
47342
+ let address;
47343
+ try {
47344
+ seed = phraseToSeed(plaintext.toString("utf8"));
47345
+ address = privateKeyToAccount(
47346
+ deriveEvmPrivateKey(seed, `m/44'/60'/0'/0/${index}`)
47347
+ ).address;
47348
+ } finally {
47349
+ seed?.fill(0);
47350
+ plaintext.fill(0);
47351
+ }
47352
+ const account = {
47353
+ name,
47354
+ kind: "derived",
47355
+ index,
47356
+ address,
47357
+ createdAt: this.now().toISOString()
47358
+ };
47359
+ file2.accounts.push(account);
47360
+ file2.nextDerivedIndex = index + 1;
47361
+ return copyAccount(account);
47362
+ });
47363
+ } catch (error61) {
47364
+ throw asVaultError(error61, "corrupt", "Unable to derive a vault account.");
47365
+ }
47366
+ }
47367
+ async importAccount(name, privateKey) {
47368
+ try {
47369
+ return await this.update(async (file2) => {
47370
+ assertAvailableName(file2, name);
47371
+ let validated;
47372
+ let address;
47373
+ try {
47374
+ validated = validatePrivateKey(privateKey);
47375
+ address = privateKeyToAccount(validated).address;
47376
+ } catch (error61) {
47377
+ throw new VaultError("bad_key", "The imported private key is not valid.", {
47378
+ cause: error61
47379
+ });
47380
+ }
47381
+ const plaintext = Buffer.from(validated.slice(2), "hex");
47382
+ let account;
47383
+ try {
47384
+ account = {
47385
+ name,
47386
+ kind: "imported",
47387
+ address,
47388
+ key: sealField(this.key, plaintext),
47389
+ createdAt: this.now().toISOString()
47390
+ };
47391
+ } finally {
47392
+ plaintext.fill(0);
47393
+ }
47394
+ file2.accounts.push(account);
47395
+ return copyAccount(account);
47396
+ });
47397
+ } catch (error61) {
47398
+ throw asVaultError(error61, "corrupt", "Unable to import a vault account.");
47399
+ }
47400
+ }
47401
+ async removeAccount(name) {
47402
+ try {
47403
+ await this.update(async (file2) => {
47404
+ const account = file2.accounts.find((candidate) => candidate.name === name);
47405
+ if (!account) {
47406
+ throw new VaultError("not_found", "No vault account has that name.");
47407
+ }
47408
+ if (!this.balanceOf) {
47409
+ throw new VaultError(
47410
+ "has_balance",
47411
+ "The account balance could not be verified, so the key was not removed."
47412
+ );
47413
+ }
47414
+ if (await this.balanceOf(account.address) > 0n) {
47415
+ throw new VaultError(
47416
+ "has_balance",
47417
+ "This account still has a balance. Run vapi sweep before removing it."
47418
+ );
47419
+ }
47420
+ file2.nextDerivedIndex = nextDerivedIndex(file2);
47421
+ const currentIndex = file2.accounts.findIndex((candidate) => candidate.name === name);
47422
+ if (currentIndex === -1) {
47423
+ throw new VaultError("not_found", "No vault account has that name.");
47424
+ }
47425
+ file2.accounts.splice(currentIndex, 1);
47426
+ });
47427
+ } catch (error61) {
47428
+ throw asVaultError(error61, "corrupt", "Unable to remove the vault account.");
47429
+ }
47430
+ }
47431
+ async rename(from, to) {
47432
+ try {
47433
+ await this.update(async (file2) => {
47434
+ const account = file2.accounts.find((candidate) => candidate.name === from);
47435
+ if (!account) {
47436
+ throw new VaultError("not_found", "No vault account has that name.");
47437
+ }
47438
+ if (from === to) return;
47439
+ assertAccountName(to);
47440
+ if (file2.accounts.some((candidate) => candidate.name === to)) {
47441
+ throw new VaultError("name_taken", "A vault account already has that name.");
47442
+ }
47443
+ account.name = to;
47444
+ });
47445
+ } catch (error61) {
47446
+ throw asVaultError(error61, "corrupt", "Unable to rename the vault account.");
47447
+ }
47448
+ }
47449
+ async signer(name) {
47450
+ try {
47451
+ const latest = await readVaultFile(this.path, this.key);
47452
+ this.file = latest;
47453
+ const stored = latest.accounts.find((account2) => account2.name === name);
47454
+ if (!stored) {
47455
+ throw new VaultError("not_found", "No vault account has that name.");
47456
+ }
47457
+ let account;
47458
+ if (stored.kind === "derived") {
47459
+ const plaintext = decryptField(this.key, latest.phrase);
47460
+ let seed;
47461
+ try {
47462
+ seed = phraseToSeed(plaintext.toString("utf8"));
47463
+ account = privateKeyToAccount(
47464
+ deriveEvmPrivateKey(seed, `m/44'/60'/0'/0/${stored.index}`)
47465
+ );
47466
+ } finally {
47467
+ seed?.fill(0);
47468
+ plaintext.fill(0);
47469
+ }
47470
+ } else {
47471
+ const plaintext = decryptField(this.key, stored.key);
47472
+ try {
47473
+ if (plaintext.byteLength !== 32) {
47474
+ throw new VaultError("corrupt", "An imported vault key has the wrong length.");
47475
+ }
47476
+ const privateKey = `0x${plaintext.toString("hex")}`;
47477
+ account = privateKeyToAccount(privateKey);
47478
+ } finally {
47479
+ plaintext.fill(0);
47480
+ }
47481
+ }
47482
+ if (account.address.toLowerCase() !== stored.address.toLowerCase()) {
47483
+ throw new VaultError("corrupt", "A vault key does not match its stored address.");
47484
+ }
47485
+ return account;
47486
+ } catch (error61) {
47487
+ throw asVaultError(error61, "corrupt", "Unable to open the vault account signer.");
47488
+ }
47489
+ }
47490
+ async phrase() {
47491
+ const latest = await readVaultFile(this.path, this.key);
47492
+ this.file = latest;
47493
+ const plaintext = decryptField(this.key, latest.phrase);
47494
+ try {
47495
+ try {
47496
+ return validateRecoveryPhrase(plaintext.toString("utf8"));
47497
+ } catch (error61) {
47498
+ throw new VaultError("corrupt", "The vault recovery phrase is not valid.", {
47499
+ cause: error61
47500
+ });
47501
+ }
47502
+ } finally {
47503
+ plaintext.fill(0);
47504
+ }
47505
+ }
47506
+ async save() {
47507
+ try {
47508
+ await this.update(async () => void 0);
47509
+ } catch (error61) {
47510
+ throw asVaultError(error61, "corrupt", "Unable to save the vault.");
47511
+ }
47512
+ }
47513
+ resealSecrets(file2) {
47514
+ const phrase = decryptField(this.key, file2.phrase);
47515
+ try {
47516
+ const accounts = file2.accounts.map((account) => {
47517
+ if (account.kind === "derived") return { ...account };
47518
+ const privateKey = decryptField(this.key, account.key);
47519
+ try {
47520
+ if (privateKey.byteLength !== 32) {
47521
+ throw new VaultError("corrupt", "An imported vault key has the wrong length.");
47522
+ }
47523
+ return { ...account, key: sealField(this.key, privateKey) };
47524
+ } finally {
47525
+ privateKey.fill(0);
47526
+ }
47527
+ });
47528
+ return {
47529
+ ...file2,
47530
+ phrase: sealField(this.key, phrase),
47531
+ accounts
47532
+ };
47533
+ } finally {
47534
+ phrase.fill(0);
47535
+ }
47536
+ }
47537
+ async update(operation) {
47538
+ return await withVaultLock(this.path, async () => {
47539
+ const latest = await readVaultFile(this.path, this.key);
47540
+ const result = await operation(latest);
47541
+ const rewritten = this.resealSecrets(latest);
47542
+ await writeVaultFile(this.path, rewritten);
47543
+ this.file = rewritten;
47544
+ return result;
47545
+ });
47546
+ }
47547
+ };
47548
+ async function openVault(args) {
47549
+ assertVaultKey(args.key);
47550
+ return new DeviceVault(args, await readVaultFile(args.path, args.key));
47551
+ }
47552
+ async function exportVaultPhrase(args) {
47553
+ return await (await openVault(args)).phrase();
47554
+ }
47555
+ async function exportVaultAccountKey(args) {
47556
+ assertVaultKey(args.key);
47557
+ try {
47558
+ const file2 = await readVaultFile(args.path, args.key);
47559
+ const account = file2.accounts.find((candidate) => candidate.name === args.name);
47560
+ if (!account) {
47561
+ throw new VaultError("not_found", "No vault account has that name.");
47562
+ }
47563
+ let privateKey;
47564
+ if (account.kind === "derived") {
47565
+ const plaintext = decryptField(args.key, file2.phrase);
47566
+ let seed;
47567
+ try {
47568
+ seed = phraseToSeed(plaintext.toString("utf8"));
47569
+ privateKey = deriveEvmPrivateKey(seed, `m/44'/60'/0'/0/${account.index}`);
47570
+ } finally {
47571
+ seed?.fill(0);
47572
+ plaintext.fill(0);
47573
+ }
47574
+ } else {
47575
+ const plaintext = decryptField(args.key, account.key);
47576
+ try {
47577
+ if (plaintext.byteLength !== 32) {
47578
+ throw new VaultError("corrupt", "An imported vault key has the wrong length.");
47579
+ }
47580
+ privateKey = `0x${plaintext.toString("hex")}`;
47581
+ } finally {
47582
+ plaintext.fill(0);
47583
+ }
47584
+ }
47585
+ if (privateKeyToAccount(privateKey).address.toLowerCase() !== account.address.toLowerCase()) {
47586
+ throw new VaultError("corrupt", "A vault key does not match its stored address.");
47587
+ }
47588
+ return privateKey;
47589
+ } catch (error61) {
47590
+ throw asVaultError(error61, "corrupt", "Unable to export the vault account key.");
47591
+ }
47592
+ }
47593
+ function assertVaultKey(key) {
47594
+ if (key.byteLength !== 32) {
47595
+ throw new VaultError("bad_key", "A device vault key must contain exactly 32 bytes.");
47596
+ }
47597
+ }
47598
+ function assertAccountName(name) {
47599
+ const parsed = walletNameSchema.safeParse(name);
47600
+ if (!parsed.success) {
47601
+ throw new VaultError("name_taken", "The vault account name is not valid.", {
47602
+ cause: parsed.error
47603
+ });
47604
+ }
47605
+ }
47606
+ function assertAvailableName(file2, name) {
47607
+ assertAccountName(name);
47608
+ if (file2.accounts.some((account) => account.name === name)) {
47609
+ throw new VaultError("name_taken", "A vault account already has that name.");
47610
+ }
47611
+ }
47612
+ function nextDerivedIndex(file2) {
47613
+ let next = file2.nextDerivedIndex ?? 0;
47614
+ for (const account of file2.accounts) {
47615
+ if (account.kind === "derived") next = Math.max(next, account.index + 1);
47616
+ }
47617
+ return next;
47618
+ }
47619
+ function copyAccount(account) {
47620
+ if (account.kind === "derived") return { ...account };
47621
+ return {
47622
+ name: account.name,
47623
+ kind: account.kind,
47624
+ address: account.address,
47625
+ createdAt: account.createdAt
47626
+ };
47627
+ }
47628
+ function sealField(key, plaintext) {
47629
+ const { nonce, ciphertext, tag } = encryptWithKey(key, plaintext);
47630
+ return [nonce, ciphertext, tag].map((part) => part.toString("base64url")).join(".");
47631
+ }
47632
+ function openField(key, value) {
47633
+ const [nonce = "", ciphertext = "", tag = ""] = value.split(".");
47634
+ return decryptWithKey(key, {
47635
+ nonce: Buffer.from(nonce, "base64url"),
47636
+ ciphertext: Buffer.from(ciphertext, "base64url"),
47637
+ tag: Buffer.from(tag, "base64url")
47638
+ });
47639
+ }
47640
+ function decryptField(key, value) {
47641
+ try {
47642
+ return openField(key, value);
47643
+ } catch (error61) {
47644
+ throw new VaultError("bad_key", "Unable to decrypt the vault with this device key.", {
47645
+ cause: error61
47646
+ });
47647
+ }
47648
+ }
47649
+ function decryptPhraseForOpen(key, phrase) {
47650
+ try {
47651
+ return openField(key, phrase);
47652
+ } catch (error61) {
47653
+ throw new VaultError("bad_key", "Unable to decrypt the vault with this device key.", {
47654
+ cause: error61
47655
+ });
47656
+ }
47657
+ }
47658
+ function parseVaultFile(raw) {
47659
+ let value;
47660
+ try {
47661
+ value = JSON.parse(raw);
47662
+ } catch (error61) {
47663
+ throw new VaultError("corrupt", "The vault file is not valid JSON.", { cause: error61 });
47664
+ }
47665
+ const parsed = vaultFileSchema.safeParse(value);
47666
+ if (!parsed.success) {
47667
+ throw new VaultError("corrupt", "The vault file has an invalid structure.", {
47668
+ cause: parsed.error
47669
+ });
47670
+ }
47671
+ if (parsed.data.protected !== (parsed.data.wrappedKey !== void 0)) {
47672
+ throw new VaultError("corrupt", "The vault protection metadata is inconsistent.");
47673
+ }
47674
+ const names = /* @__PURE__ */ new Set();
47675
+ const indexes = /* @__PURE__ */ new Set();
47676
+ for (const account of parsed.data.accounts) {
47677
+ if (names.has(account.name)) {
47678
+ throw new VaultError("corrupt", "The vault file contains duplicate account names.");
47679
+ }
47680
+ names.add(account.name);
47681
+ if (account.kind === "derived") {
47682
+ if (indexes.has(account.index)) {
47683
+ throw new VaultError("corrupt", "The vault file contains duplicate derived indexes.");
47684
+ }
47685
+ indexes.add(account.index);
47686
+ }
47687
+ }
47688
+ return parsed.data;
47689
+ }
47690
+ async function readVaultFile(path, key) {
47691
+ const file2 = await readVaultFileUnlocked(path);
47692
+ const plaintext = decryptPhraseForOpen(key, file2.phrase);
47693
+ try {
47694
+ try {
47695
+ validateRecoveryPhrase(plaintext.toString("utf8"));
47696
+ } catch (error61) {
47697
+ throw new VaultError("corrupt", "The vault recovery phrase is not valid.", {
47698
+ cause: error61
47699
+ });
47700
+ }
47701
+ } finally {
47702
+ plaintext.fill(0);
47703
+ }
47704
+ return file2;
47705
+ }
47706
+ async function readVaultFileUnlocked(path) {
47707
+ let raw;
47708
+ try {
47709
+ raw = await readFile3(path, "utf8");
47710
+ } catch (error61) {
47711
+ if (isMissingFile2(error61)) {
47712
+ throw new VaultError("not_found", "No vault file was found at the requested path.", {
47713
+ cause: error61
47714
+ });
47715
+ }
47716
+ throw new VaultError("corrupt", "Unable to read the vault file.", { cause: error61 });
47717
+ }
47718
+ return parseVaultFile(raw);
47719
+ }
47720
+ async function writeVaultFile(path, file2) {
47721
+ await mkdir3(dirname3(path), { recursive: true, mode: 448 });
47722
+ const temporaryPath = `${path}.${process.pid}.${randomBytes3(6).toString("hex")}.tmp`;
47723
+ try {
47724
+ await writeFile3(temporaryPath, `${JSON.stringify(file2, null, 2)}
47725
+ `, {
47726
+ encoding: "utf8",
47727
+ mode: 384,
47728
+ flag: "wx"
47729
+ });
47730
+ await rename3(temporaryPath, path);
47731
+ } catch (error61) {
47732
+ await rm(temporaryPath, { force: true }).catch(() => void 0);
47733
+ throw error61;
47734
+ }
47735
+ }
47736
+ async function withVaultLock(path, operation) {
47737
+ await mkdir3(dirname3(path), { recursive: true, mode: 448 });
47738
+ const lockPath = `${path}.lock`;
47739
+ const deadline = Date.now() + VAULT_LOCK_TIMEOUT_MS;
47740
+ let handle;
47741
+ while (!handle) {
47742
+ try {
47743
+ handle = await open2(lockPath, "wx", 384);
47744
+ } catch (error61) {
47745
+ if (!isExistingFile(error61)) throw error61;
47746
+ await removeStaleVaultLock(lockPath);
47747
+ if (Date.now() >= deadline) {
47748
+ throw new VaultError("corrupt", "Timed out waiting for the vault lock.");
47749
+ }
47750
+ await new Promise((resolve) => setTimeout(resolve, 20));
47751
+ }
47752
+ }
47753
+ try {
47754
+ return await operation();
47755
+ } finally {
47756
+ await handle.close();
47757
+ await unlink(lockPath).catch(() => void 0);
47758
+ }
47759
+ }
47760
+ async function removeStaleVaultLock(path) {
47761
+ try {
47762
+ const metadata = await stat3(path);
47763
+ if (Date.now() - metadata.mtimeMs > STALE_VAULT_LOCK_MS) await unlink(path);
47764
+ } catch (error61) {
47765
+ if (!isMissingFile2(error61)) throw error61;
47766
+ }
47767
+ }
47768
+ function isMissingFile2(error61) {
47769
+ return typeof error61 === "object" && error61 !== null && "code" in error61 && Reflect.get(error61, "code") === "ENOENT";
47770
+ }
47771
+ function isExistingFile(error61) {
47772
+ return typeof error61 === "object" && error61 !== null && "code" in error61 && Reflect.get(error61, "code") === "EEXIST";
47773
+ }
47774
+ function asVaultError(error61, code, message) {
47775
+ return error61 instanceof VaultError ? error61 : new VaultError(code, message, { cause: error61 });
47776
+ }
47207
47777
  export {
47208
47778
  createKeystoreWithPhrase,
47209
47779
  decryptPrivateKey,
47210
47780
  exportKeystoreKeys,
47211
- exportRecoveryPhrase
47781
+ exportRecoveryPhrase,
47782
+ exportVaultAccountKey,
47783
+ exportVaultPhrase
47212
47784
  };
47213
47785
  //# sourceMappingURL=secrets.js.map