@zeroxyz/sdk 0.9.0 → 0.11.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.
@@ -1,5 +1,6 @@
1
1
  import { serializeTransaction, parseSignature, formatUnits, isAddress, getAddress, parseUnits, createWalletClient, http, createPublicClient, bytesToHex } from 'viem';
2
2
  import { toAccount, privateKeyToAccount, mnemonicToAccount } from 'viem/accounts';
3
+ import { base, baseSepolia } from 'viem/chains';
3
4
  import { getClient, adaptViemWallet, createClient, convertViemChainToRelayChain, MAINNET_RELAY_API } from '@relayprotocol/relay-sdk';
4
5
  import { x402Client } from '@x402/core/client';
5
6
  import { x402HTTPClient, decodePaymentResponseHeader, encodePaymentRequiredHeader } from '@x402/core/http';
@@ -8,7 +9,6 @@ import { createSIWxClientHook } from '@x402/extensions/sign-in-with-x';
8
9
  import { wrapFetchWithPayment } from '@x402/fetch';
9
10
  import { Receipt, Challenge } from 'mppx';
10
11
  import { Mppx, tempo } from 'mppx/client';
11
- import { base, baseSepolia } from 'viem/chains';
12
12
  import { createHash } from 'crypto';
13
13
  import { z } from 'zod';
14
14
 
@@ -43,6 +43,28 @@ var createManagedAccount = (client, address) => toAccount({
43
43
  return await serialize(transaction, parseSignature(signature));
44
44
  }
45
45
  });
46
+ var USDC_BASE = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913";
47
+ var USDC_TEMPO = "0x20c000000000000000000000b9537d11c60e8b50";
48
+ var TEMPO_CHAIN_ID = 4217;
49
+ var baseChain = base;
50
+ var tempoChain = {
51
+ id: TEMPO_CHAIN_ID,
52
+ name: "Tempo",
53
+ nativeCurrency: { name: "USD", symbol: "USD", decimals: 18 },
54
+ rpcUrls: {
55
+ default: { http: ["https://rpc.tempo.xyz"] }
56
+ }
57
+ };
58
+ var ERC20_BALANCE_ABI = [
59
+ {
60
+ inputs: [{ name: "account", type: "address" }],
61
+ name: "balanceOf",
62
+ outputs: [{ name: "", type: "uint256" }],
63
+ stateMutability: "view",
64
+ type: "function"
65
+ }
66
+ ];
67
+ var USDC_DECIMALS = 6;
46
68
 
47
69
  // src/errors.ts
48
70
  var ZeroError = class extends Error {
@@ -71,6 +93,25 @@ var ZeroAuthError = class extends ZeroError {
71
93
  this.name = "ZeroAuthError";
72
94
  }
73
95
  };
