@vapi-network/cli 0.9.2 → 0.9.4

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/cli.js CHANGED
@@ -10116,11 +10116,9 @@ var init_call = __esm({
10116
10116
  }
10117
10117
  });
10118
10118
 
10119
- // src/cli.ts
10120
- import { spawn as spawn3 } from "node:child_process";
10121
- import { stat as stat16 } from "node:fs/promises";
10122
- import { join as join28 } from "node:path";
10123
- import { createInterface as createInterface3 } from "node:readline/promises";
10119
+ // src/update-check.ts
10120
+ import { lookup } from "node:dns/promises";
10121
+ import { mkdir as mkdir21, readFile as readFile29, writeFile as writeFile12 } from "node:fs/promises";
10124
10122
 
10125
10123
  // ../../node_modules/.pnpm/zod@4.5.4/node_modules/zod/v4/classic/external.js
10126
10124
  var external_exports = {};
@@ -39357,8 +39355,10 @@ var listingIdentitySchema = external_exports.looseObject({
39357
39355
  }).optional().catch(void 0)
39358
39356
  });
39359
39357
  var listingDisclosureShape = {
39360
- group: listingGroupSchema.optional(),
39361
- fee: listingFeeSchema.optional(),
39358
+ // Presentation metadata must not make an otherwise executable row vanish
39359
+ // when the registry adds a new group or changes the fee display shape.
39360
+ group: listingGroupSchema.optional().catch(void 0),
39361
+ fee: listingFeeSchema.optional().catch(void 0),
39362
39362
  verification: listingVerificationFieldSchema,
39363
39363
  liveness: listingLivenessSchema.optional().catch(void 0),
39364
39364
  conformance: listingConformanceSchema.optional().catch(void 0),
@@ -39366,11 +39366,13 @@ var listingDisclosureShape = {
39366
39366
  };
39367
39367
  var marketplaceBadgeCodeSchema = external_exports.enum([
39368
39368
  "live_x402",
39369
+ "mpp",
39369
39370
  "partner",
39370
39371
  "payout_wallet_confirmed",
39371
39372
  "vapi_verified",
39372
39373
  "external_catalog",
39373
- "approved_vendor"
39374
+ "approved_vendor",
39375
+ "unverified"
39374
39376
  ]);
39375
39377
  var marketplaceBadgeSchema = external_exports.looseObject({
39376
39378
  code: marketplaceBadgeCodeSchema,
@@ -39389,7 +39391,13 @@ var marketplaceCardSchema = external_exports.looseObject({
39389
39391
  summary: external_exports.string().trim().min(1).max(1e3),
39390
39392
  byline: external_exports.string().trim().min(1).max(160).optional(),
39391
39393
  bylineHref: marketplaceHrefSchema.optional(),
39392
- badges: external_exports.array(marketplaceBadgeSchema).max(8),
39394
+ badges: external_exports.preprocess(
39395
+ (badges) => Array.isArray(badges) ? badges.flatMap((badge) => {
39396
+ const parsed = marketplaceBadgeSchema.safeParse(badge);
39397
+ return parsed.success ? [parsed.data] : [];
39398
+ }) : badges,
39399
+ external_exports.array(marketplaceBadgeSchema).max(8)
39400
+ ),
39393
39401
  facts: external_exports.array(marketplacePublicFactSchema).max(12)
39394
39402
  });
39395
39403
  var marketplaceHitBase = {
@@ -44870,9 +44878,9 @@ function createPublicFetch(options) {
44870
44878
  });
44871
44879
  });
44872
44880
  }
