@vapi-network/cli 0.2.2 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -45329,8 +45329,8 @@ var DEFAULT_DISCOVERY_URL = `${DEFAULT_REGISTRY_URL}/api/call/services`;
45329
45329
  var DEFAULT_MARKETPLACE_DISCOVERY_URL = `${DEFAULT_REGISTRY_URL}/api/call/discovery`;
45330
45330
  var DEFAULT_REGISTRY_FALLBACKS = [
45331
45331
  {
45332
- discoveryUrl: "https://console.vapinetwork.ai/api/call/services",
45333
- marketplaceDiscoveryUrl: "https://console.vapinetwork.ai/api/call/discovery"
45332
+ discoveryUrl: DEFAULT_DISCOVERY_URL,
45333
+ marketplaceDiscoveryUrl: DEFAULT_MARKETPLACE_DISCOVERY_URL
45334
45334
  }
45335
45335
  ];
45336
45336
  var DEFAULT_SPEND_CAPS = {
@@ -45435,6 +45435,83 @@ async function migrateLegacyVapiHome(options = {}) {
45435
45435
  }
45436
45436
  return copied;
45437
45437
  }
45438
+ var LEGACY_REGISTRY_HOSTS = /* @__PURE__ */ new Map([
45439
+ ["console.vapinetwork.ai", "api.vapinetwork.ai"],
45440
+ ["console-staging.vapinetwork.ai", "api-staging.vapinetwork.ai"]
45441
+ ]);
45442
+ var LEGACY_REGISTRY_PATHS = /* @__PURE__ */ new Map([
45443
+ ["/api/network/services", "/api/call/services"],
45444
+ ["/api/marketplace/discovery", "/api/call/discovery"]
45445
+ ]);
45446
+ var REGISTRY_DOMAIN = "vapinetwork.ai";
45447
+ function rewriteLegacyRegistryUrl(value) {
45448
+ let url2;
45449
+ try {
45450
+ url2 = new URL(value);
45451
+ } catch {
45452
+ return value;
45453
+ }
45454
+ let rewritten = false;
45455
+ const host = LEGACY_REGISTRY_HOSTS.get(url2.hostname.toLowerCase());
45456
+ if (host) {
45457
+ url2.hostname = host;
45458
+ rewritten = true;
45459
+ }
45460
+ const hostname3 = url2.hostname.toLowerCase();
45461
+ if (hostname3 === REGISTRY_DOMAIN || hostname3.endsWith(`.${REGISTRY_DOMAIN}`)) {
45462
+ const path = LEGACY_REGISTRY_PATHS.get(url2.pathname.replace(/\/+$/, ""));
45463
+ if (path) {
45464
+ url2.pathname = path;
45465
+ rewritten = true;
45466
+ }
45467
+ }
45468
+ return rewritten ? url2.href : value;
45469
+ }
45470
+ function rewriteLegacyRegistryUrls(config2) {
45471
+ const changes = [];
45472
+ const rewrite = (field, from16) => {
45473
+ const to = rewriteLegacyRegistryUrl(from16);
45474
+ if (to !== from16) changes.push({ field, from: from16, to });
45475
+ return to;
45476
+ };
45477
+ const next = {
45478
+ ...config2,
45479
+ discoveryUrl: rewrite("discoveryUrl", config2.discoveryUrl),
45480
+ marketplaceDiscoveryUrl: rewrite("marketplaceDiscoveryUrl", config2.marketplaceDiscoveryUrl),
45481
+ ...config2.registryFallbacks ? {
45482
+ registryFallbacks: config2.registryFallbacks.map((fallback, index2) => ({
45483
+ discoveryUrl: rewrite(
45484
+ `registryFallbacks[${index2}].discoveryUrl`,
45485
+ fallback.discoveryUrl
45486
+ ),
45487
+ marketplaceDiscoveryUrl: rewrite(
45488
+ `registryFallbacks[${index2}].marketplaceDiscoveryUrl`,
45489
+ fallback.marketplaceDiscoveryUrl
45490
+ )
45491
+ }))
45492
+ } : {}
45493
+ };
45494
+ return changes.length === 0 ? { config: config2, changes } : { config: next, changes };
45495
+ }
45496
+ function formatLegacyRegistryRewrites(changes) {
45497
+ return [
45498
+ "Rewrote retired vAPI registry URLs in the local config:",
45499
+ ...changes.map((change) => ` ${change.field}: ${change.from} -> ${change.to}`)
45500
+ ].join("\n");
45501
+ }
45502
+ async function migrateLegacyRegistryConfig(path = getVapiPaths().config) {
45503
+ let raw;
45504
+ try {
45505
+ raw = await readFile(path, "utf8");
45506
+ } catch (error61) {
45507
+ if (isMissingFile(error61)) return [];
45508
+ throw error61;
45509
+ }
45510
+ const { config: config2, changes } = rewriteLegacyRegistryUrls(configSchema.parse(JSON.parse(raw)));
45511
+ if (changes.length === 0) return [];
45512
+ await writeConfigFile(path, config2);
45513
+ return changes;
45514
+ }
45438
45515
  function getDefaultConfig(source = process.env, options = {}) {
45439
45516
  const registry2 = registryEndpoints(source.VAPI_REGISTRY_URL?.trim() || DEFAULT_REGISTRY_URL);
45440
45517
  const requested = new Set(options.networks ?? ["base"]);
@@ -45473,7 +45550,7 @@ function getDefaultConfig(source = process.env, options = {}) {
45473
45550
  spendCaps: { ...DEFAULT_SPEND_CAPS }
45474
45551
  };
45475
45552
  }