96
+ var ZeroAgentAuthError = class extends ZeroError {
97
+ status;
98
+ body;
99
+ constructor(message, init) {
100
+ super("auth_error", message, { cause: init.cause });
101
+ this.name = "ZeroAgentAuthError";
102
+ this.status = init.status;
103
+ this.body = init.body;
104
+ }
105
+ // The error code the SERVER sent, distinct from ZeroError's `code`
106
+ // discriminator. WorkOS error bodies carry {code, message}; OAuth-style
107
+ // ones carry {error}.
108
+ get serverCode() {
109
+ const body = this.body;
110
+ if (typeof body?.code === "string") return body.code;
111
+ if (typeof body?.error === "string") return body.error;
112
+ return null;
113
+ }
114
+ };
74
115
  var ZeroPaymentError = class extends ZeroError {
75
116
  constructor(message, options) {
76
117
  super("payment_error", message, options);
@@ -430,15 +471,15 @@ var networkToChain = (network) => {
430
471
  var MAX_PAY_RE = /^[0-9]+(\.[0-9]{1,6})?$/;
431
472
  var UINT_RE = /^[0-9]+$/;
432
473
  var MAX_AMOUNT_DIGITS = 30;
433
- var USDC_BASE = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913";
434
- var USDC_TEMPO = "0x20c000000000000000000000b9537d11c60e8b50";
474
+ var USDC_BASE2 = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913";
475
+ var USDC_TEMPO2 = "0x20c000000000000000000000b9537d11c60e8b50";
435
476
  var PATHUSD_TEMPO = "0x20c0000000000000000000000000000000000000";
436
477
  var BASE_CHAIN_ID = 8453;
437
- var TEMPO_CHAIN_ID = 4217;
478
+ var TEMPO_CHAIN_ID2 = 4217;
438
479
  var TEMPO_TESTNET_CHAIN_ID = 42431;
439
480
  var DEFAULT_PREFUND = "1";
440
- var tempoChain = {
441
- id: TEMPO_CHAIN_ID,
481
+ var tempoChain2 = {
482
+ id: TEMPO_CHAIN_ID2,
442
483
  name: "Tempo",
443
484
  nativeCurrency: { name: "USD", symbol: "USD", decimals: 18 },
444
485
  rpcUrls: { default: { http: ["https://rpc.tempo.xyz"] } }
@@ -449,7 +490,7 @@ var tempoTestnetChain = {
449
490
  nativeCurrency: { name: "USD", symbol: "USD", decimals: 18 },
450
491
  rpcUrls: { default: { http: ["https://rpc.moderato.tempo.xyz"] } }
451
492
  };
452
- var ERC20_BALANCE_ABI = [
493
+ var ERC20_BALANCE_ABI2 = [
453
494
  {
454
495
  inputs: [{ name: "account", type: "address" }],
455
496
  name: "balanceOf",
@@ -478,7 +519,7 @@ var resolveDefaultPrefund = (displayCostAmount) => {
478
519
  var isUintString = (v) => typeof v === "string" && v.length > 0 && v.length <= MAX_AMOUNT_DIGITS && UINT_RE.test(v);
479
520
  var coerceTempoChainId = (raw) => {
480
521
  const id = typeof raw === "number" && Number.isFinite(raw) ? raw : typeof raw === "string" && /^-?[0-9]+$/.test(raw) ? Number(raw) : null;
481
- if (id === TEMPO_CHAIN_ID) return { id, chain: "tempo" };
522
+ if (id === TEMPO_CHAIN_ID2) return { id, chain: "tempo" };
482
523
  if (id === TEMPO_TESTNET_CHAIN_ID) return { id, chain: "tempo-testnet" };
483
524
  return null;
484
525
  };
@@ -965,7 +1006,7 @@ var Payments = class {
965
1006
  );
966
1007
  if (!coerced) {
967
1008
  throw new ZeroPaymentError(
968
- `MPP challenge has missing or unsupported chainId (expected ${TEMPO_CHAIN_ID} or ${TEMPO_TESTNET_CHAIN_ID})`
1009
+ `MPP challenge has missing or unsupported chainId (expected ${TEMPO_CHAIN_ID2} or ${TEMPO_TESTNET_CHAIN_ID})`
969
1010
  );
970
1011
  }
971
1012
  const tempoBalance = await this.readUsdcBalance(
@@ -1009,9 +1050,9 @@ var Payments = class {
1009
1050
  });
1010
1051
  const quote = await getClient().actions.getQuote({
1011
1052
  chainId: BASE_CHAIN_ID,
1012
- toChainId: TEMPO_CHAIN_ID,
1013
- currency: USDC_BASE,
1014
- toCurrency: USDC_TEMPO,
1053
+ toChainId: TEMPO_CHAIN_ID2,
1054
+ currency: USDC_BASE2,
1055
+ toCurrency: USDC_TEMPO2,
1015
1056
  amount: bridgeAmount.toString(),
1016
1057
  tradeType: "EXACT_INPUT",
1017
1058
  user: account.address,
@@ -1042,7 +1083,7 @@ var Payments = class {
1042
1083
  // Tempo tx hash on the Base transport.
1043
1084
  chains: [
1044
1085
  convertViemChainToRelayChain(base),
1045
- convertViemChainToRelayChain(tempoChain)
1086
+ convertViemChainToRelayChain(tempoChain2)
1046
1087
  ]
1047
1088
  });
1048
1089
  this.relayInitialized = true;
@@ -1051,14 +1092,14 @@ var Payments = class {
1051
1092
  const { viemChain, token } = (() => {
1052
1093
  switch (chain) {
1053
1094
  case "base":
1054
- return { viemChain: base, token: USDC_BASE };
1095
+ return { viemChain: base, token: USDC_BASE2 };
1055
1096
  case "base-sepolia":
1056
1097
  return {
1057
1098
  viemChain: baseSepolia,
1058
1099
  token: "0x036CbD53842c5426634e7929541eC2318f3dCF7e"
1059
1100
  };
1060
1101
  case "tempo":
1061
- return { viemChain: tempoChain, token: USDC_TEMPO };
1102
+ return { viemChain: tempoChain2, token: USDC_TEMPO2 };
1062
1103
  case "tempo-testnet":
1063
1104
  return { viemChain: tempoTestnetChain, token: PATHUSD_TEMPO };
1064
1105
  }
@@ -1066,7 +1107,7 @@ var Payments = class {
1066
1107
  const client = createPublicClient({ chain: viemChain, transport: http() });
1067
1108
  return await client.readContract({
1068
1109
  address: token,
1069
- abi: ERC20_BALANCE_ABI,
1110
+ abi: ERC20_BALANCE_ABI2,
1070
1111
  functionName: "balanceOf",
1071
1112
  args: [address]
1072
1113
  });
@@ -1116,7 +1157,7 @@ var Payments = class {
1116
1157
  }
1117
1158
  if (!coercedChainId) {
1118
1159
  throw buildOrphanFromChannelEntry(
1119
- `MPP session challenge has unsupported chainId (expected ${TEMPO_CHAIN_ID} or ${TEMPO_TESTNET_CHAIN_ID}); channel ${channelId} is open and requires reconciliation.`
1160
+ `MPP session challenge has unsupported chainId (expected ${TEMPO_CHAIN_ID2} or ${TEMPO_TESTNET_CHAIN_ID}); channel ${channelId} is open and requires reconciliation.`
1120
1161
  );
1121
1162
  }
1122
1163
  const receipt = readSessionReceipt(response);
@@ -1436,16 +1477,89 @@ var extractUpstreamErrorMessage = (body) => {
1436
1477
  }
1437
1478
  return void 0;
1438
1479
  };
1480
+ var extractFieldName = (message) => {
1481
+ const patterns = [
1482
+ /(?:field|param(?:eter)?|property|key)[:\s]+["'`]?([a-z0-9_.[\]-]+)["'`]?/i,
1483
+ /["'`]([a-z0-9_.[\]-]+)["'`]\s*(?:is\s+)?(?:required|missing|expected)/i,
1484
+ // Quotes REQUIRED here — an unquoted token after "required"/"missing"
1485
+ // is usually a generic noun ("required parameters", "missing fields"),
1486
+ // not a field name. Only trust it when the seller quoted it.
1487
+ /(?:required|missing|expected)[:\s]+["'`]([a-z0-9_.[\]-]+)["'`]/i
1488
+ ];
1489
+ for (const p of patterns) {
1490
+ const match = message.match(p);
1491
+ if (match?.[1]) return match[1];
1492
+ }
1493
+ return void 0;
1494
+ };
1495
+ var extractAllowedMethod = (text) => {
1496
+ const listed = text.match(
1497
+ /allowed[^[]*\[\s*["'`]?(GET|POST|PUT|PATCH|DELETE|HEAD)/i
1498
+ );
1499
+ if (listed?.[1]) return listed[1].toUpperCase();
1500
+ const useVerb = text.match(/\buse\s+(GET|POST|PUT|PATCH|DELETE|HEAD)\b/i);
1501
+ if (useVerb?.[1]) return useVerb[1].toUpperCase();
1502
+ return void 0;
1503
+ };
1504
+ var extractUnrecognizedKeys = (bodyRaw) => {
1505
+ if (!bodyRaw) return [];
1506
+ const parsed = tryParseJson(bodyRaw);
1507
+ if (parsed === null || typeof parsed !== "object") return [];
1508
+ const out = [];
1509
+ const visit = (node) => {
1510
+ if (!node || typeof node !== "object") return;
1511
+ const o = node;
1512
+ if (Array.isArray(o.keys)) {
1513
+ for (const k of o.keys) if (typeof k === "string") out.push(k);
1514
+ }
1515
+ for (const v of Object.values(o)) {
1516
+ if (Array.isArray(v)) v.forEach(visit);
1517
+ else if (v && typeof v === "object") visit(v);
1518
+ }
1519
+ };
1520
+ visit(parsed);
1521
+ return [...new Set(out)].slice(0, 8);
1522
+ };
1523
+ var computeFixHint = (status, message, bodyRaw) => {
1524
+ if (status < 400 || status >= 500) return void 0;
1525
+ const hay = `${message ?? ""} ${bodyRaw ?? ""}`.toLowerCase();
1526
+ if (status === 405 || /method not allowed|method_not_allowed/.test(hay)) {
1527
+ const allowed = extractAllowedMethod(`${bodyRaw ?? ""} ${message ?? ""}`);
1528
+ return allowed ? `the endpoint expects ${allowed}, not the method you used \u2014 retry with -X ${allowed}` : "the endpoint rejected this HTTP method \u2014 check the capability's method (`zero get <slug>`) and retry with the right -X";
1529
+ }
1530
+ if (/invalid json|not valid json|malformed|unexpected token|json ?parse|could not parse|failed to parse|jsondecode/.test(
1531
+ hay
1532
+ )) {
1533
+ return "the request body isn't valid JSON \u2014 check quoting/escaping (single-quote the -d value or use @file), then retry";
1534
+ }
1535
+ if (/unrecognized[_ ]?key|unexpected key|unknown (field|key|propert)|additional (?:propert|field)|not allowed in object/.test(
1536
+ hay
1537
+ )) {
1538
+ const keys = extractUnrecognizedKeys(bodyRaw ?? null);
1539
+ return keys.length ? `remove the unexpected field(s) ${keys.map((k) => `'${k}'`).join(", ")} from the request body, then retry` : "the request body has fields the capability doesn't accept \u2014 send only the documented fields, then retry";
1540
+ }
1541
+ if (status === 415 || /content-?type|unsupported media|media type/.test(hay)) {
1542
+ return "set the Content-Type header to what the capability expects (usually application/json), then retry";
1543
+ }
1544
+ if (/required|missing|must (include|provide|have|contain)|is expected/.test(hay)) {
1545
+ const field = extractFieldName(message ?? "");
1546
+ return field ? `add the required field '${field}' to the request body, then retry` : "the request is missing one or more required fields \u2014 check the capability schema (`zero get <slug>`), then retry";
1547
+ }
1548
+ return void 0;
1549
+ };
1439
1550
  var buildUpstreamError = (status, bodyRaw, bodyEncoding) => {
1440
1551
  if (status >= 200 && status < 300) return void 0;
1441
1552
  if (bodyRaw === null || bodyRaw.length === 0) return void 0;
1442
1553
  if (bodyEncoding === "base64") return void 0;
1443
1554
  const snippet = bodyRaw.slice(0, ERROR_SNIPPET_BYTES);
1555
+ const message = extractUpstreamErrorMessage(bodyRaw);
1556
+ const fixHint = computeFixHint(status, message, bodyRaw);
1444
1557
  return {
1445
1558
  status,
1446
- message: extractUpstreamErrorMessage(bodyRaw),
1559
+ message,
1447
1560
  snippetHash: createHash("sha256").update(snippet).digest("hex"),
1448
- snippetLength: snippet.length
1561
+ snippetLength: snippet.length,
1562
+ ...fixHint && { fixHint }
1449
1563
  };
1450
1564
  };
1451
1565
  var computeOutcome = (status, payment, warnings) => {
@@ -1836,77 +1950,64 @@ var clientFetch = async (client, url, opts = {}) => {
1836
1950
 
1837
1951
  // package.json
1838
1952
  var package_default = {
1839
- version: "0.9.0"};
1953
+ version: "0.11.0"};
1840
1954
 
1841
1955
  // src/version.ts
1842
1956
  var SDK_VERSION = package_default.version;
1843
- var userWalletDtoSchema = z.object({
1844
- walletAddress: z.string(),
1845
- source: z.enum(["self_custody", "privy_embedded"]),
1846
- isPrimary: z.boolean(),
1847
- linkedAt: z.union([z.string(), z.date()]).nullable().optional(),
1848
- delegationGranted: z.boolean(),
1849
- signerQuorumId: z.string().nullable(),
1850
- // Per-transaction USDC cap in base units (6 dp). null = unlimited, 0 = free-only.
1851
- maxTransactionUsdcBaseUnits: z.number().int().nullable()
1852
- });
1853
- var welcomeBonusSummarySchema = z.object({
1854
- status: z.string(),
1855
- amountUsd: z.number()
1856
- });
1857
- var internalUserDtoSchema = z.object({
1957
+ var agentAuthMetadataSchema = z.object({
1958
+ token_endpoint: z.string().optional(),
1959
+ agent_auth: z.object({
1960
+ identity_endpoint: z.string(),
1961
+ claim_endpoint: z.string().optional()
1962
+ }).loose()
1963
+ }).loose();
1964
+ var identityEnvelopeSchema = z.object({
1858
1965
  id: z.string(),
1859
- email: z.string().nullable(),
1860
- createdAt: z.union([z.string(), z.date()]).optional(),
1861
- lastLoginAt: z.union([z.string(), z.date()]).nullable().optional(),
1862
- onboardingCompleted: z.boolean().optional(),
1863
- delegationGranted: z.boolean().optional(),
1864
- wallets: z.array(userWalletDtoSchema).optional(),
1865
- balance: z.object({ amount: z.string(), asset: z.string() }).nullable().optional(),
1866
- welcomeBonus: welcomeBonusSummarySchema.nullable().optional()
1867
- });
1868
- var publicUserDtoSchema = z.object({
1869
- user: z.object({
1870
- id: z.string(),
1871
- email: z.string().nullable()
1872
- }),
1873
- walletAddress: z.string().nullable(),
1874
- balance: z.object({ amount: z.string(), asset: z.string() }).nullable(),
1875
- welcomeBonus: welcomeBonusSummarySchema.nullable()
1876
- });
1877
- var signResponseSchema = z.object({
1878
- signature: z.string().regex(/^0x[0-9a-fA-F]+$/, "must be 0x-prefixed hex"),
1879
- walletAddress: z.string().regex(/^0x[0-9a-fA-F]{40}$/, "must be 0x-prefixed 40-char hex address")
1880
- });
1881
-
1882
- // src/schemas/device.ts
1883
- var deviceStartResponseSchema = z.object({
1884
- deviceCode: z.string(),
1885
- userCode: z.string(),
1886
- verificationUri: z.string(),
1887
- pollInterval: z.number(),
1888
- expiresAt: z.number()
1889
- });
1890
- var devicePollResponseSchema = z.union([
1891
- // userCode + verificationUri are optional: older API deployments don't
1892
- // echo them on pending.
1893
- z.object({
1894
- error: z.literal("authorization_pending"),
1895
- userCode: z.string().optional(),
1896
- verificationUri: z.string().optional()
1897
- }),
1898
- z.object({ error: z.literal("expired_token") }),
1899
- z.object({
1900
- accessToken: z.string(),
1901
- refreshToken: z.string(),
1902
- expiresIn: z.number(),
1903
- user: internalUserDtoSchema
1904
- })
1905
- ]);
1906
- var sessionExchangeResponseSchema = z.object({
1907
- token: z.string(),
1908
- expiresAt: z.number()
1909
- });
1966
+ identity: z.object({
1967
+ assertion: z.string(),
1968
+ expires_at: z.string().optional(),
1969
+ refresh_token: z.object({ value: z.string() }).loose().optional()
1970
+ }).loose(),
1971
+ claim: z.object({
1972
+ token: z.string().optional(),
1973
+ expires_at: z.string().optional()
1974
+ }).loose().optional()
1975
+ }).loose().transform((raw) => ({
1976
+ registrationId: raw.id,
1977
+ identityAssertion: raw.identity.assertion,
1978
+ assertionExpires: raw.identity.expires_at ?? null,
1979
+ refreshToken: raw.identity.refresh_token?.value ?? null,
1980
+ claimToken: raw.claim?.token ?? null,
1981
+ claimTokenExpires: raw.claim?.expires_at ?? null
1982
+ }));
1983
+ var agentTokenResponseSchema = z.object({ access_token: z.string() }).loose().transform((raw) => ({ accessToken: raw.access_token }));
1984
+ var startClaimResponseSchema = z.object({
1985
+ attempt: z.object({
1986
+ verification_uri: z.string(),
1987
+ expires_at: z.string().optional()
1988
+ }).loose()
1989
+ }).loose().transform((raw) => ({
1990
+ verificationUri: raw.attempt.verification_uri,
1991
+ expiresAt: raw.attempt.expires_at ?? null
1992
+ }));
1993
+ var completeClaimResponseSchema = z.object({
1994
+ identity: z.object({
1995
+ assertion: z.string(),
1996
+ expires_at: z.string().optional(),
1997
+ refresh_token: z.object({ value: z.string() }).loose().optional()
1998
+ }).loose()
1999
+ }).loose().transform((raw) => ({
2000
+ identityAssertion: raw.identity.assertion,
2001
+ assertionExpires: raw.identity.expires_at ?? null,
2002
+ refreshToken: raw.identity.refresh_token?.value ?? null
2003
+ }));
2004
+ var agentZeroSessionSchema = z.object({
2005
+ accessToken: z.string(),
2006
+ refreshToken: z.string(),
2007
+ expiresIn: z.number(),
2008
+ user: z.object({ id: z.string() }).loose(),
2009
+ walletAddress: z.string().optional()
2010
+ }).loose();
1910
2011
  var refreshResponseSchema = z.object({
1911
2012
  accessToken: z.string(),
1912
2013
  refreshToken: z.string()
@@ -2142,6 +2243,268 @@ var request = async (client, opts, schema) => {
2142
2243
  });
2143
2244
  };
2144
2245
 
2246
+ // src/namespaces/auth-agent.ts
2247
+ var JWT_BEARER_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:jwt-bearer";
2248
+ var trimSlash = (value) => value.replace(/\/$/, "");
2249
+ var wrapNetworkError = (url, err) => {
2250
+ const cause = err?.cause;
2251
+ const causeMessage = cause instanceof Error ? cause.code ?? cause.message : err instanceof Error ? err.message : String(err);
2252
+ return new ZeroAgentAuthError(`Could not reach ${url} (${causeMessage})`, {
2253
+ status: 0,
2254
+ cause: err
2255
+ });
2256
+ };
2257
+ var AuthAgent = class {
2258
+ constructor(client) {
2259
+ this.client = client;
2260
+ }
2261
+ endpointsPromise = null;
2262
+ // Fully autonomous signup: register anonymously at the AuthKit domain,
2263
+ // exchange the identity assertion for a WorkOS access token, exchange that
2264
+ // for a Zero session. The claim token and identity assertion are returned
2265
+ // exactly once — the caller persists them.
2266
+ signup = async () => {
2267
+ const registration = await this.register();
2268
+ const token = await this.exchangeAssertion(registration.identityAssertion);
2269
+ const session = await this.exchangeAtZero(token.accessToken);
2270
+ return { registration, session };
2271
+ };
2272
+ register = async () => {
2273
+ const endpoints = await this.endpoints();
2274
+ return this.postJson(
2275
+ endpoints.identityEndpoint,
2276
+ { type: "anonymous" },
2277
+ identityEnvelopeSchema
2278
+ );
2279
+ };
2280
+ exchangeAssertion = async (identityAssertion) => {
2281
+ const endpoints = await this.endpoints();
2282
+ return this.postForm(
2283
+ endpoints.tokenEndpoint,
2284
+ { grant_type: JWT_BEARER_GRANT_TYPE, assertion: identityAssertion },
2285
+ agentTokenResponseSchema
2286
+ );
2287
+ };
2288
+ // POST /v1/auth/agent/exchange — WorkOS agent access token → Zero session
2289
+ // pair (+ the implicitly provisioned wallet address). The post-claim token
2290
+ // goes through here too: the server binds the claiming human.
2291
+ exchangeAtZero = (accessToken, opts = {}) => request(
2292
+ this.client,
2293
+ {
2294
+ method: "POST",
2295
+ path: "/v1/auth/agent/exchange",
2296
+ body: { accessToken },
2297
+ signal: opts.signal
2298
+ },
2299
+ agentZeroSessionSchema
2300
+ );
2301
+ // Mint a claim attempt. The human opens the returned verification URI,
2302
+ // signs in as `email`, and the page shows THEM a pairing code to read back
2303
+ // to the agent — completeClaim() submits it.
2304
+ startClaim = async (input) => {
2305
+ const endpoints = await this.endpoints();
2306
+ if (!endpoints.claimEndpoint) {
2307
+ throw new ZeroAgentAuthError(
2308
+ "The authorization server advertises no claim endpoint.",
2309
+ { status: 0 }
2310
+ );
2311
+ }
2312
+ return this.postJson(
2313
+ endpoints.claimEndpoint,
2314
+ {
2315
+ type: "service_auth",
2316
+ claim_token: input.claimToken,
2317
+ login_hint: input.email
2318
+ },
2319
+ startClaimResponseSchema
2320
+ );
2321
+ };
2322
+ // Submit the pairing code the human read off the hosted page. Terminal
2323
+ // states map to a typed result; anything unexpected throws.
2324
+ completeClaim = async (input) => {
2325
+ const endpoints = await this.endpoints();
2326
+ if (!endpoints.claimEndpoint) {
2327
+ throw new ZeroAgentAuthError(
2328
+ "The authorization server advertises no claim endpoint.",
2329
+ { status: 0 }
2330
+ );
2331
+ }
2332
+ try {
2333
+ const identity = await this.postJson(
2334
+ `${endpoints.claimEndpoint}/complete`,
2335
+ { claim_token: input.claimToken, user_code: input.userCode },
2336
+ completeClaimResponseSchema
2337
+ );
2338
+ return { status: "ok", ...identity };
2339
+ } catch (err) {
2340
+ if (err instanceof ZeroAgentAuthError && err.serverCode) {
2341
+ if (err.serverCode === "claim_not_confirmed") {
2342
+ return { status: "not_confirmed" };
2343
+ }
2344
+ if (err.serverCode === "invalid_user_code") {
2345
+ return { status: "invalid_code" };
2346
+ }
2347
+ if ([
2348
+ "user_code_expired",
2349
+ "claim_expired",
2350
+ "claim_revoked",
2351
+ "already_claimed",
2352
+ "claim_denied"
2353
+ ].includes(err.serverCode)) {
2354
+ return { status: "expired", code: err.serverCode };
2355
+ }
2356
+ }
2357
+ throw err;
2358
+ }
2359
+ };
2360
+ // Resolve (and cache) the agent-auth endpoints via discovery.
2361
+ endpoints = () => {
2362
+ if (!this.endpointsPromise) {
2363
+ this.endpointsPromise = this.discover().catch((err) => {
2364
+ this.endpointsPromise = null;
2365
+ throw err;
2366
+ });
2367
+ }
2368
+ return this.endpointsPromise;
2369
+ };
2370
+ discover = async () => {
2371
+ const resource = await this.getJson(
2372
+ `${trimSlash(this.client.baseUrl)}/.well-known/oauth-protected-resource`
2373
+ );
2374
+ let lastError = null;
2375
+ for (const server of resource.authorization_servers ?? []) {
2376
+ try {
2377
+ const raw = await this.getJson(
2378
+ `${trimSlash(server)}/.well-known/oauth-authorization-server`
2379
+ );
2380
+ const metadata = agentAuthMetadataSchema.parse(raw);
2381
+ return {
2382
+ authorizationServer: trimSlash(server),
2383
+ identityEndpoint: metadata.agent_auth.identity_endpoint,
2384
+ claimEndpoint: metadata.agent_auth.claim_endpoint ?? null,
2385
+ tokenEndpoint: metadata.token_endpoint ?? `${trimSlash(server)}/oauth2/token`
2386
+ };
2387
+ } catch (err) {
2388
+ lastError = err;
2389
+ }
2390
+ }
2391
+ throw new ZeroAgentAuthError(
2392
+ "No authorization server with an agent_auth block found \u2014 agent registration is not enabled for this service.",
2393
+ { status: 0, cause: lastError }
2394
+ );
2395
+ };
2396
+ getJson = async (url) => {
2397
+ const response = await this.client.fetchImpl(url).catch((err) => {
2398
+ throw wrapNetworkError(url, err);
2399
+ });
2400
+ if (!response.ok) {
2401
+ throw new ZeroAgentAuthError(`GET ${url} failed (${response.status})`, {
2402
+ status: response.status
2403
+ });
2404
+ }
2405
+ return await response.json();
2406
+ };
2407
+ postJson = (url, body, schema) => this.post(url, "application/json", JSON.stringify(body), schema);
2408
+ postForm = (url, fields, schema) => this.post(
2409
+ url,
2410
+ "application/x-www-form-urlencoded",
2411
+ new URLSearchParams(fields).toString(),
2412
+ schema
2413
+ );
2414
+ post = async (url, contentType, body, schema) => {
2415
+ const response = await this.client.fetchImpl(url, {
2416
+ method: "POST",
2417
+ headers: { "content-type": contentType },
2418
+ body
2419
+ }).catch((err) => {
2420
+ throw wrapNetworkError(url, err);
2421
+ });
2422
+ const text = await response.text();
2423
+ let parsed = null;
2424
+ try {
2425
+ parsed = text ? JSON.parse(text) : null;
2426
+ } catch {
2427
+ }
2428
+ if (!response.ok) {
2429
+ const errBody = parsed;
2430
+ const code = errBody?.code ?? errBody?.error;
2431
+ throw new ZeroAgentAuthError(
2432
+ `Agent auth request to ${url} failed (${response.status}${typeof code === "string" ? `: ${code}` : ""})`,
2433
+ { status: response.status, body: parsed ?? text }
2434
+ );
2435
+ }
2436
+ return schema.parse(parsed);
2437
+ };
2438
+ };
2439
+ var userWalletDtoSchema = z.object({
2440
+ walletAddress: z.string(),
2441
+ source: z.enum(["self_custody", "privy_embedded"]),
2442
+ isPrimary: z.boolean(),
2443
+ linkedAt: z.union([z.string(), z.date()]).nullable().optional(),
2444
+ importedAt: z.union([z.string(), z.date()]).nullable().optional(),
2445
+ delegationGranted: z.boolean(),
2446
+ signerQuorumId: z.string().nullable(),
2447
+ // Per-transaction USDC cap in base units (6 dp). null = unlimited, 0 = free-only.
2448
+ maxTransactionUsdcBaseUnits: z.number().int().nullable()
2449
+ });
2450
+ var welcomeBonusSummarySchema = z.object({
2451
+ status: z.string(),
2452
+ amountUsd: z.number()
2453
+ });
2454
+ var internalUserDtoSchema = z.object({
2455
+ id: z.string(),
2456
+ email: z.string().nullable(),
2457
+ createdAt: z.union([z.string(), z.date()]).optional(),
2458
+ lastLoginAt: z.union([z.string(), z.date()]).nullable().optional(),
2459
+ onboardingCompleted: z.boolean().optional(),
2460
+ delegationGranted: z.boolean().optional(),
2461
+ wallets: z.array(userWalletDtoSchema).optional(),
2462
+ balance: z.object({ amount: z.string(), asset: z.string() }).nullable().optional(),
2463
+ welcomeBonus: welcomeBonusSummarySchema.nullable().optional()
2464
+ });
2465
+ var publicUserDtoSchema = z.object({
2466
+ user: z.object({
2467
+ id: z.string(),
2468
+ email: z.string().nullable()
2469
+ }),
2470
+ walletAddress: z.string().nullable(),
2471
+ balance: z.object({ amount: z.string(), asset: z.string() }).nullable(),
2472
+ welcomeBonus: welcomeBonusSummarySchema.nullable()
2473
+ });
2474
+ var signResponseSchema = z.object({
2475
+ signature: z.string().regex(/^0x[0-9a-fA-F]+$/, "must be 0x-prefixed hex"),
2476
+ walletAddress: z.string().regex(/^0x[0-9a-fA-F]{40}$/, "must be 0x-prefixed 40-char hex address")
2477
+ });
2478
+
2479
+ // src/schemas/device.ts
2480
+ var deviceStartResponseSchema = z.object({
2481
+ deviceCode: z.string(),
2482
+ userCode: z.string(),
2483
+ verificationUri: z.string(),
2484
+ pollInterval: z.number(),
2485
+ expiresAt: z.number()
2486
+ });
2487
+ var devicePollResponseSchema = z.union([
2488
+ // userCode + verificationUri are optional: older API deployments don't
2489
+ // echo them on pending.
2490
+ z.object({
2491
+ error: z.literal("authorization_pending"),
2492
+ userCode: z.string().optional(),
2493
+ verificationUri: z.string().optional()
2494
+ }),
2495
+ z.object({ error: z.literal("expired_token") }),
2496
+ z.object({
2497
+ accessToken: z.string(),
2498
+ refreshToken: z.string(),
2499
+ expiresIn: z.number(),
2500
+ user: internalUserDtoSchema
2501
+ })
2502
+ ]);
2503
+ var sessionExchangeResponseSchema = z.object({
2504
+ token: z.string(),
2505
+ expiresAt: z.number()
2506
+ });
2507
+
2145
2508
  // src/namespaces/auth-device.ts
2146
2509
  var AuthDevice = class {
2147
2510
  constructor(client) {
@@ -2203,8 +2566,10 @@ var repackageSignFailure = (err, op) => {
2203
2566
  var Auth = class {
2204
2567
  constructor(client) {
2205
2568
  this.client = client;
2569
+ this.agent = new AuthAgent(client);
2206
2570
  this.device = new AuthDevice(client);
2207
2571
  }
2572
+ agent;
2208
2573
  device;
2209
2574
  // The internal (web-app facing) user profile. Richer than profile(): full
2210
2575
  // wallet list with per-wallet delegation/signer/limit metadata.
@@ -2411,8 +2776,12 @@ var createBugReportResponseSchema = z.object({
2411
2776
  searchId: z.number().nullable()
2412
2777
  })
2413
2778
  });
2414
-
2415
- // src/namespaces/bug-reports.ts
2779
+ var TEN_MIN_MS = 10 * 60 * 1e3;
2780
+ var autoIdempotencyKey = (description, contextSeed) => {
2781
+ const bucket = Math.floor(Date.now() / TEN_MIN_MS);
2782
+ const seed = `${description}|${contextSeed ?? ""}|${bucket}`;
2783
+ return `auto-${createHash("sha256").update(seed).digest("hex").slice(0, 16)}`;
2784
+ };
2416
2785
  var BugReports = class {
2417
2786
  constructor(client) {
2418
2787
  this.client = client;
@@ -2420,16 +2789,26 @@ var BugReports = class {
2420
2789
  // POST /v1/bug-reports. Wallet-attributed server-side; the SDK client's
2421
2790
  // active credential drives attribution (account-mode → EIP-191 from the
2422
2791
  // wallet, session-mode → Bearer + server resolves the user's wallet).
2423
- create = (input, opts = {}) => request(
2424
- this.client,
2425
- {
2426
- method: "POST",
2427
- path: "/v1/bug-reports",
2428
- body: input,
2429
- signal: opts.signal
2430
- },
2431
- createBugReportResponseSchema
2432
- );
2792
+ // When `idempotencyKey` is omitted, one is auto-derived from the description
2793
+ // and (optional) contextSeed, bucketed to 10-minute windows so accidental
2794
+ // retries within the window dedup naturally.
2795
+ create = (input, opts = {}) => {
2796
+ const { contextSeed, ...rest } = input;
2797
+ const withKey = {
2798
+ ...rest,
2799
+ idempotencyKey: input.idempotencyKey ?? autoIdempotencyKey(input.description, contextSeed)
2800
+ };
2801
+ return request(
2802
+ this.client,
2803
+ {
2804
+ method: "POST",
2805
+ path: "/v1/bug-reports",
2806
+ body: withKey,
2807
+ signal: opts.signal
2808
+ },
2809
+ createBugReportResponseSchema
2810
+ );
2811
+ };
2433
2812
  };
2434
2813
  var ratingSchema = z.object({
2435
2814
  score: z.string(),
@@ -2489,6 +2868,9 @@ var capabilityResponseSchema = z.object({
2489
2868
  "unknown_unparsed",
2490
2869
  "unprobed"
2491
2870
  ]).optional(),
2871
+ // ZERO-147 — provenance of displayCostAmount (settled | probe | registry |
2872
+ // unknown). `settled` means the shown price is reconciled from real charges.
2873
+ priceSource: z.string().nullable().optional(),
2492
2874
  requiresHandshake: z.boolean().optional(),
2493
2875
  reviewCount: z.number(),
2494
2876
  rating: ratingSchema,
@@ -2753,50 +3135,39 @@ var Runs = class {
2753
3135
  batchReviewResponseSchema
2754
3136
  );
2755
3137
  };
2756
- var USDC_BASE2 = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913";
2757
- var USDC_TEMPO2 = "0x20c000000000000000000000b9537d11c60e8b50";
2758
- var TEMPO_CHAIN_ID2 = 4217;
2759
- var baseChain = base;
2760
- var tempoChain2 = {
2761
- id: TEMPO_CHAIN_ID2,
2762
- name: "Tempo",
2763
- nativeCurrency: { name: "USD", symbol: "USD", decimals: 18 },
2764
- rpcUrls: {
2765
- default: { http: ["https://rpc.tempo.xyz"] }
2766
- }
2767
- };
2768
- var ERC20_BALANCE_ABI2 = [
2769
- {
2770
- inputs: [{ name: "account", type: "address" }],
2771
- name: "balanceOf",
2772
- outputs: [{ name: "", type: "uint256" }],
2773
- stateMutability: "view",
2774
- type: "function"
2775
- }
2776
- ];
2777
- var USDC_DECIMALS = 6;
2778
-
2779
- // src/namespaces/wallet.ts
2780
3138
  var fundingUrlResponseSchema = z.object({ url: z.string() });
2781
3139
  var userWalletListSchema = z.array(userWalletDtoSchema);
2782
- var migrateAuthorizationResponseSchema = z.object({
2783
- transactionHash: z.string()
2784
- });
2785
3140
  var errorMessage = (reason) => reason instanceof Error ? reason.message : String(reason);
2786
3141
  var Wallet = class {
2787
3142
  constructor(client) {
2788
3143
  this.client = client;
2789
3144
  }
2790
3145
  clients = {};
2791
- // The configured wallet address, or null if the client wasn't constructed
2792
- // with an account. Stable read — does not hit the network.
2793
- get address() {
3146
+ // The configured account's address, or null when the client runs in
3147
+ // session/no-credential mode. Local read — never hits the network.
3148
+ get configuredAddress() {
2794
3149
  const credentials = this.client.getCredentials();
2795
3150
  if (credentials.kind !== "account") return null;
2796
3151
  return credentials.account.address;
2797
3152
  }
3153
+ // The wallet address this client acts as. Account mode resolves locally
3154
+ // (no network). Session mode resolves the user's managed (Privy-embedded)
3155
+ // wallet, provisioning one on first call if the user has none yet. Throws
3156
+ // on network/auth failure — callers that want a null-fallback should wrap
3157
+ // in try/catch (the CLI does this in `resolveManagedWalletAddress`).
3158
+ address = async (opts = {}) => {
3159
+ const configured = this.configuredAddress;
3160
+ if (configured) return configured;
3161
+ const wallets = await this.list(opts);
3162
+ const primary = wallets.find(
3163
+ (w) => w.source === "privy_embedded" && w.isPrimary
3164
+ );
3165
+ if (primary) return primary.walletAddress;
3166
+ const provisioned = await this.provision(opts);
3167
+ return provisioned.walletAddress;
3168
+ };
2798
3169
  balance = async (opts = {}) => {
2799
- const target = opts.address ?? this.address;
3170
+ const target = opts.address ?? this.configuredAddress;
2800
3171
  if (!target) {
2801
3172
  throw new ZeroWalletError(
2802
3173
  "balance() requires either an `address` option or a client constructed with an account"
@@ -2856,21 +3227,48 @@ var Wallet = class {
2856
3227
  },
2857
3228
  userWalletDtoSchema
2858
3229
  );
2859
- // Relays a BYO-wallet-signed EIP-3009 ReceiveWithAuthorization, sweeping
2860
- // USDC from the BYO wallet to the user's Zero wallet on Base via the
2861
- // gas-paying relayer. The SDK does not author the authorization (chain
2862
- // nonce + EIP-712 domain coupling stays caller-side); it only forwards
2863
- // the signed payload. Session-only.
2864
- migrateAuthorization = (authorization, opts = {}) => request(
3230
+ // Link an existing self-custody wallet to the session user by private key.
3231
+ // Session-only. 409s when the wallet is already linked (to this or another
3232
+ // profile the error message says which).
3233
+ import = (opts) => request(
2865
3234
  this.client,
2866
3235
  {
2867
3236
  method: "POST",
2868
- path: "/v1/users/me/wallets/migrate",
2869
- body: { authorization },
3237
+ path: "/v1/users/me/wallets/import",
3238
+ body: {
3239
+ privateKey: opts.privateKey,
3240
+ ...opts.makePrimary !== void 0 ? { makePrimary: opts.makePrimary } : {}
3241
+ },
3242
+ signal: opts.signal
3243
+ },
3244
+ userWalletDtoSchema
3245
+ );
3246
+ // Make one of the session user's linked wallets the primary payer.
3247
+ // Session-only. 404s when the address isn't linked to the user.
3248
+ setPrimary = (opts) => request(
3249
+ this.client,
3250
+ {
3251
+ method: "PATCH",
3252
+ path: "/v1/users/me/wallets/primary",
3253
+ body: { walletAddress: opts.walletAddress },
2870
3254
  signal: opts.signal
2871
3255
  },
2872
- migrateAuthorizationResponseSchema
3256
+ userWalletDtoSchema
2873
3257
  );
3258
+ // Unlink a wallet from the session user. Session-only. 409s on the primary
3259
+ // wallet or a non-zero balance (the error message says which); 404s when
3260
+ // the address isn't linked. Responds 204 — resolves to void.
3261
+ remove = async (opts) => {
3262
+ await request(
3263
+ this.client,
3264
+ {
3265
+ method: "DELETE",
3266
+ path: `/v1/users/me/wallets/${opts.walletAddress}`,
3267
+ signal: opts.signal
3268
+ },
3269
+ z.undefined()
3270
+ );
3271
+ };
2874
3272
  // Routes by identity: account mode signs EIP-191 against /v1/wallet/fund-url,
2875
3273
  // session mode hits /v1/users/me/fund-url (Bearer-authed, wallet resolved
2876
3274
  // server-side). Both honor `provider` (coinbase | stripe).
@@ -2906,7 +3304,7 @@ var Wallet = class {
2906
3304
  if (!this.clients.tempo) {
2907
3305
  this.clients.tempo = createPublicClient({
2908
3306
  // biome-ignore lint/suspicious/noExplicitAny: tempo is a custom chain object that viem's stricter Chain type rejects shape-wise but accepts at runtime
2909
- chain: tempoChain2,
3307
+ chain: tempoChain,
2910
3308
  transport: http()
2911
3309
  });
2912
3310
  }
@@ -2922,10 +3320,10 @@ var Wallet = class {
2922
3320
  */
2923
3321
  readChainBalance = async (chain, address) => {
2924
3322
  const publicClient = this.getPublicClient(chain);
2925
- const tokenAddress = chain === "base" ? USDC_BASE2 : USDC_TEMPO2;
3323
+ const tokenAddress = chain === "base" ? USDC_BASE : USDC_TEMPO;
2926
3324
  const balance = await publicClient.readContract({
2927
3325
  address: tokenAddress,
2928
- abi: ERC20_BALANCE_ABI2,
3326
+ abi: ERC20_BALANCE_ABI,
2929
3327
  functionName: "balanceOf",
2930
3328
  args: [address]
2931
3329
  });
@@ -2963,6 +3361,10 @@ var searchResultSchema = z.object({
2963
3361
  url: z.string(),
2964
3362
  urlTemplate: z.string().nullable().optional(),
2965
3363
  cost: z.object({ amount: z.string(), asset: z.string() }),
3364
+ // ZERO-199 — the server-resolved price. Prefer `pricing.summary` over
3365
+ // deriving a label from `cost.amount` (which flattens usage-priced caps).
3366
+ // Optional: absent on older API deploys.
3367
+ pricing: z.object({ kind: z.string(), summary: z.string() }).optional(),
2966
3368
  // Payment protocol (x402 | mpp) for this result. The server emits it; the SDK
2967
3369
  // previously stripped it, leaving an agent unable to tell which handshake to
2968
3370
  // engage from a search row. Optional/nullable so older deploys still parse
@@ -3356,6 +3758,26 @@ var ZeroClient = class _ZeroClient {
3356
3758
  // path is flat on the client, same rationale as `search` above.
3357
3759
  // Orchestration lives in fetch.ts so this class stays readable.
3358
3760
  fetch = (url, opts = {}) => clientFetch(this, url, opts);
3761
+ // Poll the API server's /health endpoint until it responds OK. Useful for
3762
+ // warming up a cold container before spawning a subprocess that needs the
3763
+ // API ready. Resolves when healthy, resolves (silently) when maxAttempts
3764
+ // is exhausted — never throws, so callers proceed regardless of outcome.
3765
+ ping = async (opts = {}) => {
3766
+ const { maxAttempts = 20, timeoutMs = 2e3, retryDelayMs = 500 } = opts;
3767
+ const healthUrl = `${this.baseUrl}/health`;
3768
+ for (let i = 0; i < maxAttempts; i++) {
3769
+ try {
3770
+ const res = await this.fetchImpl(healthUrl, {
3771
+ signal: AbortSignal.timeout(timeoutMs)
3772
+ });
3773
+ if (res.ok) return;
3774
+ } catch {
3775
+ }
3776
+ if (i < maxAttempts - 1) {
3777
+ await new Promise((r) => setTimeout(r, retryDelayMs));
3778
+ }
3779
+ }
3780
+ };
3359
3781
  // No-op today. Reserved for an explicit drain/cancel handle once
3360
3782
  // long-running consumers need one — do NOT rely on this to abort
3361
3783
  // in-flight requests or flush recording. Consumers needing
@@ -3364,6 +3786,6 @@ var ZeroClient = class _ZeroClient {
3364
3786
  };
3365
3787
  };
3366
3788
 
3367
- export { Auth, AuthDevice, BUG_REPORT_CATEGORIES, BugReports, Capabilities, DEFAULT_BASE_URL, DEFAULT_MAX_RETRIES, DEFAULT_TIMEOUT_MS, FETCH_SKIP_REASONS, FETCH_WARNINGS, Payments, Runs, SDK_VERSION, TEMPO_CHAIN_ID, TEMPO_TESTNET_CHAIN_ID, Wallet, ZeroApiError, ZeroAuthError, ZeroClient, ZeroConfigurationError, ZeroError, ZeroPaymentError, ZeroSessionCloseFailedError, ZeroTimeoutError, ZeroValidationError, ZeroWalletError, asSchemaNode, coerceTempoChainId, createManagedAccount, extractInputEnvelope, isShortToken, normalizeTransportEnvelopeRequest, paymentHasAnchor, tempoChainLabelFromId, unwrapTransportEnvelope };
3368
- //# sourceMappingURL=chunk-T6JT5NV5.js.map
3369
- //# sourceMappingURL=chunk-T6JT5NV5.js.map
3789
+ export { Auth, AuthAgent, AuthDevice, BUG_REPORT_CATEGORIES, BugReports, Capabilities, DEFAULT_BASE_URL, DEFAULT_MAX_RETRIES, DEFAULT_TIMEOUT_MS, FETCH_SKIP_REASONS, FETCH_WARNINGS, Payments, Runs, SDK_VERSION, TEMPO_CHAIN_ID2 as TEMPO_CHAIN_ID, TEMPO_TESTNET_CHAIN_ID, USDC_BASE, USDC_DECIMALS, USDC_TEMPO, Wallet, ZeroAgentAuthError, ZeroApiError, ZeroAuthError, ZeroClient, ZeroConfigurationError, ZeroError, ZeroPaymentError, ZeroSessionCloseFailedError, ZeroTimeoutError, ZeroValidationError, ZeroWalletError, asSchemaNode, coerceTempoChainId, createManagedAccount, extractInputEnvelope, isShortToken, normalizeTransportEnvelopeRequest, paymentHasAnchor, tempoChainLabelFromId, unwrapTransportEnvelope };
3790
+ //# sourceMappingURL=chunk-T77VVLOM.js.map
3791
+ //# sourceMappingURL=chunk-T77VVLOM.js.map