44873
- function pinnedLookup(lookup) {
44881
+ function pinnedLookup(lookup2) {
44874
44882
  return (hostname3, dnsOptions, callback) => {
44875
- void lookup(normalizeHostname(hostname3)).then((addresses) => {
44883
+ void lookup2(normalizeHostname(hostname3)).then((addresses) => {
44876
44884
  if (addresses.length === 0 || addresses.some(isNonPublicAddress)) {
44877
44885
  callback(guardError(new URL(`https://${hostname3}`)), "");
44878
44886
  return;
@@ -46010,9 +46018,9 @@ function isRecord2(value) {
46010
46018
  var CALLABLE_CATEGORIES = ["ai", "data", "crypto", "compute", "search"];
46011
46019
  var callableCategorySchema = external_exports.enum(CALLABLE_CATEGORIES);
46012
46020
  var DISCOVERY_CATEGORIES = [...CALLABLE_CATEGORIES, "other"];
46013
- var discoveryCategorySchema = external_exports.enum(DISCOVERY_CATEGORIES);
46021
+ var discoveryCategorySchema = external_exports.string().trim().min(1);
46014
46022
  var CALLABLE_CATEGORY_SET = new Set(CALLABLE_CATEGORIES);
46015
- var serviceTierSchema = external_exports.enum(["listed", "verified", "partner"]);
46023
+ var serviceTierSchema = external_exports.string().trim().min(1);
46016
46024
  var discoveryEndpointSchema = external_exports.looseObject({
46017
46025
  name: external_exports.string(),
46018
46026
  method: external_exports.string(),
@@ -46042,6 +46050,24 @@ var discoveryEndpointSchema = external_exports.looseObject({
46042
46050
  asset: external_exports.string().refine(isSolanaAddress, "Expected a Solana base58 address."),
46043
46051
  payTo: external_exports.string().refine(isSolanaAddress, "Expected a Solana base58 address."),
46044
46052
  checkedAt: external_exports.iso.datetime()
46053
+ }),
46054
+ external_exports.looseObject({
46055
+ protocol: external_exports.literal("mpp"),
46056
+ intent: external_exports.enum(["charge", "authorize", "subscription"]).nullable(),
46057
+ method: external_exports.string().trim().min(1).max(40).nullable(),
46058
+ checkedAt: external_exports.iso.datetime(),
46059
+ // Keep common x402 properties readable on the union while making it
46060
+ // impossible for MPP metadata to masquerade as executable x402 terms.
46061
+ network: external_exports.never().optional(),
46062
+ payTo: external_exports.never().optional()
46063
+ }),
46064
+ external_exports.looseObject({
46065
+ // Future payment protocols remain visible to callers so the payment
46066
+ // layer can refuse them explicitly instead of discovery losing the row.
46067
+ protocol: external_exports.string().trim().min(1).max(40),
46068
+ checkedAt: external_exports.iso.datetime(),
46069
+ network: external_exports.never().optional(),
46070
+ payTo: external_exports.never().optional()
46045
46071
  })
46046
46072
  ]).optional()
46047
46073
  });
@@ -46135,7 +46161,7 @@ function resolveTarget(service, endpointName) {
46135
46161
  }
46136
46162
  if (targets.size !== 1) {
46137
46163
  const paymentNetworks = service.endpoints.flatMap(
46138
- (endpoint) => endpoint.payment ? [endpoint.payment.network] : []
46164
+ (endpoint) => typeof endpoint.payment?.network === "string" ? [endpoint.payment.network] : []
46139
46165
  );
46140
46166
  throw new DiscoveryCatalogError(
46141
46167
  "ambiguous_default",
@@ -47110,7 +47136,8 @@ function getVapiPaths(directory = process.env.VAPI_HOME?.trim() || join2(homedir
47110
47136
  keystore: join2(directory, "keystore.json"),
47111
47137
  receipts: join2(directory, "receipts.jsonl"),
47112
47138
  searches: join2(directory, "searches.jsonl"),
47113
- ledger: join2(directory, "spend-ledger.json")
47139
+ ledger: join2(directory, "spend-ledger.json"),
47140
+ updateCheck: join2(directory, "update-check.json")
47114
47141
  };
47115
47142
  }
47116
47143
  var getAgentCashPaths = getVapiPaths;
@@ -70374,7 +70401,7 @@ function windowsDataProtectionStore(platform2, run, env, installed) {
70374
70401
  };
70375
70402
  }
70376
70403
  function libsecretStore(platform2, run, installed) {
70377
- const lookup = async (name) => {
70404
+ const lookup2 = async (name) => {
70378
70405
  const result = await run(SECRET_TOOL_BINARY, [
70379
70406
  "lookup",
70380
70407
  "service",
@@ -70393,9 +70420,9 @@ function libsecretStore(platform2, run, installed) {
70393
70420
  available: installed(SECRET_TOOL_BINARY),
70394
70421
  platform: platform2,
70395
70422
  description: "the libsecret keyring",
70396
- get: lookup,
70423
+ get: lookup2,
70397
70424
  async has(name) {
70398
- return await lookup(name) !== void 0;
70425
+ return await lookup2(name) !== void 0;
70399
70426
  },
70400
70427
  async set(name, passphrase) {
70401
70428
  requirePassphrase(passphrase);
@@ -70417,7 +70444,7 @@ function libsecretStore(platform2, run, installed) {
70417
70444
  requireSuccess(result, `store the passphrase of ${name} in the secret service`);
70418
70445
  },
70419
70446
  async remove(name) {
70420
- const existed = await lookup(name) !== void 0;
70447
+ const existed = await lookup2(name) !== void 0;
70421
70448
  const result = await run(SECRET_TOOL_BINARY, [
70422
70449
  "clear",
70423
70450
  "service",
@@ -71480,6 +71507,8 @@ var VAULT_KEY_ACCOUNT = "vault-key";
71480
71507
  var VAULT_SESSION_ACCOUNT = "vault-session";
71481
71508
  var DEVICE_KEY_LENGTH = 32;
71482
71509
  var DEFAULT_SESSION_HOURS = 8;
71510
+ var RESTORE_GAP_LIMIT = 5;
71511
+ var MAX_RESTORE_ACCOUNTS = 1e4;
71483
71512
  var BASE64_PATTERN = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/u;
71484
71513
  var deviceKeyOperations = Promise.resolve();
71485
71514
  var sessionSchema = external_exports.object({
@@ -71629,26 +71658,41 @@ async function restoreVault(args) {
71629
71658
  throw new VaultError("corrupt", "That recovery phrase is not valid.", { cause: error61 });
71630
71659
  }
71631
71660
  let seed;
71632
- let discoveredAccounts = 0;
71661
+ if (args.accountCount !== void 0 && (!Number.isSafeInteger(args.accountCount) || args.accountCount < 1 || args.accountCount > MAX_RESTORE_ACCOUNTS)) {
71662
+ throw new VaultError(
71663
+ "corrupt",
71664
+ `Account count must be a positive integer no greater than ${MAX_RESTORE_ACCOUNTS}.`
71665
+ );
71666
+ }
71667
+ let discoveredAccounts = args.accountCount ?? 0;
71668
+ let scannedAddresses = 0;
71633
71669
  try {
71634
71670
  seed = phraseToSeed(phrase);
71635
71671
  } catch (error61) {
71636
71672
  throw asVaultError2(error61, "Unable to derive an account while restoring the vault.");
71637
71673
  }
71638
71674
  try {
71639
- for (let index2 = 0; ; index2 += 1) {
71675
+ for (let index2 = 0, emptyRun = 0; args.accountCount === void 0; index2 += 1) {
71640
71676
  let address4;
71641
71677
  try {
71642
71678
  address4 = privateKeyToAccount(deriveEvmPrivateKey(seed, `m/44'/60'/0'/0/${index2}`)).address;
71643
71679
  } catch (error61) {
71644
71680
  throw asVaultError2(error61, "Unable to derive an account while restoring the vault.");
71645
71681
  }
71646
- if (!await args.probe(address4)) break;
71647
- discoveredAccounts += 1;
71682
+ const funded = await args.probe(address4);
71683
+ scannedAddresses += 1;
71684
+ if (funded) {
71685
+ discoveredAccounts = index2 + 1;
71686
+ emptyRun = 0;
71687
+ } else {
71688
+ emptyRun += 1;
71689
+ if (emptyRun === RESTORE_GAP_LIMIT) break;
71690
+ }
71648
71691
  }
71649
71692
  } finally {
71650
71693
  seed.fill(0);
71651
71694
  }
71695
+ args.onScanComplete?.(scannedAddresses);
71652
71696
  let vault;
71653
71697
  try {
71654
71698
  vault = await createVault({ path: args.path, key: args.key, phrase, now: args.now });
@@ -72137,6 +72181,26 @@ var WalletStore = class _WalletStore {
72137
72181
  }
72138
72182
  );
72139
72183
  }
72184
+ /** Checks a cached signer's current vault session without decrypting its account again. */
72185
+ async assertSigningAuthority(name) {
72186
+ const stored = await this.readVaultUnlocked();
72187
+ if (!stored?.protected || !stored.accounts.some((account) => account.name === name)) return;
72188
+ try {
72189
+ const key2 = await unlockProtectedVault({
72190
+ path: this.vaultPath,
72191
+ secrets: this.secrets,
72192
+ env: this.environment,
72193
+ now: () => this.now()
72194
+ });
72195
+ key2.fill(0);
72196
+ } catch (error61) {
72197
+ this.dropVaultCache();
72198
+ if (error61 instanceof VaultError && error61.code === "password_required") {
72199
+ throw new KeystoreError(VAULT_LOCKED_MESSAGE, { cause: error61 });
72200
+ }
72201
+ throw error61;
72202
+ }
72203
+ }
72140
72204
  /** Returns one wallet's vault signer, migrating its legacy keystore if needed. */
72141
72205
  async unlock(name, passphrase) {
72142
72206
  const wallet = this.resolve({ name });
@@ -77523,7 +77587,7 @@ function createRunBudget(args) {
77523
77587
  import { mkdir as mkdir15, writeFile as writeFile8 } from "node:fs/promises";
77524
77588
  import { arch, platform, release } from "node:os";
77525
77589
  import { join as join13 } from "node:path";
77526
- var VAPI_CLIENT_VERSION = "0.9.2";
77590
+ var VAPI_CLIENT_VERSION = "0.9.4";
77527
77591
  async function createSupportReport(options) {
77528
77592
  const message = options.message.trim();
77529
77593
  if (!message) throw new Error("Support report message must not be empty.");
@@ -77796,7 +77860,7 @@ async function completeX402Payment(args) {
77796
77860
  let paidAttempt;
77797
77861
  const requestStarted = nowMs();
77798
77862
  try {
77799
- paidAttempt = await args.send(paidRequest);
77863
+ paidAttempt = await args.send(paidRequest, quote);
77800
77864
  } catch (error61) {
77801
77865
  if (trace) trace.phases.requestMs = elapsed(nowMs, requestStarted);
77802
77866
  throw settlementUnknown(
@@ -77814,7 +77878,7 @@ async function completeX402Payment(args) {
77814
77878
  const settleStarted = nowMs();
77815
77879
  const settlement = parseSettlementResponse(paidResponse.headers);
77816
77880
  const settlementOutcome = classifySettlement(settlement);
77817
- const receiptSettlement = sanitizeReceiptValue(settlement, receiptSecrets);
77881
+ const receiptSettlement = sanitizeReturnedValue(settlement, receiptSecrets);
77818
77882
  if (trace) {
77819
77883
  const transaction2 = settlementTransaction(receiptSettlement);
77820
77884
  const explorerUrl = transaction2 ? explorerTransactionUrl(quote.accepted.network, transaction2) : void 0;
@@ -77891,6 +77955,7 @@ async function completeX402Payment(args) {
77891
77955
  trace.phases.requestMs = (trace.phases.requestMs ?? 0) + elapsed(nowMs, bodyStarted);
77892
77956
  }
77893
77957
  }
77958
+ body = sanitizeReturnedValue(body, receiptSecrets);
77894
77959
  }
77895
77960
  const result = {
77896
77961
  response: paidResponse,
@@ -77899,7 +77964,11 @@ async function completeX402Payment(args) {
77899
77964
  quote,
77900
77965
  payer,
77901
77966
  ...payment.paymentId ? { paymentId: payment.paymentId } : {},
77902
- settlement,
77967
+ paymentProof: sanitizeReturnedValue(
77968
+ paidResponse.headers.get("x-vapi-payment-proof"),
77969
+ receiptSecrets
77970
+ ),
77971
+ settlement: receiptSettlement,
77903
77972
  settlementOutcome
77904
77973
  };
77905
77974
  if (settlementOutcome === "succeeded" && args.ceilingSweep) {
@@ -78036,19 +78105,70 @@ function sanitizeThrownError(error61, secrets) {
78036
78105
  }
78037
78106
  return new Error(message, cause === void 0 ? void 0 : { cause });
78038
78107
  }
78039
- function sanitizeReceiptValue(value, secrets, depth = 0) {
78108
+ function sanitizeReturnedValue(value, secrets) {
78040
78109
  if (typeof value === "string") return redactKnownSecrets(value, secrets);
78041
- if (value === null || typeof value !== "object") return value;
78042
- if (depth >= 16) return "[redacted nested settlement value]";
78043
- if (Array.isArray(value)) {
78044
- return value.map((item) => sanitizeReceiptValue(item, secrets, depth + 1));
78110
+ if (!isPlainReturnedContainer(value)) return value;
78111
+ const nodes = /* @__PURE__ */ new WeakMap();
78112
+ const ordered = [];
78113
+ const nodeFor = (source) => {
78114
+ const existing = nodes.get(source);
78115
+ if (existing) return existing;
78116
+ const copy = Array.isArray(source) ? new Array(source.length) : Object.create(Object.getPrototypeOf(source));
78117
+ const node2 = { source, copy, parents: /* @__PURE__ */ new Set(), changed: false };
78118
+ nodes.set(source, node2);
78119
+ ordered.push(node2);
78120
+ return node2;
78121
+ };
78122
+ const root = nodeFor(value);
78123
+ for (let index2 = 0; index2 < ordered.length; index2 += 1) {
78124
+ const node2 = ordered[index2];
78125
+ for (const [key2, item] of Object.entries(node2.source)) {
78126
+ const safeKey = Array.isArray(node2.source) ? key2 : redactKnownSecrets(key2, secrets);
78127
+ if (safeKey !== key2) node2.changed = true;
78128
+ if (typeof item === "string") {
78129
+ const safeItem = redactKnownSecrets(item, secrets);
78130
+ defineReturnedProperty(node2.copy, safeKey, safeItem);
78131
+ if (safeItem !== item) node2.changed = true;
78132
+ } else if (isPlainReturnedContainer(item)) {
78133
+ const child = nodeFor(item);
78134
+ child.parents.add(node2);
78135
+ defineReturnedProperty(node2.copy, safeKey, child.copy);
78136
+ } else {
78137
+ defineReturnedProperty(node2.copy, safeKey, item);
78138
+ }
78139
+ }
78045
78140
  }
78046
- return Object.fromEntries(
78047
- Object.entries(value).map(([key2, item]) => [
78048
- redactKnownSecrets(key2, secrets),
78049
- sanitizeReceiptValue(item, secrets, depth + 1)
78050
- ])
78051
- );
78141
+ const changed = ordered.filter((node2) => node2.changed);
78142
+ for (let index2 = 0; index2 < changed.length; index2 += 1) {
78143
+ for (const parent of changed[index2].parents) {
78144
+ if (parent.changed) continue;
78145
+ parent.changed = true;
78146
+ changed.push(parent);
78147
+ }
78148
+ }
78149
+ for (const node2 of ordered) {
78150
+ for (const [key2, item] of Object.entries(node2.source)) {
78151
+ if (!isPlainReturnedContainer(item)) continue;
78152
+ const safeKey = Array.isArray(node2.source) ? key2 : redactKnownSecrets(key2, secrets);
78153
+ const child = nodes.get(item);
78154
+ defineReturnedProperty(node2.copy, safeKey, child.changed ? child.copy : item);
78155
+ }
78156
+ }
78157
+ return root.changed ? root.copy : value;
78158
+ }
78159
+ function defineReturnedProperty(target, key2, value) {
78160
+ Object.defineProperty(target, key2, {
78161
+ value,
78162
+ enumerable: true,
78163
+ configurable: true,
78164
+ writable: true
78165
+ });
78166
+ }
78167
+ function isPlainReturnedContainer(value) {
78168
+ if (Array.isArray(value)) return true;
78169
+ if (value === null || typeof value !== "object") return false;
78170
+ const prototype = Object.getPrototypeOf(value);
78171
+ return prototype === Object.prototype || prototype === null;
78052
78172
  }
78053
78173
  function redactKnownSecrets(value, secrets) {
78054
78174
  let redacted = value;
@@ -78083,9 +78203,7 @@ function possibleSettlement(quote, payment, payer, receipt) {
78083
78203
  payTo: quote.accepted.payTo,
78084
78204
  payer,
78085
78205
  ...payment.paymentId ? { paymentId: payment.paymentId } : {},
78086
- ...isSvmPaymentRequirements(quote.accepted) ? {
78087
- authorizationTransaction: "transaction" in payment.payload.payload ? payment.payload.payload.transaction : ""
78088
- } : "authorization" in payment.payload.payload ? {
78206
+ ...isSvmPaymentRequirements(quote.accepted) ? {} : "authorization" in payment.payload.payload ? {
78089
78207
  authorizationNonce: payment.payload.payload.authorization.nonce,
78090
78208
  authorizationExpiresAt: payment.payload.payload.authorization.validBefore
78091
78209
  } : {},
@@ -78463,13 +78581,22 @@ async function sendRouterChat(deps, routerBaseUrl, routerKey, request, keyUsed)
78463
78581
  }
78464
78582
  const body = await responseText(response);
78465
78583
  if (!response.ok) {
78466
- if (response.status === 429 || body.includes("Budget has been exceeded") || body.includes("ExceededBudget")) {
78584
+ if (/budget (?:has been )?exceeded|exceededbudget/iu.test(body)) {
78467
78585
  throw new RouterClientError(
78468
78586
  "budget_exhausted",
78469
78587
  "The Router key has no remaining budget.",
78470
78588
  response.status
78471
78589
  );
78472
78590
  }
78591
+ if (response.status === 429) {
78592
+ const retryAfter = response.headers.get("retry-after")?.trim();
78593
+ const retrySeconds = retryAfter !== void 0 && /^\d+$/u.test(retryAfter) ? Number(retryAfter) : void 0;
78594
+ throw new RouterClientError(
78595
+ "rate_limited",
78596
+ `The vAPI Router is rate limited. ${retrySeconds !== void 0 && Number.isFinite(retrySeconds) ? `Retry after ${retrySeconds} seconds.` : "Wait and retry shortly."}`,
78597
+ response.status
78598
+ );
78599
+ }
78473
78600
  if (response.status === 401) {
78474
78601
  throw new RouterClientError(
78475
78602
  "no_router_key",
@@ -87055,6 +87182,12 @@ function isRecord4(value) {
87055
87182
  return typeof value === "object" && value !== null && !Array.isArray(value);
87056
87183
  }
87057
87184
 
87185
+ // src/cli.ts
87186
+ import { spawn as spawn3 } from "node:child_process";
87187
+ import { stat as stat16 } from "node:fs/promises";
87188
+ import { join as join28 } from "node:path";
87189
+ import { createInterface as createInterface3 } from "node:readline/promises";
87190
+
87058
87191
  // ../core/src/member-bundle.ts
87059
87192
  var MEMBER_BUNDLE_MAX_BYTES = 32 * 1024;
87060
87193
  var REMOTE_BLOCKED_GRANTS = /* @__PURE__ */ new Set([
@@ -87746,12 +87879,13 @@ var BODY_HEADER_NAMES = [
87746
87879
  "content-type"
87747
87880
  ];
87748
87881
  async function callService(args) {
87882
+ validateTimeoutSeconds(args.input.timeoutSeconds);
87749
87883
  const trace = createCallTrace(args);
87750
87884
  let execution;
87751
87885
  try {
87752
87886
  execution = await executeCallService(args, trace);
87753
87887
  } catch (error61) {
87754
- if (!(error61 instanceof VapiCallError && error61.code === "unsupported_network")) {
87888
+ if (!(error61 instanceof VapiCallError && (error61.code === "unsupported_network" || error61.code === "unsupported_payment"))) {
87755
87889
  await recordCallError(args, error61, trace).catch(() => void 0);
87756
87890
  }
87757
87891
  throw error61;
@@ -87865,6 +87999,8 @@ async function executeCallService(args, trace) {
87865
87999
  body: method === "GET" || method === "HEAD" ? void 0 : requestBody
87866
88000
  });
87867
88001
  const request = createRequest(headers);
88002
+ const probeTimeoutMs = input2.timeoutSeconds === void 0 ? args.timeoutMs ?? 3e4 : Math.min(input2.timeoutSeconds, 30) * 1e3;
88003
+ let requestTimeoutMs = probeTimeoutMs;
87868
88004
  const fetchWithGuardedRedirects = async (candidateRequest, followRedirects = true) => {
87869
88005
  let currentRequest = candidateRequest;
87870
88006
  let followedRedirects = 0;
@@ -87872,7 +88008,7 @@ async function executeCallService(args, trace) {
87872
88008
  const attempt = await fetchAttempt(
87873
88009
  fetchImpl,
87874
88010
  new Request(currentRequest.clone(), { redirect: "manual" }),
87875
- args.timeoutMs ?? 3e4
88011
+ requestTimeoutMs
87876
88012
  );
87877
88013
  const response = attempt.response;
87878
88014
  if (!isRedirectStatus2(response.status)) {
@@ -88027,7 +88163,12 @@ async function executeCallService(args, trace) {
88027
88163
  account,
88028
88164
  caps: args.spendCaps ?? config2.spendCaps,
88029
88165
  config: payableConfig(config2, account),
88030
- send: async (paidRequest) => {
88166
+ send: async (paidRequest, quote) => {
88167
+ requestTimeoutMs = paidRequestTimeoutMs(
88168
+ input2.timeoutSeconds,
88169
+ args.timeoutMs,
88170
+ quote.accepted.maxTimeoutSeconds
88171
+ );
88031
88172
  paidAttempt = await fetchWithGuardedRedirects(paidRequest, false);
88032
88173
  return paidAttempt;
88033
88174
  },
@@ -88066,7 +88207,7 @@ async function executeCallService(args, trace) {
88066
88207
  asset: completed.quote.accepted.asset,
88067
88208
  payTo: completed.quote.accepted.payTo,
88068
88209
  settlement: settlementWithExplorer2(completed.settlement, completed.quote.accepted.network),
88069
- proof: completed.response.headers.get("x-vapi-payment-proof")
88210
+ proof: completed.paymentProof
88070
88211
  },
88071
88212
  ...verificationFor(endpoint),
88072
88213
  ...expectedRequestFor(endpoint, completed.response.status)
@@ -88313,8 +88454,16 @@ async function resolveCallEndpoint(id2, endpointName, marketplaceHit, config2, f
88313
88454
  );
88314
88455
  }
88315
88456
  function registeredEndpoint(listing, verification) {
88457
+ const { payment, ...endpoint } = listing.endpoint;
88458
+ if (payment && (typeof payment.protocol === "string" || typeof payment.network !== "string")) {
88459
+ throw new VapiCallError(
88460
+ "unsupported_payment",
88461
+ `Payment protocol ${payment.protocol} is not supported. This client pays x402 exact USDC endpoints only.`
88462
+ );
88463
+ }
88316
88464
  return {
88317
- ...listing.endpoint,
88465
+ ...endpoint,
88466
+ ...payment ? { payment: { network: payment.network, payTo: payment.payTo } } : {},
88318
88467
  verification: verification ?? listing.service.verification,
88319
88468
  serviceNetworks: listing.service.networks,
88320
88469
  registered: true,
@@ -88476,7 +88625,7 @@ async function readResponseBody2(response) {
88476
88625
  async function fetchAttempt(fetchImpl, request, timeoutMs) {
88477
88626
  const controller = new AbortController();
88478
88627
  let rejectTimeout = null;
88479
- const timeoutError = new Error(`API request timed out after ${timeoutMs}ms.`);
88628
+ const timeoutError = new Error(`API request timed out after ${timeoutMs / 1e3} seconds.`);
88480
88629
  const timedOut = new Promise((_resolve, reject) => {
88481
88630
  rejectTimeout = reject;
88482
88631
  });
@@ -88516,6 +88665,19 @@ async function fetchAttempt(fetchImpl, request, timeoutMs) {
88516
88665
  throw error61;
88517
88666
  }
88518
88667
  }
88668
+ function validateTimeoutSeconds(timeoutSeconds) {
88669
+ if (timeoutSeconds !== void 0 && (!Number.isInteger(timeoutSeconds) || timeoutSeconds < 1 || timeoutSeconds > 300)) {
88670
+ throw new Error("call timeoutSeconds must be an integer from 1 to 300 seconds.");
88671
+ }
88672
+ }
88673
+ function paidRequestTimeoutMs(timeoutSeconds, internalTimeoutMs, offerTimeoutSeconds) {
88674
+ const usableOfferTimeout = Number.isInteger(offerTimeoutSeconds) && offerTimeoutSeconds > 0 ? offerTimeoutSeconds : void 0;
88675
+ if (timeoutSeconds !== void 0) {
88676
+ return Math.min(timeoutSeconds, usableOfferTimeout ?? timeoutSeconds) * 1e3;
88677
+ }
88678
+ if (internalTimeoutMs !== void 0) return internalTimeoutMs;
88679
+ return usableOfferTimeout === void 0 ? 12e4 : Math.min(12e4, Math.max(3e4, usableOfferTimeout * 1e3));
88680
+ }
88519
88681
  function mapX402PaymentError(error61, resumeId) {
88520
88682
  if (!(error61 instanceof X402PaymentError)) return error61;
88521
88683
  if (error61.code === "payment_rejected") {
@@ -88531,9 +88693,10 @@ function mapX402PaymentError(error61, resumeId) {
88531
88693
  });
88532
88694
  }
88533
88695
  const resume = resumeId !== void 0 && error61.possibleSettlement?.authorizationNonce ? ` Check whether it settled with \`vapi pay --resume ${resumeId}\` before paying again.` : "";
88696
+ const timeoutDetail = error61.cause instanceof Error && error61.cause.message.startsWith("API request timed out after ") ? `${error61.cause.message} ` : "";
88534
88697
  return new VapiCallError(
88535
88698
  "settlement_unknown",
88536
- `${error61.message}${resume}`,
88699
+ `${timeoutDetail}${error61.message}${resume}`,
88537
88700
  error61.possibleSettlement,
88538
88701
  { cause: error61.cause }
88539
88702
  );
@@ -88665,13 +88828,7 @@ var inspectToolResultSchema = external_exports.object({
88665
88828
  liveness: listingLivenessSchema.optional(),
88666
88829
  conformance: listingConformanceSchema.optional(),
88667
88830
  identity: listingIdentitySchema.optional(),
88668
- payment: external_exports.object({
88669
- scheme: external_exports.literal("exact"),
88670
- network: external_exports.string(),
88671
- asset: external_exports.string(),
88672
- payTo: external_exports.string(),
88673
- checkedAt: external_exports.string()
88674
- }).nullable()
88831
+ payment: discoveryEndpointSchema.shape.payment.unwrap().nullable()
88675
88832
  });
88676
88833
  var callSearchInputSchema = external_exports.object({
88677
88834
  query: external_exports.string().trim().max(200).optional().describe("Capability, API, service, or request."),
@@ -88700,7 +88857,10 @@ var callPayInputSchema = external_exports.object({
88700
88857
  (value) => /^0x[0-9a-fA-F]{40}$/.test(value) || isSolanaAddress(value),
88701
88858
  "Expected an EVM or Solana address."
88702
88859
  ).optional(),
88703
- maxPriceUsd: external_exports.union([external_exports.number().nonnegative(), external_exports.string().regex(/^\d+(?:\.\d{1,6})?$/)]).optional()
88860
+ maxPriceUsd: external_exports.union([external_exports.number().nonnegative(), external_exports.string().regex(/^\d+(?:\.\d{1,6})?$/)]).optional(),
88861
+ timeoutSeconds: external_exports.number().int().min(1).max(300).optional().describe(
88862
+ "Seconds to wait for the paid response. Default: the provider's maxTimeoutSeconds, at most 120."
88863
+ )
88704
88864
  });
88705
88865
  var callSearch = defineAction({
88706
88866
  name: "call.search",
@@ -88963,7 +89123,7 @@ async function readResult(response, signal, secrets = []) {
88963
89123
  return {
88964
89124
  status: response.status,
88965
89125
  contentType: contentType === null ? null : redactKnownSecrets2(contentType, secrets),
88966
- body: sanitizeReturnedValue(await readResponseBody3(response, contentType, signal), secrets)
89126
+ body: sanitizeReturnedValue2(await readResponseBody3(response, contentType, signal), secrets)
88967
89127
  };
88968
89128
  }
88969
89129
  async function readResponseBody3(response, contentType, signal) {
@@ -89008,17 +89168,17 @@ function isJsonContentType2(value) {
89008
89168
  const mediaType = value?.split(";", 1)[0]?.trim().toLowerCase() ?? "";
89009
89169
  return mediaType === "application/json" || mediaType.endsWith("+json");
89010
89170
  }
89011
- function sanitizeReturnedValue(value, secrets, depth = 0) {
89171
+ function sanitizeReturnedValue2(value, secrets, depth = 0) {
89012
89172
  if (typeof value === "string") return redactKnownSecrets2(value, secrets);
89013
89173
  if (value === null || typeof value !== "object") return value;
89014
89174
  if (depth >= 16) return "[redacted nested response value]";
89015
89175
  if (Array.isArray(value)) {
89016
- return value.map((item) => sanitizeReturnedValue(item, secrets, depth + 1));
89176
+ return value.map((item) => sanitizeReturnedValue2(item, secrets, depth + 1));
89017
89177
  }
89018
89178
  return Object.fromEntries(
89019
89179
  Object.entries(value).map(([key2, item]) => [
89020
89180
  redactKnownSecrets2(key2, secrets),
89021
- sanitizeReturnedValue(item, secrets, depth + 1)
89181
+ sanitizeReturnedValue2(item, secrets, depth + 1)
89022
89182
  ])
89023
89183
  );
89024
89184
  }
@@ -94524,7 +94684,13 @@ function toolError(error61) {
94524
94684
  {
94525
94685
  error: error61.message,
94526
94686
  code: error61.code,
94527
- ...error61.possibleSettlement ? { possibleSettlement: error61.possibleSettlement } : {},
94687
+ ...error61.possibleSettlement ? {
94688
+ possibleSettlement: Object.fromEntries(
94689
+ Object.entries(error61.possibleSettlement).filter(
94690
+ ([key2]) => key2 !== "authorizationTransaction"
94691
+ )
94692
+ )
94693
+ } : {},
94528
94694
  ...error61.paymentRejection ? { paymentRejection: error61.paymentRejection } : {},
94529
94695
  ...error61.confirmedSettlement ? { confirmedSettlement: error61.confirmedSettlement } : {}
94530
94696
  },
@@ -98018,7 +98184,7 @@ async function readProcessStdin() {
98018
98184
  }
98019
98185
 
98020
98186
  // src/version.ts
98021
- var CLI_VERSION = "0.9.2";
98187
+ var CLI_VERSION = "0.9.4";
98022
98188
 
98023
98189
  // src/status.ts
98024
98190
  async function statusCommand(argv, json2, io, dependencies) {
@@ -101520,7 +101686,7 @@ function createLocalRuntime(options) {
101520
101686
  const processCommand = options.processCommand ?? defaultProcessCommand;
101521
101687
  const ownsProcess = (handle, pid) => processCommand(pid)?.includes(runResultPath(home, handle.runId)) === true;
101522
101688
  const closeFd = options.closeFd ?? closeSync2;
101523
- const readFile29 = options.readFile ?? nodeReadFile;
101689
+ const readFile30 = options.readFile ?? nodeReadFile;
101524
101690
  const now = options.now ?? (() => /* @__PURE__ */ new Date());
101525
101691
  const openLog = options.openLog ?? ((path) => {
101526
101692
  mkdirSync2(runsDirectory(home), { recursive: true, mode: 448 });
@@ -101575,7 +101741,7 @@ function createLocalRuntime(options) {
101575
101741
  return handle;
101576
101742
  },
101577
101743
  async status(handle) {
101578
- const result = await readResultFile(readFile29, runResultPath(home, handle.runId));
101744
+ const result = await readResultFile(readFile30, runResultPath(home, handle.runId));
101579
101745
  if (result !== void 0) return result;
101580
101746
  const pid = localPid(handle);
101581
101747
  if (pid === void 0 || handle.kind !== "local") {
@@ -101678,10 +101844,10 @@ function childArguments(member, run) {
101678
101844
  ...run.drawUsd ? ["--draw", run.drawUsd] : []
101679
101845
  ];
101680
101846
  }
101681
- async function readResultFile(readFile29, path) {
101847
+ async function readResultFile(readFile30, path) {
101682
101848
  let text;
101683
101849
  try {
101684
- text = await readFile29(path, "utf8");
101850
+ text = await readFile30(path, "utf8");
101685
101851
  } catch (error61) {
101686
101852
  if (hasCode3(error61, "ENOENT")) return void 0;
101687
101853
  return { state: "failed", detail: "unreadable result file" };
@@ -102531,7 +102697,7 @@ function isMcpInstallApp(value) {
102531
102697
  function serverEntry(home, registry2) {
102532
102698
  return {
102533
102699
  command: "npx",
102534
- args: ["-y", "vapi-network", "mcp"],
102700
+ args: ["-y", "vapi-network@latest", "mcp"],
102535
102701
  env: { VAPI_HOME: home, VAPI_REGISTRY_URL: registry2 }
102536
102702
  };
102537
102703
  }
@@ -102580,7 +102746,7 @@ function tomlConfig(existing, entry) {
102580
102746
  const block = [
102581
102747
  "[mcp_servers.vapi]",
102582
102748
  'command = "npx"',
102583
- 'args = ["-y", "vapi-network", "mcp"]',
102749
+ 'args = ["-y", "vapi-network@latest", "mcp"]',
102584
102750
  `env = { VAPI_HOME = "${escapeToml(entry.env.VAPI_HOME)}", VAPI_REGISTRY_URL = "${escapeToml(entry.env.VAPI_REGISTRY_URL)}" }`,
102585
102751
  ""
102586
102752
  ].join("\n");
@@ -102747,13 +102913,13 @@ async function sendConfig(dependencies) {
102747
102913
  // src/setup.ts
102748
102914
  import { rm as rm5 } from "node:fs/promises";
102749
102915
  import { join as join26 } from "node:path";
102750
- var SETUP_ARGUMENT_ERROR = "vapi setup accepts only --no-cloud-backup; a recovery phrase is typed at the prompt, never passed on the command line.";
102916
+ var SETUP_ARGUMENT_ERROR = `vapi setup accepts only --no-cloud-backup and --accounts <n> (1-${MAX_RESTORE_ACCOUNTS}); a recovery phrase is typed at the prompt, never passed on the command line.`;
102751
102917
  var VAULT_CHOICE = "Create a new vault, or restore one from a recovery phrase? [new/restore] ";
102752
102918
  async function setupCommand(argv, json2, io, dependencies) {
102753
102919
  let parsed;
102754
102920
  try {
102755
102921
  parsed = parseArguments(argv, {
102756
- valueOptions: /* @__PURE__ */ new Set(),
102922
+ valueOptions: /* @__PURE__ */ new Set(["--accounts"]),
102757
102923
  booleanOptions: /* @__PURE__ */ new Set(["--no-cloud-backup"]),
102758
102924
  maximumPositionals: 0
102759
102925
  });
@@ -102764,6 +102930,7 @@ async function setupCommand(argv, json2, io, dependencies) {
102764
102930
  const paths = getVapiPaths();
102765
102931
  const steps = [];
102766
102932
  const humanIo = json2 ? stderrIo(io) : io;
102933
+ const accountCount = parseAccountCount(parsed.one("--accounts"));
102767
102934
  if (!await fileExists2(paths.config)) {
102768
102935
  await writeDefaultConfig(paths.config, process.env, { networks: ["base"] });
102769
102936
  steps.push("config");
@@ -102774,6 +102941,7 @@ async function setupCommand(argv, json2, io, dependencies) {
102774
102941
  let changedVault = false;
102775
102942
  let changedAccount = false;
102776
102943
  let changedLink = false;
102944
+ let scannedAddresses;
102777
102945
  if (!await fileExists2(join26(paths.directory, "vault.json"))) {
102778
102946
  const choice = await vaultChoice(dependencies);
102779
102947
  if (choice === "restore") {
@@ -102781,12 +102949,14 @@ async function setupCommand(argv, json2, io, dependencies) {
102781
102949
  throw new Error("Restoring needs a terminal to type the phrase.");
102782
102950
  }
102783
102951
  const readSecret = dependencies.prompts?.secret ?? promptForSecret;
102784
- await restoreVaultFromPhrase(
102952
+ const restored = await restoreVaultFromPhrase(
102785
102953
  await readSecret("Recovery phrase: "),
102786
102954
  config2,
102787
102955
  humanIo,
102788
- dependencies
102956
+ dependencies,
102957
+ accountCount
102789
102958
  );
102959
+ scannedAddresses = restored.scannedAddresses;
102790
102960
  store = await WalletStore.open(paths.directory, {
102791
102961
  secrets: getSecretStore(dependencies),
102792
102962
  env: dependencies.env ?? process.env,
@@ -102839,22 +103009,35 @@ async function setupCommand(argv, json2, io, dependencies) {
102839
103009
  }
102840
103010
  if (json2) {
102841
103011
  io.stdout(
102842
- JSON.stringify({ vault, account, address: address4, linked, steps, cloudBackup })
103012
+ JSON.stringify({
103013
+ vault,
103014
+ account,
103015
+ address: address4,
103016
+ linked,
103017
+ steps,
103018
+ cloudBackup,
103019
+ ...scannedAddresses === void 0 ? {} : { scannedAddresses }
103020
+ })
102843
103021
  );
102844
103022
  }
102845
103023
  }
102846
- async function restoreVaultFromPhrase(phrase, config2, io, dependencies) {
103024
+ async function restoreVaultFromPhrase(phrase, config2, io, dependencies, accountCount) {
102847
103025
  const paths = getVapiPaths();
102848
103026
  const validatedPhrase = validateRecoveryPhrase(phrase);
102849
103027
  const readBalance3 = baseUsdcBalanceReader(config2, dependencies);
102850
103028
  const key2 = await loadOrCreateDeviceKey({ secrets: getSecretStore(dependencies) });
102851
103029
  let names;
103030
+ let scannedAddresses = 0;
102852
103031
  try {
102853
103032
  const restored = await restoreVault({
102854
103033
  path: join26(paths.directory, "vault.json"),
102855
103034
  key: key2,
102856
103035
  phrase: validatedPhrase,
102857
103036
  probe: async (address4) => await readBalance3(address4) > 0n,
103037
+ ...accountCount === void 0 ? {} : { accountCount },
103038
+ onScanComplete: (count) => {
103039
+ scannedAddresses = count;
103040
+ },
102858
103041
  ...dependencies.now === void 0 ? {} : { now: dependencies.now }
102859
103042
  });
102860
103043
  names = restored.accounts().map((account) => account.name);
@@ -102866,8 +103049,25 @@ async function restoreVaultFromPhrase(phrase, config2, io, dependencies) {
102866
103049
  ...names[0] === void 0 ? {} : { wallet: names[0] },
102867
103050
  detail: `${names.length} account(s)`
102868
103051
  });
102869
- io.stdout(`Vault restored with ${names.length} account(s): ${names.join(", ")}`);
102870
- return { names };
103052
+ io.stdout(
103053
+ `Vault restored with ${names.length} account(s): ${names.join(", ")}. ${scannedAddresses} address(es) scanned.`
103054
+ );
103055
+ return { names, scannedAddresses };
103056
+ }
103057
+ function parseAccountCount(value) {
103058
+ if (value === void 0) return void 0;
103059
+ if (!/^\d+$/u.test(value)) {
103060
+ throw new UsageError(
103061
+ `--accounts must be a positive integer no greater than ${MAX_RESTORE_ACCOUNTS}.`
103062
+ );
103063
+ }
103064
+ const count = Number(value);
103065
+ if (!Number.isSafeInteger(count) || count < 1 || count > MAX_RESTORE_ACCOUNTS) {
103066
+ throw new UsageError(
103067
+ `--accounts must be a positive integer no greater than ${MAX_RESTORE_ACCOUNTS}.`
103068
+ );
103069
+ }
103070
+ return count;
102871
103071
  }
102872
103072
  async function vaultChoice(dependencies) {
102873
103073
  const answer = dependencies.prompts?.line === void 0 ? isInteractive(dependencies) ? await promptForLine(VAULT_CHOICE) : "" : await dependencies.prompts.line(VAULT_CHOICE);
@@ -104975,6 +105175,7 @@ async function referencedSleep(milliseconds, signal) {
104975
105175
  // src/vault.ts
104976
105176
  import { join as join27 } from "node:path";
104977
105177
  var VAULT_USAGE = "Usage: vapi vault protect|unprotect|lock|unlock|status [--json]";
105178
+ var RESTORE_USAGE = "Usage: vapi restore [--accounts <n> | --from-owner [--owner <0x\u2026>]] [--json]";
104978
105179
  var LEGACY_PASSWORD_NOTICE = "VAPI_KEYSTORE_PASSWORD is deprecated for vault passwords; use VAPI_VAULT_PASSWORD instead.";
104979
105180
  async function vaultCommand(argv, json2, io, dependencies) {
104980
105181
  const subcommand = argv[0];
@@ -105004,9 +105205,17 @@ async function restoreCommand(argv, json2, io, dependencies) {
105004
105205
  await restoreFromOwnerCommand(argv, json2, io, dependencies);
105005
105206
  return;
105006
105207
  }
105007
- if (argv.length > 0) {
105008
- throw new UsageError("Usage: vapi restore [--from-owner [--owner <0x\u2026>]] [--json]");
105208
+ let parsed;
105209
+ try {
105210
+ parsed = parseArguments(argv, {
105211
+ valueOptions: /* @__PURE__ */ new Set(["--accounts"]),
105212
+ maximumPositionals: 0
105213
+ });
105214
+ } catch (error61) {
105215
+ if (error61 instanceof UsageError) throw new UsageError(RESTORE_USAGE);
105216
+ throw error61;
105009
105217
  }
105218
+ const accountCount = parseRestoreAccountCount(parsed.one("--accounts"));
105010
105219
  const paths = getVapiPaths();
105011
105220
  const path = join27(paths.directory, "vault.json");
105012
105221
  if (await fileExists2(path)) {
@@ -105020,13 +105229,39 @@ async function restoreCommand(argv, json2, io, dependencies) {
105020
105229
  const config2 = await readConfig(paths.config, json2 ? quietIo(io) : io);
105021
105230
  const phrase = isStdinInteractive(dependencies) ? await getPrompts(dependencies).secret("Recovery phrase: ") : await (dependencies.readStdin ?? readProcessStdin3)();
105022
105231
  const restoreIo = json2 ? quietIo(io) : io;
105023
- const { names } = await restoreVaultFromPhrase(phrase.trim(), config2, restoreIo, dependencies);
105232
+ const { names, scannedAddresses } = await restoreVaultFromPhrase(
105233
+ phrase.trim(),
105234
+ config2,
105235
+ restoreIo,
105236
+ dependencies,
105237
+ accountCount
105238
+ );
105024
105239
  if (json2) {
105025
- output3(io, true, { command: "restore", vault: "restored", accounts: names }, "");
105240
+ output3(
105241
+ io,
105242
+ true,
105243
+ { command: "restore", vault: "restored", accounts: names, scannedAddresses },
105244
+ ""
105245
+ );
105026
105246
  return;
105027
105247
  }
105028
105248
  io.stdout(`Run vapi setup to link ${names[0]}.`);
105029
105249
  }
105250
+ function parseRestoreAccountCount(value) {
105251
+ if (value === void 0) return void 0;
105252
+ if (!/^\d+$/u.test(value)) {
105253
+ throw new UsageError(
105254
+ `--accounts must be a positive integer no greater than ${MAX_RESTORE_ACCOUNTS}.`
105255
+ );
105256
+ }
105257
+ const count = Number(value);
105258
+ if (!Number.isSafeInteger(count) || count < 1 || count > MAX_RESTORE_ACCOUNTS) {
105259
+ throw new UsageError(
105260
+ `--accounts must be a positive integer no greater than ${MAX_RESTORE_ACCOUNTS}.`
105261
+ );
105262
+ }
105263
+ return count;
105264
+ }
105030
105265
  async function protectCommand(argv, json2, io, dependencies) {
105031
105266
  requireNoArguments(argv);
105032
105267
  const env = getEnvironment(dependencies);
@@ -105156,8 +105391,8 @@ var HELP_HEADING = "vAPI Network";
105156
105391
  var HELP = `${HELP_HEADING}
105157
105392
 
105158
105393
  Usage:
105159
- vapi init [--networks <base,arc,solana>] [--json]
105160
- vapi setup [--no-cloud-backup] [--json]
105394
+ vapi init [--networks <base,arc>] [--json]
105395
+ vapi setup [--no-cloud-backup] [--accounts <n>] [--json]
105161
105396
  vapi accounts [--all] [--json]
105162
105397
  vapi accounts add <name> [--label <text>] [--no-link] [--json]
105163
105398
  vapi accounts import <name> [--keystore <path> | --key-file <path>] [--label <text>] [--json]
@@ -105187,7 +105422,7 @@ Usage:
105187
105422
  vapi docs read <url-or-path> [--json]
105188
105423
  vapi search [query] [--kind <kind>] [--network <caip2>] [--limit <n>] [--cursor <cursor>] [--include-unverified] [--json]
105189
105424
  vapi inspect <id> [--endpoint <name>] [--json]
105190
- vapi pay <id-or-url> [--method <method>] [--endpoint <name>] [--body <json>] [--content-type <type>] [--network <caip2>] [--expected-pay-to <address>] [--max <amount>] [--account <name>] [--json]
105425
+ vapi pay <id-or-url> [--method <method>] [--endpoint <name>] [--body <json>] [--content-type <type>] [--network <caip2>] [--expected-pay-to <address>] [--max <amount>] [--timeout <seconds>] [--account <name>] [--json]
105191
105426
  vapi pay --resume <receipt-id> [--json]
105192
105427
  vapi check <url> [--method <method>] [--json]
105193
105428
  vapi balance [--account <name>] [--json]
@@ -105233,8 +105468,8 @@ Usage:
105233
105468
  vapi task status <id> [--account <name>] [--json]
105234
105469
  vapi export-key [--network <caip2>] [--account <name>] [--json]
105235
105470
  vapi backup [--account <name>] [--cloud [off]] [--json]
105236
- vapi restore [--from-owner [--owner <0x\u2026>]] [--json]
105237
- vapi import (--phrase | --key) [--account <name>] [--networks <base,arc,solana>] [--replace] [--force] [--json]
105471
+ vapi restore [--accounts <n> | --from-owner [--owner <0x\u2026>]] [--json]
105472
+ vapi import (--phrase | --key) [--account <name>] [--networks <base,arc>] [--replace] [--force] [--json]
105238
105473
  vapi passphrase [--account <name>] [--json]
105239
105474
  vapi vault protect [--json]
105240
105475
  vapi vault unprotect [--json]
@@ -105262,6 +105497,8 @@ Every command that touches a wallet takes \`--account <name>\`, falls back to \`
105262
105497
 
105263
105498
  \`vapi search\` answers with vAPI-verified listings plus the mirrored external catalogs. \`--include-unverified\` also returns self-listed APIs that passed vAPI's automated x402 probe but were never reviewed; every result is tagged \`[verified]\`, \`[requested]\`, \`[unverified]\` or \`[external]\`.
105264
105499
 
105500
+ Solana listings remain searchable; inspect and pay return \`unsupported_network\`. Solana keys are not part of the device vault yet.
105501
+
105265
105502
  \`vapi docs\` asks the public vAPI documentation assistant; \`vapi docs search\` searches documentation excerpts and \`vapi docs read\` reads one Markdown page. They need no account, wallet or payment.
105266
105503
 
105267
105504
  \`vapi task --help\` lists task options and availability. Task exit codes: 0 ok, 1 error, 2 policy refusal (JSON ok:false with a policy. reason) or invalid usage (JSON error), 3 approval needed in non-interactive mode. Posting moves no money; the current create-order contract does not store amount, deadline, intake, max awards or webhook yet.
@@ -105274,7 +105511,7 @@ Every command that touches a wallet takes \`--account <name>\`, falls back to \`
105274
105511
 
105275
105512
  \`vapi vault protect\` adds a password to the device vault. \`vapi vault unlock\` opens it on this device for 8 hours; for unattended use, set \`VAPI_VAULT_PASSWORD\`. \`vapi unlock\` and \`vapi lock\` remain aliases for one release.
105276
105513
 
105277
- \`vapi backup\` and \`vapi export-key\` print a secret, so they run only on a real terminal, never for an agent, and ask you to type the wallet name first. \`vapi backup --cloud\` keeps an encrypted vault copy that only the owner can approve for recovery. \`vapi restore\` rebuilds the vault from the phrase and re-derives accounts until an unused address. Set \`VAPI_NO_SECRETS=1\` to switch exports off entirely. Every export and every wallet change is logged to ~/.vapi/audit.log.
105514
+ \`vapi backup\` and \`vapi export-key\` print a secret, so they run only on a real terminal, never for an agent, and ask you to type the wallet name first. \`vapi backup --cloud\` keeps an encrypted vault copy that only the owner can approve for recovery. \`vapi restore\` rebuilds the vault from the phrase and re-derives accounts until five consecutive addresses have no Base USDC, and reports the number scanned. Use \`--accounts <n>\` with restore or setup to recover a fixed count without querying balances. Set \`VAPI_NO_SECRETS=1\` to switch exports off entirely. Every export and every wallet change is logged to ~/.vapi/audit.log.
105278
105515
 
105279
105516
  \`vapi publish <url>\` lists an API you own. vAPI probes the URL \u2014 an origin, one endpoint, or an OpenAPI document \u2014 you choose which endpoints to list, and your wallet signs one line naming where the payouts go. Listing is permissionless: a listing that passed the probe goes live with \`vapi publish activate <slug>\`, and \`vapi publish verify-request <slug>\` asks for the review that puts it in the default search. Deploying the FeeSplitter that receives the payouts is a wallet transaction and stays in the console. A catalog of more than 20 endpoints is listed as several listings of up to 20, with one result line per endpoint; \`--resume\` skips every endpoint this key already lists.
105280
105517
 
@@ -106063,7 +106300,7 @@ async function inspectCommand(argv, json2, io, dependencies) {
106063
106300
  );
106064
106301
  output3(io, json2, result, formatInspect(result));
106065
106302
  }
106066
- var PAY_USAGE = "Usage: vapi pay <id-or-url> [--method <method>] [--endpoint <name>] [--body <json>] [--content-type <type>] [--network <caip2>] [--expected-pay-to <address>] [--max <amount>] [--account <name>] [--json]";
106303
+ var PAY_USAGE = "Usage: vapi pay <id-or-url> [--method <method>] [--endpoint <name>] [--body <json>] [--content-type <type>] [--network <caip2>] [--expected-pay-to <address>] [--max <amount>] [--timeout <seconds>] [--account <name>] [--json]";
106067
106304
  async function payCommand(argv, json2, io, dependencies) {
106068
106305
  if (argv.includes(RESUME_OPTION)) {
106069
106306
  await payResumeCommand(argv, json2, io, dependencies);
@@ -106079,11 +106316,13 @@ async function payCommand(argv, json2, io, dependencies) {
106079
106316
  "--expected-pay-to",
106080
106317
  "--max",
106081
106318
  "--max-price-usd",
106319
+ "--timeout",
106082
106320
  WALLET_OPTION5
106083
106321
  ]),
106084
106322
  maximumPositionals: 1
106085
106323
  });
106086
106324
  const target = requiredPositional(parsed.positionals[0], "<id-or-url>", PAY_USAGE);
106325
+ const timeoutSeconds = optionalBoundedInteger(parsed.one("--timeout"), "--timeout", 1, 300);
106087
106326
  const paths = getVapiPaths();
106088
106327
  const config2 = await readConfig(paths.config, io);
106089
106328
  const wallet = await targetWallet(parsed, dependencies);
@@ -106099,7 +106338,8 @@ async function payCommand(argv, json2, io, dependencies) {
106099
106338
  ...parsed.one("--content-type") ? { contentType: parsed.one("--content-type") } : {},
106100
106339
  ...parsed.one("--network") ? { network: parsed.one("--network") } : {},
106101
106340
  ...parsed.one("--expected-pay-to") ? { expectedPayTo: parsed.one("--expected-pay-to") } : {},
106102
- ...maxPriceUsd ? { maxPriceUsd } : {}
106341
+ ...maxPriceUsd ? { maxPriceUsd } : {},
106342
+ ...timeoutSeconds === void 0 ? {} : { timeoutSeconds }
106103
106343
  };
106104
106344
  const call2 = createCallPort({
106105
106345
  config: config2,
@@ -107774,7 +108014,7 @@ function buildNextSteps(address4) {
107774
108014
  formatNextStep("Pay", "vapi pay <ref> --max 0.02"),
107775
108015
  formatNextStep(
107776
108016
  "Agent",
107777
- 'add {"command":"npx","args":["-y","vapi-network","mcp"]} to your MCP config'
108017
+ 'add {"command":"npx","args":["-y","vapi-network@latest","mcp"]} to your MCP config'
107778
108018
  )
107779
108019
  ];
107780
108020
  }
@@ -107955,6 +108195,17 @@ function optionalPositiveInteger(value, option) {
107955
108195
  if (result === 0) throw new UsageError(`${option} must be a positive integer.`);
107956
108196
  return result;
107957
108197
  }
108198
+ function optionalBoundedInteger(value, option, minimum, maximum) {
108199
+ if (value === void 0) return void 0;
108200
+ if (!/^\d+$/.test(value)) {
108201
+ throw new UsageError(`${option} must be an integer from ${minimum} to ${maximum}.`);
108202
+ }
108203
+ const parsed = Number(value);
108204
+ if (!Number.isSafeInteger(parsed) || parsed < minimum || parsed > maximum) {
108205
+ throw new UsageError(`${option} must be an integer from ${minimum} to ${maximum}.`);
108206
+ }
108207
+ return parsed;
108208
+ }
107958
108209
  function optionalUsdAmount(value, option) {
107959
108210
  if (value === void 0) return void 0;
107960
108211
  if (!/^\d+(?:\.\d{1,2})?$/.test(value)) {
@@ -108015,12 +108266,136 @@ function parseInitNetworks(value) {
108015
108266
  }
108016
108267
  for (const network of networks) {
108017
108268
  if (network !== "base" && network !== "arc" && network !== "solana") {
108018
- throw new UsageError("--networks supports base, arc and solana.");
108269
+ throw new UsageError("--networks supports base and arc; Solana vault setup is unavailable.");
108019
108270
  }
108020
108271
  }
108021
108272
  return networks;
108022
108273
  }
108023
108274
 
108275
+ // src/update-check.ts
108276
+ var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
108277
+ var CHECK_TIMEOUT_MS = 1500;
108278
+ var LATEST_URL = "https://registry.npmjs.org/vapi-network/latest";
108279
+ async function runCliWithUpdateCheck(argv = process.argv.slice(2), io = {
108280
+ stdout: (message) => console.log(message),
108281
+ stderr: (message) => console.error(message)
108282
+ }, dependencies = {}, options = {}) {
108283
+ const notice = checkForUpdate({ ...options, env: options.env ?? dependencies.env, argv });
108284
+ const code = await runCli(argv, io, dependencies);
108285
+ const message = await notice;
108286
+ if (message) io.stderr(message);
108287
+ return code;
108288
+ }
108289
+ async function checkForUpdate(options = {}) {
108290
+ const env = options.env ?? process.env;
108291
+ const argv = options.argv ?? process.argv.slice(2);
108292
+ if (!(options.stderrIsTTY ?? process.stderr.isTTY) || argv.includes("--json") || argv[0] === "mcp" && argv[1] !== "install" || env.VAPI_NO_UPDATE_CHECK === "1" || env.CI !== void 0) {
108293
+ return void 0;
108294
+ }
108295
+ const controller = new AbortController();
108296
+ let timer;
108297
+ try {
108298
+ return await Promise.race([
108299
+ latestNotice(options, env, controller.signal),
108300
+ new Promise((resolve2) => {
108301
+ timer = setTimeout(() => {
108302
+ controller.abort();
108303
+ resolve2(void 0);
108304
+ }, CHECK_TIMEOUT_MS);
108305
+ })
108306
+ ]);
108307
+ } catch {
108308
+ return void 0;
108309
+ } finally {
108310
+ if (timer !== void 0) clearTimeout(timer);
108311
+ controller.abort();
108312
+ }
108313
+ }
108314
+ async function latestNotice(options, env, signal) {
108315
+ const paths = getVapiPaths(options.home ?? (env.VAPI_HOME?.trim() || void 0));
108316
+ const now = (options.nowMs ?? Date.now)();
108317
+ let cached2;
108318
+ try {
108319
+ const value = JSON.parse(await readFile29(paths.updateCheck, "utf8"));
108320
+ if (isRecord12(value) && typeof value.checkedAt === "number" && Number.isFinite(value.checkedAt) && typeof value.latest === "string" && parseVersion(value.latest)) {
108321
+ cached2 = { checkedAt: value.checkedAt, latest: value.latest };
108322
+ }
108323
+ } catch {
108324
+ }
108325
+ signal.throwIfAborted();
108326
+ let latest = cached2?.latest ?? CLI_VERSION;
108327
+ if (cached2 === void 0 || now - cached2.checkedAt >= CHECK_INTERVAL_MS || now < cached2.checkedAt) {
108328
+ await mkdir21(paths.directory, { recursive: true });
108329
+ signal.throwIfAborted();
108330
+ await writeFile12(paths.updateCheck, JSON.stringify({ checkedAt: now, latest }), { mode: 384 });
108331
+ signal.throwIfAborted();
108332
+ const fetchImpl = options.fetchImpl ?? createPublicFetch({
108333
+ allowPrivateNetwork: false,
108334
+ lookup: async (hostname3) => {
108335
+ let onAbort;
108336
+ const aborted2 = new Promise((_resolve, reject) => {
108337
+ onAbort = () => reject(signal.reason);
108338
+ signal.addEventListener("abort", onAbort, { once: true });
108339
+ });
108340
+ try {
108341
+ signal.throwIfAborted();
108342
+ const addresses = await Promise.race([lookup(hostname3, { all: true }), aborted2]);
108343
+ return addresses.map(({ address: address4 }) => address4);
108344
+ } finally {
108345
+ signal.removeEventListener("abort", onAbort);
108346
+ }
108347
+ }
108348
+ });
108349
+ const response = await fetchImpl(LATEST_URL, { signal, redirect: "error" });
108350
+ if (!response.ok) return void 0;
108351
+ const body = await response.json();
108352
+ signal.throwIfAborted();
108353
+ if (!isRecord12(body) || typeof body.version !== "string" || !parseVersion(body.version)) {
108354
+ return void 0;
108355
+ }
108356
+ latest = body.version;
108357
+ await writeFile12(paths.updateCheck, JSON.stringify({ checkedAt: now, latest }), { mode: 384 });
108358
+ }
108359
+ signal.throwIfAborted();
108360
+ return isNewerVersion(latest, CLI_VERSION) ? `vapi-network ${latest} is available (you have ${CLI_VERSION}). Update: npm i -g vapi-network@latest` : void 0;
108361
+ }
108362
+ function isRecord12(value) {
108363
+ return typeof value === "object" && value !== null && !Array.isArray(value);
108364
+ }
108365
+ function parseVersion(version5) {
108366
+ const match = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([\da-zA-Z.-]+))?(?:\+[\da-zA-Z-]+(?:\.[\da-zA-Z-]+)*)?$/.exec(
108367
+ version5
108368
+ );
108369
+ if (!match) return void 0;
108370
+ const prerelease = match[4]?.split(".");
108371
+ if (prerelease?.some((part) => part === "" || /^\d+$/.test(part) && /^0\d/.test(part))) {
108372
+ return void 0;
108373
+ }
108374
+ return { core: match.slice(1, 4).map(BigInt), prerelease };
108375
+ }
108376
+ function isNewerVersion(latest, current) {
108377
+ const next = parseVersion(latest);
108378
+ const running = parseVersion(current);
108379
+ for (let index2 = 0; index2 < 3; index2 += 1) {
108380
+ if (next.core[index2] !== running.core[index2]) return next.core[index2] > running.core[index2];
108381
+ }
108382
+ if (running.prerelease === void 0) return false;
108383
+ if (next.prerelease === void 0) return true;
108384
+ for (let index2 = 0; index2 < Math.max(next.prerelease.length, running.prerelease.length); index2 += 1) {
108385
+ const a = next.prerelease[index2];
108386
+ const b = running.prerelease[index2];
108387
+ if (a === b) continue;
108388
+ if (a === void 0) return false;
108389
+ if (b === void 0) return true;
108390
+ const aNumeric = /^\d+$/.test(a);
108391
+ const bNumeric = /^\d+$/.test(b);
108392
+ if (aNumeric && bNumeric) return BigInt(a) > BigInt(b);
108393
+ if (aNumeric !== bNumeric) return !aNumeric;
108394
+ return a > b;
108395
+ }
108396
+ return false;
108397
+ }
108398
+
108024
108399
  // src/main-module.ts
108025
108400
  import { realpathSync as realpathSync2 } from "node:fs";
108026
108401
  import { pathToFileURL } from "node:url";
@@ -108037,7 +108412,7 @@ function isMainModule(entry, moduleUrl, realpath = realpathSync2) {
108037
108412
 
108038
108413
  // src/bin.ts
108039
108414
  if (isMainModule(process.argv[1], import.meta.url)) {
108040
- process.exitCode = await runCli();
108415
+ process.exitCode = await runCliWithUpdateCheck();
108041
108416
  }
108042
108417
  export {
108043
108418
  CUSTODY_NOTICE,