45476
- async function loadConfig(path = getVapiPaths().config, source = process.env) {
45553
+ async function loadConfig(path = getVapiPaths().config, source = process.env, options = {}) {
45477
45554
  if (!process.env.VAPI_HOME?.trim() && path === getVapiPaths(join(homedir(), ".vapi")).config) {
45478
45555
  await migrateLegacyVapiHome({ targetDirectory: join(homedir(), ".vapi") });
45479
45556
  }
@@ -45484,8 +45561,16 @@ async function loadConfig(path = getVapiPaths().config, source = process.env) {
45484
45561
  if (isMissingFile(error61)) return getDefaultConfig(source);
45485
45562
  throw error61;
45486
45563
  }
45487
- const config2 = configSchema.parse(JSON.parse(raw));
45488
- config2.registryFallbacks ??= DEFAULT_REGISTRY_FALLBACKS.map((fallback) => ({ ...fallback }));
45564
+ const parsed = configSchema.parse(JSON.parse(raw));
45565
+ parsed.registryFallbacks ??= DEFAULT_REGISTRY_FALLBACKS.map((fallback) => ({ ...fallback }));
45566
+ const migrated = rewriteLegacyRegistryUrls(parsed);
45567
+ const config2 = migrated.config;
45568
+ if (migrated.changes.length > 0 && options.notice) {
45569
+ options.notice(
45570
+ `${formatLegacyRegistryRewrites(migrated.changes)}
45571
+ Run vapi init to write the new URLs to ${path}.`
45572
+ );
45573
+ }
45489
45574
  const baseRpcUrl = source.BASE_RPC_URL?.trim();
45490
45575
  const arcRpcUrl = source.ARC_TESTNET_RPC_URL?.trim();
45491
45576
  const solanaRpcUrl = source.SOLANA_RPC_URL?.trim();
@@ -45541,15 +45626,7 @@ async function writeDefaultConfig(path = getVapiPaths().config, source = process
45541
45626
  if (copied.includes(path)) return await loadConfig(path, source);
45542
45627
  }
45543
45628
  const config2 = getDefaultConfig(source, options);
45544
- await mkdir(dirname(path), { recursive: true, mode: 448 });
45545
- const temporaryPath = `${path}.${process.pid}.tmp`;
45546
- await writeFile(temporaryPath, `${JSON.stringify(config2, null, 2)}
45547
- `, {
45548
- encoding: "utf8",
45549
- mode: 384,
45550
- flag: "wx"
45551
- });
45552
- await rename(temporaryPath, path);
45629
+ await writeConfigFile(path, config2);
45553
45630
  return config2;
45554
45631
  }
45555
45632
  async function enableDefaultNetwork(network, path = getVapiPaths().config, source = process.env) {
@@ -45560,6 +45637,10 @@ async function enableDefaultNetwork(network, path = getVapiPaths().config, sourc
45560
45637
  usdc: NETWORKS[SOLANA_MAINNET_CAIP2].usdc
45561
45638
  };
45562
45639
  }
45640
+ await writeConfigFile(path, config2);
45641
+ return config2;
45642
+ }
45643
+ async function writeConfigFile(path, config2) {
45563
45644
  await mkdir(dirname(path), { recursive: true, mode: 448 });
45564
45645
  const temporaryPath = `${path}.${process.pid}.tmp`;
45565
45646
  await writeFile(temporaryPath, `${JSON.stringify(config2, null, 2)}
@@ -45569,7 +45650,6 @@ async function enableDefaultNetwork(network, path = getVapiPaths().config, sourc
45569
45650
  flag: "wx"
45570
45651
  });
45571
45652
  await rename(temporaryPath, path);
45572
- return config2;
45573
45653
  }
45574
45654
  async function pathExists(path) {
45575
45655
  try {
@@ -45990,6 +46070,49 @@ async function unlockKeystore(passphrase, path = getVapiPaths().keystore) {
45990
46070
  }
45991
46071
  return await paymentAccountFromKeys(keys);
45992
46072
  }
46073
+ async function readKeystoreAddress(path = getVapiPaths().keystore) {
46074
+ let raw;
46075
+ try {
46076
+ raw = await readFile3(path, "utf8");
46077
+ } catch {
46078
+ return void 0;
46079
+ }
46080
+ let parsed;
46081
+ try {
46082
+ parsed = JSON.parse(raw);
46083
+ } catch {
46084
+ return void 0;
46085
+ }
46086
+ const address2 = typeof parsed === "object" && parsed !== null ? Reflect.get(parsed, "address") : void 0;
46087
+ return typeof address2 === "string" ? address2 : void 0;
46088
+ }
46089
+ async function exportKeystoreKeys(passphrase, path = getVapiPaths().keystore) {
46090
+ let raw;
46091
+ try {
46092
+ raw = await readFile3(path, "utf8");
46093
+ } catch (error61) {
46094
+ if (isMissingFile(error61)) {
46095
+ throw new KeystoreError(`No vAPI keystore found at ${path}. Run vapi init.`);
46096
+ }
46097
+ throw error61;
46098
+ }
46099
+ const keys = await decryptKeys(parseAnyKeystore(JSON.parse(raw)), passphrase);
46100
+ const evmPrivateKey = keys.evm;
46101
+ const exported = {
46102
+ evm: { address: privateKeyToAccount(evmPrivateKey).address, privateKey: evmPrivateKey }
46103
+ };
46104
+ if (!keys.solana) return exported;
46105
+ const seed = Uint8Array.from(Buffer.from(keys.solana, "base64"));
46106
+ const signer = await createSolanaSigner(Uint8Array.from(seed));
46107
+ const publicKey = Uint8Array.from(getBase58Encoder().encode(signer.address));
46108
+ const secretKey = new Uint8Array(seed.length + publicKey.length);
46109
+ secretKey.set(seed);
46110
+ secretKey.set(publicKey, seed.length);
46111
+ const encoded = getBase58Decoder().decode(secretKey);
46112
+ seed.fill(0);
46113
+ secretKey.fill(0);
46114
+ return { ...exported, solana: { address: signer.address, secretKey: encoded } };
46115
+ }
45993
46116
  async function enableSolanaKey(passphrase, path = getVapiPaths().keystore) {
45994
46117
  let raw;
45995
46118
  try {
@@ -46669,7 +46792,7 @@ function csvCell(value) {
46669
46792
  import { mkdir as mkdir6, writeFile as writeFile4 } from "node:fs/promises";
46670
46793
  import { arch, platform, release } from "node:os";
46671
46794
  import { join as join2 } from "node:path";
46672
- var VAPI_CLIENT_VERSION = "0.2.2";
46795
+ var VAPI_CLIENT_VERSION = "0.2.3";
46673
46796
  async function createSupportReport(options) {
46674
46797
  const message = options.message.trim();
46675
46798
  if (!message) throw new Error("Support report message must not be empty.");
@@ -48962,7 +49085,7 @@ function rgbToAnsi256(red, green, blue) {
48962
49085
  }
48963
49086
 
48964
49087
  // src/version.ts
48965
- var CLI_VERSION = "0.2.2";
49088
+ var CLI_VERSION = "0.2.3";
48966
49089
 
48967
49090
  // src/cli.ts
48968
49091
  var HELP_HEADING = "vAPI Network";
@@ -48980,6 +49103,7 @@ Usage:
48980
49103
  vapi receipts export --format <json|csv> [--range <24h|7d|30d>]
48981
49104
  vapi stats [--range <24h|7d|30d>] [--json]
48982
49105
  vapi sweep <address> [--network <caip2>] [--json]
49106
+ vapi export-key [--network <caip2>] [--json]
48983
49107
  vapi report "<what happened>" [--include-addresses] [--send] [--json]
48984
49108
  vapi mcp [--json]
48985
49109
  vapi serve [--json]
@@ -49048,6 +49172,9 @@ async function runCli(argv = process.argv.slice(2), io = processIo, dependencies
49048
49172
  case "sweep":
49049
49173
  await sweepCommand(args.slice(1), json2, io);
49050
49174
  return 0;
49175
+ case "export-key":
49176
+ await exportKeyCommand(args.slice(1), json2, io);
49177
+ return 0;
49051
49178
  case "report":
49052
49179
  await reportCommand(args.slice(1), json2, io, dependencies);
49053
49180
  return 0;
@@ -49087,8 +49214,18 @@ async function initCommand(argv, json2, io, dependencies) {
49087
49214
  if (!process.env.VAPI_HOME?.trim()) {
49088
49215
  migrated = await migrateLegacyVapiHome({ targetDirectory: paths.directory, notice: io.stderr });
49089
49216
  }
49217
+ const adopted = migrated.includes(paths.keystore);
49218
+ if (!adopted && await fileExists(paths.keystore)) {
49219
+ const address2 = await readKeystoreAddress(paths.keystore);
49220
+ throw new Error(
49221
+ [
49222
+ `Keystore already exists at ${paths.keystore}. Refusing to replace the local payment key.`,
49223
+ ...address2 ? [`Address: ${address2}`] : []
49224
+ ].join("\n")
49225
+ );
49226
+ }
49090
49227
  const passphrase = await getKeystorePassphrase({ confirm: true });
49091
- let account = migrated.includes(paths.keystore) ? await unlockKeystore(passphrase, paths.keystore) : await createKeystore(passphrase, paths.keystore, { enableSolana });
49228
+ let account = adopted ? await unlockKeystore(passphrase, paths.keystore) : await createKeystore(passphrase, paths.keystore, { enableSolana });
49092
49229
  if (enableSolana && !account.solana) {
49093
49230
  account = await enableSolanaKey(passphrase, paths.keystore);
49094
49231
  }
@@ -49097,7 +49234,8 @@ async function initCommand(argv, json2, io, dependencies) {
49097
49234
  } else if (enableSolana) {
49098
49235
  await enableDefaultNetwork("solana", paths.config);
49099
49236
  }
49100
- const config2 = await loadConfig(paths.config);
49237
+ const configRewrites = await migrateLegacyRegistryConfig(paths.config);
49238
+ const config2 = await readConfig(paths.config, io);
49101
49239
  const accounts = await listAccounts({
49102
49240
  address: account.address,
49103
49241
  ...account.solana ? { solanaAddress: account.solana.address } : {},
@@ -49109,6 +49247,7 @@ async function initCommand(argv, json2, io, dependencies) {
49109
49247
  accounts,
49110
49248
  config: paths.config,
49111
49249
  keystore: paths.keystore,
49250
+ ...configRewrites.length > 0 ? { configRewrites } : {},
49112
49251
  message: "vAPI wallet created. Its encrypted key stays on this machine.",
49113
49252
  nextSteps: buildNextSteps(account.address)
49114
49253
  };
@@ -49116,6 +49255,7 @@ async function initCommand(argv, json2, io, dependencies) {
49116
49255
  io.stdout(JSON.stringify(result));
49117
49256
  return;
49118
49257
  }
49258
+ if (configRewrites.length > 0) io.stdout(formatLegacyRegistryRewrites(configRewrites));
49119
49259
  io.stdout(result.message);
49120
49260
  io.stdout(`Address: ${result.address}`);
49121
49261
  io.stdout(
@@ -49140,7 +49280,7 @@ async function fundCommand(argv, json2, io, dependencies) {
49140
49280
  });
49141
49281
  const fiatAmount = optionalUsdAmount(parsed.one("--amount"), "--amount");
49142
49282
  const paths = getVapiPaths();
49143
- const config2 = await loadConfig(paths.config);
49283
+ const config2 = await readConfig(paths.config, io);
49144
49284
  const account = await unlockKeystore(await getKeystorePassphrase(), paths.keystore);
49145
49285
  const session = await createOnrampSession({
49146
49286
  address: account.address,
@@ -49160,7 +49300,7 @@ async function searchCommand(argv, json2, io, dependencies) {
49160
49300
  maximumPositionals: 1
49161
49301
  });
49162
49302
  const limit = optionalPositiveInteger(parsed.one("--limit"), "--limit");
49163
- const config2 = await loadConfig(getVapiPaths().config);
49303
+ const config2 = await readConfig(getVapiPaths().config, io);
49164
49304
  const page = await searchMarketplace(
49165
49305
  {
49166
49306
  ...parsed.positionals[0] ? { query: parsed.positionals[0] } : {},
@@ -49184,7 +49324,7 @@ async function inspectCommand(argv, json2, io, dependencies) {
49184
49324
  parsed.positionals[0],
49185
49325
  "Usage: vapi inspect <id> [--endpoint <name>]"
49186
49326
  );
49187
- const config2 = await loadConfig(getVapiPaths().config);
49327
+ const config2 = await readConfig(getVapiPaths().config, io);
49188
49328
  const result = await inspectService(
49189
49329
  { id, ...parsed.one("--endpoint") ? { endpoint: parsed.one("--endpoint") } : {} },
49190
49330
  config2,
@@ -49208,7 +49348,7 @@ async function payCommand(argv, json2, io) {
49208
49348
  });
49209
49349
  const target = requiredPositional(parsed.positionals[0], "Usage: vapi pay <id-or-url> [options]");
49210
49350
  const paths = getVapiPaths();
49211
- const config2 = await loadConfig(paths.config);
49351
+ const config2 = await readConfig(paths.config, io);
49212
49352
  const account = await unlockKeystore(await getKeystorePassphrase(), paths.keystore);
49213
49353
  const bodyText = parsed.one("--body");
49214
49354
  const maxPriceUsd = aliasedOption(parsed, "--max", "--max-price-usd");
@@ -49234,7 +49374,7 @@ async function payCommand(argv, json2, io) {
49234
49374
  async function balanceCommand(argv, json2, io, dependencies) {
49235
49375
  requireNoArguments(argv, "balance");
49236
49376
  const paths = getVapiPaths();
49237
- const config2 = await loadConfig(paths.config);
49377
+ const config2 = await readConfig(paths.config, io);
49238
49378
  const account = await unlockKeystore(await getKeystorePassphrase(), paths.keystore);
49239
49379
  const wallet = await getWallet(account, config2, dependencies);
49240
49380
  output2(io, json2, wallet, formatWallet(wallet));
@@ -49252,7 +49392,7 @@ async function accountsCommand(argv, json2, io, dependencies) {
49252
49392
  const passphrase = await getKeystorePassphrase();
49253
49393
  const account = enable ? await enableSolanaKey(passphrase, paths.keystore) : await unlockKeystore(passphrase, paths.keystore);
49254
49394
  if (enable) await enableDefaultNetwork("solana", paths.config);
49255
- const config2 = await loadConfig(paths.config);
49395
+ const config2 = await readConfig(paths.config, io);
49256
49396
  const accounts = await listAccounts({
49257
49397
  address: account.address,
49258
49398
  ...account.solana ? { solanaAddress: account.solana.address } : {},
@@ -49321,7 +49461,7 @@ async function sweepCommand(argv, json2, io) {
49321
49461
  );
49322
49462
  const requestedNetwork = parsed.one("--network");
49323
49463
  const paths = getVapiPaths();
49324
- const config2 = await loadConfig(paths.config);
49464
+ const config2 = await readConfig(paths.config, io);
49325
49465
  const account = await unlockKeystore(await getKeystorePassphrase(), paths.keystore);
49326
49466
  if (requestedNetwork && !isNetworkConfigured(config2.networks, requestedNetwork)) {
49327
49467
  throw new Error(`Network ${requestedNetwork} is not configured.`);
@@ -49365,6 +49505,33 @@ async function reportCommand(argv, json2, io, dependencies) {
49365
49505
  });
49366
49506
  output2(io, json2, result, formatSupportReport(result));
49367
49507
  }
49508
+ var EXPORT_KEY_WARNING = "Anyone with this key can spend the wallet. Never paste it into a website or chat.";
49509
+ async function exportKeyCommand(argv, json2, io) {
49510
+ const parsed = parseArguments(argv, {
49511
+ valueOptions: /* @__PURE__ */ new Set(["--network"]),
49512
+ maximumPositionals: 0
49513
+ });
49514
+ const network = parsed.one("--network") ?? BASE_MAINNET_CAIP2;
49515
+ const solana = isSolanaNetwork(network);
49516
+ if (!solana && !network.startsWith("eip155:")) {
49517
+ throw new UsageError("--network must be an eip155:<chainId> or Solana network identifier.");
49518
+ }
49519
+ const paths = getVapiPaths();
49520
+ const keys = await exportKeystoreKeys(await getKeystorePassphrase(), paths.keystore);
49521
+ if (solana && !keys.solana) {
49522
+ throw new Error(
49523
+ `No Solana key is enabled in ${paths.keystore}. Run vapi accounts --enable solana first.`
49524
+ );
49525
+ }
49526
+ const selected = solana && keys.solana ? keys.solana : keys.evm;
49527
+ const result = {
49528
+ network,
49529
+ address: selected.address,
49530
+ privateKey: "secretKey" in selected ? selected.secretKey : selected.privateKey
49531
+ };
49532
+ io.stderr(EXPORT_KEY_WARNING);
49533
+ io.stdout(json2 ? JSON.stringify(result) : result.privateKey);
49534
+ }
49368
49535
  async function mcpCommand(argv) {
49369
49536
  requireNoArguments(argv, "mcp");
49370
49537
  const paths = getVapiPaths();
@@ -49378,6 +49545,9 @@ async function mcpCommand(argv) {
49378
49545
  searchesPath: paths.searches
49379
49546
  });
49380
49547
  }
49548
+ async function readConfig(path, io) {
49549
+ return await loadConfig(path, process.env, { notice: io.stderr });
49550
+ }
49381
49551
  function output2(io, json2, value, human) {
49382
49552
  io.stdout(json2 ? JSON.stringify(value) : human);
49383
49553
  }