@zeroxyz/cli 1.8.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +1619 -1772
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -76582,7 +76582,7 @@ init_esm_shims();
76582
76582
 
76583
76583
  // package.json
76584
76584
  var package_default = {
76585
- version: "1.8.0"};
76585
+ version: "1.9.0"};
76586
76586
 
76587
76587
  // src/app.ts
76588
76588
  init_esm_shims();
@@ -77590,7 +77590,7 @@ init_esm_shims();
77590
77590
  // ../sdk/dist/index.js
77591
77591
  init_esm_shims();
77592
77592
 
77593
- // ../sdk/dist/chunk-W7JBWVWW.js
77593
+ // ../sdk/dist/chunk-26IHUYNK.js
77594
77594
  init_esm_shims();
77595
77595
 
77596
77596
  // ../../node_modules/.pnpm/viem@2.52.2_bufferutil@4.1.0_typescript@5.9.3_utf-8-validate@6.0.6_zod@4.3.6/node_modules/viem/_esm/index.js
@@ -142971,6 +142971,8 @@ config(en_default2());
142971
142971
 
142972
142972
  // ../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/index.js
142973
142973
  var zod_default = external_exports3;
142974
+
142975
+ // ../sdk/dist/chunk-26IHUYNK.js
142974
142976
  var createManagedAccount = (client, address2) => toAccount({
142975
142977
  address: address2,
142976
142978
  async signMessage({ message: message2 }) {
@@ -144161,718 +144163,1338 @@ var Payments = class {
144161
144163
  };
144162
144164
  };
144163
144165
  };
144164
- var package_default2 = {
144165
- version: "0.6.0"
144166
- };
144167
- var SDK_VERSION2 = package_default2.version;
144168
- var userWalletDtoSchema = external_exports3.object({
144169
- walletAddress: external_exports3.string(),
144170
- source: external_exports3.enum(["self_custody", "privy_embedded"]),
144171
- isPrimary: external_exports3.boolean(),
144172
- linkedAt: external_exports3.union([external_exports3.string(), external_exports3.date()]).nullable().optional(),
144173
- delegationGranted: external_exports3.boolean(),
144174
- signerQuorumId: external_exports3.string().nullable(),
144175
- // Per-transaction USDC cap in base units (6 dp). null = unlimited, 0 = free-only.
144176
- maxTransactionUsdcBaseUnits: external_exports3.number().int().nullable()
144177
- });
144178
- var welcomeBonusSummarySchema = external_exports3.object({
144179
- status: external_exports3.string(),
144180
- amountUsd: external_exports3.number()
144181
- });
144182
- var internalUserDtoSchema = external_exports3.object({
144183
- id: external_exports3.string(),
144184
- email: external_exports3.string().nullable(),
144185
- createdAt: external_exports3.union([external_exports3.string(), external_exports3.date()]).optional(),
144186
- lastLoginAt: external_exports3.union([external_exports3.string(), external_exports3.date()]).nullable().optional(),
144187
- onboardingCompleted: external_exports3.boolean().optional(),
144188
- delegationGranted: external_exports3.boolean().optional(),
144189
- wallets: external_exports3.array(userWalletDtoSchema).optional(),
144190
- balance: external_exports3.object({ amount: external_exports3.string(), asset: external_exports3.string() }).nullable().optional(),
144191
- welcomeBonus: welcomeBonusSummarySchema.nullable().optional()
144192
- });
144193
- var publicUserDtoSchema = external_exports3.object({
144194
- user: external_exports3.object({
144195
- id: external_exports3.string(),
144196
- email: external_exports3.string().nullable()
144197
- }),
144198
- walletAddress: external_exports3.string().nullable(),
144199
- balance: external_exports3.object({ amount: external_exports3.string(), asset: external_exports3.string() }).nullable(),
144200
- welcomeBonus: welcomeBonusSummarySchema.nullable()
144201
- });
144202
- var signResponseSchema = external_exports3.object({
144203
- signature: external_exports3.string().regex(/^0x[0-9a-fA-F]+$/, "must be 0x-prefixed hex"),
144204
- walletAddress: external_exports3.string().regex(/^0x[0-9a-fA-F]{40}$/, "must be 0x-prefixed 40-char hex address")
144205
- });
144206
- var deviceStartResponseSchema = external_exports3.object({
144207
- deviceCode: external_exports3.string(),
144208
- userCode: external_exports3.string(),
144209
- verificationUri: external_exports3.string(),
144210
- pollInterval: external_exports3.number(),
144211
- expiresAt: external_exports3.number()
144212
- });
144213
- var devicePollResponseSchema = external_exports3.union([
144214
- // userCode + verificationUri are optional: older API deployments don't
144215
- // echo them on pending.
144216
- external_exports3.object({
144217
- error: external_exports3.literal("authorization_pending"),
144218
- userCode: external_exports3.string().optional(),
144219
- verificationUri: external_exports3.string().optional()
144220
- }),
144221
- external_exports3.object({ error: external_exports3.literal("expired_token") }),
144222
- external_exports3.object({
144223
- accessToken: external_exports3.string(),
144224
- refreshToken: external_exports3.string(),
144225
- expiresIn: external_exports3.number(),
144226
- user: internalUserDtoSchema
144227
- })
144228
- ]);
144229
- var sessionExchangeResponseSchema = external_exports3.object({
144230
- token: external_exports3.string(),
144231
- expiresAt: external_exports3.number()
144232
- });
144233
- var refreshResponseSchema = external_exports3.object({
144234
- accessToken: external_exports3.string(),
144235
- refreshToken: external_exports3.string()
144236
- });
144237
- var tryRefreshSession = async (client, callerSignal) => {
144238
- const credentials = client.getCredentials();
144239
- if (credentials.kind !== "session") return false;
144240
- if (!credentials.refreshToken) return false;
144241
- const url4 = `${client.baseUrl.replace(/\/$/, "")}/v1/auth/refresh`;
144242
- const timeoutSignal = AbortSignal.timeout(client.timeout);
144243
- const signal = callerSignal ? AbortSignal.any([callerSignal, timeoutSignal]) : timeoutSignal;
144244
- let response;
144245
- try {
144246
- response = await client.fetchImpl(url4, {
144247
- method: "POST",
144248
- headers: { "content-type": "application/json" },
144249
- body: JSON.stringify({ refreshToken: credentials.refreshToken }),
144250
- signal
144251
- });
144252
- } catch {
144253
- return false;
144166
+ var isShortToken = (id) => id.startsWith("z_") && id.indexOf(".", 2) !== -1;
144167
+ var MAX_402_PROBE_BYTES = 1024 * 1024;
144168
+ var MAX_RESPONSE_BYTES = 10 * 1024 * 1024;
144169
+ var TEXT_CONTENT_TYPES = [
144170
+ "application/json",
144171
+ "application/xml",
144172
+ "application/javascript",
144173
+ "application/ecmascript",
144174
+ "application/x-www-form-urlencoded",
144175
+ "text/",
144176
+ "+json",
144177
+ "+xml"
144178
+ ];
144179
+ var MPP_SCHEME_TOKENS = /* @__PURE__ */ new Set(["payment", "mpp"]);
144180
+ var splitWwwAuthenticateChallenges = (header) => {
144181
+ const chunks = [];
144182
+ let buf = "";
144183
+ let inQuotes = false;
144184
+ for (let i = 0; i < header.length; i++) {
144185
+ const ch = header[i];
144186
+ if (inQuotes) {
144187
+ if (ch === "\\" && i + 1 < header.length) {
144188
+ buf += ch + header[i + 1];
144189
+ i++;
144190
+ continue;
144191
+ }
144192
+ if (ch === '"') inQuotes = false;
144193
+ buf += ch;
144194
+ continue;
144195
+ }
144196
+ if (ch === '"') {
144197
+ inQuotes = true;
144198
+ buf += ch;
144199
+ continue;
144200
+ }
144201
+ if (ch === ",") {
144202
+ chunks.push(buf);
144203
+ buf = "";
144204
+ continue;
144205
+ }
144206
+ buf += ch;
144254
144207
  }
144255
- if (!response.ok) return false;
144256
- let body;
144208
+ if (buf.length > 0) chunks.push(buf);
144209
+ return chunks;
144210
+ };
144211
+ var isTextContentType = (contentType) => {
144212
+ if (!contentType) return true;
144213
+ const lower = contentType.toLowerCase();
144214
+ return TEXT_CONTENT_TYPES.some((marker) => lower.includes(marker));
144215
+ };
144216
+ var redactUrl2 = (url4) => {
144257
144217
  try {
144258
- body = await response.json();
144218
+ const u2 = new URL(url4);
144219
+ return `${u2.origin}${u2.pathname}`;
144259
144220
  } catch {
144260
- return false;
144221
+ return "<invalid url>";
144261
144222
  }
144262
- const parsed = refreshResponseSchema.safeParse(body);
144263
- if (!parsed.success) return false;
144264
- await client.applyRefreshedTokens({
144265
- accessToken: parsed.data.accessToken,
144266
- refreshToken: parsed.data.refreshToken
144267
- });
144268
- return true;
144269
- };
144270
- var buildSessionAuthHeaders = (accessToken) => ({
144271
- authorization: `Bearer ${accessToken}`
144272
- });
144273
- var buildCanonicalMessage = (method, pathWithQuery, body, timestamp, nonce2) => {
144274
- const bodyHash = createHash("sha256").update(body ?? "").digest("hex");
144275
- return `${method}:${pathWithQuery}:${bodyHash}:${timestamp}:${nonce2}`;
144276
144223
  };
144277
- var buildAccountAuthHeaders = async (account, method, pathWithQuery, body) => {
144278
- const timestamp = Math.floor(Date.now() / 1e3).toString();
144279
- const nonce2 = crypto.randomUUID();
144280
- const message2 = buildCanonicalMessage(
144281
- method,
144282
- pathWithQuery,
144283
- body,
144284
- timestamp,
144285
- nonce2
144224
+ var ECHO_DETECT_MIN_LEN = 16;
144225
+ var ECHO_DETECT_MAX_SCAN = 64 * 1024;
144226
+ var requestBodyEchoed = (candidate, requestBody) => {
144227
+ if (requestBody === void 0) return false;
144228
+ const asString = typeof requestBody === "string" ? requestBody : new TextDecoder("utf-8", { fatal: false }).decode(
144229
+ requestBody.byteLength > ECHO_DETECT_MAX_SCAN ? requestBody.subarray(0, ECHO_DETECT_MAX_SCAN) : requestBody
144286
144230
  );
144287
- const signature2 = await account.signMessage({ message: message2 });
144288
- return {
144289
- "x-zero-address": account.address,
144290
- "x-zero-timestamp": timestamp,
144291
- "x-zero-nonce": nonce2,
144292
- "x-zero-signature": signature2
144293
- };
144294
- };
144295
- var RETRYABLE_STATUS = /* @__PURE__ */ new Set([408, 502, 503, 504]);
144296
- var sleep2 = (ms, signal) => new Promise((resolve6, reject) => {
144297
- if (signal?.aborted) {
144298
- reject(new DOMException("Aborted", "AbortError"));
144299
- return;
144300
- }
144301
- const timer = setTimeout(() => {
144302
- signal?.removeEventListener("abort", onAbort);
144303
- resolve6();
144304
- }, ms);
144305
- const onAbort = () => {
144306
- clearTimeout(timer);
144307
- reject(new DOMException("Aborted", "AbortError"));
144308
- };
144309
- signal?.addEventListener("abort", onAbort, { once: true });
144310
- });
144311
- var buildPathWithQuery = (path3, query) => {
144312
- const normalizedPath = path3.startsWith("/") ? path3 : `/${path3}`;
144313
- if (!query) return normalizedPath;
144314
- const params = new URLSearchParams();
144315
- for (const [key, value] of Object.entries(query)) {
144316
- if (value !== void 0) params.set(key, String(value));
144317
- }
144318
- const qs = params.toString();
144319
- return qs ? `${normalizedPath}?${qs}` : normalizedPath;
144320
- };
144321
- var joinUrl = (baseUrl, pathWithQuery) => {
144322
- const base3 = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
144323
- return `${base3}${pathWithQuery}`;
144324
- };
144325
- var buildHeaders = async (client, method, pathWithQuery, hasBody, bodyStr) => {
144326
- const headers = { ...client.defaultHeaders };
144327
- if (hasBody) headers["content-type"] = "application/json";
144328
- headers["x-zero-sdk-version"] = SDK_VERSION2;
144329
- if (client.apiVersion) headers["x-zero-api-version"] = client.apiVersion;
144330
- const credentials = client.getCredentials();
144331
- if (credentials.kind === "session") {
144332
- Object.assign(headers, buildSessionAuthHeaders(credentials.accessToken));
144333
- } else if (credentials.kind === "account") {
144334
- const signed = await buildAccountAuthHeaders(
144335
- credentials.account,
144336
- method,
144337
- pathWithQuery,
144338
- bodyStr
144339
- );
144340
- Object.assign(headers, signed);
144231
+ if (asString.length < ECHO_DETECT_MIN_LEN) return false;
144232
+ const scanLen = Math.min(asString.length, ECHO_DETECT_MAX_SCAN);
144233
+ for (let i = 0; i + ECHO_DETECT_MIN_LEN <= scanLen; i++) {
144234
+ if (candidate.includes(asString.slice(i, i + ECHO_DETECT_MIN_LEN))) {
144235
+ return true;
144236
+ }
144341
144237
  }
144342
- return headers;
144238
+ return false;
144343
144239
  };
144344
- var parseErrorBody = async (response) => {
144240
+ var cancelBody2 = (response) => {
144345
144241
  try {
144346
- return await response.clone().json();
144242
+ response.body?.cancel().catch(() => {
144243
+ });
144347
144244
  } catch {
144245
+ }
144246
+ };
144247
+ var detectPaymentRequirement = async (response) => {
144248
+ if (response.status !== 402) return null;
144249
+ const x402Header = response.headers.get("payment-required") ?? response.headers.get("x-payment-required");
144250
+ if (x402Header) {
144251
+ cancelBody2(response);
144348
144252
  try {
144349
- return await response.text();
144253
+ const decoded = JSON.parse(
144254
+ Buffer.from(x402Header, "base64").toString("utf8")
144255
+ );
144256
+ return { protocol: "x402", raw: decoded };
144350
144257
  } catch {
144351
- return void 0;
144258
+ return { protocol: "x402", raw: { encoded: x402Header } };
144352
144259
  }
144353
144260
  }
144354
- };
144355
- var errorMessageFromBody = (body, status) => {
144356
- if (body !== null && typeof body === "object" && "error" in body && typeof body.error === "string") {
144357
- return body.error;
144358
- }
144359
- return `HTTP ${status}`;
144360
- };
144361
- var request2 = async (client, opts, schema) => {
144362
- const pathWithQuery = buildPathWithQuery(opts.path, opts.query);
144363
- const url4 = joinUrl(client.baseUrl, pathWithQuery);
144364
- const hasBody = opts.body !== void 0;
144365
- const bodyStr = hasBody ? JSON.stringify(
144366
- opts.body,
144367
- (_k, v) => typeof v === "bigint" ? v.toString() : v
144368
- ) : void 0;
144369
- const configuredFetch = buildConfiguredFetch(client);
144370
- const performOnce = async () => configuredFetch(url4, {
144371
- method: opts.method,
144372
- headers: await buildHeaders(
144373
- client,
144374
- opts.method,
144375
- pathWithQuery,
144376
- hasBody,
144377
- bodyStr
144378
- ),
144379
- body: bodyStr,
144380
- signal: opts.signal
144381
- });
144382
- let refreshAttempted = false;
144383
- const executeWithRefresh = async () => {
144384
- const response = await performOnce();
144385
- if (response.status !== 401 || refreshAttempted) return response;
144386
- const credentials = client.getCredentials();
144387
- if (credentials.kind !== "session") return response;
144388
- refreshAttempted = true;
144389
- const refreshed = await client.refreshSessionOnce(opts.signal);
144390
- if (!refreshed) return response;
144391
- return await performOnce();
144392
- };
144393
- let lastNetworkError;
144394
- for (let attempt = 0; attempt <= client.maxRetries; attempt++) {
144395
- let response;
144261
+ const declaredLen = Number.parseInt(
144262
+ response.headers.get("content-length") ?? "",
144263
+ 10
144264
+ );
144265
+ const bodyFits = !Number.isFinite(declaredLen) || declaredLen <= MAX_402_PROBE_BYTES;
144266
+ if (bodyFits) {
144396
144267
  try {
144397
- response = await executeWithRefresh();
144398
- } catch (e2) {
144399
- if (e2 instanceof ZeroError) throw e2;
144400
- lastNetworkError = e2;
144401
- if (attempt < client.maxRetries) {
144402
- try {
144403
- await sleep2(2 ** attempt * 200, opts.signal);
144404
- } catch (sleepErr) {
144405
- throw new ZeroApiError("Request aborted by caller", {
144406
- status: 0,
144407
- cause: sleepErr
144408
- });
144268
+ const text = await readClippedText(response, MAX_402_PROBE_BYTES);
144269
+ if (text) {
144270
+ const parsed = JSON.parse(text);
144271
+ if (looksLikeX402Body(parsed)) {
144272
+ return { protocol: "x402", raw: parsed };
144409
144273
  }
144410
- continue;
144411
- }
144412
- throw new ZeroApiError("Network request failed", {
144413
- status: 0,
144414
- cause: e2
144415
- });
144416
- }
144417
- if (response.ok) {
144418
- let text;
144419
- try {
144420
- text = await response.text();
144421
- } catch (e2) {
144422
- throw new ZeroApiError("Response body read failed", {
144423
- status: response.status,
144424
- cause: e2
144425
- });
144426
- }
144427
- let json3;
144428
- try {
144429
- json3 = JSON.parse(text);
144430
- } catch {
144431
- }
144432
- const parsed = schema.safeParse(json3);
144433
- if (!parsed.success) {
144434
- throw new ZeroValidationError(
144435
- `Response did not match expected schema for ${opts.method} ${opts.path}`,
144436
- { cause: parsed.error, body: text }
144437
- );
144438
144274
  }
144439
- return parsed.data;
144275
+ } catch {
144440
144276
  }
144441
- if (RETRYABLE_STATUS.has(response.status) && attempt < client.maxRetries) {
144442
- try {
144443
- await sleep2(2 ** attempt * 200, opts.signal);
144444
- } catch (sleepErr) {
144445
- throw new ZeroApiError("Request aborted by caller", {
144446
- status: 0,
144447
- cause: sleepErr
144448
- });
144449
- }
144450
- continue;
144277
+ }
144278
+ const wwwAuth = response.headers.get("www-authenticate");
144279
+ if (wwwAuth) {
144280
+ const schemes = splitWwwAuthenticateChallenges(wwwAuth).map(
144281
+ (challenge2) => challenge2.trim().split(/\s+/)[0]?.toLowerCase() ?? ""
144282
+ );
144283
+ if (schemes.some((scheme) => MPP_SCHEME_TOKENS.has(scheme))) {
144284
+ cancelBody2(response);
144285
+ return { protocol: "mpp", raw: { "www-authenticate": wwwAuth } };
144451
144286
  }
144452
- const errorBody = await parseErrorBody(response);
144453
- const requestId = response.headers.get("x-request-id") ?? void 0;
144454
- throw new ZeroApiError(errorMessageFromBody(errorBody, response.status), {
144455
- status: response.status,
144456
- requestId,
144457
- body: errorBody
144458
- });
144459
144287
  }
144460
- throw new ZeroApiError("Exhausted retries", {
144461
- status: 0,
144462
- cause: lastNetworkError
144463
- });
144288
+ cancelBody2(response);
144289
+ return { protocol: "unknown", raw: {} };
144464
144290
  };
144465
- var AuthDevice = class {
144466
- constructor(client) {
144467
- this.client = client;
144468
- }
144469
- start = (opts = {}) => request2(
144470
- this.client,
144471
- {
144472
- method: "POST",
144473
- path: "/v1/auth/device/start",
144474
- signal: opts.signal
144475
- },
144476
- deviceStartResponseSchema
144477
- );
144478
- poll = async (deviceCode, opts = {}) => {
144479
- const parsed = await request2(
144480
- this.client,
144481
- {
144482
- method: "POST",
144483
- path: "/v1/auth/device/poll",
144484
- body: { deviceCode },
144485
- signal: opts.signal
144486
- },
144487
- devicePollResponseSchema
144488
- );
144489
- if ("error" in parsed) {
144490
- return parsed.error === "authorization_pending" ? {
144491
- status: "pending",
144492
- userCode: parsed.userCode,
144493
- verificationUri: parsed.verificationUri
144494
- } : { status: "expired" };
144291
+ var readClippedBytes = async (response, max) => {
144292
+ if (response.body === null) return new Uint8Array(0);
144293
+ const reader = response.body.getReader();
144294
+ const chunks = [];
144295
+ let total = 0;
144296
+ try {
144297
+ while (total < max) {
144298
+ const { done, value } = await reader.read();
144299
+ if (done) break;
144300
+ if (!value) continue;
144301
+ chunks.push(value);
144302
+ total += value.byteLength;
144303
+ }
144304
+ } finally {
144305
+ try {
144306
+ await reader.cancel();
144307
+ } catch {
144495
144308
  }
144309
+ }
144310
+ const merged = new Uint8Array(Math.min(total, max));
144311
+ let offset = 0;
144312
+ for (const c of chunks) {
144313
+ const take = Math.min(c.byteLength, max - offset);
144314
+ merged.set(c.subarray(0, take), offset);
144315
+ offset += take;
144316
+ if (offset >= max) break;
144317
+ }
144318
+ return merged;
144319
+ };
144320
+ var readClippedText = async (response, max) => {
144321
+ const bytes = await readClippedBytes(response, max);
144322
+ return new TextDecoder().decode(bytes);
144323
+ };
144324
+ var parseResponseBody = async (response, max) => {
144325
+ const contentType = response.headers.get("content-type");
144326
+ const bytes = await readClippedBytes(response, max + 1);
144327
+ const truncated = bytes.byteLength > max;
144328
+ const view = truncated ? bytes.subarray(0, max) : bytes;
144329
+ const buf = Buffer.from(view.buffer, view.byteOffset, view.byteLength);
144330
+ if (!isTextContentType(contentType)) {
144331
+ const base644 = buf.toString("base64");
144496
144332
  return {
144497
- status: "ok",
144498
- accessToken: parsed.accessToken,
144499
- refreshToken: parsed.refreshToken,
144500
- expiresIn: parsed.expiresIn,
144501
- user: parsed.user
144333
+ body: base644,
144334
+ bodyRaw: base644,
144335
+ bodyEncoding: "base64",
144336
+ truncated
144502
144337
  };
144503
- };
144338
+ }
144339
+ const text = buf.toString("utf8");
144340
+ if (contentType?.toLowerCase().includes("json")) {
144341
+ try {
144342
+ return { body: JSON.parse(text), bodyRaw: text, truncated };
144343
+ } catch {
144344
+ }
144345
+ }
144346
+ return { body: text, bodyRaw: text, truncated };
144504
144347
  };
144505
- var userWalletsSchema = external_exports3.array(userWalletDtoSchema);
144506
- var normalizeMessage = (message2) => {
144507
- if (typeof message2 === "string") return message2;
144508
- if (typeof message2.raw === "string") return message2.raw;
144509
- return bytesToHex(message2.raw);
144348
+ var UPSTREAM_ERROR_FIELDS = [
144349
+ "error",
144350
+ "message",
144351
+ "detail",
144352
+ "reason",
144353
+ "error_description"
144354
+ ];
144355
+ var UPSTREAM_ERROR_MAX_LEN = 200;
144356
+ var ERROR_SNIPPET_BYTES = 500;
144357
+ var clampUpstreamMessage = (value) => {
144358
+ const trimmed = value.trim();
144359
+ if (!trimmed) return void 0;
144360
+ return trimmed.length > UPSTREAM_ERROR_MAX_LEN ? `${trimmed.slice(0, UPSTREAM_ERROR_MAX_LEN)}\u2026` : trimmed;
144510
144361
  };
144511
- var repackageSignFailure = (err, op) => {
144512
- if (err instanceof ZeroApiError && err.status === 402) {
144513
- const body = err.body;
144514
- const detail = body && typeof body.error === "string" ? `: ${body.error}` : "";
144515
- throw new ZeroPaymentError(
144516
- `Sign endpoint refused ${op} (HTTP 402)${detail}`,
144517
- { cause: err }
144518
- );
144362
+ var tryParseJson = (text) => {
144363
+ try {
144364
+ return JSON.parse(text);
144365
+ } catch {
144366
+ return null;
144519
144367
  }
144520
- throw err;
144521
144368
  };
144522
- var Auth = class {
144523
- constructor(client) {
144524
- this.client = client;
144525
- this.device = new AuthDevice(client);
144369
+ var extractUpstreamErrorMessage = (body) => {
144370
+ const parsed = tryParseJson(body);
144371
+ if (parsed === null || typeof parsed !== "object") {
144372
+ return clampUpstreamMessage(body);
144526
144373
  }
144527
- device;
144528
- // The internal (web-app facing) user profile. Richer than profile(): full
144529
- // wallet list with per-wallet delegation/signer/limit metadata.
144530
- me = (opts = {}) => request2(
144531
- this.client,
144532
- { method: "GET", path: "/v1/users/me", signal: opts.signal },
144533
- internalUserDtoSchema
144534
- );
144535
- // The public (agent-facing) profile — identity + primary wallet address +
144536
- // balance + welcome-bonus, no per-wallet metadata. Same shape the MCP
144537
- // get_profile tool returns; rendered by `zero auth whoami`.
144538
- profile = (opts = {}) => request2(
144539
- this.client,
144540
- { method: "GET", path: "/v1/users/me/profile", signal: opts.signal },
144541
- publicUserDtoSchema
144374
+ const record3 = parsed;
144375
+ for (const field of UPSTREAM_ERROR_FIELDS) {
144376
+ const value = record3[field];
144377
+ if (typeof value === "string" && value.length > 0) {
144378
+ return clampUpstreamMessage(value);
144379
+ }
144380
+ if (value && typeof value === "object") {
144381
+ const nested = value.message;
144382
+ if (typeof nested === "string" && nested.length > 0) {
144383
+ return clampUpstreamMessage(nested);
144384
+ }
144385
+ }
144386
+ }
144387
+ return void 0;
144388
+ };
144389
+ var buildUpstreamError = (status, bodyRaw, bodyEncoding) => {
144390
+ if (status >= 200 && status < 300) return void 0;
144391
+ if (bodyRaw === null || bodyRaw.length === 0) return void 0;
144392
+ if (bodyEncoding === "base64") return void 0;
144393
+ const snippet = bodyRaw.slice(0, ERROR_SNIPPET_BYTES);
144394
+ return {
144395
+ status,
144396
+ message: extractUpstreamErrorMessage(bodyRaw),
144397
+ snippetHash: createHash("sha256").update(snippet).digest("hex"),
144398
+ snippetLength: snippet.length
144399
+ };
144400
+ };
144401
+ var computeOutcome = (status, payment, warnings) => {
144402
+ if (warnings.includes(FETCH_WARNINGS.mppSessionCloseFailed))
144403
+ return "payment_close_failed";
144404
+ if (status === 402 && payment !== null) return "payment_rejected";
144405
+ if (status === 402) return "payment_failed";
144406
+ if (status >= 400) return "server_error";
144407
+ return "success";
144408
+ };
144409
+ var errorFetchResult = (startedAt, error48, skipReason, capabilityIdRequested, outcome, status = null) => {
144410
+ const result = {
144411
+ runId: null,
144412
+ ok: false,
144413
+ status,
144414
+ latencyMs: Date.now() - startedAt,
144415
+ payment: null,
144416
+ outcome,
144417
+ body: null,
144418
+ bodyRaw: null,
144419
+ error: error48
144420
+ };
144421
+ if (capabilityIdRequested) result.runTrackingSkipped = [skipReason];
144422
+ return result;
144423
+ };
144424
+ var resolveRecordingClient = (client, opts) => {
144425
+ const credentials = client.getCredentials();
144426
+ const recordingClient = opts.account ? client.withAccount(opts.account) : client;
144427
+ const canRecord = opts.account !== void 0 || credentials.kind === "account" || credentials.kind === "session";
144428
+ return { recordingClient, canRecord };
144429
+ };
144430
+ var recordErrorFetchRun = async (client, opts, result, capabilityIdOverride) => {
144431
+ const effectiveCapId = opts.capabilityId;
144432
+ if (effectiveCapId === void 0) return;
144433
+ const { recordingClient, canRecord } = resolveRecordingClient(client, opts);
144434
+ if (!canRecord) return;
144435
+ const fetchOriginForRecord = opts.capabilityId === void 0 ? "direct_url" : isShortToken(effectiveCapId) ? "from_search" : opts.fetchOrigin;
144436
+ try {
144437
+ const created = await recordingClient.runs.create({
144438
+ capabilityId: effectiveCapId,
144439
+ ...opts.searchId && { searchId: opts.searchId },
144440
+ ...fetchOriginForRecord && { fetchOrigin: fetchOriginForRecord },
144441
+ ...result.status !== null && { status: result.status },
144442
+ latencyMs: result.latencyMs,
144443
+ ...opts.requestSchema && { requestSchema: opts.requestSchema }
144444
+ });
144445
+ result.runId = created.runId;
144446
+ delete result.runTrackingSkipped;
144447
+ } catch {
144448
+ }
144449
+ };
144450
+ var recordTimeoutRun = async (client, opts, err, startedAt, capabilityIdRequested, capabilityIdOverride) => {
144451
+ const result = errorFetchResult(
144452
+ startedAt,
144453
+ err.message,
144454
+ "timeout",
144455
+ capabilityIdRequested,
144456
+ "network_error"
144542
144457
  );
144543
- // The user's wallets only. Lightweight sibling of `me()` for callers that
144544
- // just need wallet addresses (e.g. managed-account resolution on the pay
144545
- // path) — avoids the heavier internal-profile computation behind `me()`.
144546
- wallets = (opts = {}) => request2(
144547
- this.client,
144548
- { method: "GET", path: "/v1/users/me/wallets", signal: opts.signal },
144549
- userWalletsSchema
144458
+ await recordErrorFetchRun(client, opts, result);
144459
+ };
144460
+ var sniffJsonShapeBytes = (buf) => {
144461
+ let i = 0;
144462
+ if (buf.length >= 3 && buf[0] === 239 && buf[1] === 187 && buf[2] === 191) {
144463
+ i = 3;
144464
+ }
144465
+ while (i < buf.length && (buf[i] === 32 || buf[i] === 9 || buf[i] === 10 || buf[i] === 13)) {
144466
+ i++;
144467
+ }
144468
+ if (i >= buf.length) return false;
144469
+ return buf[i] === 123 || buf[i] === 91;
144470
+ };
144471
+ var withDefaultContentType = (headers, body) => {
144472
+ if (!headers && body === void 0) return void 0;
144473
+ const next = headers ? { ...headers } : {};
144474
+ if (body === void 0) return next;
144475
+ const hasContentType = Object.keys(next).some(
144476
+ (k3) => k3.toLowerCase() === "content-type"
144550
144477
  );
144551
- // Exchange an ephemeral session code (e.g. runner / sandbox handoff) for
144552
- // a session access token. The returned token has no refresh path — pair
144553
- // with `refreshToken: ""` when constructing the client.
144554
- exchangeSessionCode = (code, opts = {}) => request2(
144555
- this.client,
144556
- {
144557
- method: "POST",
144558
- path: "/v1/session/exchange",
144559
- body: { code },
144560
- signal: opts.signal
144561
- },
144562
- sessionExchangeResponseSchema
144478
+ if (!hasContentType) {
144479
+ next["content-type"] = typeof body === "string" || sniffJsonShapeBytes(body) ? "application/json" : "application/octet-stream";
144480
+ }
144481
+ return next;
144482
+ };
144483
+ var clientFetch = async (client, url4, opts = {}) => {
144484
+ const startedAt = Date.now();
144485
+ const capabilityIdRequested = opts.capabilityId !== void 0;
144486
+ const normalized = normalizeTransportEnvelopeRequest(url4, {
144487
+ method: opts.method,
144488
+ body: opts.body
144489
+ });
144490
+ const requestUrl = normalized.url;
144491
+ const requestBody = normalized.body;
144492
+ const method = normalized.method ?? (requestBody !== void 0 ? "POST" : "GET");
144493
+ const { canRecord: canRecordForResolve } = resolveRecordingClient(
144494
+ client,
144495
+ opts
144563
144496
  );
144564
- // Server-side revoke of the refresh token. Throws ZeroApiError on any
144565
- // non-2xx — callers that have already cleared local state (e.g. CLI
144566
- // logout) typically want to swallow with `.catch(() => {})` so a flaky
144567
- // server doesn't surface as a user-visible failure for a sign-out that
144568
- // already succeeded locally. Response body is opaque; schema is
144569
- // `z.unknown()` and the resolved value is discarded.
144570
- logout = async (refreshToken, opts = {}) => {
144571
- await request2(
144572
- this.client,
144573
- {
144574
- method: "POST",
144575
- path: "/v1/auth/logout",
144576
- body: { refreshToken },
144577
- signal: opts.signal
144578
- },
144579
- external_exports3.unknown()
144580
- );
144497
+ const resolvePromise = !capabilityIdRequested && canRecordForResolve ? client.capabilities.resolveByUrl(redactUrl2(requestUrl), method).catch(() => null) : Promise.resolve(null);
144498
+ const probeHeaders = withDefaultContentType(opts.headers, requestBody);
144499
+ const requestInit = {
144500
+ method,
144501
+ headers: probeHeaders,
144502
+ body: requestBody,
144503
+ signal: opts.signal
144581
144504
  };
144582
- // Force a session refresh. Routes through refreshSessionOnce() so a
144583
- // proactive call doesn't race a 401-driven transport refresh —
144584
- // concurrent POSTs with the same refresh token kill the session
144585
- // family (RFC 6819 token-theft response).
144586
- refresh = async () => {
144587
- const credentials = this.client.getCredentials();
144588
- if (credentials.kind !== "session") {
144589
- throw new ZeroAuthError(
144590
- "auth_no_credentials",
144591
- "refresh() requires the client to be constructed with a session \u2014 got credentials.kind=" + credentials.kind
144592
- );
144593
- }
144594
- const ok = await this.client.refreshSessionOnce();
144595
- if (!ok) {
144596
- throw new ZeroAuthError(
144597
- "auth_session_expired",
144598
- "Session refresh failed. The refresh token may be revoked or expired."
144505
+ const configuredFetch = buildConfiguredFetch(client, {
144506
+ timeoutMs: opts.timeoutMs
144507
+ });
144508
+ let response;
144509
+ let payment = null;
144510
+ const skipReasons = [];
144511
+ const warnings = [];
144512
+ try {
144513
+ response = await configuredFetch(requestUrl, requestInit);
144514
+ } catch (err) {
144515
+ if (err instanceof ZeroTimeoutError) {
144516
+ await recordTimeoutRun(
144517
+ client,
144518
+ opts,
144519
+ err,
144520
+ startedAt,
144521
+ capabilityIdRequested
144599
144522
  );
144523
+ throw err;
144600
144524
  }
144601
- };
144602
- signMessage = async (input, opts = {}) => {
144603
- const credentials = this.client.getCredentials();
144604
- if (credentials.kind === "account") {
144605
- return await credentials.account.signMessage({ message: input.message });
144606
- }
144607
- if (credentials.kind === "session") {
144608
- try {
144609
- const parsed = await request2(
144610
- this.client,
144611
- {
144612
- method: "POST",
144613
- path: "/v1/users/me/sign-message",
144614
- body: { message: normalizeMessage(input.message) }
144615
- },
144616
- signResponseSchema
144617
- );
144618
- this.assertSignerMatches(parsed.walletAddress, opts.expectedAddress);
144619
- return parsed.signature;
144620
- } catch (err) {
144621
- repackageSignFailure(err, "signMessage");
144622
- }
144623
- }
144624
- throw new ZeroAuthError(
144625
- "auth_no_credentials",
144626
- "signMessage() requires the client to be constructed with `account` or `session`."
144525
+ if (err instanceof ZeroError) throw err;
144526
+ const errMessage = err instanceof Error ? err.message : String(err);
144527
+ const result2 = errorFetchResult(
144528
+ startedAt,
144529
+ errMessage,
144530
+ "fetch_failed_before_response",
144531
+ capabilityIdRequested,
144532
+ "network_error"
144627
144533
  );
144628
- };
144629
- // Managed-only: BYO accounts sign locally via viem's account.signTransaction.
144630
- signTransaction = async (input, opts = {}) => {
144631
- const credentials = this.client.getCredentials();
144632
- if (credentials.kind === "session") {
144534
+ await recordErrorFetchRun(client, opts, result2);
144535
+ return result2;
144536
+ }
144537
+ if (response.status === 402) {
144538
+ const requirement = await detectPaymentRequirement(response);
144539
+ if (requirement?.protocol === "x402" || requirement?.protocol === "mpp") {
144540
+ const payCall = requirement.protocol === "x402" ? client.payments.pay : client.payments.payMpp;
144633
144541
  try {
144634
- const parsed = await request2(
144635
- this.client,
144636
- {
144637
- method: "POST",
144638
- path: "/v1/users/me/sign-transaction",
144639
- body: { unsignedTransaction: input.unsignedTransaction }
144640
- },
144641
- signResponseSchema
144642
- );
144643
- this.assertSignerMatches(parsed.walletAddress, opts.expectedAddress);
144644
- return parsed.signature;
144542
+ const paid = await payCall({
144543
+ url: requestUrl,
144544
+ method,
144545
+ // Fresh clone for the paid retry — same rationale as
144546
+ // the probe-side clone above. Re-derives the default
144547
+ // Content-Type so the paid leg matches the probe.
144548
+ headers: withDefaultContentType(opts.headers, requestBody),
144549
+ body: requestBody,
144550
+ maxPay: opts.maxPay,
144551
+ account: opts.account,
144552
+ signal: opts.signal,
144553
+ timeoutMs: opts.timeoutMs,
144554
+ ...opts.displayCostAmount && {
144555
+ displayCostAmount: opts.displayCostAmount
144556
+ }
144557
+ });
144558
+ response = paid.response;
144559
+ payment = paid.payment;
144560
+ if (paid.warnings) warnings.push(...paid.warnings);
144645
144561
  } catch (err) {
144646
- repackageSignFailure(err, "signTransaction");
144562
+ if (err instanceof ZeroSessionCloseFailedError) {
144563
+ response = err.response;
144564
+ payment = {
144565
+ protocol: "mpp",
144566
+ chain: tempoChainLabelFromId(err.session.chainId),
144567
+ txHash: null,
144568
+ amount: err.capturedAmount,
144569
+ asset: "USDC",
144570
+ session: err.session
144571
+ };
144572
+ warnings.push(FETCH_WARNINGS.mppSessionCloseFailed);
144573
+ } else if (err instanceof ZeroInsufficientFundsError) {
144574
+ return errorFetchResult(
144575
+ startedAt,
144576
+ err.message,
144577
+ FETCH_SKIP_REASONS.insufficientFunds,
144578
+ capabilityIdRequested,
144579
+ "insufficient_funds",
144580
+ null
144581
+ );
144582
+ } else if (err instanceof ZeroPaymentError || err instanceof ZeroAuthError) {
144583
+ const result2 = errorFetchResult(
144584
+ startedAt,
144585
+ err.message,
144586
+ "payment_failed",
144587
+ capabilityIdRequested,
144588
+ "payment_failed",
144589
+ 402
144590
+ );
144591
+ await recordErrorFetchRun(client, opts, result2);
144592
+ return result2;
144593
+ } else if (err instanceof ZeroTimeoutError) {
144594
+ await recordTimeoutRun(
144595
+ client,
144596
+ opts,
144597
+ err,
144598
+ startedAt,
144599
+ capabilityIdRequested
144600
+ );
144601
+ throw err;
144602
+ } else {
144603
+ throw err;
144604
+ }
144647
144605
  }
144648
- }
144649
- if (credentials.kind === "account") {
144650
- throw new ZeroAuthError(
144651
- "auth_error",
144652
- "signTransaction() is a managed-signing operation and is not supported in account mode. Sign locally with viem's account.signTransaction instead."
144606
+ } else if (requirement?.protocol === "unknown") {
144607
+ const result2 = errorFetchResult(
144608
+ startedAt,
144609
+ "Capability returned 402 with an unrecognized payment protocol. Neither x402 nor MPP markers were detected in the response headers or body.",
144610
+ "unrecognized_402_protocol",
144611
+ capabilityIdRequested,
144612
+ "payment_failed",
144613
+ 402
144653
144614
  );
144615
+ await recordErrorFetchRun(client, opts, result2);
144616
+ return result2;
144654
144617
  }
144655
- throw new ZeroAuthError(
144656
- "auth_no_credentials",
144657
- "signTransaction() requires session credentials (managed signing)."
144658
- );
144659
- };
144660
- signTypedData = async (input, opts = {}) => {
144661
- const credentials = this.client.getCredentials();
144662
- if (credentials.kind === "account") {
144663
- if (!credentials.account.signTypedData) {
144664
- throw new ZeroAuthError(
144665
- "auth_error",
144666
- "The configured viem account does not implement signTypedData."
144667
- );
144668
- }
144669
- return await credentials.account.signTypedData(input);
144618
+ }
144619
+ const latencyMs = Date.now() - startedAt;
144620
+ const maxResponseBytes = opts.maxResponseBytes ?? MAX_RESPONSE_BYTES;
144621
+ let body;
144622
+ let bodyRaw;
144623
+ let bodyEncoding;
144624
+ try {
144625
+ const parsed = await parseResponseBody(response, maxResponseBytes);
144626
+ body = parsed.body;
144627
+ bodyRaw = parsed.bodyRaw;
144628
+ bodyEncoding = parsed.bodyEncoding;
144629
+ if (parsed.truncated) warnings.push(FETCH_WARNINGS.bodyTruncated);
144630
+ } catch (err) {
144631
+ if (err instanceof Error && err.name === "AbortError") {
144632
+ throw new ZeroApiError("Request aborted by caller", {
144633
+ status: 0,
144634
+ cause: err
144635
+ });
144670
144636
  }
144671
- if (credentials.kind === "session") {
144637
+ const message2 = err instanceof Error ? err.message : String(err);
144638
+ const skips = [];
144639
+ const settledLocally = paymentHasAnchor(payment);
144640
+ if (settledLocally && response.status === 402) {
144641
+ warnings.push(FETCH_WARNINGS.paidButStill402NotRecorded);
144642
+ skips.push(FETCH_WARNINGS.paidButStill402NotRecorded);
144643
+ }
144644
+ if (payment !== null && !settledLocally) {
144645
+ warnings.push(FETCH_WARNINGS.paymentSettlementUnconfirmed);
144646
+ skips.push(FETCH_WARNINGS.paymentSettlementUnconfirmed);
144647
+ }
144648
+ if (payment !== null && payment.amount === "unknown") {
144649
+ warnings.push(FETCH_WARNINGS.paymentAmountUnknown);
144650
+ }
144651
+ skips.push(FETCH_SKIP_REASONS.bodyReadFailed);
144652
+ const result2 = {
144653
+ runId: null,
144654
+ ok: false,
144655
+ status: response.status,
144656
+ latencyMs: Date.now() - startedAt,
144657
+ payment,
144658
+ outcome: computeOutcome(response.status, payment, warnings),
144659
+ body: null,
144660
+ bodyRaw: null,
144661
+ error: message2
144662
+ };
144663
+ if (capabilityIdRequested) result2.runTrackingSkipped = skips;
144664
+ if (warnings.length > 0) result2.warnings = warnings;
144665
+ return result2;
144666
+ }
144667
+ const settled = paymentHasAnchor(payment);
144668
+ const paidButStill402 = settled && response.status === 402;
144669
+ const settlementUnconfirmed = payment !== null && !settled;
144670
+ if (paidButStill402) warnings.push(FETCH_WARNINGS.paidButStill402NotRecorded);
144671
+ if (settlementUnconfirmed)
144672
+ warnings.push(FETCH_WARNINGS.paymentSettlementUnconfirmed);
144673
+ if (payment !== null && payment.amount === "unknown") {
144674
+ warnings.push(FETCH_WARNINGS.paymentAmountUnknown);
144675
+ }
144676
+ let runId = null;
144677
+ const closeFailed = warnings.includes(FETCH_WARNINGS.mppSessionCloseFailed);
144678
+ const upstreamErrorForRecord = buildUpstreamError(
144679
+ response.status,
144680
+ bodyRaw,
144681
+ bodyEncoding
144682
+ );
144683
+ if (capabilityIdRequested) {
144684
+ if (paidButStill402)
144685
+ skipReasons.push(FETCH_WARNINGS.paidButStill402NotRecorded);
144686
+ else if (closeFailed)
144687
+ skipReasons.push(FETCH_SKIP_REASONS.mppSessionCloseFailedNotRecorded);
144688
+ }
144689
+ const resolvedCap = await resolvePromise;
144690
+ const effectiveCapabilityId = opts.capabilityId ?? resolvedCap?.capabilityId;
144691
+ const capabilityResolution = !capabilityIdRequested && canRecordForResolve ? resolvedCap !== null ? {
144692
+ resolved: true,
144693
+ capabilityId: resolvedCap.capabilityId,
144694
+ capabilitySlug: resolvedCap.capabilitySlug,
144695
+ matchedVia: resolvedCap.matchedVia
144696
+ } : { resolved: false } : void 0;
144697
+ if (effectiveCapabilityId !== void 0 && !paidButStill402 && !closeFailed) {
144698
+ const { recordingClient, canRecord } = resolveRecordingClient(client, opts);
144699
+ if (!canRecord) {
144700
+ skipReasons.push("no_credentials");
144701
+ } else {
144702
+ const knownAmount = payment !== null && payment.amount !== "unknown";
144672
144703
  try {
144673
- const parsed = await request2(
144674
- this.client,
144675
- {
144676
- method: "POST",
144677
- path: "/v1/users/me/sign-typed-data",
144678
- body: { typedData: input }
144704
+ let derived;
144705
+ if (opts.deriveRunMetadata) {
144706
+ try {
144707
+ derived = opts.deriveRunMetadata({
144708
+ status: response.status,
144709
+ ok: response.ok,
144710
+ bodyRaw,
144711
+ ...bodyEncoding && { bodyEncoding }
144712
+ });
144713
+ } catch {
144714
+ }
144715
+ }
144716
+ const result2 = await recordingClient.runs.create({
144717
+ capabilityId: effectiveCapabilityId,
144718
+ searchId: opts.searchId,
144719
+ // Auto-resolved runs are always direct_url — they only fire when
144720
+ // no capabilityId was supplied (the direct_url path by definition).
144721
+ ...opts.capabilityId === void 0 ? { fetchOrigin: "direct_url" } : opts.fetchOrigin && { fetchOrigin: opts.fetchOrigin },
144722
+ status: response.status,
144723
+ latencyMs,
144724
+ ...opts.requestSchema && { requestSchema: opts.requestSchema },
144725
+ ...derived?.responseSchema && {
144726
+ responseSchema: derived.responseSchema
144679
144727
  },
144680
- signResponseSchema
144681
- );
144682
- this.assertSignerMatches(parsed.walletAddress, opts.expectedAddress);
144683
- return parsed.signature;
144728
+ ...upstreamErrorForRecord && {
144729
+ errorSnippetHash: upstreamErrorForRecord.snippetHash,
144730
+ errorSnippetLength: upstreamErrorForRecord.snippetLength
144731
+ },
144732
+ ...payment && {
144733
+ ...knownAmount && {
144734
+ costAmount: payment.amount,
144735
+ costAsset: payment.asset
144736
+ },
144737
+ paymentProtocol: payment.protocol,
144738
+ paymentChain: payment.chain,
144739
+ paymentTxHash: payment.txHash ?? void 0,
144740
+ paymentMode: payment.session ? "session" : "charge"
144741
+ }
144742
+ });
144743
+ runId = result2.runId;
144684
144744
  } catch (err) {
144685
- repackageSignFailure(err, "signTypedData");
144745
+ const errorMessage22 = err instanceof Error ? err.message : String(err);
144746
+ const code = err instanceof ZeroError ? err.code : void 0;
144747
+ const skipMsg = code ? `run_create_failed:${code}: ${errorMessage22}` : `run_create_failed: ${errorMessage22}`;
144748
+ skipReasons.push(skipMsg);
144749
+ client.logger?.({
144750
+ level: "warn",
144751
+ message: "Failed to record run for fetch",
144752
+ meta: {
144753
+ url: redactUrl2(url4),
144754
+ capabilityId: effectiveCapabilityId,
144755
+ error: errorMessage22,
144756
+ errorCode: code
144757
+ }
144758
+ });
144686
144759
  }
144687
144760
  }
144688
- throw new ZeroAuthError(
144689
- "auth_no_credentials",
144690
- "signTypedData() requires the client to be constructed with `account` or `session`."
144691
- );
144692
- };
144693
- // Compare against the caller's pinned address (not shared cache) so
144694
- // concurrent pay()s can't race past each other. Mismatch → clear + throw;
144695
- // the caller's retry rebuilds against the new wallet.
144696
- assertSignerMatches = (serverAddr, expectedAddr) => {
144697
- if (!expectedAddr) return;
144698
- const canonicalServer = getAddress(serverAddr);
144699
- const canonicalExpected = getAddress(expectedAddr);
144700
- if (canonicalExpected === canonicalServer) return;
144701
- this.client.clearManagedAccount();
144702
- throw new ZeroAuthError(
144703
- "auth_error",
144704
- `Managed signing wallet changed (caller expected ${canonicalExpected}, server signed as ${canonicalServer}). Cache cleared \u2014 retry to rebuild against the current wallet.`
144705
- );
144761
+ }
144762
+ const result = {
144763
+ runId,
144764
+ ok: response.ok,
144765
+ status: response.status,
144766
+ latencyMs,
144767
+ payment,
144768
+ outcome: computeOutcome(response.status, payment, warnings),
144769
+ body,
144770
+ bodyRaw
144706
144771
  };
144772
+ if (bodyEncoding) result.bodyEncoding = bodyEncoding;
144773
+ if (!response.ok) {
144774
+ const candidate = bodyRaw !== null && bodyRaw.length > 0 && bodyEncoding !== "base64" ? bodyRaw.slice(0, 240) : null;
144775
+ result.error = candidate !== null && !requestBodyEchoed(candidate, opts.body) ? candidate : `HTTP ${response.status}`;
144776
+ if (upstreamErrorForRecord) result.upstreamError = upstreamErrorForRecord;
144777
+ }
144778
+ if (capabilityIdRequested && skipReasons.length > 0) {
144779
+ result.runTrackingSkipped = skipReasons;
144780
+ }
144781
+ if (warnings.length > 0) result.warnings = warnings;
144782
+ if (capabilityResolution !== void 0)
144783
+ result.capabilityResolution = capabilityResolution;
144784
+ return result;
144707
144785
  };
144708
- var BUG_REPORT_CATEGORIES = [
144709
- "search_relevance",
144710
- "ranking_issue",
144711
- "missing_capability",
144712
- "wrong_schema",
144713
- "misleading_description",
144714
- "broken_execution",
144715
- "payment_failure",
144716
- "billing_anomaly",
144717
- "cli_bug",
144718
- "security",
144719
- "other"
144720
- ];
144721
- var createBugReportResponseSchema = external_exports3.object({
144722
- bugReportId: external_exports3.string(),
144786
+ var package_default2 = {
144787
+ version: "0.7.0"
144788
+ };
144789
+ var SDK_VERSION2 = package_default2.version;
144790
+ var userWalletDtoSchema = external_exports3.object({
144791
+ walletAddress: external_exports3.string(),
144792
+ source: external_exports3.enum(["self_custody", "privy_embedded"]),
144793
+ isPrimary: external_exports3.boolean(),
144794
+ linkedAt: external_exports3.union([external_exports3.string(), external_exports3.date()]).nullable().optional(),
144795
+ delegationGranted: external_exports3.boolean(),
144796
+ signerQuorumId: external_exports3.string().nullable(),
144797
+ // Per-transaction USDC cap in base units (6 dp). null = unlimited, 0 = free-only.
144798
+ maxTransactionUsdcBaseUnits: external_exports3.number().int().nullable()
144799
+ });
144800
+ var welcomeBonusSummarySchema = external_exports3.object({
144723
144801
  status: external_exports3.string(),
144724
- deduped: external_exports3.boolean(),
144725
- category: external_exports3.enum(BUG_REPORT_CATEGORIES).nullable(),
144726
- title: external_exports3.string().nullable(),
144727
- attached: external_exports3.object({
144728
- capabilityId: external_exports3.number().nullable(),
144729
- runId: external_exports3.number().nullable(),
144730
- searchId: external_exports3.number().nullable()
144802
+ amountUsd: external_exports3.number()
144803
+ });
144804
+ var internalUserDtoSchema = external_exports3.object({
144805
+ id: external_exports3.string(),
144806
+ email: external_exports3.string().nullable(),
144807
+ createdAt: external_exports3.union([external_exports3.string(), external_exports3.date()]).optional(),
144808
+ lastLoginAt: external_exports3.union([external_exports3.string(), external_exports3.date()]).nullable().optional(),
144809
+ onboardingCompleted: external_exports3.boolean().optional(),
144810
+ delegationGranted: external_exports3.boolean().optional(),
144811
+ wallets: external_exports3.array(userWalletDtoSchema).optional(),
144812
+ balance: external_exports3.object({ amount: external_exports3.string(), asset: external_exports3.string() }).nullable().optional(),
144813
+ welcomeBonus: welcomeBonusSummarySchema.nullable().optional()
144814
+ });
144815
+ var publicUserDtoSchema = external_exports3.object({
144816
+ user: external_exports3.object({
144817
+ id: external_exports3.string(),
144818
+ email: external_exports3.string().nullable()
144819
+ }),
144820
+ walletAddress: external_exports3.string().nullable(),
144821
+ balance: external_exports3.object({ amount: external_exports3.string(), asset: external_exports3.string() }).nullable(),
144822
+ welcomeBonus: welcomeBonusSummarySchema.nullable()
144823
+ });
144824
+ var signResponseSchema = external_exports3.object({
144825
+ signature: external_exports3.string().regex(/^0x[0-9a-fA-F]+$/, "must be 0x-prefixed hex"),
144826
+ walletAddress: external_exports3.string().regex(/^0x[0-9a-fA-F]{40}$/, "must be 0x-prefixed 40-char hex address")
144827
+ });
144828
+ var deviceStartResponseSchema = external_exports3.object({
144829
+ deviceCode: external_exports3.string(),
144830
+ userCode: external_exports3.string(),
144831
+ verificationUri: external_exports3.string(),
144832
+ pollInterval: external_exports3.number(),
144833
+ expiresAt: external_exports3.number()
144834
+ });
144835
+ var devicePollResponseSchema = external_exports3.union([
144836
+ // userCode + verificationUri are optional: older API deployments don't
144837
+ // echo them on pending.
144838
+ external_exports3.object({
144839
+ error: external_exports3.literal("authorization_pending"),
144840
+ userCode: external_exports3.string().optional(),
144841
+ verificationUri: external_exports3.string().optional()
144842
+ }),
144843
+ external_exports3.object({ error: external_exports3.literal("expired_token") }),
144844
+ external_exports3.object({
144845
+ accessToken: external_exports3.string(),
144846
+ refreshToken: external_exports3.string(),
144847
+ expiresIn: external_exports3.number(),
144848
+ user: internalUserDtoSchema
144731
144849
  })
144850
+ ]);
144851
+ var sessionExchangeResponseSchema = external_exports3.object({
144852
+ token: external_exports3.string(),
144853
+ expiresAt: external_exports3.number()
144732
144854
  });
144733
- var BugReports = class {
144734
- constructor(client) {
144735
- this.client = client;
144736
- }
144737
- // POST /v1/bug-reports. Wallet-attributed server-side; the SDK client's
144738
- // active credential drives attribution (account-mode → EIP-191 from the
144739
- // wallet, session-mode → Bearer + server resolves the user's wallet).
144740
- create = (input, opts = {}) => request2(
144741
- this.client,
144742
- {
144855
+ var refreshResponseSchema = external_exports3.object({
144856
+ accessToken: external_exports3.string(),
144857
+ refreshToken: external_exports3.string()
144858
+ });
144859
+ var tryRefreshSession = async (client, callerSignal) => {
144860
+ const credentials = client.getCredentials();
144861
+ if (credentials.kind !== "session") return false;
144862
+ if (!credentials.refreshToken) return false;
144863
+ const url4 = `${client.baseUrl.replace(/\/$/, "")}/v1/auth/refresh`;
144864
+ const timeoutSignal = AbortSignal.timeout(client.timeout);
144865
+ const signal = callerSignal ? AbortSignal.any([callerSignal, timeoutSignal]) : timeoutSignal;
144866
+ let response;
144867
+ try {
144868
+ response = await client.fetchImpl(url4, {
144743
144869
  method: "POST",
144744
- path: "/v1/bug-reports",
144745
- body: input,
144746
- signal: opts.signal
144747
- },
144748
- createBugReportResponseSchema
144749
- );
144870
+ headers: { "content-type": "application/json" },
144871
+ body: JSON.stringify({ refreshToken: credentials.refreshToken }),
144872
+ signal
144873
+ });
144874
+ } catch {
144875
+ return false;
144876
+ }
144877
+ if (!response.ok) return false;
144878
+ let body;
144879
+ try {
144880
+ body = await response.json();
144881
+ } catch {
144882
+ return false;
144883
+ }
144884
+ const parsed = refreshResponseSchema.safeParse(body);
144885
+ if (!parsed.success) return false;
144886
+ await client.applyRefreshedTokens({
144887
+ accessToken: parsed.data.accessToken,
144888
+ refreshToken: parsed.data.refreshToken
144889
+ });
144890
+ return true;
144750
144891
  };
144751
- var ratingSchema = external_exports3.object({
144752
- score: external_exports3.string(),
144753
- successRate: external_exports3.string(),
144754
- reviews: external_exports3.number(),
144755
- stars: external_exports3.string().nullable().optional(),
144756
- state: external_exports3.enum(["unrated", "rated"]).optional()
144757
- });
144758
- var capabilityResponseSchema = external_exports3.object({
144759
- uid: external_exports3.string(),
144760
- slug: external_exports3.string(),
144761
- name: external_exports3.string(),
144762
- description: external_exports3.string(),
144763
- url: external_exports3.string(),
144764
- urlTemplate: external_exports3.string().nullable().optional(),
144765
- method: external_exports3.string(),
144766
- headers: external_exports3.record(external_exports3.string(), external_exports3.string()).nullable(),
144767
- bodySchema: external_exports3.record(external_exports3.string(), external_exports3.unknown()).nullable(),
144768
- responseSchema: external_exports3.record(external_exports3.string(), external_exports3.unknown()).nullable(),
144769
- example: external_exports3.object({ request: external_exports3.unknown(), response: external_exports3.unknown() }).nullable(),
144770
- tags: external_exports3.array(external_exports3.string()).nullable(),
144771
- exampleAgentPrompt: external_exports3.string().nullable().optional(),
144772
- displayCostAmount: external_exports3.string(),
144773
- displayCostAsset: external_exports3.string(),
144774
- reviewCount: external_exports3.number(),
144775
- rating: ratingSchema,
144776
- priceObserved: external_exports3.object({
144777
- minCents: external_exports3.string().nullable(),
144778
- medianCents: external_exports3.string().nullable(),
144779
- maxCents: external_exports3.string().nullable(),
144780
- // Deprecated alias for maxCents; kept for back-compat with older
144781
- // API versions that still emit it.
144782
- p95Cents: external_exports3.string().nullable(),
144783
- sampleCount: external_exports3.number(),
144784
- varies: external_exports3.boolean(),
144785
- failureChargeRate: external_exports3.number().nullable()
144786
- }).nullable().optional(),
144787
- paymentMethods: external_exports3.array(
144788
- external_exports3.object({
144789
- uid: external_exports3.string(),
144790
- protocol: external_exports3.string(),
144791
- methodType: external_exports3.string(),
144792
- chain: external_exports3.string().nullable(),
144793
- mode: external_exports3.string(),
144794
- costAmount: external_exports3.string(),
144795
- costPer: external_exports3.string(),
144796
- priority: external_exports3.number()
144797
- })
144798
- ).nullable(),
144799
- availabilityStatus: external_exports3.enum(["healthy", "unknown", "down"]).nullable().optional(),
144800
- /**
144801
- * @deprecated The API no longer emits `displayStatus`; it has been replaced
144802
- * by the consolidated 3-value {@link capabilityResponseSchema.shape.availabilityStatus}.
144803
- * Kept as an optional field for one release for back-compat (ZERO-89 soft
144804
- * migration) and will be removed in the next major version.
144805
- */
144806
- displayStatus: external_exports3.enum(["healthy", "stable", "degraded", "unhealthy", "unknown"]).optional(),
144807
- activationCount: external_exports3.number().optional(),
144808
- lastUsedAt: external_exports3.string().nullable().optional(),
144809
- lastSuccessfullyRanAt: external_exports3.string().nullable().optional()
144892
+ var buildSessionAuthHeaders = (accessToken) => ({
144893
+ authorization: `Bearer ${accessToken}`
144810
144894
  });
144811
- var resolveCapabilityResponseSchema = external_exports3.object({
144812
- capabilityId: external_exports3.string(),
144813
- capabilitySlug: external_exports3.string(),
144814
- matchedVia: external_exports3.enum(["exact", "template"])
144895
+ var buildCanonicalMessage = (method, pathWithQuery, body, timestamp, nonce2) => {
144896
+ const bodyHash = createHash("sha256").update(body ?? "").digest("hex");
144897
+ return `${method}:${pathWithQuery}:${bodyHash}:${timestamp}:${nonce2}`;
144898
+ };
144899
+ var buildAccountAuthHeaders = async (account, method, pathWithQuery, body) => {
144900
+ const timestamp = Math.floor(Date.now() / 1e3).toString();
144901
+ const nonce2 = crypto.randomUUID();
144902
+ const message2 = buildCanonicalMessage(
144903
+ method,
144904
+ pathWithQuery,
144905
+ body,
144906
+ timestamp,
144907
+ nonce2
144908
+ );
144909
+ const signature2 = await account.signMessage({ message: message2 });
144910
+ return {
144911
+ "x-zero-address": account.address,
144912
+ "x-zero-timestamp": timestamp,
144913
+ "x-zero-nonce": nonce2,
144914
+ "x-zero-signature": signature2
144915
+ };
144916
+ };
144917
+ var RETRYABLE_STATUS = /* @__PURE__ */ new Set([408, 502, 503, 504]);
144918
+ var sleep2 = (ms, signal) => new Promise((resolve6, reject) => {
144919
+ if (signal?.aborted) {
144920
+ reject(new DOMException("Aborted", "AbortError"));
144921
+ return;
144922
+ }
144923
+ const timer = setTimeout(() => {
144924
+ signal?.removeEventListener("abort", onAbort);
144925
+ resolve6();
144926
+ }, ms);
144927
+ const onAbort = () => {
144928
+ clearTimeout(timer);
144929
+ reject(new DOMException("Aborted", "AbortError"));
144930
+ };
144931
+ signal?.addEventListener("abort", onAbort, { once: true });
144815
144932
  });
144816
- var Capabilities = class {
144933
+ var buildPathWithQuery = (path3, query) => {
144934
+ const normalizedPath = path3.startsWith("/") ? path3 : `/${path3}`;
144935
+ if (!query) return normalizedPath;
144936
+ const params = new URLSearchParams();
144937
+ for (const [key, value] of Object.entries(query)) {
144938
+ if (value !== void 0) params.set(key, String(value));
144939
+ }
144940
+ const qs = params.toString();
144941
+ return qs ? `${normalizedPath}?${qs}` : normalizedPath;
144942
+ };
144943
+ var joinUrl = (baseUrl, pathWithQuery) => {
144944
+ const base3 = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
144945
+ return `${base3}${pathWithQuery}`;
144946
+ };
144947
+ var buildHeaders = async (client, method, pathWithQuery, hasBody, bodyStr) => {
144948
+ const headers = { ...client.defaultHeaders };
144949
+ if (hasBody) headers["content-type"] = "application/json";
144950
+ headers["x-zero-sdk-version"] = SDK_VERSION2;
144951
+ if (client.apiVersion) headers["x-zero-api-version"] = client.apiVersion;
144952
+ const credentials = client.getCredentials();
144953
+ if (credentials.kind === "session") {
144954
+ Object.assign(headers, buildSessionAuthHeaders(credentials.accessToken));
144955
+ } else if (credentials.kind === "account") {
144956
+ const signed = await buildAccountAuthHeaders(
144957
+ credentials.account,
144958
+ method,
144959
+ pathWithQuery,
144960
+ bodyStr
144961
+ );
144962
+ Object.assign(headers, signed);
144963
+ }
144964
+ return headers;
144965
+ };
144966
+ var parseErrorBody = async (response) => {
144967
+ try {
144968
+ return await response.clone().json();
144969
+ } catch {
144970
+ try {
144971
+ return await response.text();
144972
+ } catch {
144973
+ return void 0;
144974
+ }
144975
+ }
144976
+ };
144977
+ var errorMessageFromBody = (body, status) => {
144978
+ if (body !== null && typeof body === "object" && "error" in body && typeof body.error === "string") {
144979
+ return body.error;
144980
+ }
144981
+ return `HTTP ${status}`;
144982
+ };
144983
+ var request2 = async (client, opts, schema) => {
144984
+ const pathWithQuery = buildPathWithQuery(opts.path, opts.query);
144985
+ const url4 = joinUrl(client.baseUrl, pathWithQuery);
144986
+ const hasBody = opts.body !== void 0;
144987
+ const bodyStr = hasBody ? JSON.stringify(
144988
+ opts.body,
144989
+ (_k, v) => typeof v === "bigint" ? v.toString() : v
144990
+ ) : void 0;
144991
+ const configuredFetch = buildConfiguredFetch(client);
144992
+ const performOnce = async () => configuredFetch(url4, {
144993
+ method: opts.method,
144994
+ headers: await buildHeaders(
144995
+ client,
144996
+ opts.method,
144997
+ pathWithQuery,
144998
+ hasBody,
144999
+ bodyStr
145000
+ ),
145001
+ body: bodyStr,
145002
+ signal: opts.signal
145003
+ });
145004
+ let refreshAttempted = false;
145005
+ const executeWithRefresh = async () => {
145006
+ const response = await performOnce();
145007
+ if (response.status !== 401 || refreshAttempted) return response;
145008
+ const credentials = client.getCredentials();
145009
+ if (credentials.kind !== "session") return response;
145010
+ refreshAttempted = true;
145011
+ const refreshed = await client.refreshSessionOnce(opts.signal);
145012
+ if (!refreshed) return response;
145013
+ return await performOnce();
145014
+ };
145015
+ let lastNetworkError;
145016
+ for (let attempt = 0; attempt <= client.maxRetries; attempt++) {
145017
+ let response;
145018
+ try {
145019
+ response = await executeWithRefresh();
145020
+ } catch (e2) {
145021
+ if (e2 instanceof ZeroError) throw e2;
145022
+ lastNetworkError = e2;
145023
+ if (attempt < client.maxRetries) {
145024
+ try {
145025
+ await sleep2(2 ** attempt * 200, opts.signal);
145026
+ } catch (sleepErr) {
145027
+ throw new ZeroApiError("Request aborted by caller", {
145028
+ status: 0,
145029
+ cause: sleepErr
145030
+ });
145031
+ }
145032
+ continue;
145033
+ }
145034
+ throw new ZeroApiError("Network request failed", {
145035
+ status: 0,
145036
+ cause: e2
145037
+ });
145038
+ }
145039
+ if (response.ok) {
145040
+ let text;
145041
+ try {
145042
+ text = await response.text();
145043
+ } catch (e2) {
145044
+ throw new ZeroApiError("Response body read failed", {
145045
+ status: response.status,
145046
+ cause: e2
145047
+ });
145048
+ }
145049
+ let json3;
145050
+ try {
145051
+ json3 = JSON.parse(text);
145052
+ } catch {
145053
+ }
145054
+ const parsed = schema.safeParse(json3);
145055
+ if (!parsed.success) {
145056
+ throw new ZeroValidationError(
145057
+ `Response did not match expected schema for ${opts.method} ${opts.path}`,
145058
+ { cause: parsed.error, body: text }
145059
+ );
145060
+ }
145061
+ return parsed.data;
145062
+ }
145063
+ if (RETRYABLE_STATUS.has(response.status) && attempt < client.maxRetries) {
145064
+ try {
145065
+ await sleep2(2 ** attempt * 200, opts.signal);
145066
+ } catch (sleepErr) {
145067
+ throw new ZeroApiError("Request aborted by caller", {
145068
+ status: 0,
145069
+ cause: sleepErr
145070
+ });
145071
+ }
145072
+ continue;
145073
+ }
145074
+ const errorBody = await parseErrorBody(response);
145075
+ const requestId = response.headers.get("x-request-id") ?? void 0;
145076
+ throw new ZeroApiError(errorMessageFromBody(errorBody, response.status), {
145077
+ status: response.status,
145078
+ requestId,
145079
+ body: errorBody
145080
+ });
145081
+ }
145082
+ throw new ZeroApiError("Exhausted retries", {
145083
+ status: 0,
145084
+ cause: lastNetworkError
145085
+ });
145086
+ };
145087
+ var AuthDevice = class {
144817
145088
  constructor(client) {
144818
145089
  this.client = client;
144819
145090
  }
144820
- get = (id, opts = {}) => request2(
145091
+ start = (opts = {}) => request2(
144821
145092
  this.client,
144822
145093
  {
144823
- method: "GET",
144824
- path: `/v1/capabilities/${encodeURIComponent(id)}`,
144825
- query: opts.searchId ? { searchId: opts.searchId } : void 0,
145094
+ method: "POST",
145095
+ path: "/v1/auth/device/start",
144826
145096
  signal: opts.signal
144827
145097
  },
144828
- capabilityResponseSchema
145098
+ deviceStartResponseSchema
144829
145099
  );
144830
- // Resolve a raw endpoint URL + method back to the capability that owns it,
144831
- // for the `zero fetch <url>` case where the client has no local search
144832
- // context to match against (a memoized/cross-session URL). Returns null on
144833
- // 404 — i.e. no match or an ambiguous multi-match, both of which the server
144834
- // reports as 404 (resolveByUrl fails closed rather than mis-attribute).
144835
- // The URL travels in the POST body so it stays out of access logs and is
144836
- // never stored; the caller uses the returned capabilityId to record an
144837
- // attributed run without the URL ever touching the run row (ZERO-335).
144838
- resolveByUrl = async (url4, method, opts = {}) => {
144839
- try {
144840
- return await request2(
144841
- this.client,
144842
- {
144843
- method: "POST",
144844
- path: "/v1/capabilities/resolve",
144845
- body: { url: url4, method },
144846
- signal: opts.signal
144847
- },
144848
- resolveCapabilityResponseSchema
144849
- );
144850
- } catch (err) {
144851
- if (err instanceof ZeroApiError && err.status === 404) return null;
144852
- throw err;
145100
+ poll = async (deviceCode, opts = {}) => {
145101
+ const parsed = await request2(
145102
+ this.client,
145103
+ {
145104
+ method: "POST",
145105
+ path: "/v1/auth/device/poll",
145106
+ body: { deviceCode },
145107
+ signal: opts.signal
145108
+ },
145109
+ devicePollResponseSchema
145110
+ );
145111
+ if ("error" in parsed) {
145112
+ return parsed.error === "authorization_pending" ? {
145113
+ status: "pending",
145114
+ userCode: parsed.userCode,
145115
+ verificationUri: parsed.verificationUri
145116
+ } : { status: "expired" };
144853
145117
  }
145118
+ return {
145119
+ status: "ok",
145120
+ accessToken: parsed.accessToken,
145121
+ refreshToken: parsed.refreshToken,
145122
+ expiresIn: parsed.expiresIn,
145123
+ user: parsed.user
145124
+ };
144854
145125
  };
144855
145126
  };
144856
- var createRunResponseSchema = external_exports3.object({
144857
- runId: external_exports3.string()
144858
- });
144859
- var runListItemSchema = external_exports3.object({
144860
- uid: external_exports3.string(),
144861
- capabilityUid: external_exports3.string(),
144862
- capabilitySlug: external_exports3.string(),
144863
- capabilityName: external_exports3.string(),
144864
- status: external_exports3.number().nullable(),
144865
- latencyMs: external_exports3.number().nullable(),
144866
- cost: external_exports3.object({ amount: external_exports3.string(), asset: external_exports3.string().nullable() }).nullable(),
144867
- payment: external_exports3.object({
144868
- protocol: external_exports3.string(),
144869
- chain: external_exports3.string().nullable(),
144870
- txHash: external_exports3.string().nullable(),
144871
- mode: external_exports3.string().nullable()
144872
- }).nullable(),
144873
- createdAt: external_exports3.coerce.date(),
144874
- reviewed: external_exports3.boolean()
144875
- });
145127
+ var userWalletsSchema = external_exports3.array(userWalletDtoSchema);
145128
+ var normalizeMessage = (message2) => {
145129
+ if (typeof message2 === "string") return message2;
145130
+ if (typeof message2.raw === "string") return message2.raw;
145131
+ return bytesToHex(message2.raw);
145132
+ };
145133
+ var repackageSignFailure = (err, op) => {
145134
+ if (err instanceof ZeroApiError && err.status === 402) {
145135
+ const body = err.body;
145136
+ const detail = body && typeof body.error === "string" ? `: ${body.error}` : "";
145137
+ throw new ZeroPaymentError(
145138
+ `Sign endpoint refused ${op} (HTTP 402)${detail}`,
145139
+ { cause: err }
145140
+ );
145141
+ }
145142
+ throw err;
145143
+ };
145144
+ var Auth = class {
145145
+ constructor(client) {
145146
+ this.client = client;
145147
+ this.device = new AuthDevice(client);
145148
+ }
145149
+ device;
145150
+ // The internal (web-app facing) user profile. Richer than profile(): full
145151
+ // wallet list with per-wallet delegation/signer/limit metadata.
145152
+ me = (opts = {}) => request2(
145153
+ this.client,
145154
+ { method: "GET", path: "/v1/users/me", signal: opts.signal },
145155
+ internalUserDtoSchema
145156
+ );
145157
+ // The public (agent-facing) profile — identity + primary wallet address +
145158
+ // balance + welcome-bonus, no per-wallet metadata. Same shape the MCP
145159
+ // get_profile tool returns; rendered by `zero auth whoami`.
145160
+ profile = (opts = {}) => request2(
145161
+ this.client,
145162
+ { method: "GET", path: "/v1/users/me/profile", signal: opts.signal },
145163
+ publicUserDtoSchema
145164
+ );
145165
+ // The user's wallets only. Lightweight sibling of `me()` for callers that
145166
+ // just need wallet addresses (e.g. managed-account resolution on the pay
145167
+ // path) — avoids the heavier internal-profile computation behind `me()`.
145168
+ wallets = (opts = {}) => request2(
145169
+ this.client,
145170
+ { method: "GET", path: "/v1/users/me/wallets", signal: opts.signal },
145171
+ userWalletsSchema
145172
+ );
145173
+ // Exchange an ephemeral session code (e.g. runner / sandbox handoff) for
145174
+ // a session access token. The returned token has no refresh path — pair
145175
+ // with `refreshToken: ""` when constructing the client.
145176
+ exchangeSessionCode = (code, opts = {}) => request2(
145177
+ this.client,
145178
+ {
145179
+ method: "POST",
145180
+ path: "/v1/session/exchange",
145181
+ body: { code },
145182
+ signal: opts.signal
145183
+ },
145184
+ sessionExchangeResponseSchema
145185
+ );
145186
+ // Server-side revoke of the refresh token. Throws ZeroApiError on any
145187
+ // non-2xx — callers that have already cleared local state (e.g. CLI
145188
+ // logout) typically want to swallow with `.catch(() => {})` so a flaky
145189
+ // server doesn't surface as a user-visible failure for a sign-out that
145190
+ // already succeeded locally. Response body is opaque; schema is
145191
+ // `z.unknown()` and the resolved value is discarded.
145192
+ logout = async (refreshToken, opts = {}) => {
145193
+ await request2(
145194
+ this.client,
145195
+ {
145196
+ method: "POST",
145197
+ path: "/v1/auth/logout",
145198
+ body: { refreshToken },
145199
+ signal: opts.signal
145200
+ },
145201
+ external_exports3.unknown()
145202
+ );
145203
+ };
145204
+ // Force a session refresh. Routes through refreshSessionOnce() so a
145205
+ // proactive call doesn't race a 401-driven transport refresh —
145206
+ // concurrent POSTs with the same refresh token kill the session
145207
+ // family (RFC 6819 token-theft response).
145208
+ refresh = async () => {
145209
+ const credentials = this.client.getCredentials();
145210
+ if (credentials.kind !== "session") {
145211
+ throw new ZeroAuthError(
145212
+ "auth_no_credentials",
145213
+ "refresh() requires the client to be constructed with a session \u2014 got credentials.kind=" + credentials.kind
145214
+ );
145215
+ }
145216
+ const ok = await this.client.refreshSessionOnce();
145217
+ if (!ok) {
145218
+ throw new ZeroAuthError(
145219
+ "auth_session_expired",
145220
+ "Session refresh failed. The refresh token may be revoked or expired."
145221
+ );
145222
+ }
145223
+ };
145224
+ signMessage = async (input, opts = {}) => {
145225
+ const credentials = this.client.getCredentials();
145226
+ if (credentials.kind === "account") {
145227
+ return await credentials.account.signMessage({ message: input.message });
145228
+ }
145229
+ if (credentials.kind === "session") {
145230
+ try {
145231
+ const parsed = await request2(
145232
+ this.client,
145233
+ {
145234
+ method: "POST",
145235
+ path: "/v1/users/me/sign-message",
145236
+ body: { message: normalizeMessage(input.message) }
145237
+ },
145238
+ signResponseSchema
145239
+ );
145240
+ this.assertSignerMatches(parsed.walletAddress, opts.expectedAddress);
145241
+ return parsed.signature;
145242
+ } catch (err) {
145243
+ repackageSignFailure(err, "signMessage");
145244
+ }
145245
+ }
145246
+ throw new ZeroAuthError(
145247
+ "auth_no_credentials",
145248
+ "signMessage() requires the client to be constructed with `account` or `session`."
145249
+ );
145250
+ };
145251
+ // Managed-only: BYO accounts sign locally via viem's account.signTransaction.
145252
+ signTransaction = async (input, opts = {}) => {
145253
+ const credentials = this.client.getCredentials();
145254
+ if (credentials.kind === "session") {
145255
+ try {
145256
+ const parsed = await request2(
145257
+ this.client,
145258
+ {
145259
+ method: "POST",
145260
+ path: "/v1/users/me/sign-transaction",
145261
+ body: { unsignedTransaction: input.unsignedTransaction }
145262
+ },
145263
+ signResponseSchema
145264
+ );
145265
+ this.assertSignerMatches(parsed.walletAddress, opts.expectedAddress);
145266
+ return parsed.signature;
145267
+ } catch (err) {
145268
+ repackageSignFailure(err, "signTransaction");
145269
+ }
145270
+ }
145271
+ if (credentials.kind === "account") {
145272
+ throw new ZeroAuthError(
145273
+ "auth_error",
145274
+ "signTransaction() is a managed-signing operation and is not supported in account mode. Sign locally with viem's account.signTransaction instead."
145275
+ );
145276
+ }
145277
+ throw new ZeroAuthError(
145278
+ "auth_no_credentials",
145279
+ "signTransaction() requires session credentials (managed signing)."
145280
+ );
145281
+ };
145282
+ signTypedData = async (input, opts = {}) => {
145283
+ const credentials = this.client.getCredentials();
145284
+ if (credentials.kind === "account") {
145285
+ if (!credentials.account.signTypedData) {
145286
+ throw new ZeroAuthError(
145287
+ "auth_error",
145288
+ "The configured viem account does not implement signTypedData."
145289
+ );
145290
+ }
145291
+ return await credentials.account.signTypedData(input);
145292
+ }
145293
+ if (credentials.kind === "session") {
145294
+ try {
145295
+ const parsed = await request2(
145296
+ this.client,
145297
+ {
145298
+ method: "POST",
145299
+ path: "/v1/users/me/sign-typed-data",
145300
+ body: { typedData: input }
145301
+ },
145302
+ signResponseSchema
145303
+ );
145304
+ this.assertSignerMatches(parsed.walletAddress, opts.expectedAddress);
145305
+ return parsed.signature;
145306
+ } catch (err) {
145307
+ repackageSignFailure(err, "signTypedData");
145308
+ }
145309
+ }
145310
+ throw new ZeroAuthError(
145311
+ "auth_no_credentials",
145312
+ "signTypedData() requires the client to be constructed with `account` or `session`."
145313
+ );
145314
+ };
145315
+ // Compare against the caller's pinned address (not shared cache) so
145316
+ // concurrent pay()s can't race past each other. Mismatch → clear + throw;
145317
+ // the caller's retry rebuilds against the new wallet.
145318
+ assertSignerMatches = (serverAddr, expectedAddr) => {
145319
+ if (!expectedAddr) return;
145320
+ const canonicalServer = getAddress(serverAddr);
145321
+ const canonicalExpected = getAddress(expectedAddr);
145322
+ if (canonicalExpected === canonicalServer) return;
145323
+ this.client.clearManagedAccount();
145324
+ throw new ZeroAuthError(
145325
+ "auth_error",
145326
+ `Managed signing wallet changed (caller expected ${canonicalExpected}, server signed as ${canonicalServer}). Cache cleared \u2014 retry to rebuild against the current wallet.`
145327
+ );
145328
+ };
145329
+ };
145330
+ var BUG_REPORT_CATEGORIES = [
145331
+ "search_relevance",
145332
+ "ranking_issue",
145333
+ "missing_capability",
145334
+ "wrong_schema",
145335
+ "misleading_description",
145336
+ "broken_execution",
145337
+ "payment_failure",
145338
+ "billing_anomaly",
145339
+ "cli_bug",
145340
+ "security",
145341
+ "other"
145342
+ ];
145343
+ var createBugReportResponseSchema = external_exports3.object({
145344
+ bugReportId: external_exports3.string(),
145345
+ status: external_exports3.string(),
145346
+ deduped: external_exports3.boolean(),
145347
+ category: external_exports3.enum(BUG_REPORT_CATEGORIES).nullable(),
145348
+ title: external_exports3.string().nullable(),
145349
+ attached: external_exports3.object({
145350
+ capabilityId: external_exports3.number().nullable(),
145351
+ runId: external_exports3.number().nullable(),
145352
+ searchId: external_exports3.number().nullable()
145353
+ })
145354
+ });
145355
+ var BugReports = class {
145356
+ constructor(client) {
145357
+ this.client = client;
145358
+ }
145359
+ // POST /v1/bug-reports. Wallet-attributed server-side; the SDK client's
145360
+ // active credential drives attribution (account-mode → EIP-191 from the
145361
+ // wallet, session-mode → Bearer + server resolves the user's wallet).
145362
+ create = (input, opts = {}) => request2(
145363
+ this.client,
145364
+ {
145365
+ method: "POST",
145366
+ path: "/v1/bug-reports",
145367
+ body: input,
145368
+ signal: opts.signal
145369
+ },
145370
+ createBugReportResponseSchema
145371
+ );
145372
+ };
145373
+ var ratingSchema = external_exports3.object({
145374
+ score: external_exports3.string(),
145375
+ successRate: external_exports3.string(),
145376
+ reviews: external_exports3.number(),
145377
+ stars: external_exports3.string().nullable().optional(),
145378
+ state: external_exports3.enum(["unrated", "rated"]).optional()
145379
+ });
145380
+ var capabilityResponseSchema = external_exports3.object({
145381
+ uid: external_exports3.string(),
145382
+ slug: external_exports3.string(),
145383
+ name: external_exports3.string(),
145384
+ description: external_exports3.string(),
145385
+ url: external_exports3.string(),
145386
+ urlTemplate: external_exports3.string().nullable().optional(),
145387
+ method: external_exports3.string(),
145388
+ headers: external_exports3.record(external_exports3.string(), external_exports3.string()).nullable(),
145389
+ bodySchema: external_exports3.record(external_exports3.string(), external_exports3.unknown()).nullable(),
145390
+ responseSchema: external_exports3.record(external_exports3.string(), external_exports3.unknown()).nullable(),
145391
+ example: external_exports3.object({ request: external_exports3.unknown(), response: external_exports3.unknown() }).nullable(),
145392
+ tags: external_exports3.array(external_exports3.string()).nullable(),
145393
+ exampleAgentPrompt: external_exports3.string().nullable().optional(),
145394
+ displayCostAmount: external_exports3.string(),
145395
+ displayCostAsset: external_exports3.string(),
145396
+ reviewCount: external_exports3.number(),
145397
+ rating: ratingSchema,
145398
+ priceObserved: external_exports3.object({
145399
+ minCents: external_exports3.string().nullable(),
145400
+ medianCents: external_exports3.string().nullable(),
145401
+ maxCents: external_exports3.string().nullable(),
145402
+ // Deprecated alias for maxCents; kept for back-compat with older
145403
+ // API versions that still emit it.
145404
+ p95Cents: external_exports3.string().nullable(),
145405
+ sampleCount: external_exports3.number(),
145406
+ varies: external_exports3.boolean(),
145407
+ failureChargeRate: external_exports3.number().nullable()
145408
+ }).nullable().optional(),
145409
+ paymentMethods: external_exports3.array(
145410
+ external_exports3.object({
145411
+ uid: external_exports3.string(),
145412
+ protocol: external_exports3.string(),
145413
+ methodType: external_exports3.string(),
145414
+ chain: external_exports3.string().nullable(),
145415
+ mode: external_exports3.string(),
145416
+ costAmount: external_exports3.string(),
145417
+ costPer: external_exports3.string(),
145418
+ priority: external_exports3.number()
145419
+ })
145420
+ ).nullable(),
145421
+ availabilityStatus: external_exports3.enum(["healthy", "unknown", "down"]).nullable().optional(),
145422
+ /**
145423
+ * @deprecated The API no longer emits `displayStatus`; it has been replaced
145424
+ * by the consolidated 3-value {@link capabilityResponseSchema.shape.availabilityStatus}.
145425
+ * Kept as an optional field for one release for back-compat (ZERO-89 soft
145426
+ * migration) and will be removed in the next major version.
145427
+ */
145428
+ displayStatus: external_exports3.enum(["healthy", "stable", "degraded", "unhealthy", "unknown"]).optional(),
145429
+ activationCount: external_exports3.number().optional(),
145430
+ lastUsedAt: external_exports3.string().nullable().optional(),
145431
+ lastSuccessfullyRanAt: external_exports3.string().nullable().optional()
145432
+ });
145433
+ var resolveCapabilityResponseSchema = external_exports3.object({
145434
+ capabilityId: external_exports3.string(),
145435
+ capabilitySlug: external_exports3.string(),
145436
+ matchedVia: external_exports3.enum(["exact", "template"])
145437
+ });
145438
+ var Capabilities = class {
145439
+ constructor(client) {
145440
+ this.client = client;
145441
+ }
145442
+ get = (id, opts = {}) => request2(
145443
+ this.client,
145444
+ {
145445
+ method: "GET",
145446
+ path: `/v1/capabilities/${encodeURIComponent(id)}`,
145447
+ query: opts.searchId ? { searchId: opts.searchId } : void 0,
145448
+ signal: opts.signal
145449
+ },
145450
+ capabilityResponseSchema
145451
+ );
145452
+ // Resolve a raw endpoint URL + method back to the capability that owns it,
145453
+ // for the `zero fetch <url>` case where the client has no local search
145454
+ // context to match against (a memoized/cross-session URL). Returns null on
145455
+ // 404 — i.e. no match or an ambiguous multi-match, both of which the server
145456
+ // reports as 404 (resolveByUrl fails closed rather than mis-attribute).
145457
+ // The URL travels in the POST body so it stays out of access logs and is
145458
+ // never stored; the caller uses the returned capabilityId to record an
145459
+ // attributed run without the URL ever touching the run row (ZERO-335).
145460
+ resolveByUrl = async (url4, method, opts = {}) => {
145461
+ try {
145462
+ return await request2(
145463
+ this.client,
145464
+ {
145465
+ method: "POST",
145466
+ path: "/v1/capabilities/resolve",
145467
+ body: { url: url4, method },
145468
+ signal: opts.signal
145469
+ },
145470
+ resolveCapabilityResponseSchema
145471
+ );
145472
+ } catch (err) {
145473
+ if (err instanceof ZeroApiError && err.status === 404) return null;
145474
+ throw err;
145475
+ }
145476
+ };
145477
+ };
145478
+ var createRunResponseSchema = external_exports3.object({
145479
+ runId: external_exports3.string()
145480
+ });
145481
+ var runListItemSchema = external_exports3.object({
145482
+ uid: external_exports3.string(),
145483
+ capabilityUid: external_exports3.string(),
145484
+ capabilitySlug: external_exports3.string(),
145485
+ capabilityName: external_exports3.string(),
145486
+ status: external_exports3.number().nullable(),
145487
+ latencyMs: external_exports3.number().nullable(),
145488
+ cost: external_exports3.object({ amount: external_exports3.string(), asset: external_exports3.string().nullable() }).nullable(),
145489
+ payment: external_exports3.object({
145490
+ protocol: external_exports3.string(),
145491
+ chain: external_exports3.string().nullable(),
145492
+ txHash: external_exports3.string().nullable(),
145493
+ mode: external_exports3.string().nullable()
145494
+ }).nullable(),
145495
+ createdAt: external_exports3.coerce.date(),
145496
+ reviewed: external_exports3.boolean()
145497
+ });
144876
145498
  var listRunsResponseSchema = external_exports3.object({
144877
145499
  runs: external_exports3.array(runListItemSchema),
144878
145500
  nextCursor: external_exports3.string().nullable()
@@ -144935,886 +145557,271 @@ var Runs = class {
144935
145557
  }
144936
145558
  create = (input, opts = {}) => request2(
144937
145559
  this.client,
144938
- {
144939
- method: "POST",
144940
- path: "/v1/runs",
144941
- body: input,
144942
- signal: opts.signal
144943
- },
144944
- createRunResponseSchema
144945
- );
144946
- list = (params = {}, opts = {}) => {
144947
- if (params.limit === 0) {
144948
- if (opts.signal?.aborted) {
144949
- return Promise.reject(
144950
- new ZeroApiError("Request aborted by caller", { status: 0 })
144951
- );
144952
- }
144953
- if (params.cursor === void 0) {
144954
- return Promise.reject(
144955
- new ZeroApiError(
144956
- "runs.list({ limit: 0 }) requires a cursor \u2014 without one the empty page is indistinguishable from end-of-stream",
144957
- { status: 0 }
144958
- )
144959
- );
144960
- }
144961
- return Promise.resolve({
144962
- runs: [],
144963
- nextCursor: params.cursor
144964
- });
144965
- }
144966
- const query = {};
144967
- if (params.capabilityId !== void 0)
144968
- query.capabilityId = params.capabilityId;
144969
- if (params.unreviewed !== void 0) query.unreviewed = params.unreviewed;
144970
- if (params.limit !== void 0) query.limit = params.limit;
144971
- if (params.cursor !== void 0) query.cursor = params.cursor;
144972
- return request2(
144973
- this.client,
144974
- {
144975
- method: "GET",
144976
- path: "/v1/runs",
144977
- query,
144978
- signal: opts.signal
144979
- },
144980
- listRunsResponseSchema
144981
- );
144982
- };
144983
- get = (runId, opts = {}) => request2(
144984
- this.client,
144985
- {
144986
- method: "GET",
144987
- path: `/v1/runs/${runId}`,
144988
- signal: opts.signal
144989
- },
144990
- runDetailSchema
144991
- );
144992
- review = (input, opts = {}) => request2(
144993
- this.client,
144994
- {
144995
- method: "POST",
144996
- path: "/v1/reviews",
144997
- body: input,
144998
- signal: opts.signal
144999
- },
145000
- createReviewResponseSchema
145001
- );
145002
- // Bulk review submission. The CLI's bulk JSONL parser stays CLI-side —
145003
- // SDK consumers pass already-parsed structured input.
145004
- createReviewsBatch = (reviews, opts = {}) => request2(
145005
- this.client,
145006
- {
145007
- method: "POST",
145008
- path: "/v1/reviews/batch",
145009
- body: { reviews },
145010
- signal: opts.signal
145011
- },
145012
- batchReviewResponseSchema
145013
- );
145014
- };
145015
- var USDC_BASE2 = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913";
145016
- var USDC_TEMPO2 = "0x20c000000000000000000000b9537d11c60e8b50";
145017
- var TEMPO_CHAIN_ID2 = 4217;
145018
- var baseChain = base;
145019
- var tempoChain2 = {
145020
- id: TEMPO_CHAIN_ID2,
145021
- name: "Tempo",
145022
- nativeCurrency: { name: "USD", symbol: "USD", decimals: 18 },
145023
- rpcUrls: {
145024
- default: { http: ["https://rpc.tempo.xyz"] }
145025
- }
145026
- };
145027
- var ERC20_BALANCE_ABI2 = [
145028
- {
145029
- inputs: [{ name: "account", type: "address" }],
145030
- name: "balanceOf",
145031
- outputs: [{ name: "", type: "uint256" }],
145032
- stateMutability: "view",
145033
- type: "function"
145034
- }
145035
- ];
145036
- var USDC_DECIMALS = 6;
145037
- var fundingUrlResponseSchema = external_exports3.object({ url: external_exports3.string() });
145038
- var userWalletListSchema = external_exports3.array(userWalletDtoSchema);
145039
- var migrateAuthorizationResponseSchema = external_exports3.object({
145040
- transactionHash: external_exports3.string()
145041
- });
145042
- var errorMessage = (reason) => reason instanceof Error ? reason.message : String(reason);
145043
- var Wallet = class {
145044
- constructor(client) {
145045
- this.client = client;
145046
- }
145047
- clients = {};
145048
- // The configured wallet address, or null if the client wasn't constructed
145049
- // with an account. Stable read — does not hit the network.
145050
- get address() {
145051
- const credentials = this.client.getCredentials();
145052
- if (credentials.kind !== "account") return null;
145053
- return credentials.account.address;
145054
- }
145055
- balance = async (opts = {}) => {
145056
- const target = opts.address ?? this.address;
145057
- if (!target) {
145058
- throw new ZeroWalletError(
145059
- "balance() requires either an `address` option or a client constructed with an account"
145060
- );
145061
- }
145062
- const chain2 = opts.chain ?? "total";
145063
- if (chain2 !== "total") {
145064
- const raw = await this.readChainBalance(chain2, target);
145065
- return { amount: formatUnits(raw, USDC_DECIMALS), asset: "USDC" };
145066
- }
145067
- const chains = ["base", "tempo"];
145068
- const settled = await Promise.allSettled(
145069
- chains.map((c) => this.readChainBalance(c, target))
145070
- );
145071
- let sum = 0n;
145072
- const errors = [];
145073
- for (let i = 0; i < settled.length; i++) {
145074
- const result = settled[i];
145075
- const chainName = chains[i];
145076
- if (!result || !chainName) continue;
145077
- if (result.status === "fulfilled") {
145078
- sum += result.value;
145079
- } else {
145080
- errors.push({ chain: chainName, message: errorMessage(result.reason) });
145081
- }
145082
- }
145083
- if (errors.length === settled.length) {
145084
- throw new ZeroWalletError(
145085
- `balance() failed for every chain: ${errors.map((e2) => `${e2.chain} (${e2.message})`).join(", ")}`
145086
- );
145087
- }
145088
- const balance = {
145089
- amount: formatUnits(sum, USDC_DECIMALS),
145090
- asset: "USDC"
145091
- };
145092
- if (errors.length > 0) balance.errors = errors;
145093
- return balance;
145094
- };
145095
- // All wallets linked to the authenticated session user. Session-only.
145096
- list = (opts = {}) => request2(
145097
- this.client,
145098
- {
145099
- method: "GET",
145100
- path: "/v1/users/me/wallets",
145101
- signal: opts.signal
145102
- },
145103
- userWalletListSchema
145104
- );
145105
- // Provision a managed (Privy-embedded) wallet for the session user.
145106
- // Idempotent server-side — repeat calls return the existing wallet.
145107
- provision = (opts = {}) => request2(
145108
- this.client,
145109
- {
145110
- method: "POST",
145111
- path: "/v1/users/me/wallets/provision",
145112
- signal: opts.signal
145113
- },
145114
- userWalletDtoSchema
145115
- );
145116
- // Relays a BYO-wallet-signed EIP-3009 ReceiveWithAuthorization, sweeping
145117
- // USDC from the BYO wallet to the user's Zero wallet on Base via the
145118
- // gas-paying relayer. The SDK does not author the authorization (chain
145119
- // nonce + EIP-712 domain coupling stays caller-side); it only forwards
145120
- // the signed payload. Session-only.
145121
- migrateAuthorization = (authorization, opts = {}) => request2(
145122
- this.client,
145123
- {
145124
- method: "POST",
145125
- path: "/v1/users/me/wallets/migrate",
145126
- body: { authorization },
145127
- signal: opts.signal
145128
- },
145129
- migrateAuthorizationResponseSchema
145130
- );
145131
- // Routes by identity: account mode signs EIP-191 against /v1/wallet/fund-url,
145132
- // session mode hits /v1/users/me/fund-url (Bearer-authed, wallet resolved
145133
- // server-side). Both honor `provider` (coinbase | stripe).
145134
- fundingUrl = async (opts = {}) => {
145135
- const credentials = this.client.getCredentials();
145136
- const path3 = credentials.kind === "account" ? "/v1/wallet/fund-url" : credentials.kind === "session" ? "/v1/users/me/fund-url" : null;
145137
- if (!path3) {
145138
- throw new ZeroWalletError(
145139
- "fundingUrl() requires a client constructed with an account or session credentials"
145140
- );
145141
- }
145142
- const query = {
145143
- provider: opts.provider ?? "coinbase"
145144
- };
145145
- if (opts.amount) query.amount = opts.amount;
145146
- const { url: url4 } = await request2(
145147
- this.client,
145148
- { method: "GET", path: path3, query, signal: opts.signal },
145149
- fundingUrlResponseSchema
145150
- );
145151
- return url4;
145152
- };
145153
- getPublicClient = (chain2) => {
145154
- if (chain2 === "base") {
145155
- if (!this.clients.base) {
145156
- this.clients.base = createPublicClient({
145157
- chain: baseChain,
145158
- transport: http()
145159
- });
145160
- }
145161
- return this.clients.base;
145162
- }
145163
- if (!this.clients.tempo) {
145164
- this.clients.tempo = createPublicClient({
145165
- // biome-ignore lint/suspicious/noExplicitAny: tempo is a custom chain object that viem's stricter Chain type rejects shape-wise but accepts at runtime
145166
- chain: tempoChain2,
145167
- transport: http()
145168
- });
145169
- }
145170
- return this.clients.tempo;
145171
- };
145172
- /**
145173
- * @internal
145174
- *
145175
- * Per-chain USDC balance read. Exposed (rather than private) so unit
145176
- * tests can stub it without standing up a real viem public client —
145177
- * viem's `http()` transport doesn't go through `client.fetchImpl`, so
145178
- * the standard mock-fetch trick doesn't cover this path.
145179
- */
145180
- readChainBalance = async (chain2, address2) => {
145181
- const publicClient = this.getPublicClient(chain2);
145182
- const tokenAddress = chain2 === "base" ? USDC_BASE2 : USDC_TEMPO2;
145183
- const balance = await publicClient.readContract({
145184
- address: tokenAddress,
145185
- abi: ERC20_BALANCE_ABI2,
145186
- functionName: "balanceOf",
145187
- args: [address2]
145188
- });
145189
- return balance;
145190
- };
145191
- };
145192
- var DEFAULT_BASE_URL = "https://api.zero.xyz";
145193
- var DEFAULT_TIMEOUT_MS = 18e4;
145194
- var DEFAULT_MAX_RETRIES = 2;
145195
- var noopRefresh = async () => {
145196
- };
145197
- var MAX_402_PROBE_BYTES = 1024 * 1024;
145198
- var MAX_RESPONSE_BYTES = 10 * 1024 * 1024;
145199
- var TEXT_CONTENT_TYPES = [
145200
- "application/json",
145201
- "application/xml",
145202
- "application/javascript",
145203
- "application/ecmascript",
145204
- "application/x-www-form-urlencoded",
145205
- "text/",
145206
- "+json",
145207
- "+xml"
145208
- ];
145209
- var MPP_SCHEME_TOKENS = /* @__PURE__ */ new Set(["payment", "mpp"]);
145210
- var splitWwwAuthenticateChallenges = (header) => {
145211
- const chunks = [];
145212
- let buf = "";
145213
- let inQuotes = false;
145214
- for (let i = 0; i < header.length; i++) {
145215
- const ch = header[i];
145216
- if (inQuotes) {
145217
- if (ch === "\\" && i + 1 < header.length) {
145218
- buf += ch + header[i + 1];
145219
- i++;
145220
- continue;
145221
- }
145222
- if (ch === '"') inQuotes = false;
145223
- buf += ch;
145224
- continue;
145225
- }
145226
- if (ch === '"') {
145227
- inQuotes = true;
145228
- buf += ch;
145229
- continue;
145230
- }
145231
- if (ch === ",") {
145232
- chunks.push(buf);
145233
- buf = "";
145234
- continue;
145235
- }
145236
- buf += ch;
145237
- }
145238
- if (buf.length > 0) chunks.push(buf);
145239
- return chunks;
145240
- };
145241
- var isTextContentType = (contentType) => {
145242
- if (!contentType) return true;
145243
- const lower = contentType.toLowerCase();
145244
- return TEXT_CONTENT_TYPES.some((marker) => lower.includes(marker));
145245
- };
145246
- var redactUrl2 = (url4) => {
145247
- try {
145248
- const u2 = new URL(url4);
145249
- return `${u2.origin}${u2.pathname}`;
145250
- } catch {
145251
- return "<invalid url>";
145252
- }
145253
- };
145254
- var ECHO_DETECT_MIN_LEN = 16;
145255
- var ECHO_DETECT_MAX_SCAN = 64 * 1024;
145256
- var requestBodyEchoed = (candidate, requestBody) => {
145257
- if (requestBody === void 0) return false;
145258
- const asString = typeof requestBody === "string" ? requestBody : new TextDecoder("utf-8", { fatal: false }).decode(
145259
- requestBody.byteLength > ECHO_DETECT_MAX_SCAN ? requestBody.subarray(0, ECHO_DETECT_MAX_SCAN) : requestBody
145260
- );
145261
- if (asString.length < ECHO_DETECT_MIN_LEN) return false;
145262
- const scanLen = Math.min(asString.length, ECHO_DETECT_MAX_SCAN);
145263
- for (let i = 0; i + ECHO_DETECT_MIN_LEN <= scanLen; i++) {
145264
- if (candidate.includes(asString.slice(i, i + ECHO_DETECT_MIN_LEN))) {
145265
- return true;
145266
- }
145267
- }
145268
- return false;
145269
- };
145270
- var cancelBody2 = (response) => {
145271
- try {
145272
- response.body?.cancel().catch(() => {
145273
- });
145274
- } catch {
145275
- }
145276
- };
145277
- var detectPaymentRequirement = async (response) => {
145278
- if (response.status !== 402) return null;
145279
- const x402Header = response.headers.get("payment-required") ?? response.headers.get("x-payment-required");
145280
- if (x402Header) {
145281
- cancelBody2(response);
145282
- try {
145283
- const decoded = JSON.parse(
145284
- Buffer.from(x402Header, "base64").toString("utf8")
145285
- );
145286
- return { protocol: "x402", raw: decoded };
145287
- } catch {
145288
- return { protocol: "x402", raw: { encoded: x402Header } };
145289
- }
145290
- }
145291
- const declaredLen = Number.parseInt(
145292
- response.headers.get("content-length") ?? "",
145293
- 10
145294
- );
145295
- const bodyFits = !Number.isFinite(declaredLen) || declaredLen <= MAX_402_PROBE_BYTES;
145296
- if (bodyFits) {
145297
- try {
145298
- const text = await readClippedText(response, MAX_402_PROBE_BYTES);
145299
- if (text) {
145300
- const parsed = JSON.parse(text);
145301
- if (looksLikeX402Body(parsed)) {
145302
- return { protocol: "x402", raw: parsed };
145303
- }
145304
- }
145305
- } catch {
145306
- }
145307
- }
145308
- const wwwAuth = response.headers.get("www-authenticate");
145309
- if (wwwAuth) {
145310
- const schemes = splitWwwAuthenticateChallenges(wwwAuth).map(
145311
- (challenge2) => challenge2.trim().split(/\s+/)[0]?.toLowerCase() ?? ""
145312
- );
145313
- if (schemes.some((scheme) => MPP_SCHEME_TOKENS.has(scheme))) {
145314
- cancelBody2(response);
145315
- return { protocol: "mpp", raw: { "www-authenticate": wwwAuth } };
145316
- }
145317
- }
145318
- cancelBody2(response);
145319
- return { protocol: "unknown", raw: {} };
145320
- };
145321
- var readClippedBytes = async (response, max) => {
145322
- if (response.body === null) return new Uint8Array(0);
145323
- const reader = response.body.getReader();
145324
- const chunks = [];
145325
- let total = 0;
145326
- try {
145327
- while (total < max) {
145328
- const { done, value } = await reader.read();
145329
- if (done) break;
145330
- if (!value) continue;
145331
- chunks.push(value);
145332
- total += value.byteLength;
145333
- }
145334
- } finally {
145335
- try {
145336
- await reader.cancel();
145337
- } catch {
145338
- }
145339
- }
145340
- const merged = new Uint8Array(Math.min(total, max));
145341
- let offset = 0;
145342
- for (const c of chunks) {
145343
- const take = Math.min(c.byteLength, max - offset);
145344
- merged.set(c.subarray(0, take), offset);
145345
- offset += take;
145346
- if (offset >= max) break;
145347
- }
145348
- return merged;
145349
- };
145350
- var readClippedText = async (response, max) => {
145351
- const bytes = await readClippedBytes(response, max);
145352
- return new TextDecoder().decode(bytes);
145353
- };
145354
- var parseResponseBody = async (response, max) => {
145355
- const contentType = response.headers.get("content-type");
145356
- const bytes = await readClippedBytes(response, max + 1);
145357
- const truncated = bytes.byteLength > max;
145358
- const view = truncated ? bytes.subarray(0, max) : bytes;
145359
- const buf = Buffer.from(view.buffer, view.byteOffset, view.byteLength);
145360
- if (!isTextContentType(contentType)) {
145361
- const base644 = buf.toString("base64");
145362
- return {
145363
- body: base644,
145364
- bodyRaw: base644,
145365
- bodyEncoding: "base64",
145366
- truncated
145367
- };
145368
- }
145369
- const text = buf.toString("utf8");
145370
- if (contentType?.toLowerCase().includes("json")) {
145371
- try {
145372
- return { body: JSON.parse(text), bodyRaw: text, truncated };
145373
- } catch {
145374
- }
145375
- }
145376
- return { body: text, bodyRaw: text, truncated };
145377
- };
145378
- var UPSTREAM_ERROR_FIELDS = [
145379
- "error",
145380
- "message",
145381
- "detail",
145382
- "reason",
145383
- "error_description"
145384
- ];
145385
- var UPSTREAM_ERROR_MAX_LEN = 200;
145386
- var ERROR_SNIPPET_BYTES = 500;
145387
- var clampUpstreamMessage = (value) => {
145388
- const trimmed = value.trim();
145389
- if (!trimmed) return void 0;
145390
- return trimmed.length > UPSTREAM_ERROR_MAX_LEN ? `${trimmed.slice(0, UPSTREAM_ERROR_MAX_LEN)}\u2026` : trimmed;
145391
- };
145392
- var tryParseJson = (text) => {
145393
- try {
145394
- return JSON.parse(text);
145395
- } catch {
145396
- return null;
145397
- }
145398
- };
145399
- var extractUpstreamErrorMessage = (body) => {
145400
- const parsed = tryParseJson(body);
145401
- if (parsed === null || typeof parsed !== "object") {
145402
- return clampUpstreamMessage(body);
145403
- }
145404
- const record3 = parsed;
145405
- for (const field of UPSTREAM_ERROR_FIELDS) {
145406
- const value = record3[field];
145407
- if (typeof value === "string" && value.length > 0) {
145408
- return clampUpstreamMessage(value);
145409
- }
145410
- if (value && typeof value === "object") {
145411
- const nested = value.message;
145412
- if (typeof nested === "string" && nested.length > 0) {
145413
- return clampUpstreamMessage(nested);
145414
- }
145415
- }
145416
- }
145417
- return void 0;
145418
- };
145419
- var buildUpstreamError = (status, bodyRaw, bodyEncoding) => {
145420
- if (status >= 200 && status < 300) return void 0;
145421
- if (bodyRaw === null || bodyRaw.length === 0) return void 0;
145422
- if (bodyEncoding === "base64") return void 0;
145423
- const snippet = bodyRaw.slice(0, ERROR_SNIPPET_BYTES);
145424
- return {
145425
- status,
145426
- message: extractUpstreamErrorMessage(bodyRaw),
145427
- snippetHash: createHash("sha256").update(snippet).digest("hex"),
145428
- snippetLength: snippet.length
145429
- };
145430
- };
145431
- var computeOutcome = (status, payment, warnings) => {
145432
- if (warnings.includes(FETCH_WARNINGS.mppSessionCloseFailed))
145433
- return "payment_close_failed";
145434
- if (status === 402 && payment !== null) return "payment_rejected";
145435
- if (status === 402) return "payment_failed";
145436
- if (status >= 400) return "server_error";
145437
- return "success";
145438
- };
145439
- var errorFetchResult = (startedAt, error48, skipReason, capabilityIdRequested, outcome, status = null) => {
145440
- const result = {
145441
- runId: null,
145442
- ok: false,
145443
- status,
145444
- latencyMs: Date.now() - startedAt,
145445
- payment: null,
145446
- outcome,
145447
- body: null,
145448
- bodyRaw: null,
145449
- error: error48
145450
- };
145451
- if (capabilityIdRequested) result.runTrackingSkipped = [skipReason];
145452
- return result;
145453
- };
145454
- var resolveRecordingClient = (client, opts) => {
145455
- const credentials = client.getCredentials();
145456
- const recordingClient = opts.account ? client.withAccount(opts.account) : client;
145457
- const canRecord = opts.account !== void 0 || credentials.kind === "account" || credentials.kind === "session";
145458
- return { recordingClient, canRecord };
145459
- };
145460
- var recordErrorFetchRun = async (client, opts, result, capabilityIdOverride) => {
145461
- const effectiveCapId = opts.capabilityId;
145462
- if (effectiveCapId === void 0) return;
145463
- const { recordingClient, canRecord } = resolveRecordingClient(client, opts);
145464
- if (!canRecord) return;
145465
- const fetchOriginForRecord = opts.capabilityId === void 0 ? "direct_url" : opts.fetchOrigin;
145466
- try {
145467
- const created = await recordingClient.runs.create({
145468
- capabilityId: effectiveCapId,
145469
- ...opts.searchId && { searchId: opts.searchId },
145470
- ...fetchOriginForRecord && { fetchOrigin: fetchOriginForRecord },
145471
- ...result.status !== null && { status: result.status },
145472
- latencyMs: result.latencyMs,
145473
- ...opts.requestSchema && { requestSchema: opts.requestSchema }
145474
- });
145475
- result.runId = created.runId;
145476
- delete result.runTrackingSkipped;
145477
- } catch {
145478
- }
145479
- };
145480
- var recordTimeoutRun = async (client, opts, err, startedAt, capabilityIdRequested, capabilityIdOverride) => {
145481
- const result = errorFetchResult(
145482
- startedAt,
145483
- err.message,
145484
- "timeout",
145485
- capabilityIdRequested,
145486
- "network_error"
145487
- );
145488
- await recordErrorFetchRun(client, opts, result);
145489
- };
145490
- var sniffJsonShapeBytes = (buf) => {
145491
- let i = 0;
145492
- if (buf.length >= 3 && buf[0] === 239 && buf[1] === 187 && buf[2] === 191) {
145493
- i = 3;
145494
- }
145495
- while (i < buf.length && (buf[i] === 32 || buf[i] === 9 || buf[i] === 10 || buf[i] === 13)) {
145496
- i++;
145497
- }
145498
- if (i >= buf.length) return false;
145499
- return buf[i] === 123 || buf[i] === 91;
145500
- };
145501
- var withDefaultContentType = (headers, body) => {
145502
- if (!headers && body === void 0) return void 0;
145503
- const next = headers ? { ...headers } : {};
145504
- if (body === void 0) return next;
145505
- const hasContentType = Object.keys(next).some(
145506
- (k3) => k3.toLowerCase() === "content-type"
145507
- );
145508
- if (!hasContentType) {
145509
- next["content-type"] = typeof body === "string" || sniffJsonShapeBytes(body) ? "application/json" : "application/octet-stream";
145510
- }
145511
- return next;
145512
- };
145513
- var clientFetch = async (client, url4, opts = {}) => {
145514
- const startedAt = Date.now();
145515
- const capabilityIdRequested = opts.capabilityId !== void 0;
145516
- const normalized = normalizeTransportEnvelopeRequest(url4, {
145517
- method: opts.method,
145518
- body: opts.body
145519
- });
145520
- const requestUrl = normalized.url;
145521
- const requestBody = normalized.body;
145522
- const method = normalized.method ?? (requestBody !== void 0 ? "POST" : "GET");
145523
- const { canRecord: canRecordForResolve } = resolveRecordingClient(
145524
- client,
145525
- opts
145526
- );
145527
- const resolvePromise = !capabilityIdRequested && canRecordForResolve ? client.capabilities.resolveByUrl(redactUrl2(requestUrl), method).catch(() => null) : Promise.resolve(null);
145528
- const probeHeaders = withDefaultContentType(opts.headers, requestBody);
145529
- const requestInit = {
145530
- method,
145531
- headers: probeHeaders,
145532
- body: requestBody,
145533
- signal: opts.signal
145534
- };
145535
- const configuredFetch = buildConfiguredFetch(client, {
145536
- timeoutMs: opts.timeoutMs
145537
- });
145538
- let response;
145539
- let payment = null;
145540
- const skipReasons = [];
145541
- const warnings = [];
145542
- try {
145543
- response = await configuredFetch(requestUrl, requestInit);
145544
- } catch (err) {
145545
- if (err instanceof ZeroTimeoutError) {
145546
- await recordTimeoutRun(
145547
- client,
145548
- opts,
145549
- err,
145550
- startedAt,
145551
- capabilityIdRequested
145552
- );
145553
- throw err;
145554
- }
145555
- if (err instanceof ZeroError) throw err;
145556
- const errMessage = err instanceof Error ? err.message : String(err);
145557
- const result2 = errorFetchResult(
145558
- startedAt,
145559
- errMessage,
145560
- "fetch_failed_before_response",
145561
- capabilityIdRequested,
145562
- "network_error"
145563
- );
145564
- await recordErrorFetchRun(client, opts, result2);
145565
- return result2;
145566
- }
145567
- if (response.status === 402) {
145568
- const requirement = await detectPaymentRequirement(response);
145569
- if (requirement?.protocol === "x402" || requirement?.protocol === "mpp") {
145570
- const payCall = requirement.protocol === "x402" ? client.payments.pay : client.payments.payMpp;
145571
- try {
145572
- const paid = await payCall({
145573
- url: requestUrl,
145574
- method,
145575
- // Fresh clone for the paid retry — same rationale as
145576
- // the probe-side clone above. Re-derives the default
145577
- // Content-Type so the paid leg matches the probe.
145578
- headers: withDefaultContentType(opts.headers, requestBody),
145579
- body: requestBody,
145580
- maxPay: opts.maxPay,
145581
- account: opts.account,
145582
- signal: opts.signal,
145583
- timeoutMs: opts.timeoutMs,
145584
- ...opts.displayCostAmount && {
145585
- displayCostAmount: opts.displayCostAmount
145586
- }
145587
- });
145588
- response = paid.response;
145589
- payment = paid.payment;
145590
- if (paid.warnings) warnings.push(...paid.warnings);
145591
- } catch (err) {
145592
- if (err instanceof ZeroSessionCloseFailedError) {
145593
- response = err.response;
145594
- payment = {
145595
- protocol: "mpp",
145596
- chain: tempoChainLabelFromId(err.session.chainId),
145597
- txHash: null,
145598
- amount: err.capturedAmount,
145599
- asset: "USDC",
145600
- session: err.session
145601
- };
145602
- warnings.push(FETCH_WARNINGS.mppSessionCloseFailed);
145603
- } else if (err instanceof ZeroInsufficientFundsError) {
145604
- return errorFetchResult(
145605
- startedAt,
145606
- err.message,
145607
- FETCH_SKIP_REASONS.insufficientFunds,
145608
- capabilityIdRequested,
145609
- "insufficient_funds",
145610
- null
145611
- );
145612
- } else if (err instanceof ZeroPaymentError || err instanceof ZeroAuthError) {
145613
- const result2 = errorFetchResult(
145614
- startedAt,
145615
- err.message,
145616
- "payment_failed",
145617
- capabilityIdRequested,
145618
- "payment_failed",
145619
- 402
145620
- );
145621
- await recordErrorFetchRun(client, opts, result2);
145622
- return result2;
145623
- } else if (err instanceof ZeroTimeoutError) {
145624
- await recordTimeoutRun(
145625
- client,
145626
- opts,
145627
- err,
145628
- startedAt,
145629
- capabilityIdRequested
145630
- );
145631
- throw err;
145632
- } else {
145633
- throw err;
145634
- }
145560
+ {
145561
+ method: "POST",
145562
+ path: "/v1/runs",
145563
+ body: input,
145564
+ signal: opts.signal
145565
+ },
145566
+ createRunResponseSchema
145567
+ );
145568
+ list = (params = {}, opts = {}) => {
145569
+ if (params.limit === 0) {
145570
+ if (opts.signal?.aborted) {
145571
+ return Promise.reject(
145572
+ new ZeroApiError("Request aborted by caller", { status: 0 })
145573
+ );
145635
145574
  }
145636
- } else if (requirement?.protocol === "unknown") {
145637
- const result2 = errorFetchResult(
145638
- startedAt,
145639
- "Capability returned 402 with an unrecognized payment protocol. Neither x402 nor MPP markers were detected in the response headers or body.",
145640
- "unrecognized_402_protocol",
145641
- capabilityIdRequested,
145642
- "payment_failed",
145643
- 402
145644
- );
145645
- await recordErrorFetchRun(client, opts, result2);
145646
- return result2;
145575
+ if (params.cursor === void 0) {
145576
+ return Promise.reject(
145577
+ new ZeroApiError(
145578
+ "runs.list({ limit: 0 }) requires a cursor \u2014 without one the empty page is indistinguishable from end-of-stream",
145579
+ { status: 0 }
145580
+ )
145581
+ );
145582
+ }
145583
+ return Promise.resolve({
145584
+ runs: [],
145585
+ nextCursor: params.cursor
145586
+ });
145647
145587
  }
145588
+ const query = {};
145589
+ if (params.capabilityId !== void 0)
145590
+ query.capabilityId = params.capabilityId;
145591
+ if (params.unreviewed !== void 0) query.unreviewed = params.unreviewed;
145592
+ if (params.limit !== void 0) query.limit = params.limit;
145593
+ if (params.cursor !== void 0) query.cursor = params.cursor;
145594
+ return request2(
145595
+ this.client,
145596
+ {
145597
+ method: "GET",
145598
+ path: "/v1/runs",
145599
+ query,
145600
+ signal: opts.signal
145601
+ },
145602
+ listRunsResponseSchema
145603
+ );
145604
+ };
145605
+ get = (runId, opts = {}) => request2(
145606
+ this.client,
145607
+ {
145608
+ method: "GET",
145609
+ path: `/v1/runs/${runId}`,
145610
+ signal: opts.signal
145611
+ },
145612
+ runDetailSchema
145613
+ );
145614
+ review = (input, opts = {}) => request2(
145615
+ this.client,
145616
+ {
145617
+ method: "POST",
145618
+ path: "/v1/reviews",
145619
+ body: input,
145620
+ signal: opts.signal
145621
+ },
145622
+ createReviewResponseSchema
145623
+ );
145624
+ // Bulk review submission. The CLI's bulk JSONL parser stays CLI-side —
145625
+ // SDK consumers pass already-parsed structured input.
145626
+ createReviewsBatch = (reviews, opts = {}) => request2(
145627
+ this.client,
145628
+ {
145629
+ method: "POST",
145630
+ path: "/v1/reviews/batch",
145631
+ body: { reviews },
145632
+ signal: opts.signal
145633
+ },
145634
+ batchReviewResponseSchema
145635
+ );
145636
+ };
145637
+ var USDC_BASE2 = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913";
145638
+ var USDC_TEMPO2 = "0x20c000000000000000000000b9537d11c60e8b50";
145639
+ var TEMPO_CHAIN_ID2 = 4217;
145640
+ var baseChain = base;
145641
+ var tempoChain2 = {
145642
+ id: TEMPO_CHAIN_ID2,
145643
+ name: "Tempo",
145644
+ nativeCurrency: { name: "USD", symbol: "USD", decimals: 18 },
145645
+ rpcUrls: {
145646
+ default: { http: ["https://rpc.tempo.xyz"] }
145648
145647
  }
145649
- const latencyMs = Date.now() - startedAt;
145650
- const maxResponseBytes = opts.maxResponseBytes ?? MAX_RESPONSE_BYTES;
145651
- let body;
145652
- let bodyRaw;
145653
- let bodyEncoding;
145654
- try {
145655
- const parsed = await parseResponseBody(response, maxResponseBytes);
145656
- body = parsed.body;
145657
- bodyRaw = parsed.bodyRaw;
145658
- bodyEncoding = parsed.bodyEncoding;
145659
- if (parsed.truncated) warnings.push(FETCH_WARNINGS.bodyTruncated);
145660
- } catch (err) {
145661
- if (err instanceof Error && err.name === "AbortError") {
145662
- throw new ZeroApiError("Request aborted by caller", {
145663
- status: 0,
145664
- cause: err
145665
- });
145648
+ };
145649
+ var ERC20_BALANCE_ABI2 = [
145650
+ {
145651
+ inputs: [{ name: "account", type: "address" }],
145652
+ name: "balanceOf",
145653
+ outputs: [{ name: "", type: "uint256" }],
145654
+ stateMutability: "view",
145655
+ type: "function"
145656
+ }
145657
+ ];
145658
+ var USDC_DECIMALS = 6;
145659
+ var fundingUrlResponseSchema = external_exports3.object({ url: external_exports3.string() });
145660
+ var userWalletListSchema = external_exports3.array(userWalletDtoSchema);
145661
+ var migrateAuthorizationResponseSchema = external_exports3.object({
145662
+ transactionHash: external_exports3.string()
145663
+ });
145664
+ var errorMessage = (reason) => reason instanceof Error ? reason.message : String(reason);
145665
+ var Wallet = class {
145666
+ constructor(client) {
145667
+ this.client = client;
145668
+ }
145669
+ clients = {};
145670
+ // The configured wallet address, or null if the client wasn't constructed
145671
+ // with an account. Stable read — does not hit the network.
145672
+ get address() {
145673
+ const credentials = this.client.getCredentials();
145674
+ if (credentials.kind !== "account") return null;
145675
+ return credentials.account.address;
145676
+ }
145677
+ balance = async (opts = {}) => {
145678
+ const target = opts.address ?? this.address;
145679
+ if (!target) {
145680
+ throw new ZeroWalletError(
145681
+ "balance() requires either an `address` option or a client constructed with an account"
145682
+ );
145666
145683
  }
145667
- const message2 = err instanceof Error ? err.message : String(err);
145668
- const skips = [];
145669
- const settledLocally = paymentHasAnchor(payment);
145670
- if (settledLocally && response.status === 402) {
145671
- warnings.push(FETCH_WARNINGS.paidButStill402NotRecorded);
145672
- skips.push(FETCH_WARNINGS.paidButStill402NotRecorded);
145684
+ const chain2 = opts.chain ?? "total";
145685
+ if (chain2 !== "total") {
145686
+ const raw = await this.readChainBalance(chain2, target);
145687
+ return { amount: formatUnits(raw, USDC_DECIMALS), asset: "USDC" };
145673
145688
  }
145674
- if (payment !== null && !settledLocally) {
145675
- warnings.push(FETCH_WARNINGS.paymentSettlementUnconfirmed);
145676
- skips.push(FETCH_WARNINGS.paymentSettlementUnconfirmed);
145689
+ const chains = ["base", "tempo"];
145690
+ const settled = await Promise.allSettled(
145691
+ chains.map((c) => this.readChainBalance(c, target))
145692
+ );
145693
+ let sum = 0n;
145694
+ const errors = [];
145695
+ for (let i = 0; i < settled.length; i++) {
145696
+ const result = settled[i];
145697
+ const chainName = chains[i];
145698
+ if (!result || !chainName) continue;
145699
+ if (result.status === "fulfilled") {
145700
+ sum += result.value;
145701
+ } else {
145702
+ errors.push({ chain: chainName, message: errorMessage(result.reason) });
145703
+ }
145677
145704
  }
145678
- if (payment !== null && payment.amount === "unknown") {
145679
- warnings.push(FETCH_WARNINGS.paymentAmountUnknown);
145705
+ if (errors.length === settled.length) {
145706
+ throw new ZeroWalletError(
145707
+ `balance() failed for every chain: ${errors.map((e2) => `${e2.chain} (${e2.message})`).join(", ")}`
145708
+ );
145680
145709
  }
145681
- skips.push(FETCH_SKIP_REASONS.bodyReadFailed);
145682
- const result2 = {
145683
- runId: null,
145684
- ok: false,
145685
- status: response.status,
145686
- latencyMs: Date.now() - startedAt,
145687
- payment,
145688
- outcome: computeOutcome(response.status, payment, warnings),
145689
- body: null,
145690
- bodyRaw: null,
145691
- error: message2
145710
+ const balance = {
145711
+ amount: formatUnits(sum, USDC_DECIMALS),
145712
+ asset: "USDC"
145692
145713
  };
145693
- if (capabilityIdRequested) result2.runTrackingSkipped = skips;
145694
- if (warnings.length > 0) result2.warnings = warnings;
145695
- return result2;
145696
- }
145697
- const settled = paymentHasAnchor(payment);
145698
- const paidButStill402 = settled && response.status === 402;
145699
- const settlementUnconfirmed = payment !== null && !settled;
145700
- if (paidButStill402) warnings.push(FETCH_WARNINGS.paidButStill402NotRecorded);
145701
- if (settlementUnconfirmed)
145702
- warnings.push(FETCH_WARNINGS.paymentSettlementUnconfirmed);
145703
- if (payment !== null && payment.amount === "unknown") {
145704
- warnings.push(FETCH_WARNINGS.paymentAmountUnknown);
145705
- }
145706
- let runId = null;
145707
- const closeFailed = warnings.includes(FETCH_WARNINGS.mppSessionCloseFailed);
145708
- const upstreamErrorForRecord = buildUpstreamError(
145709
- response.status,
145710
- bodyRaw,
145711
- bodyEncoding
145714
+ if (errors.length > 0) balance.errors = errors;
145715
+ return balance;
145716
+ };
145717
+ // All wallets linked to the authenticated session user. Session-only.
145718
+ list = (opts = {}) => request2(
145719
+ this.client,
145720
+ {
145721
+ method: "GET",
145722
+ path: "/v1/users/me/wallets",
145723
+ signal: opts.signal
145724
+ },
145725
+ userWalletListSchema
145712
145726
  );
145713
- if (capabilityIdRequested) {
145714
- if (paidButStill402)
145715
- skipReasons.push(FETCH_WARNINGS.paidButStill402NotRecorded);
145716
- else if (closeFailed)
145717
- skipReasons.push(FETCH_SKIP_REASONS.mppSessionCloseFailedNotRecorded);
145718
- }
145719
- const resolvedCap = await resolvePromise;
145720
- const effectiveCapabilityId = opts.capabilityId ?? resolvedCap?.capabilityId;
145721
- const capabilityResolution = !capabilityIdRequested && canRecordForResolve ? resolvedCap !== null ? {
145722
- resolved: true,
145723
- capabilityId: resolvedCap.capabilityId,
145724
- capabilitySlug: resolvedCap.capabilitySlug,
145725
- matchedVia: resolvedCap.matchedVia
145726
- } : { resolved: false } : void 0;
145727
- if (effectiveCapabilityId !== void 0 && !paidButStill402 && !closeFailed) {
145728
- const { recordingClient, canRecord } = resolveRecordingClient(client, opts);
145729
- if (!canRecord) {
145730
- skipReasons.push("no_credentials");
145731
- } else {
145732
- const knownAmount = payment !== null && payment.amount !== "unknown";
145733
- try {
145734
- let derived;
145735
- if (opts.deriveRunMetadata) {
145736
- try {
145737
- derived = opts.deriveRunMetadata({
145738
- status: response.status,
145739
- ok: response.ok,
145740
- bodyRaw,
145741
- ...bodyEncoding && { bodyEncoding }
145742
- });
145743
- } catch {
145744
- }
145745
- }
145746
- const result2 = await recordingClient.runs.create({
145747
- capabilityId: effectiveCapabilityId,
145748
- searchId: opts.searchId,
145749
- // Auto-resolved runs are always direct_url — they only fire when
145750
- // no capabilityId was supplied (the direct_url path by definition).
145751
- ...opts.capabilityId === void 0 ? { fetchOrigin: "direct_url" } : opts.fetchOrigin && { fetchOrigin: opts.fetchOrigin },
145752
- status: response.status,
145753
- latencyMs,
145754
- ...opts.requestSchema && { requestSchema: opts.requestSchema },
145755
- ...derived?.responseSchema && {
145756
- responseSchema: derived.responseSchema
145757
- },
145758
- ...upstreamErrorForRecord && {
145759
- errorSnippetHash: upstreamErrorForRecord.snippetHash,
145760
- errorSnippetLength: upstreamErrorForRecord.snippetLength
145761
- },
145762
- ...payment && {
145763
- ...knownAmount && {
145764
- costAmount: payment.amount,
145765
- costAsset: payment.asset
145766
- },
145767
- paymentProtocol: payment.protocol,
145768
- paymentChain: payment.chain,
145769
- paymentTxHash: payment.txHash ?? void 0,
145770
- paymentMode: payment.session ? "session" : "charge"
145771
- }
145772
- });
145773
- runId = result2.runId;
145774
- } catch (err) {
145775
- const errorMessage22 = err instanceof Error ? err.message : String(err);
145776
- const code = err instanceof ZeroError ? err.code : void 0;
145777
- const skipMsg = code ? `run_create_failed:${code}: ${errorMessage22}` : `run_create_failed: ${errorMessage22}`;
145778
- skipReasons.push(skipMsg);
145779
- client.logger?.({
145780
- level: "warn",
145781
- message: "Failed to record run for fetch",
145782
- meta: {
145783
- url: redactUrl2(url4),
145784
- capabilityId: effectiveCapabilityId,
145785
- error: errorMessage22,
145786
- errorCode: code
145787
- }
145727
+ // Provision a managed (Privy-embedded) wallet for the session user.
145728
+ // Idempotent server-side — repeat calls return the existing wallet.
145729
+ provision = (opts = {}) => request2(
145730
+ this.client,
145731
+ {
145732
+ method: "POST",
145733
+ path: "/v1/users/me/wallets/provision",
145734
+ signal: opts.signal
145735
+ },
145736
+ userWalletDtoSchema
145737
+ );
145738
+ // Relays a BYO-wallet-signed EIP-3009 ReceiveWithAuthorization, sweeping
145739
+ // USDC from the BYO wallet to the user's Zero wallet on Base via the
145740
+ // gas-paying relayer. The SDK does not author the authorization (chain
145741
+ // nonce + EIP-712 domain coupling stays caller-side); it only forwards
145742
+ // the signed payload. Session-only.
145743
+ migrateAuthorization = (authorization, opts = {}) => request2(
145744
+ this.client,
145745
+ {
145746
+ method: "POST",
145747
+ path: "/v1/users/me/wallets/migrate",
145748
+ body: { authorization },
145749
+ signal: opts.signal
145750
+ },
145751
+ migrateAuthorizationResponseSchema
145752
+ );
145753
+ // Routes by identity: account mode signs EIP-191 against /v1/wallet/fund-url,
145754
+ // session mode hits /v1/users/me/fund-url (Bearer-authed, wallet resolved
145755
+ // server-side). Both honor `provider` (coinbase | stripe).
145756
+ fundingUrl = async (opts = {}) => {
145757
+ const credentials = this.client.getCredentials();
145758
+ const path3 = credentials.kind === "account" ? "/v1/wallet/fund-url" : credentials.kind === "session" ? "/v1/users/me/fund-url" : null;
145759
+ if (!path3) {
145760
+ throw new ZeroWalletError(
145761
+ "fundingUrl() requires a client constructed with an account or session credentials"
145762
+ );
145763
+ }
145764
+ const query = {
145765
+ provider: opts.provider ?? "coinbase"
145766
+ };
145767
+ if (opts.amount) query.amount = opts.amount;
145768
+ const { url: url4 } = await request2(
145769
+ this.client,
145770
+ { method: "GET", path: path3, query, signal: opts.signal },
145771
+ fundingUrlResponseSchema
145772
+ );
145773
+ return url4;
145774
+ };
145775
+ getPublicClient = (chain2) => {
145776
+ if (chain2 === "base") {
145777
+ if (!this.clients.base) {
145778
+ this.clients.base = createPublicClient({
145779
+ chain: baseChain,
145780
+ transport: http()
145788
145781
  });
145789
145782
  }
145783
+ return this.clients.base;
145790
145784
  }
145791
- }
145792
- const result = {
145793
- runId,
145794
- ok: response.ok,
145795
- status: response.status,
145796
- latencyMs,
145797
- payment,
145798
- outcome: computeOutcome(response.status, payment, warnings),
145799
- body,
145800
- bodyRaw
145785
+ if (!this.clients.tempo) {
145786
+ this.clients.tempo = createPublicClient({
145787
+ // biome-ignore lint/suspicious/noExplicitAny: tempo is a custom chain object that viem's stricter Chain type rejects shape-wise but accepts at runtime
145788
+ chain: tempoChain2,
145789
+ transport: http()
145790
+ });
145791
+ }
145792
+ return this.clients.tempo;
145801
145793
  };
145802
- if (bodyEncoding) result.bodyEncoding = bodyEncoding;
145803
- if (!response.ok) {
145804
- const candidate = bodyRaw !== null && bodyRaw.length > 0 && bodyEncoding !== "base64" ? bodyRaw.slice(0, 240) : null;
145805
- result.error = candidate !== null && !requestBodyEchoed(candidate, opts.body) ? candidate : `HTTP ${response.status}`;
145806
- if (upstreamErrorForRecord) result.upstreamError = upstreamErrorForRecord;
145807
- }
145808
- if (capabilityIdRequested && skipReasons.length > 0) {
145809
- result.runTrackingSkipped = skipReasons;
145810
- }
145811
- if (warnings.length > 0) result.warnings = warnings;
145812
- if (capabilityResolution !== void 0)
145813
- result.capabilityResolution = capabilityResolution;
145814
- return result;
145794
+ /**
145795
+ * @internal
145796
+ *
145797
+ * Per-chain USDC balance read. Exposed (rather than private) so unit
145798
+ * tests can stub it without standing up a real viem public client —
145799
+ * viem's `http()` transport doesn't go through `client.fetchImpl`, so
145800
+ * the standard mock-fetch trick doesn't cover this path.
145801
+ */
145802
+ readChainBalance = async (chain2, address2) => {
145803
+ const publicClient = this.getPublicClient(chain2);
145804
+ const tokenAddress = chain2 === "base" ? USDC_BASE2 : USDC_TEMPO2;
145805
+ const balance = await publicClient.readContract({
145806
+ address: tokenAddress,
145807
+ abi: ERC20_BALANCE_ABI2,
145808
+ functionName: "balanceOf",
145809
+ args: [address2]
145810
+ });
145811
+ return balance;
145812
+ };
145813
+ };
145814
+ var DEFAULT_BASE_URL = "https://api.zero.xyz";
145815
+ var DEFAULT_TIMEOUT_MS = 18e4;
145816
+ var DEFAULT_MAX_RETRIES = 2;
145817
+ var noopRefresh = async () => {
145815
145818
  };
145816
145819
  var searchResultSchema = external_exports3.object({
145817
145820
  id: external_exports3.string(),
145821
+ // Opaque attribution token (format: `z_{shortId}.{position}`). Optional
145822
+ // so old API deploys that don't emit it still parse cleanly. Pass as
145823
+ // capabilityId to client.fetch() for exact search attribution.
145824
+ token: external_exports3.string().nullable().optional(),
145818
145825
  position: external_exports3.number(),
145819
145826
  slug: external_exports3.string(),
145820
145827
  name: external_exports3.string(),
@@ -146316,7 +146323,7 @@ Categories the classifier picks from:
146316
146323
  ).action(
146317
146324
  async (description, options) => {
146318
146325
  try {
146319
- const { analyticsService, client, stateService, walletAddress } = appContext.services;
146326
+ const { analyticsService, client, walletAddress } = appContext.services;
146320
146327
  if (!walletAddress) {
146321
146328
  console.error(
146322
146329
  "Wallet auth required \u2014 run `zero auth login`, or `zero wallet set <key>` / set ZERO_PRIVATE_KEY."
@@ -146397,12 +146404,6 @@ Bulk bug-report complete: ${ok} ok, ${failed} failed`
146397
146404
  let { capability, run, search } = options;
146398
146405
  const useAutoContext = options.context !== false;
146399
146406
  if (useAutoContext) {
146400
- const lastSearch = stateService.loadLastSearch();
146401
- if (lastSearch) {
146402
- search = search ?? lastSearch.searchId;
146403
- const recent = lastSearch.capabilities[0];
146404
- capability = capability ?? recent?.id;
146405
- }
146406
146407
  if (!run) {
146407
146408
  try {
146408
146409
  const list2 = await client.runs.list({ limit: 1 });
@@ -146610,11 +146611,6 @@ var sniffJsonShape = (buf) => {
146610
146611
  if (i >= buf.length) return false;
146611
146612
  return buf[i] === 123 || buf[i] === 91;
146612
146613
  };
146613
- var urlMatchesTemplate = (url4, template) => {
146614
- const escaped = template.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
146615
- const withCaptures = escaped.replace(/\\\{[^/\\}]+\\\}/g, "[^/]+");
146616
- return new RegExp(`^${withCaptures}$`).test(url4);
146617
- };
146618
146614
  var resolveRequestBody = (rawData, readStdin2) => {
146619
146615
  const fromFile = (spec) => {
146620
146616
  if (spec === "@-") return readFileSync(0);
@@ -146681,27 +146677,25 @@ var fetchCommand = (appContext) => new Command("fetch").description(
146681
146677
  ).action(
146682
146678
  async (url4, options) => {
146683
146679
  try {
146684
- const {
146685
- analyticsService,
146686
- client,
146687
- stateService,
146688
- walletAddress,
146689
- lowBalanceWarning
146690
- } = appContext.services;
146680
+ const { analyticsService, client, walletAddress, lowBalanceWarning } = appContext.services;
146681
+ const fromToken = options.capability ? isShortToken(options.capability) : false;
146682
+ const effectiveCapRef = options.capability;
146691
146683
  let resolvedUrl;
146692
146684
  let resolvedMethodFromCapability;
146685
+ let capFromNoUrl;
146693
146686
  if (!url4) {
146694
146687
  if (!options.capability) {
146695
146688
  console.error(
146696
- "Missing URL. Pass a URL argument or --capability <uid|slug> so the URL can be resolved from the capability."
146689
+ "Missing URL. Pass a URL argument or --capability <uid|slug|token> so the URL can be resolved from the capability."
146697
146690
  );
146698
146691
  process.exitCode = 1;
146699
146692
  return;
146700
146693
  }
146701
146694
  try {
146702
- const cap = await client.capabilities.get(options.capability);
146695
+ const cap = await client.capabilities.get(effectiveCapRef ?? "");
146703
146696
  resolvedUrl = cap.url;
146704
146697
  resolvedMethodFromCapability = cap.method;
146698
+ capFromNoUrl = cap;
146705
146699
  } catch (err) {
146706
146700
  console.error(
146707
146701
  `Failed to resolve --capability ${options.capability}: ${err instanceof Error ? err.message : String(err)}`
@@ -146758,24 +146752,31 @@ var fetchCommand = (appContext) => new Command("fetch").description(
146758
146752
  }
146759
146753
  const log2 = (msg) => console.error(` ${msg}`);
146760
146754
  const method = options.method ? options.method.toUpperCase() : resolvedMethodFromCapability ? resolvedMethodFromCapability.toUpperCase() : resolvedBody ? "POST" : "GET";
146761
- const explicitCapabilityCtx = options.capability ? stateService.findSearchContextByCapability(options.capability) : null;
146762
- const urlCtx = stateService.findSearchContextByUrl(resolvedUrl, {
146763
- matchTemplate: (template) => {
146764
- try {
146765
- return urlMatchesTemplate(resolvedUrl, template);
146766
- } catch {
146767
- return false;
146768
- }
146769
- }
146770
- });
146771
- const matchCtx = explicitCapabilityCtx ?? urlCtx;
146772
- const capabilityId = options.capability ?? matchCtx?.capabilityId ?? null;
146773
- let capabilityUid = matchCtx?.capabilityUid ?? null;
146774
- let capabilitySlug = matchCtx?.capabilitySlug ?? null;
146775
- const searchId = matchCtx?.searchId;
146776
- const resultRank = matchCtx?.resultRank;
146777
- const fetchOrigin = matchCtx ? "from_search" : options.capability ? "direct_slug" : "direct_url";
146778
- const matchedDisplayCostAmount = matchCtx?.displayCostAmount;
146755
+ let capabilityId = null;
146756
+ let capabilityUid = null;
146757
+ let capabilitySlug = null;
146758
+ let searchId;
146759
+ let displayCostAmount;
146760
+ let fetchOrigin;
146761
+ if (fromToken) {
146762
+ capabilityUid = capFromNoUrl?.uid ?? null;
146763
+ capabilityId = options.capability ?? "";
146764
+ capabilitySlug = capFromNoUrl?.slug ?? null;
146765
+ displayCostAmount = capFromNoUrl?.displayCostAmount;
146766
+ fetchOrigin = "from_search";
146767
+ } else if (options.capability) {
146768
+ capabilityUid = capFromNoUrl?.uid ?? null;
146769
+ capabilitySlug = capFromNoUrl?.slug ?? null;
146770
+ capabilityId = options.capability;
146771
+ displayCostAmount = capFromNoUrl?.displayCostAmount;
146772
+ fetchOrigin = "direct_slug";
146773
+ } else {
146774
+ const resolved = await client.capabilities.resolveByUrl(resolvedUrl, options.method ?? "GET").catch(() => null);
146775
+ capabilityUid = resolved?.capabilityId ?? null;
146776
+ capabilityId = capabilityUid;
146777
+ fetchOrigin = "direct_url";
146778
+ }
146779
+ const resultRank = void 0;
146779
146780
  const cliSkipReasons = [];
146780
146781
  if (!walletAddress) {
146781
146782
  cliSkipReasons.push(
@@ -146811,16 +146812,17 @@ var fetchCommand = (appContext) => new Command("fetch").description(
146811
146812
  // Adaptive MPP channel prefund: when the seller sends no
146812
146813
  // suggestedDeposit and the caller didn't pass --max-pay,
146813
146814
  // the SDK scales the channel to 10× displayCostAmount so
146814
- // an expensive single call doesn't under-fund. Threaded
146815
- // from the search-cache entry.
146816
- ...matchedDisplayCostAmount && {
146817
- displayCostAmount: matchedDisplayCostAmount
146818
- },
146815
+ // an expensive single call doesn't under-fund. Derived from
146816
+ // the capability details when available (token or direct-slug
146817
+ // paths that triggered a capabilities.get).
146818
+ ...displayCostAmount && { displayCostAmount },
146819
146819
  // Only attach the capability when wallet auth is available;
146820
146820
  // recording without a wallet would skip server-side anyway,
146821
146821
  // and omitting here keeps result.runTrackingSkipped clean.
146822
146822
  ...capabilityId && walletAddress && { capabilityId },
146823
- ...searchId && { searchId },
146823
+ // searchId intentionally omitted: server derives attribution
146824
+ // via SearchAttributor (wallet + capId + 30-min window) so
146825
+ // parallel fetches are race-free without shared client state.
146824
146826
  // Stamp how the agent arrived (from_search | direct_slug |
146825
146827
  // direct_url) onto the run's metadata so direct execution is
146826
146828
  // distinguishable from search-driven runs server-side. Only
@@ -146947,19 +146949,12 @@ var fetchCommand = (appContext) => new Command("fetch").description(
146947
146949
  } else {
146948
146950
  if (walletAddress && !runId) {
146949
146951
  cliSkipReasons.push(
146950
- "no capability resolved \u2014 pass --capability <uid|slug> or run `zero search` first so the URL can be matched"
146952
+ "no capability resolved \u2014 pass --capability <uid|slug|token> or run `zero search` first so the URL can be matched"
146951
146953
  );
146952
146954
  }
146953
- const lastSearch = stateService.loadLastSearch();
146954
- const cached2 = lastSearch?.capabilities ?? [];
146955
146955
  analyticsService.capture("capability_resolution_missed", {
146956
146956
  url: redactUrl(resolvedUrl),
146957
- method,
146958
- hasLastSearch: lastSearch !== null,
146959
- lastSearchSize: cached2.length,
146960
- lastSearchHasTemplates: cached2.some(
146961
- (c) => Boolean(c.urlTemplate)
146962
- )
146957
+ method
146963
146958
  });
146964
146959
  }
146965
146960
  }
@@ -147474,66 +147469,26 @@ var formatCapability = (capability) => {
147474
147469
  lines.push(...buildTryItExample(capability));
147475
147470
  return lines.join("\n");
147476
147471
  };
147477
- var getCommand = (appContext) => new Command("get").description(
147478
- "Get details for a capability by position from last search, or by slug"
147479
- ).argument(
147472
+ var getCommand = (appContext) => new Command("get").description("Get details for a capability by token, slug, or uid").argument(
147480
147473
  "<identifier>",
147481
- "Position number from search results, or a capability slug"
147474
+ "Attribution token (z_xxx.N from search results), capability slug, or uid"
147482
147475
  ).option("--formatted", "Output formatted capability details").option("--json", "Output raw JSON to stdout (default)").option(
147483
147476
  "--agent <name>",
147484
147477
  "Identify your agent host for this invocation. Overrides auto-detect for this call only."
147485
147478
  ).action(async (identifier, options) => {
147486
147479
  try {
147487
- const { analyticsService, client, stateService } = appContext.services;
147488
- const position = Number.parseInt(identifier, 10);
147489
- const isPosition = !Number.isNaN(position) && position >= 1;
147490
- let capabilityId;
147491
- let searchId;
147492
- if (isPosition) {
147493
- const lastSearch = stateService.loadLastSearch();
147494
- if (!lastSearch) {
147495
- console.error("No recent search found. Run `zero search` first.");
147496
- process.exitCode = 1;
147497
- return;
147498
- }
147499
- const entry = lastSearch.capabilities.find(
147500
- (c) => c.position === position
147501
- );
147502
- if (!entry) {
147503
- console.error(
147504
- `No capability at position ${position}. Positions: ${lastSearch.capabilities.map((c) => c.position).join(", ")}`
147505
- );
147506
- process.exitCode = 1;
147507
- return;
147508
- }
147509
- capabilityId = entry.id;
147510
- searchId = lastSearch.searchId;
147511
- } else {
147512
- capabilityId = identifier;
147513
- const ctx = stateService.findSearchContextByCapability(identifier);
147514
- if (ctx) searchId = ctx.searchId;
147515
- }
147516
- const capability = await client.capabilities.get(capabilityId, {
147517
- searchId
147518
- });
147480
+ const { analyticsService, client } = appContext.services;
147481
+ const capability = await client.capabilities.get(identifier);
147519
147482
  if (options.formatted && !options.json) {
147520
147483
  console.log(formatCapability(capability));
147521
147484
  } else {
147522
147485
  console.log(JSON.stringify(capability, null, 2));
147523
147486
  }
147524
147487
  analyticsService.capture("capability_viewed", {
147525
- // Existing field — raw user input (uid, slug, or position
147526
- // resolved to uid via the last-search cache). Preserved
147527
- // for back-compat with existing dashboards.
147528
- capabilityId,
147529
- // New canonical identifier fields read off the API response,
147530
- // so they are always populated regardless of how the user
147531
- // referenced the capability.
147488
+ capabilityRef: identifier,
147532
147489
  capabilityUid: capability.uid,
147533
147490
  capabilitySlug: capability.slug,
147534
- fromLastSearch: isPosition,
147535
- ...isPosition ? { position } : {},
147536
- ...searchId ? { searchId } : {}
147491
+ fromToken: isShortToken(identifier)
147537
147492
  });
147538
147493
  } catch (err) {
147539
147494
  console.error(err instanceof Error ? err.message : "Get failed");
@@ -148610,7 +148565,7 @@ var formatDetail = (r2) => {
148610
148565
  }
148611
148566
  return lines.join("\n");
148612
148567
  };
148613
- var runsDetailCommand = (appContext) => new Command("detail").description("Show full metadata for a single run").argument("<runId>", "Run ID (run_\u2026)").option("--json", "Output raw JSON to stdout").action(async (runId, _opts, cmd) => {
148568
+ var runsDetailCommand = (appContext) => new Command("detail").description("Show full metadata for a single run (from server)").argument("<runId>", "Run ID (run_\u2026)").option("--json", "Output raw JSON to stdout").action(async (runId, _opts, cmd) => {
148614
148569
  const { json: json3 } = cmd.opts();
148615
148570
  try {
148616
148571
  const { client } = appContext.services;
@@ -148625,9 +148580,10 @@ var runsDetailCommand = (appContext) => new Command("detail").description("Show
148625
148580
  process.exitCode = 1;
148626
148581
  }
148627
148582
  });
148628
- var runsCommand = (appContext) => new Command("runs").description("List your recent capability runs").addHelpText(
148629
- "after",
148630
- `
148583
+ var runsCommand = (appContext) => {
148584
+ const cmd = new Command("runs").description("List your recent capability runs").addHelpText(
148585
+ "after",
148586
+ `
148631
148587
  View your recent capability runs \u2014 status, latency, cost, and payment info.
148632
148588
  A leading [\u2713] means the run already has a review; [ ] means it does not.
148633
148589
 
@@ -148636,52 +148592,55 @@ Examples:
148636
148592
  zero runs --unreviewed # filter to runs without a review
148637
148593
  zero runs --capability translate-en # filter to one capability (uid or slug)
148638
148594
  zero runs --json # full structured output for scripts
148639
- zero runs detail <runId> # full metadata for a single run`
148640
- ).addCommand(runsDetailCommand(appContext)).option("--capability <id>", "Filter by capability uid or slug").option("--unreviewed", "Only show runs without a review").option(
148641
- "--limit <n>",
148642
- "Max rows (1-100, default 25)",
148643
- (v) => Number.parseInt(v, 10)
148644
- ).option("--json", "Output raw JSON to stdout").option(
148645
- "--agent <name>",
148646
- "Identify your agent host for this invocation. Overrides auto-detect for this call only."
148647
- ).action(
148648
- async (options) => {
148649
- try {
148650
- const { client } = appContext.services;
148651
- const result = await client.runs.list({
148652
- capabilityId: options.capability,
148653
- unreviewed: options.unreviewed,
148654
- limit: options.limit
148655
- });
148656
- if (options.json) {
148657
- console.log(JSON.stringify(result, null, 2));
148658
- return;
148659
- }
148660
- if (result.runs.length === 0) {
148661
- console.log("No runs found.");
148662
- return;
148663
- }
148664
- for (const r2 of result.runs) {
148665
- const when = r2.createdAt.toISOString().replace("T", " ").slice(0, 19);
148666
- const mark = r2.reviewed ? "\u2713" : " ";
148667
- const status = r2.status ?? "\u2014";
148668
- const latency = r2.latencyMs != null ? `${r2.latencyMs}ms` : "\u2014";
148669
- const cost = formatCost2(r2.cost);
148670
- const payment = formatPayment(r2.payment);
148671
- console.log(
148672
- `[${mark}] ${r2.uid} ${r2.capabilitySlug} status=${status} ${latency} ${cost} ${payment} ${when}`
148673
- );
148674
- }
148675
- if (result.nextCursor) {
148676
- console.log(`
148595
+ zero runs detail <runId> # full server-side metadata for a run`
148596
+ ).option("--capability <id>", "Filter by capability uid or slug").option("--unreviewed", "Only show runs without a review").option(
148597
+ "--limit <n>",
148598
+ "Max rows (1-100, default 25)",
148599
+ (v) => Number.parseInt(v, 10)
148600
+ ).option("--json", "Output raw JSON to stdout").option(
148601
+ "--agent <name>",
148602
+ "Identify your agent host for this invocation. Overrides auto-detect for this call only."
148603
+ ).action(
148604
+ async (options) => {
148605
+ try {
148606
+ const { client } = appContext.services;
148607
+ const result = await client.runs.list({
148608
+ capabilityId: options.capability,
148609
+ unreviewed: options.unreviewed,
148610
+ limit: options.limit
148611
+ });
148612
+ if (options.json) {
148613
+ console.log(JSON.stringify(result, null, 2));
148614
+ return;
148615
+ }
148616
+ if (result.runs.length === 0) {
148617
+ console.log("No runs found.");
148618
+ return;
148619
+ }
148620
+ for (const r2 of result.runs) {
148621
+ const when = r2.createdAt.toISOString().replace("T", " ").slice(0, 19);
148622
+ const mark = r2.reviewed ? "\u2713" : " ";
148623
+ const status = r2.status ?? "\u2014";
148624
+ const latency = r2.latencyMs != null ? `${r2.latencyMs}ms` : "\u2014";
148625
+ const cost = formatCost2(r2.cost);
148626
+ const payment = formatPayment(r2.payment);
148627
+ console.log(
148628
+ `[${mark}] ${r2.uid} ${r2.capabilitySlug} status=${status} ${latency} ${cost} ${payment} ${when}`
148629
+ );
148630
+ }
148631
+ if (result.nextCursor) {
148632
+ console.log(`
148677
148633
  (more available \u2014 use --limit to see more)`);
148634
+ }
148635
+ } catch (err) {
148636
+ console.error(err instanceof Error ? err.message : "Failed");
148637
+ process.exitCode = 1;
148678
148638
  }
148679
- } catch (err) {
148680
- console.error(err instanceof Error ? err.message : "Failed");
148681
- process.exitCode = 1;
148682
148639
  }
148683
- }
148684
- );
148640
+ );
148641
+ cmd.addCommand(runsDetailCommand(appContext));
148642
+ return cmd;
148643
+ };
148685
148644
 
148686
148645
  // src/commands/search-command.ts
148687
148646
  init_esm_shims();
@@ -148742,7 +148701,7 @@ var searchCommand = (appContext) => new Command("search").description("Search fo
148742
148701
  ).action(
148743
148702
  async (query, options) => {
148744
148703
  try {
148745
- const { analyticsService, client, stateService } = appContext.services;
148704
+ const { analyticsService, client } = appContext.services;
148746
148705
  if (options.protocol && !["x402", "mpp"].includes(options.protocol)) {
148747
148706
  console.error(
148748
148707
  `Invalid protocol "${options.protocol}". Must be x402 or mpp.`
@@ -148811,17 +148770,6 @@ var searchCommand = (appContext) => new Command("search").description("Search fo
148811
148770
  );
148812
148771
  return;
148813
148772
  }
148814
- stateService.saveLastSearch({
148815
- searchId: result.searchId,
148816
- capabilities: result.capabilities.map((c) => ({
148817
- position: c.position,
148818
- id: c.id,
148819
- slug: c.slug,
148820
- url: c.url,
148821
- urlTemplate: c.urlTemplate ?? null,
148822
- displayCostAmount: c.cost.amount
148823
- }))
148824
- });
148825
148773
  console.log(formatSearchResults(result.capabilities));
148826
148774
  if (result.hasMore) {
148827
148775
  console.error(
@@ -148831,7 +148779,7 @@ var searchCommand = (appContext) => new Command("search").description("Search fo
148831
148779
  }
148832
148780
  if (result.capabilities.length > 0) {
148833
148781
  console.error(
148834
- "\n Run `zero get {NUMBER}` to view a capability.\n"
148782
+ "\n Run `zero get <token>` (or `zero get <slug>`) to view a capability.\n"
148835
148783
  );
148836
148784
  }
148837
148785
  } catch (err) {
@@ -154114,105 +154062,6 @@ var AnalyticsService = class {
154114
154062
  }
154115
154063
  };
154116
154064
 
154117
- // src/services/state-service.ts
154118
- init_esm_shims();
154119
- var RECENT_SEARCH_LIMIT = 10;
154120
- var StateService = class {
154121
- constructor(zeroDir) {
154122
- this.zeroDir = zeroDir;
154123
- this.lastSearchPath = join(zeroDir, "last_search.json");
154124
- this.recentSearchesPath = join(zeroDir, "recent_searches.json");
154125
- }
154126
- lastSearchPath;
154127
- recentSearchesPath;
154128
- saveLastSearch = (data) => {
154129
- mkdirSync(this.zeroDir, { recursive: true });
154130
- writeFileSync(this.lastSearchPath, JSON.stringify(data, null, 2));
154131
- const recent = this.loadRecentSearches();
154132
- const filtered = recent.searches.filter(
154133
- (s) => s.searchId !== data.searchId
154134
- );
154135
- const next = [data, ...filtered].slice(0, RECENT_SEARCH_LIMIT);
154136
- writeFileSync(
154137
- this.recentSearchesPath,
154138
- JSON.stringify({ searches: next }, null, 2)
154139
- );
154140
- };
154141
- loadLastSearch = () => {
154142
- try {
154143
- if (!existsSync(this.lastSearchPath)) return null;
154144
- const raw = readFileSync(this.lastSearchPath, "utf8");
154145
- return JSON.parse(raw);
154146
- } catch {
154147
- return null;
154148
- }
154149
- };
154150
- loadRecentSearches = () => {
154151
- try {
154152
- if (!existsSync(this.recentSearchesPath)) {
154153
- const last = this.loadLastSearch();
154154
- return { searches: last ? [last] : [] };
154155
- }
154156
- const raw = readFileSync(this.recentSearchesPath, "utf8");
154157
- const parsed = JSON.parse(raw);
154158
- return { searches: parsed.searches ?? [] };
154159
- } catch {
154160
- return { searches: [] };
154161
- }
154162
- };
154163
- // Walk recent searches newest-first, returning the first one whose
154164
- // results contain `capabilityRef` (matches against uid OR slug).
154165
- // Preferred over "loadLastSearch" for attributing rank — handles the
154166
- // parallel-search case where the most recent search isn't the one being
154167
- // fetched.
154168
- findSearchContextByCapability = (capabilityRef) => {
154169
- const recent = this.loadRecentSearches();
154170
- for (const search of recent.searches) {
154171
- const entry = search.capabilities.find(
154172
- (c) => c.id === capabilityRef || c.slug === capabilityRef
154173
- );
154174
- if (entry) {
154175
- return {
154176
- searchId: search.searchId,
154177
- resultRank: entry.position,
154178
- capabilityId: entry.id,
154179
- capabilityUid: entry.id,
154180
- capabilitySlug: entry.slug ?? null,
154181
- url: entry.url,
154182
- displayCostAmount: entry.displayCostAmount
154183
- };
154184
- }
154185
- }
154186
- return null;
154187
- };
154188
- // URL-based attribution for `zero fetch <url>`. Matches by URL prefix
154189
- // (some capabilities accept query params or path tails the agent appends)
154190
- // across the recent ring, newest match wins. Falls back to a caller-
154191
- // supplied urlTemplate matcher (ADS-509 path-parameterized URLs).
154192
- findSearchContextByUrl = (url4, options) => {
154193
- const recent = this.loadRecentSearches();
154194
- for (const search of recent.searches) {
154195
- const prefixHit = search.capabilities.find((c) => url4.startsWith(c.url));
154196
- const templateHit = prefixHit ?? search.capabilities.find(
154197
- (c) => c.urlTemplate && options?.matchTemplate ? options.matchTemplate(c.urlTemplate) : false
154198
- );
154199
- const entry = templateHit;
154200
- if (entry) {
154201
- return {
154202
- searchId: search.searchId,
154203
- resultRank: entry.position,
154204
- capabilityId: entry.id,
154205
- capabilityUid: entry.id,
154206
- capabilitySlug: entry.slug ?? null,
154207
- url: entry.url,
154208
- displayCostAmount: entry.displayCostAmount
154209
- };
154210
- }
154211
- }
154212
- return null;
154213
- };
154214
- };
154215
-
154216
154065
  // src/services/wallet-migration-service.ts
154217
154066
  init_esm_shims();
154218
154067
  var USDC_BASE3 = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913";
@@ -154496,7 +154345,6 @@ var getServices = async (env) => {
154496
154345
  const walletMigrationService = new WalletMigrationService(
154497
154346
  byoAccount ?? legacyAccount
154498
154347
  );
154499
- const stateService = new StateService(zeroDir);
154500
154348
  const analyticsService = new AnalyticsService({
154501
154349
  walletAddress: account?.address ?? null,
154502
154350
  configPath,
@@ -154509,7 +154357,6 @@ var getServices = async (env) => {
154509
154357
  analyticsService,
154510
154358
  client,
154511
154359
  walletMigrationService,
154512
- stateService,
154513
154360
  lowBalanceWarning,
154514
154361
  // Canonical resolved wallet address across the CLI — covers BYO,
154515
154362
  // managed (Privy embedded) and legacy paths via the precedence above.