@zeroxyz/sdk 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/README.md +6 -3
- package/dist/{chunk-3YKWO4B2.js → chunk-26IHUYNK.js} +1560 -1552
- package/dist/chunk-26IHUYNK.js.map +1 -0
- package/dist/{chunk-UIK7QKNQ.cjs → chunk-2HZJHVDN.cjs} +1560 -1551
- package/dist/chunk-2HZJHVDN.cjs.map +1 -0
- package/dist/{client-CDk1PVU-.d.cts → client-7zp9ZyYd.d.cts} +20 -1
- package/dist/{client-CDk1PVU-.d.ts → client-7zp9ZyYd.d.ts} +20 -1
- package/dist/index.cjs +39 -35
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/testing.cjs +2 -2
- package/dist/testing.d.cts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-3YKWO4B2.js.map +0 -1
- package/dist/chunk-UIK7QKNQ.cjs.map +0 -1
|
@@ -9,8 +9,8 @@ import { wrapFetchWithPayment } from '@x402/fetch';
|
|
|
9
9
|
import { Receipt, Challenge } from 'mppx';
|
|
10
10
|
import { Mppx, tempo } from 'mppx/client';
|
|
11
11
|
import { base, baseSepolia } from 'viem/chains';
|
|
12
|
-
import { z } from 'zod';
|
|
13
12
|
import { createHash } from 'crypto';
|
|
13
|
+
import { z } from 'zod';
|
|
14
14
|
|
|
15
15
|
// src/auth/managed-account.ts
|
|
16
16
|
var createManagedAccount = (client, address) => toAccount({
|
|
@@ -1213,1683 +1213,1688 @@ var Payments = class {
|
|
|
1213
1213
|
};
|
|
1214
1214
|
};
|
|
1215
1215
|
};
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
var
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
var signResponseSchema = z.object({
|
|
1258
|
-
signature: z.string().regex(/^0x[0-9a-fA-F]+$/, "must be 0x-prefixed hex"),
|
|
1259
|
-
walletAddress: z.string().regex(/^0x[0-9a-fA-F]{40}$/, "must be 0x-prefixed 40-char hex address")
|
|
1260
|
-
});
|
|
1261
|
-
|
|
1262
|
-
// src/schemas/device.ts
|
|
1263
|
-
var deviceStartResponseSchema = z.object({
|
|
1264
|
-
deviceCode: z.string(),
|
|
1265
|
-
userCode: z.string(),
|
|
1266
|
-
verificationUri: z.string(),
|
|
1267
|
-
pollInterval: z.number(),
|
|
1268
|
-
expiresAt: z.number()
|
|
1269
|
-
});
|
|
1270
|
-
var devicePollResponseSchema = z.union([
|
|
1271
|
-
// userCode + verificationUri are optional: older API deployments don't
|
|
1272
|
-
// echo them on pending.
|
|
1273
|
-
z.object({
|
|
1274
|
-
error: z.literal("authorization_pending"),
|
|
1275
|
-
userCode: z.string().optional(),
|
|
1276
|
-
verificationUri: z.string().optional()
|
|
1277
|
-
}),
|
|
1278
|
-
z.object({ error: z.literal("expired_token") }),
|
|
1279
|
-
z.object({
|
|
1280
|
-
accessToken: z.string(),
|
|
1281
|
-
refreshToken: z.string(),
|
|
1282
|
-
expiresIn: z.number(),
|
|
1283
|
-
user: internalUserDtoSchema
|
|
1284
|
-
})
|
|
1285
|
-
]);
|
|
1286
|
-
var sessionExchangeResponseSchema = z.object({
|
|
1287
|
-
token: z.string(),
|
|
1288
|
-
expiresAt: z.number()
|
|
1289
|
-
});
|
|
1290
|
-
var refreshResponseSchema = z.object({
|
|
1291
|
-
accessToken: z.string(),
|
|
1292
|
-
refreshToken: z.string()
|
|
1293
|
-
});
|
|
1294
|
-
var tryRefreshSession = async (client, callerSignal) => {
|
|
1295
|
-
const credentials = client.getCredentials();
|
|
1296
|
-
if (credentials.kind !== "session") return false;
|
|
1297
|
-
if (!credentials.refreshToken) return false;
|
|
1298
|
-
const url = `${client.baseUrl.replace(/\/$/, "")}/v1/auth/refresh`;
|
|
1299
|
-
const timeoutSignal = AbortSignal.timeout(client.timeout);
|
|
1300
|
-
const signal = callerSignal ? AbortSignal.any([callerSignal, timeoutSignal]) : timeoutSignal;
|
|
1301
|
-
let response;
|
|
1302
|
-
try {
|
|
1303
|
-
response = await client.fetchImpl(url, {
|
|
1304
|
-
method: "POST",
|
|
1305
|
-
headers: { "content-type": "application/json" },
|
|
1306
|
-
body: JSON.stringify({ refreshToken: credentials.refreshToken }),
|
|
1307
|
-
signal
|
|
1308
|
-
});
|
|
1309
|
-
} catch {
|
|
1310
|
-
return false;
|
|
1216
|
+
var isShortToken = (id) => id.startsWith("z_") && id.indexOf(".", 2) !== -1;
|
|
1217
|
+
var MAX_402_PROBE_BYTES = 1024 * 1024;
|
|
1218
|
+
var MAX_RESPONSE_BYTES = 10 * 1024 * 1024;
|
|
1219
|
+
var TEXT_CONTENT_TYPES = [
|
|
1220
|
+
"application/json",
|
|
1221
|
+
"application/xml",
|
|
1222
|
+
"application/javascript",
|
|
1223
|
+
"application/ecmascript",
|
|
1224
|
+
"application/x-www-form-urlencoded",
|
|
1225
|
+
"text/",
|
|
1226
|
+
"+json",
|
|
1227
|
+
"+xml"
|
|
1228
|
+
];
|
|
1229
|
+
var MPP_SCHEME_TOKENS = /* @__PURE__ */ new Set(["payment", "mpp"]);
|
|
1230
|
+
var splitWwwAuthenticateChallenges = (header) => {
|
|
1231
|
+
const chunks = [];
|
|
1232
|
+
let buf = "";
|
|
1233
|
+
let inQuotes = false;
|
|
1234
|
+
for (let i = 0; i < header.length; i++) {
|
|
1235
|
+
const ch = header[i];
|
|
1236
|
+
if (inQuotes) {
|
|
1237
|
+
if (ch === "\\" && i + 1 < header.length) {
|
|
1238
|
+
buf += ch + header[i + 1];
|
|
1239
|
+
i++;
|
|
1240
|
+
continue;
|
|
1241
|
+
}
|
|
1242
|
+
if (ch === '"') inQuotes = false;
|
|
1243
|
+
buf += ch;
|
|
1244
|
+
continue;
|
|
1245
|
+
}
|
|
1246
|
+
if (ch === '"') {
|
|
1247
|
+
inQuotes = true;
|
|
1248
|
+
buf += ch;
|
|
1249
|
+
continue;
|
|
1250
|
+
}
|
|
1251
|
+
if (ch === ",") {
|
|
1252
|
+
chunks.push(buf);
|
|
1253
|
+
buf = "";
|
|
1254
|
+
continue;
|
|
1255
|
+
}
|
|
1256
|
+
buf += ch;
|
|
1311
1257
|
}
|
|
1312
|
-
if (
|
|
1313
|
-
|
|
1258
|
+
if (buf.length > 0) chunks.push(buf);
|
|
1259
|
+
return chunks;
|
|
1260
|
+
};
|
|
1261
|
+
var isTextContentType = (contentType) => {
|
|
1262
|
+
if (!contentType) return true;
|
|
1263
|
+
const lower = contentType.toLowerCase();
|
|
1264
|
+
return TEXT_CONTENT_TYPES.some((marker) => lower.includes(marker));
|
|
1265
|
+
};
|
|
1266
|
+
var redactUrl = (url) => {
|
|
1314
1267
|
try {
|
|
1315
|
-
|
|
1268
|
+
const u = new URL(url);
|
|
1269
|
+
return `${u.origin}${u.pathname}`;
|
|
1316
1270
|
} catch {
|
|
1317
|
-
return
|
|
1271
|
+
return "<invalid url>";
|
|
1318
1272
|
}
|
|
1319
|
-
const parsed = refreshResponseSchema.safeParse(body);
|
|
1320
|
-
if (!parsed.success) return false;
|
|
1321
|
-
await client.applyRefreshedTokens({
|
|
1322
|
-
accessToken: parsed.data.accessToken,
|
|
1323
|
-
refreshToken: parsed.data.refreshToken
|
|
1324
|
-
});
|
|
1325
|
-
return true;
|
|
1326
|
-
};
|
|
1327
|
-
var buildSessionAuthHeaders = (accessToken) => ({
|
|
1328
|
-
authorization: `Bearer ${accessToken}`
|
|
1329
|
-
});
|
|
1330
|
-
var buildCanonicalMessage = (method, pathWithQuery, body, timestamp, nonce) => {
|
|
1331
|
-
const bodyHash = createHash("sha256").update(body ?? "").digest("hex");
|
|
1332
|
-
return `${method}:${pathWithQuery}:${bodyHash}:${timestamp}:${nonce}`;
|
|
1333
1273
|
};
|
|
1334
|
-
var
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
body,
|
|
1341
|
-
timestamp,
|
|
1342
|
-
nonce
|
|
1274
|
+
var ECHO_DETECT_MIN_LEN = 16;
|
|
1275
|
+
var ECHO_DETECT_MAX_SCAN = 64 * 1024;
|
|
1276
|
+
var requestBodyEchoed = (candidate, requestBody) => {
|
|
1277
|
+
if (requestBody === void 0) return false;
|
|
1278
|
+
const asString = typeof requestBody === "string" ? requestBody : new TextDecoder("utf-8", { fatal: false }).decode(
|
|
1279
|
+
requestBody.byteLength > ECHO_DETECT_MAX_SCAN ? requestBody.subarray(0, ECHO_DETECT_MAX_SCAN) : requestBody
|
|
1343
1280
|
);
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
};
|
|
1351
|
-
};
|
|
1352
|
-
|
|
1353
|
-
// src/transport/http.ts
|
|
1354
|
-
var RETRYABLE_STATUS = /* @__PURE__ */ new Set([408, 502, 503, 504]);
|
|
1355
|
-
var sleep = (ms, signal) => new Promise((resolve, reject) => {
|
|
1356
|
-
if (signal?.aborted) {
|
|
1357
|
-
reject(new DOMException("Aborted", "AbortError"));
|
|
1358
|
-
return;
|
|
1359
|
-
}
|
|
1360
|
-
const timer = setTimeout(() => {
|
|
1361
|
-
signal?.removeEventListener("abort", onAbort);
|
|
1362
|
-
resolve();
|
|
1363
|
-
}, ms);
|
|
1364
|
-
const onAbort = () => {
|
|
1365
|
-
clearTimeout(timer);
|
|
1366
|
-
reject(new DOMException("Aborted", "AbortError"));
|
|
1367
|
-
};
|
|
1368
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1369
|
-
});
|
|
1370
|
-
var buildPathWithQuery = (path, query) => {
|
|
1371
|
-
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
|
1372
|
-
if (!query) return normalizedPath;
|
|
1373
|
-
const params = new URLSearchParams();
|
|
1374
|
-
for (const [key, value] of Object.entries(query)) {
|
|
1375
|
-
if (value !== void 0) params.set(key, String(value));
|
|
1376
|
-
}
|
|
1377
|
-
const qs = params.toString();
|
|
1378
|
-
return qs ? `${normalizedPath}?${qs}` : normalizedPath;
|
|
1379
|
-
};
|
|
1380
|
-
var joinUrl = (baseUrl, pathWithQuery) => {
|
|
1381
|
-
const base3 = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
|
|
1382
|
-
return `${base3}${pathWithQuery}`;
|
|
1383
|
-
};
|
|
1384
|
-
var buildHeaders = async (client, method, pathWithQuery, hasBody, bodyStr) => {
|
|
1385
|
-
const headers = { ...client.defaultHeaders };
|
|
1386
|
-
if (hasBody) headers["content-type"] = "application/json";
|
|
1387
|
-
headers["x-zero-sdk-version"] = SDK_VERSION;
|
|
1388
|
-
if (client.apiVersion) headers["x-zero-api-version"] = client.apiVersion;
|
|
1389
|
-
const credentials = client.getCredentials();
|
|
1390
|
-
if (credentials.kind === "session") {
|
|
1391
|
-
Object.assign(headers, buildSessionAuthHeaders(credentials.accessToken));
|
|
1392
|
-
} else if (credentials.kind === "account") {
|
|
1393
|
-
const signed = await buildAccountAuthHeaders(
|
|
1394
|
-
credentials.account,
|
|
1395
|
-
method,
|
|
1396
|
-
pathWithQuery,
|
|
1397
|
-
bodyStr
|
|
1398
|
-
);
|
|
1399
|
-
Object.assign(headers, signed);
|
|
1281
|
+
if (asString.length < ECHO_DETECT_MIN_LEN) return false;
|
|
1282
|
+
const scanLen = Math.min(asString.length, ECHO_DETECT_MAX_SCAN);
|
|
1283
|
+
for (let i = 0; i + ECHO_DETECT_MIN_LEN <= scanLen; i++) {
|
|
1284
|
+
if (candidate.includes(asString.slice(i, i + ECHO_DETECT_MIN_LEN))) {
|
|
1285
|
+
return true;
|
|
1286
|
+
}
|
|
1400
1287
|
}
|
|
1401
|
-
return
|
|
1288
|
+
return false;
|
|
1402
1289
|
};
|
|
1403
|
-
var
|
|
1290
|
+
var cancelBody2 = (response) => {
|
|
1404
1291
|
try {
|
|
1405
|
-
|
|
1292
|
+
response.body?.cancel().catch(() => {
|
|
1293
|
+
});
|
|
1406
1294
|
} catch {
|
|
1407
|
-
try {
|
|
1408
|
-
return await response.text();
|
|
1409
|
-
} catch {
|
|
1410
|
-
return void 0;
|
|
1411
|
-
}
|
|
1412
1295
|
}
|
|
1413
1296
|
};
|
|
1414
|
-
var
|
|
1415
|
-
if (
|
|
1416
|
-
|
|
1297
|
+
var detectPaymentRequirement = async (response) => {
|
|
1298
|
+
if (response.status !== 402) return null;
|
|
1299
|
+
const x402Header = response.headers.get("payment-required") ?? response.headers.get("x-payment-required");
|
|
1300
|
+
if (x402Header) {
|
|
1301
|
+
cancelBody2(response);
|
|
1302
|
+
try {
|
|
1303
|
+
const decoded = JSON.parse(
|
|
1304
|
+
Buffer.from(x402Header, "base64").toString("utf8")
|
|
1305
|
+
);
|
|
1306
|
+
return { protocol: "x402", raw: decoded };
|
|
1307
|
+
} catch {
|
|
1308
|
+
return { protocol: "x402", raw: { encoded: x402Header } };
|
|
1309
|
+
}
|
|
1417
1310
|
}
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
const
|
|
1423
|
-
|
|
1424
|
-
const bodyStr = hasBody ? JSON.stringify(
|
|
1425
|
-
opts.body,
|
|
1426
|
-
(_k, v) => typeof v === "bigint" ? v.toString() : v
|
|
1427
|
-
) : void 0;
|
|
1428
|
-
const configuredFetch = buildConfiguredFetch(client);
|
|
1429
|
-
const performOnce = async () => configuredFetch(url, {
|
|
1430
|
-
method: opts.method,
|
|
1431
|
-
headers: await buildHeaders(
|
|
1432
|
-
client,
|
|
1433
|
-
opts.method,
|
|
1434
|
-
pathWithQuery,
|
|
1435
|
-
hasBody,
|
|
1436
|
-
bodyStr
|
|
1437
|
-
),
|
|
1438
|
-
body: bodyStr,
|
|
1439
|
-
signal: opts.signal
|
|
1440
|
-
});
|
|
1441
|
-
let refreshAttempted = false;
|
|
1442
|
-
const executeWithRefresh = async () => {
|
|
1443
|
-
const response = await performOnce();
|
|
1444
|
-
if (response.status !== 401 || refreshAttempted) return response;
|
|
1445
|
-
const credentials = client.getCredentials();
|
|
1446
|
-
if (credentials.kind !== "session") return response;
|
|
1447
|
-
refreshAttempted = true;
|
|
1448
|
-
const refreshed = await client.refreshSessionOnce(opts.signal);
|
|
1449
|
-
if (!refreshed) return response;
|
|
1450
|
-
return await performOnce();
|
|
1451
|
-
};
|
|
1452
|
-
let lastNetworkError;
|
|
1453
|
-
for (let attempt = 0; attempt <= client.maxRetries; attempt++) {
|
|
1454
|
-
let response;
|
|
1311
|
+
const declaredLen = Number.parseInt(
|
|
1312
|
+
response.headers.get("content-length") ?? "",
|
|
1313
|
+
10
|
|
1314
|
+
);
|
|
1315
|
+
const bodyFits = !Number.isFinite(declaredLen) || declaredLen <= MAX_402_PROBE_BYTES;
|
|
1316
|
+
if (bodyFits) {
|
|
1455
1317
|
try {
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
try {
|
|
1462
|
-
await sleep(2 ** attempt * 200, opts.signal);
|
|
1463
|
-
} catch (sleepErr) {
|
|
1464
|
-
throw new ZeroApiError("Request aborted by caller", {
|
|
1465
|
-
status: 0,
|
|
1466
|
-
cause: sleepErr
|
|
1467
|
-
});
|
|
1318
|
+
const text = await readClippedText(response, MAX_402_PROBE_BYTES);
|
|
1319
|
+
if (text) {
|
|
1320
|
+
const parsed = JSON.parse(text);
|
|
1321
|
+
if (looksLikeX402Body(parsed)) {
|
|
1322
|
+
return { protocol: "x402", raw: parsed };
|
|
1468
1323
|
}
|
|
1469
|
-
continue;
|
|
1470
|
-
}
|
|
1471
|
-
throw new ZeroApiError("Network request failed", {
|
|
1472
|
-
status: 0,
|
|
1473
|
-
cause: e
|
|
1474
|
-
});
|
|
1475
|
-
}
|
|
1476
|
-
if (response.ok) {
|
|
1477
|
-
let text;
|
|
1478
|
-
try {
|
|
1479
|
-
text = await response.text();
|
|
1480
|
-
} catch (e) {
|
|
1481
|
-
throw new ZeroApiError("Response body read failed", {
|
|
1482
|
-
status: response.status,
|
|
1483
|
-
cause: e
|
|
1484
|
-
});
|
|
1485
|
-
}
|
|
1486
|
-
let json;
|
|
1487
|
-
try {
|
|
1488
|
-
json = JSON.parse(text);
|
|
1489
|
-
} catch {
|
|
1490
1324
|
}
|
|
1491
|
-
|
|
1492
|
-
if (!parsed.success) {
|
|
1493
|
-
throw new ZeroValidationError(
|
|
1494
|
-
`Response did not match expected schema for ${opts.method} ${opts.path}`,
|
|
1495
|
-
{ cause: parsed.error, body: text }
|
|
1496
|
-
);
|
|
1497
|
-
}
|
|
1498
|
-
return parsed.data;
|
|
1325
|
+
} catch {
|
|
1499
1326
|
}
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
}
|
|
1509
|
-
continue;
|
|
1327
|
+
}
|
|
1328
|
+
const wwwAuth = response.headers.get("www-authenticate");
|
|
1329
|
+
if (wwwAuth) {
|
|
1330
|
+
const schemes = splitWwwAuthenticateChallenges(wwwAuth).map(
|
|
1331
|
+
(challenge) => challenge.trim().split(/\s+/)[0]?.toLowerCase() ?? ""
|
|
1332
|
+
);
|
|
1333
|
+
if (schemes.some((scheme) => MPP_SCHEME_TOKENS.has(scheme))) {
|
|
1334
|
+
cancelBody2(response);
|
|
1335
|
+
return { protocol: "mpp", raw: { "www-authenticate": wwwAuth } };
|
|
1510
1336
|
}
|
|
1511
|
-
const errorBody = await parseErrorBody(response);
|
|
1512
|
-
const requestId = response.headers.get("x-request-id") ?? void 0;
|
|
1513
|
-
throw new ZeroApiError(errorMessageFromBody(errorBody, response.status), {
|
|
1514
|
-
status: response.status,
|
|
1515
|
-
requestId,
|
|
1516
|
-
body: errorBody
|
|
1517
|
-
});
|
|
1518
1337
|
}
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
cause: lastNetworkError
|
|
1522
|
-
});
|
|
1338
|
+
cancelBody2(response);
|
|
1339
|
+
return { protocol: "unknown", raw: {} };
|
|
1523
1340
|
};
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
},
|
|
1537
|
-
deviceStartResponseSchema
|
|
1538
|
-
);
|
|
1539
|
-
poll = async (deviceCode, opts = {}) => {
|
|
1540
|
-
const parsed = await request(
|
|
1541
|
-
this.client,
|
|
1542
|
-
{
|
|
1543
|
-
method: "POST",
|
|
1544
|
-
path: "/v1/auth/device/poll",
|
|
1545
|
-
body: { deviceCode },
|
|
1546
|
-
signal: opts.signal
|
|
1547
|
-
},
|
|
1548
|
-
devicePollResponseSchema
|
|
1549
|
-
);
|
|
1550
|
-
if ("error" in parsed) {
|
|
1551
|
-
return parsed.error === "authorization_pending" ? {
|
|
1552
|
-
status: "pending",
|
|
1553
|
-
userCode: parsed.userCode,
|
|
1554
|
-
verificationUri: parsed.verificationUri
|
|
1555
|
-
} : { status: "expired" };
|
|
1341
|
+
var readClippedBytes = async (response, max) => {
|
|
1342
|
+
if (response.body === null) return new Uint8Array(0);
|
|
1343
|
+
const reader = response.body.getReader();
|
|
1344
|
+
const chunks = [];
|
|
1345
|
+
let total = 0;
|
|
1346
|
+
try {
|
|
1347
|
+
while (total < max) {
|
|
1348
|
+
const { done, value } = await reader.read();
|
|
1349
|
+
if (done) break;
|
|
1350
|
+
if (!value) continue;
|
|
1351
|
+
chunks.push(value);
|
|
1352
|
+
total += value.byteLength;
|
|
1556
1353
|
}
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1354
|
+
} finally {
|
|
1355
|
+
try {
|
|
1356
|
+
await reader.cancel();
|
|
1357
|
+
} catch {
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
const merged = new Uint8Array(Math.min(total, max));
|
|
1361
|
+
let offset = 0;
|
|
1362
|
+
for (const c of chunks) {
|
|
1363
|
+
const take = Math.min(c.byteLength, max - offset);
|
|
1364
|
+
merged.set(c.subarray(0, take), offset);
|
|
1365
|
+
offset += take;
|
|
1366
|
+
if (offset >= max) break;
|
|
1367
|
+
}
|
|
1368
|
+
return merged;
|
|
1565
1369
|
};
|
|
1566
|
-
var
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
if (typeof message.raw === "string") return message.raw;
|
|
1570
|
-
return bytesToHex(message.raw);
|
|
1370
|
+
var readClippedText = async (response, max) => {
|
|
1371
|
+
const bytes = await readClippedBytes(response, max);
|
|
1372
|
+
return new TextDecoder().decode(bytes);
|
|
1571
1373
|
};
|
|
1572
|
-
var
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
);
|
|
1374
|
+
var parseResponseBody = async (response, max) => {
|
|
1375
|
+
const contentType = response.headers.get("content-type");
|
|
1376
|
+
const bytes = await readClippedBytes(response, max + 1);
|
|
1377
|
+
const truncated = bytes.byteLength > max;
|
|
1378
|
+
const view = truncated ? bytes.subarray(0, max) : bytes;
|
|
1379
|
+
const buf = Buffer.from(view.buffer, view.byteOffset, view.byteLength);
|
|
1380
|
+
if (!isTextContentType(contentType)) {
|
|
1381
|
+
const base64 = buf.toString("base64");
|
|
1382
|
+
return {
|
|
1383
|
+
body: base64,
|
|
1384
|
+
bodyRaw: base64,
|
|
1385
|
+
bodyEncoding: "base64",
|
|
1386
|
+
truncated
|
|
1387
|
+
};
|
|
1580
1388
|
}
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1389
|
+
const text = buf.toString("utf8");
|
|
1390
|
+
if (contentType?.toLowerCase().includes("json")) {
|
|
1391
|
+
try {
|
|
1392
|
+
return { body: JSON.parse(text), bodyRaw: text, truncated };
|
|
1393
|
+
} catch {
|
|
1394
|
+
}
|
|
1587
1395
|
}
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1396
|
+
return { body: text, bodyRaw: text, truncated };
|
|
1397
|
+
};
|
|
1398
|
+
var UPSTREAM_ERROR_FIELDS = [
|
|
1399
|
+
"error",
|
|
1400
|
+
"message",
|
|
1401
|
+
"detail",
|
|
1402
|
+
"reason",
|
|
1403
|
+
"error_description"
|
|
1404
|
+
];
|
|
1405
|
+
var UPSTREAM_ERROR_MAX_LEN = 200;
|
|
1406
|
+
var ERROR_SNIPPET_BYTES = 500;
|
|
1407
|
+
var clampUpstreamMessage = (value) => {
|
|
1408
|
+
const trimmed = value.trim();
|
|
1409
|
+
if (!trimmed) return void 0;
|
|
1410
|
+
return trimmed.length > UPSTREAM_ERROR_MAX_LEN ? `${trimmed.slice(0, UPSTREAM_ERROR_MAX_LEN)}\u2026` : trimmed;
|
|
1411
|
+
};
|
|
1412
|
+
var tryParseJson = (text) => {
|
|
1413
|
+
try {
|
|
1414
|
+
return JSON.parse(text);
|
|
1415
|
+
} catch {
|
|
1416
|
+
return null;
|
|
1417
|
+
}
|
|
1418
|
+
};
|
|
1419
|
+
var extractUpstreamErrorMessage = (body) => {
|
|
1420
|
+
const parsed = tryParseJson(body);
|
|
1421
|
+
if (parsed === null || typeof parsed !== "object") {
|
|
1422
|
+
return clampUpstreamMessage(body);
|
|
1423
|
+
}
|
|
1424
|
+
const record = parsed;
|
|
1425
|
+
for (const field of UPSTREAM_ERROR_FIELDS) {
|
|
1426
|
+
const value = record[field];
|
|
1427
|
+
if (typeof value === "string" && value.length > 0) {
|
|
1428
|
+
return clampUpstreamMessage(value);
|
|
1429
|
+
}
|
|
1430
|
+
if (value && typeof value === "object") {
|
|
1431
|
+
const nested = value.message;
|
|
1432
|
+
if (typeof nested === "string" && nested.length > 0) {
|
|
1433
|
+
return clampUpstreamMessage(nested);
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
return void 0;
|
|
1438
|
+
};
|
|
1439
|
+
var buildUpstreamError = (status, bodyRaw, bodyEncoding) => {
|
|
1440
|
+
if (status >= 200 && status < 300) return void 0;
|
|
1441
|
+
if (bodyRaw === null || bodyRaw.length === 0) return void 0;
|
|
1442
|
+
if (bodyEncoding === "base64") return void 0;
|
|
1443
|
+
const snippet = bodyRaw.slice(0, ERROR_SNIPPET_BYTES);
|
|
1444
|
+
return {
|
|
1445
|
+
status,
|
|
1446
|
+
message: extractUpstreamErrorMessage(bodyRaw),
|
|
1447
|
+
snippetHash: createHash("sha256").update(snippet).digest("hex"),
|
|
1448
|
+
snippetLength: snippet.length
|
|
1449
|
+
};
|
|
1450
|
+
};
|
|
1451
|
+
var computeOutcome = (status, payment, warnings) => {
|
|
1452
|
+
if (warnings.includes(FETCH_WARNINGS.mppSessionCloseFailed))
|
|
1453
|
+
return "payment_close_failed";
|
|
1454
|
+
if (status === 402 && payment !== null) return "payment_rejected";
|
|
1455
|
+
if (status === 402) return "payment_failed";
|
|
1456
|
+
if (status >= 400) return "server_error";
|
|
1457
|
+
return "success";
|
|
1458
|
+
};
|
|
1459
|
+
var errorFetchResult = (startedAt, error, skipReason, capabilityIdRequested, outcome, status = null) => {
|
|
1460
|
+
const result = {
|
|
1461
|
+
runId: null,
|
|
1462
|
+
ok: false,
|
|
1463
|
+
status,
|
|
1464
|
+
latencyMs: Date.now() - startedAt,
|
|
1465
|
+
payment: null,
|
|
1466
|
+
outcome,
|
|
1467
|
+
body: null,
|
|
1468
|
+
bodyRaw: null,
|
|
1469
|
+
error
|
|
1470
|
+
};
|
|
1471
|
+
if (capabilityIdRequested) result.runTrackingSkipped = [skipReason];
|
|
1472
|
+
return result;
|
|
1473
|
+
};
|
|
1474
|
+
var resolveRecordingClient = (client, opts) => {
|
|
1475
|
+
const credentials = client.getCredentials();
|
|
1476
|
+
const recordingClient = opts.account ? client.withAccount(opts.account) : client;
|
|
1477
|
+
const canRecord = opts.account !== void 0 || credentials.kind === "account" || credentials.kind === "session";
|
|
1478
|
+
return { recordingClient, canRecord };
|
|
1479
|
+
};
|
|
1480
|
+
var recordErrorFetchRun = async (client, opts, result, capabilityIdOverride) => {
|
|
1481
|
+
const effectiveCapId = opts.capabilityId;
|
|
1482
|
+
if (effectiveCapId === void 0) return;
|
|
1483
|
+
const { recordingClient, canRecord } = resolveRecordingClient(client, opts);
|
|
1484
|
+
if (!canRecord) return;
|
|
1485
|
+
const fetchOriginForRecord = opts.capabilityId === void 0 ? "direct_url" : isShortToken(effectiveCapId) ? "from_search" : opts.fetchOrigin;
|
|
1486
|
+
try {
|
|
1487
|
+
const created = await recordingClient.runs.create({
|
|
1488
|
+
capabilityId: effectiveCapId,
|
|
1489
|
+
...opts.searchId && { searchId: opts.searchId },
|
|
1490
|
+
...fetchOriginForRecord && { fetchOrigin: fetchOriginForRecord },
|
|
1491
|
+
...result.status !== null && { status: result.status },
|
|
1492
|
+
latencyMs: result.latencyMs,
|
|
1493
|
+
...opts.requestSchema && { requestSchema: opts.requestSchema }
|
|
1494
|
+
});
|
|
1495
|
+
result.runId = created.runId;
|
|
1496
|
+
delete result.runTrackingSkipped;
|
|
1497
|
+
} catch {
|
|
1498
|
+
}
|
|
1499
|
+
};
|
|
1500
|
+
var recordTimeoutRun = async (client, opts, err, startedAt, capabilityIdRequested, capabilityIdOverride) => {
|
|
1501
|
+
const result = errorFetchResult(
|
|
1502
|
+
startedAt,
|
|
1503
|
+
err.message,
|
|
1504
|
+
"timeout",
|
|
1505
|
+
capabilityIdRequested,
|
|
1506
|
+
"network_error"
|
|
1603
1507
|
);
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1508
|
+
await recordErrorFetchRun(client, opts, result);
|
|
1509
|
+
};
|
|
1510
|
+
var sniffJsonShapeBytes = (buf) => {
|
|
1511
|
+
let i = 0;
|
|
1512
|
+
if (buf.length >= 3 && buf[0] === 239 && buf[1] === 187 && buf[2] === 191) {
|
|
1513
|
+
i = 3;
|
|
1514
|
+
}
|
|
1515
|
+
while (i < buf.length && (buf[i] === 32 || buf[i] === 9 || buf[i] === 10 || buf[i] === 13)) {
|
|
1516
|
+
i++;
|
|
1517
|
+
}
|
|
1518
|
+
if (i >= buf.length) return false;
|
|
1519
|
+
return buf[i] === 123 || buf[i] === 91;
|
|
1520
|
+
};
|
|
1521
|
+
var withDefaultContentType = (headers, body) => {
|
|
1522
|
+
if (!headers && body === void 0) return void 0;
|
|
1523
|
+
const next = headers ? { ...headers } : {};
|
|
1524
|
+
if (body === void 0) return next;
|
|
1525
|
+
const hasContentType = Object.keys(next).some(
|
|
1526
|
+
(k) => k.toLowerCase() === "content-type"
|
|
1611
1527
|
);
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1528
|
+
if (!hasContentType) {
|
|
1529
|
+
next["content-type"] = typeof body === "string" || sniffJsonShapeBytes(body) ? "application/json" : "application/octet-stream";
|
|
1530
|
+
}
|
|
1531
|
+
return next;
|
|
1532
|
+
};
|
|
1533
|
+
var clientFetch = async (client, url, opts = {}) => {
|
|
1534
|
+
const startedAt = Date.now();
|
|
1535
|
+
const capabilityIdRequested = opts.capabilityId !== void 0;
|
|
1536
|
+
const normalized = normalizeTransportEnvelopeRequest(url, {
|
|
1537
|
+
method: opts.method,
|
|
1538
|
+
body: opts.body
|
|
1539
|
+
});
|
|
1540
|
+
const requestUrl = normalized.url;
|
|
1541
|
+
const requestBody = normalized.body;
|
|
1542
|
+
const method = normalized.method ?? (requestBody !== void 0 ? "POST" : "GET");
|
|
1543
|
+
const { canRecord: canRecordForResolve } = resolveRecordingClient(
|
|
1544
|
+
client,
|
|
1545
|
+
opts
|
|
1624
1546
|
);
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
await request(
|
|
1633
|
-
this.client,
|
|
1634
|
-
{
|
|
1635
|
-
method: "POST",
|
|
1636
|
-
path: "/v1/auth/logout",
|
|
1637
|
-
body: { refreshToken },
|
|
1638
|
-
signal: opts.signal
|
|
1639
|
-
},
|
|
1640
|
-
z.unknown()
|
|
1641
|
-
);
|
|
1547
|
+
const resolvePromise = !capabilityIdRequested && canRecordForResolve ? client.capabilities.resolveByUrl(redactUrl(requestUrl), method).catch(() => null) : Promise.resolve(null);
|
|
1548
|
+
const probeHeaders = withDefaultContentType(opts.headers, requestBody);
|
|
1549
|
+
const requestInit = {
|
|
1550
|
+
method,
|
|
1551
|
+
headers: probeHeaders,
|
|
1552
|
+
body: requestBody,
|
|
1553
|
+
signal: opts.signal
|
|
1642
1554
|
};
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1555
|
+
const configuredFetch = buildConfiguredFetch(client, {
|
|
1556
|
+
timeoutMs: opts.timeoutMs
|
|
1557
|
+
});
|
|
1558
|
+
let response;
|
|
1559
|
+
let payment = null;
|
|
1560
|
+
const skipReasons = [];
|
|
1561
|
+
const warnings = [];
|
|
1562
|
+
try {
|
|
1563
|
+
response = await configuredFetch(requestUrl, requestInit);
|
|
1564
|
+
} catch (err) {
|
|
1565
|
+
if (err instanceof ZeroTimeoutError) {
|
|
1566
|
+
await recordTimeoutRun(
|
|
1567
|
+
client,
|
|
1568
|
+
opts,
|
|
1569
|
+
err,
|
|
1570
|
+
startedAt,
|
|
1571
|
+
capabilityIdRequested
|
|
1660
1572
|
);
|
|
1573
|
+
throw err;
|
|
1661
1574
|
}
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
const
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1575
|
+
if (err instanceof ZeroError) throw err;
|
|
1576
|
+
const errMessage = err instanceof Error ? err.message : String(err);
|
|
1577
|
+
const result2 = errorFetchResult(
|
|
1578
|
+
startedAt,
|
|
1579
|
+
errMessage,
|
|
1580
|
+
"fetch_failed_before_response",
|
|
1581
|
+
capabilityIdRequested,
|
|
1582
|
+
"network_error"
|
|
1583
|
+
);
|
|
1584
|
+
await recordErrorFetchRun(client, opts, result2);
|
|
1585
|
+
return result2;
|
|
1586
|
+
}
|
|
1587
|
+
if (response.status === 402) {
|
|
1588
|
+
const requirement = await detectPaymentRequirement(response);
|
|
1589
|
+
if (requirement?.protocol === "x402" || requirement?.protocol === "mpp") {
|
|
1590
|
+
const payCall = requirement.protocol === "x402" ? client.payments.pay : client.payments.payMpp;
|
|
1669
1591
|
try {
|
|
1670
|
-
const
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1592
|
+
const paid = await payCall({
|
|
1593
|
+
url: requestUrl,
|
|
1594
|
+
method,
|
|
1595
|
+
// Fresh clone for the paid retry — same rationale as
|
|
1596
|
+
// the probe-side clone above. Re-derives the default
|
|
1597
|
+
// Content-Type so the paid leg matches the probe.
|
|
1598
|
+
headers: withDefaultContentType(opts.headers, requestBody),
|
|
1599
|
+
body: requestBody,
|
|
1600
|
+
maxPay: opts.maxPay,
|
|
1601
|
+
account: opts.account,
|
|
1602
|
+
signal: opts.signal,
|
|
1603
|
+
timeoutMs: opts.timeoutMs,
|
|
1604
|
+
...opts.displayCostAmount && {
|
|
1605
|
+
displayCostAmount: opts.displayCostAmount
|
|
1606
|
+
}
|
|
1607
|
+
});
|
|
1608
|
+
response = paid.response;
|
|
1609
|
+
payment = paid.payment;
|
|
1610
|
+
if (paid.warnings) warnings.push(...paid.warnings);
|
|
1681
1611
|
} catch (err) {
|
|
1682
|
-
|
|
1612
|
+
if (err instanceof ZeroSessionCloseFailedError) {
|
|
1613
|
+
response = err.response;
|
|
1614
|
+
payment = {
|
|
1615
|
+
protocol: "mpp",
|
|
1616
|
+
chain: tempoChainLabelFromId(err.session.chainId),
|
|
1617
|
+
txHash: null,
|
|
1618
|
+
amount: err.capturedAmount,
|
|
1619
|
+
asset: "USDC",
|
|
1620
|
+
session: err.session
|
|
1621
|
+
};
|
|
1622
|
+
warnings.push(FETCH_WARNINGS.mppSessionCloseFailed);
|
|
1623
|
+
} else if (err instanceof ZeroInsufficientFundsError) {
|
|
1624
|
+
return errorFetchResult(
|
|
1625
|
+
startedAt,
|
|
1626
|
+
err.message,
|
|
1627
|
+
FETCH_SKIP_REASONS.insufficientFunds,
|
|
1628
|
+
capabilityIdRequested,
|
|
1629
|
+
"insufficient_funds",
|
|
1630
|
+
null
|
|
1631
|
+
);
|
|
1632
|
+
} else if (err instanceof ZeroPaymentError || err instanceof ZeroAuthError) {
|
|
1633
|
+
const result2 = errorFetchResult(
|
|
1634
|
+
startedAt,
|
|
1635
|
+
err.message,
|
|
1636
|
+
"payment_failed",
|
|
1637
|
+
capabilityIdRequested,
|
|
1638
|
+
"payment_failed",
|
|
1639
|
+
402
|
|
1640
|
+
);
|
|
1641
|
+
await recordErrorFetchRun(client, opts, result2);
|
|
1642
|
+
return result2;
|
|
1643
|
+
} else if (err instanceof ZeroTimeoutError) {
|
|
1644
|
+
await recordTimeoutRun(
|
|
1645
|
+
client,
|
|
1646
|
+
opts,
|
|
1647
|
+
err,
|
|
1648
|
+
startedAt,
|
|
1649
|
+
capabilityIdRequested
|
|
1650
|
+
);
|
|
1651
|
+
throw err;
|
|
1652
|
+
} else {
|
|
1653
|
+
throw err;
|
|
1654
|
+
}
|
|
1683
1655
|
}
|
|
1656
|
+
} else if (requirement?.protocol === "unknown") {
|
|
1657
|
+
const result2 = errorFetchResult(
|
|
1658
|
+
startedAt,
|
|
1659
|
+
"Capability returned 402 with an unrecognized payment protocol. Neither x402 nor MPP markers were detected in the response headers or body.",
|
|
1660
|
+
"unrecognized_402_protocol",
|
|
1661
|
+
capabilityIdRequested,
|
|
1662
|
+
"payment_failed",
|
|
1663
|
+
402
|
|
1664
|
+
);
|
|
1665
|
+
await recordErrorFetchRun(client, opts, result2);
|
|
1666
|
+
return result2;
|
|
1684
1667
|
}
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
const
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
);
|
|
1704
|
-
this.assertSignerMatches(parsed.walletAddress, opts.expectedAddress);
|
|
1705
|
-
return parsed.signature;
|
|
1706
|
-
} catch (err) {
|
|
1707
|
-
repackageSignFailure(err, "signTransaction");
|
|
1708
|
-
}
|
|
1668
|
+
}
|
|
1669
|
+
const latencyMs = Date.now() - startedAt;
|
|
1670
|
+
const maxResponseBytes = opts.maxResponseBytes ?? MAX_RESPONSE_BYTES;
|
|
1671
|
+
let body;
|
|
1672
|
+
let bodyRaw;
|
|
1673
|
+
let bodyEncoding;
|
|
1674
|
+
try {
|
|
1675
|
+
const parsed = await parseResponseBody(response, maxResponseBytes);
|
|
1676
|
+
body = parsed.body;
|
|
1677
|
+
bodyRaw = parsed.bodyRaw;
|
|
1678
|
+
bodyEncoding = parsed.bodyEncoding;
|
|
1679
|
+
if (parsed.truncated) warnings.push(FETCH_WARNINGS.bodyTruncated);
|
|
1680
|
+
} catch (err) {
|
|
1681
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
1682
|
+
throw new ZeroApiError("Request aborted by caller", {
|
|
1683
|
+
status: 0,
|
|
1684
|
+
cause: err
|
|
1685
|
+
});
|
|
1709
1686
|
}
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
);
|
|
1687
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1688
|
+
const skips = [];
|
|
1689
|
+
const settledLocally = paymentHasAnchor(payment);
|
|
1690
|
+
if (settledLocally && response.status === 402) {
|
|
1691
|
+
warnings.push(FETCH_WARNINGS.paidButStill402NotRecorded);
|
|
1692
|
+
skips.push(FETCH_WARNINGS.paidButStill402NotRecorded);
|
|
1715
1693
|
}
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
);
|
|
1720
|
-
};
|
|
1721
|
-
signTypedData = async (input, opts = {}) => {
|
|
1722
|
-
const credentials = this.client.getCredentials();
|
|
1723
|
-
if (credentials.kind === "account") {
|
|
1724
|
-
if (!credentials.account.signTypedData) {
|
|
1725
|
-
throw new ZeroAuthError(
|
|
1726
|
-
"auth_error",
|
|
1727
|
-
"The configured viem account does not implement signTypedData."
|
|
1728
|
-
);
|
|
1729
|
-
}
|
|
1730
|
-
return await credentials.account.signTypedData(input);
|
|
1694
|
+
if (payment !== null && !settledLocally) {
|
|
1695
|
+
warnings.push(FETCH_WARNINGS.paymentSettlementUnconfirmed);
|
|
1696
|
+
skips.push(FETCH_WARNINGS.paymentSettlementUnconfirmed);
|
|
1731
1697
|
}
|
|
1732
|
-
if (
|
|
1733
|
-
|
|
1734
|
-
const parsed = await request(
|
|
1735
|
-
this.client,
|
|
1736
|
-
{
|
|
1737
|
-
method: "POST",
|
|
1738
|
-
path: "/v1/users/me/sign-typed-data",
|
|
1739
|
-
body: { typedData: input }
|
|
1740
|
-
},
|
|
1741
|
-
signResponseSchema
|
|
1742
|
-
);
|
|
1743
|
-
this.assertSignerMatches(parsed.walletAddress, opts.expectedAddress);
|
|
1744
|
-
return parsed.signature;
|
|
1745
|
-
} catch (err) {
|
|
1746
|
-
repackageSignFailure(err, "signTypedData");
|
|
1747
|
-
}
|
|
1698
|
+
if (payment !== null && payment.amount === "unknown") {
|
|
1699
|
+
warnings.push(FETCH_WARNINGS.paymentAmountUnknown);
|
|
1748
1700
|
}
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
if (
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
"auth_error",
|
|
1765
|
-
`Managed signing wallet changed (caller expected ${canonicalExpected}, server signed as ${canonicalServer}). Cache cleared \u2014 retry to rebuild against the current wallet.`
|
|
1766
|
-
);
|
|
1767
|
-
};
|
|
1768
|
-
};
|
|
1769
|
-
var BUG_REPORT_CATEGORIES = [
|
|
1770
|
-
"search_relevance",
|
|
1771
|
-
"ranking_issue",
|
|
1772
|
-
"missing_capability",
|
|
1773
|
-
"wrong_schema",
|
|
1774
|
-
"misleading_description",
|
|
1775
|
-
"broken_execution",
|
|
1776
|
-
"payment_failure",
|
|
1777
|
-
"billing_anomaly",
|
|
1778
|
-
"cli_bug",
|
|
1779
|
-
"security",
|
|
1780
|
-
"other"
|
|
1781
|
-
];
|
|
1782
|
-
var createBugReportResponseSchema = z.object({
|
|
1783
|
-
bugReportId: z.string(),
|
|
1784
|
-
status: z.string(),
|
|
1785
|
-
deduped: z.boolean(),
|
|
1786
|
-
category: z.enum(BUG_REPORT_CATEGORIES).nullable(),
|
|
1787
|
-
title: z.string().nullable(),
|
|
1788
|
-
attached: z.object({
|
|
1789
|
-
capabilityId: z.number().nullable(),
|
|
1790
|
-
runId: z.number().nullable(),
|
|
1791
|
-
searchId: z.number().nullable()
|
|
1792
|
-
})
|
|
1793
|
-
});
|
|
1794
|
-
|
|
1795
|
-
// src/namespaces/bug-reports.ts
|
|
1796
|
-
var BugReports = class {
|
|
1797
|
-
constructor(client) {
|
|
1798
|
-
this.client = client;
|
|
1701
|
+
skips.push(FETCH_SKIP_REASONS.bodyReadFailed);
|
|
1702
|
+
const result2 = {
|
|
1703
|
+
runId: null,
|
|
1704
|
+
ok: false,
|
|
1705
|
+
status: response.status,
|
|
1706
|
+
latencyMs: Date.now() - startedAt,
|
|
1707
|
+
payment,
|
|
1708
|
+
outcome: computeOutcome(response.status, payment, warnings),
|
|
1709
|
+
body: null,
|
|
1710
|
+
bodyRaw: null,
|
|
1711
|
+
error: message
|
|
1712
|
+
};
|
|
1713
|
+
if (capabilityIdRequested) result2.runTrackingSkipped = skips;
|
|
1714
|
+
if (warnings.length > 0) result2.warnings = warnings;
|
|
1715
|
+
return result2;
|
|
1799
1716
|
}
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
body: input,
|
|
1809
|
-
signal: opts.signal
|
|
1810
|
-
},
|
|
1811
|
-
createBugReportResponseSchema
|
|
1812
|
-
);
|
|
1813
|
-
};
|
|
1814
|
-
var ratingSchema = z.object({
|
|
1815
|
-
score: z.string(),
|
|
1816
|
-
successRate: z.string(),
|
|
1817
|
-
reviews: z.number(),
|
|
1818
|
-
stars: z.string().nullable().optional(),
|
|
1819
|
-
state: z.enum(["unrated", "rated"]).optional()
|
|
1820
|
-
});
|
|
1821
|
-
|
|
1822
|
-
// src/schemas/capabilities.ts
|
|
1823
|
-
var capabilityResponseSchema = z.object({
|
|
1824
|
-
uid: z.string(),
|
|
1825
|
-
slug: z.string(),
|
|
1826
|
-
name: z.string(),
|
|
1827
|
-
description: z.string(),
|
|
1828
|
-
url: z.string(),
|
|
1829
|
-
urlTemplate: z.string().nullable().optional(),
|
|
1830
|
-
method: z.string(),
|
|
1831
|
-
headers: z.record(z.string(), z.string()).nullable(),
|
|
1832
|
-
bodySchema: z.record(z.string(), z.unknown()).nullable(),
|
|
1833
|
-
responseSchema: z.record(z.string(), z.unknown()).nullable(),
|
|
1834
|
-
example: z.object({ request: z.unknown(), response: z.unknown() }).nullable(),
|
|
1835
|
-
tags: z.array(z.string()).nullable(),
|
|
1836
|
-
exampleAgentPrompt: z.string().nullable().optional(),
|
|
1837
|
-
displayCostAmount: z.string(),
|
|
1838
|
-
displayCostAsset: z.string(),
|
|
1839
|
-
reviewCount: z.number(),
|
|
1840
|
-
rating: ratingSchema,
|
|
1841
|
-
priceObserved: z.object({
|
|
1842
|
-
minCents: z.string().nullable(),
|
|
1843
|
-
medianCents: z.string().nullable(),
|
|
1844
|
-
maxCents: z.string().nullable(),
|
|
1845
|
-
// Deprecated alias for maxCents; kept for back-compat with older
|
|
1846
|
-
// API versions that still emit it.
|
|
1847
|
-
p95Cents: z.string().nullable(),
|
|
1848
|
-
sampleCount: z.number(),
|
|
1849
|
-
varies: z.boolean(),
|
|
1850
|
-
failureChargeRate: z.number().nullable()
|
|
1851
|
-
}).nullable().optional(),
|
|
1852
|
-
paymentMethods: z.array(
|
|
1853
|
-
z.object({
|
|
1854
|
-
uid: z.string(),
|
|
1855
|
-
protocol: z.string(),
|
|
1856
|
-
methodType: z.string(),
|
|
1857
|
-
chain: z.string().nullable(),
|
|
1858
|
-
mode: z.string(),
|
|
1859
|
-
costAmount: z.string(),
|
|
1860
|
-
costPer: z.string(),
|
|
1861
|
-
priority: z.number()
|
|
1862
|
-
})
|
|
1863
|
-
).nullable(),
|
|
1864
|
-
availabilityStatus: z.enum(["healthy", "unknown", "down"]).nullable().optional(),
|
|
1865
|
-
/**
|
|
1866
|
-
* @deprecated The API no longer emits `displayStatus`; it has been replaced
|
|
1867
|
-
* by the consolidated 3-value {@link capabilityResponseSchema.shape.availabilityStatus}.
|
|
1868
|
-
* Kept as an optional field for one release for back-compat (ZERO-89 soft
|
|
1869
|
-
* migration) and will be removed in the next major version.
|
|
1870
|
-
*/
|
|
1871
|
-
displayStatus: z.enum(["healthy", "stable", "degraded", "unhealthy", "unknown"]).optional(),
|
|
1872
|
-
activationCount: z.number().optional(),
|
|
1873
|
-
lastUsedAt: z.string().nullable().optional(),
|
|
1874
|
-
lastSuccessfullyRanAt: z.string().nullable().optional()
|
|
1875
|
-
});
|
|
1876
|
-
var resolveCapabilityResponseSchema = z.object({
|
|
1877
|
-
capabilityId: z.string(),
|
|
1878
|
-
capabilitySlug: z.string(),
|
|
1879
|
-
matchedVia: z.enum(["exact", "template"])
|
|
1880
|
-
});
|
|
1881
|
-
|
|
1882
|
-
// src/namespaces/capabilities.ts
|
|
1883
|
-
var Capabilities = class {
|
|
1884
|
-
constructor(client) {
|
|
1885
|
-
this.client = client;
|
|
1717
|
+
const settled = paymentHasAnchor(payment);
|
|
1718
|
+
const paidButStill402 = settled && response.status === 402;
|
|
1719
|
+
const settlementUnconfirmed = payment !== null && !settled;
|
|
1720
|
+
if (paidButStill402) warnings.push(FETCH_WARNINGS.paidButStill402NotRecorded);
|
|
1721
|
+
if (settlementUnconfirmed)
|
|
1722
|
+
warnings.push(FETCH_WARNINGS.paymentSettlementUnconfirmed);
|
|
1723
|
+
if (payment !== null && payment.amount === "unknown") {
|
|
1724
|
+
warnings.push(FETCH_WARNINGS.paymentAmountUnknown);
|
|
1886
1725
|
}
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
signal: opts.signal
|
|
1894
|
-
},
|
|
1895
|
-
capabilityResponseSchema
|
|
1726
|
+
let runId = null;
|
|
1727
|
+
const closeFailed = warnings.includes(FETCH_WARNINGS.mppSessionCloseFailed);
|
|
1728
|
+
const upstreamErrorForRecord = buildUpstreamError(
|
|
1729
|
+
response.status,
|
|
1730
|
+
bodyRaw,
|
|
1731
|
+
bodyEncoding
|
|
1896
1732
|
);
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1733
|
+
if (capabilityIdRequested) {
|
|
1734
|
+
if (paidButStill402)
|
|
1735
|
+
skipReasons.push(FETCH_WARNINGS.paidButStill402NotRecorded);
|
|
1736
|
+
else if (closeFailed)
|
|
1737
|
+
skipReasons.push(FETCH_SKIP_REASONS.mppSessionCloseFailedNotRecorded);
|
|
1738
|
+
}
|
|
1739
|
+
const resolvedCap = await resolvePromise;
|
|
1740
|
+
const effectiveCapabilityId = opts.capabilityId ?? resolvedCap?.capabilityId;
|
|
1741
|
+
const capabilityResolution = !capabilityIdRequested && canRecordForResolve ? resolvedCap !== null ? {
|
|
1742
|
+
resolved: true,
|
|
1743
|
+
capabilityId: resolvedCap.capabilityId,
|
|
1744
|
+
capabilitySlug: resolvedCap.capabilitySlug,
|
|
1745
|
+
matchedVia: resolvedCap.matchedVia
|
|
1746
|
+
} : { resolved: false } : void 0;
|
|
1747
|
+
if (effectiveCapabilityId !== void 0 && !paidButStill402 && !closeFailed) {
|
|
1748
|
+
const { recordingClient, canRecord } = resolveRecordingClient(client, opts);
|
|
1749
|
+
if (!canRecord) {
|
|
1750
|
+
skipReasons.push("no_credentials");
|
|
1751
|
+
} else {
|
|
1752
|
+
const knownAmount = payment !== null && payment.amount !== "unknown";
|
|
1753
|
+
try {
|
|
1754
|
+
let derived;
|
|
1755
|
+
if (opts.deriveRunMetadata) {
|
|
1756
|
+
try {
|
|
1757
|
+
derived = opts.deriveRunMetadata({
|
|
1758
|
+
status: response.status,
|
|
1759
|
+
ok: response.ok,
|
|
1760
|
+
bodyRaw,
|
|
1761
|
+
...bodyEncoding && { bodyEncoding }
|
|
1762
|
+
});
|
|
1763
|
+
} catch {
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1766
|
+
const result2 = await recordingClient.runs.create({
|
|
1767
|
+
capabilityId: effectiveCapabilityId,
|
|
1768
|
+
searchId: opts.searchId,
|
|
1769
|
+
// Auto-resolved runs are always direct_url — they only fire when
|
|
1770
|
+
// no capabilityId was supplied (the direct_url path by definition).
|
|
1771
|
+
...opts.capabilityId === void 0 ? { fetchOrigin: "direct_url" } : opts.fetchOrigin && { fetchOrigin: opts.fetchOrigin },
|
|
1772
|
+
status: response.status,
|
|
1773
|
+
latencyMs,
|
|
1774
|
+
...opts.requestSchema && { requestSchema: opts.requestSchema },
|
|
1775
|
+
...derived?.responseSchema && {
|
|
1776
|
+
responseSchema: derived.responseSchema
|
|
1777
|
+
},
|
|
1778
|
+
...upstreamErrorForRecord && {
|
|
1779
|
+
errorSnippetHash: upstreamErrorForRecord.snippetHash,
|
|
1780
|
+
errorSnippetLength: upstreamErrorForRecord.snippetLength
|
|
1781
|
+
},
|
|
1782
|
+
...payment && {
|
|
1783
|
+
...knownAmount && {
|
|
1784
|
+
costAmount: payment.amount,
|
|
1785
|
+
costAsset: payment.asset
|
|
1786
|
+
},
|
|
1787
|
+
paymentProtocol: payment.protocol,
|
|
1788
|
+
paymentChain: payment.chain,
|
|
1789
|
+
paymentTxHash: payment.txHash ?? void 0,
|
|
1790
|
+
paymentMode: payment.session ? "session" : "charge"
|
|
1791
|
+
}
|
|
1792
|
+
});
|
|
1793
|
+
runId = result2.runId;
|
|
1794
|
+
} catch (err) {
|
|
1795
|
+
const errorMessage2 = err instanceof Error ? err.message : String(err);
|
|
1796
|
+
const code = err instanceof ZeroError ? err.code : void 0;
|
|
1797
|
+
const skipMsg = code ? `run_create_failed:${code}: ${errorMessage2}` : `run_create_failed: ${errorMessage2}`;
|
|
1798
|
+
skipReasons.push(skipMsg);
|
|
1799
|
+
client.logger?.({
|
|
1800
|
+
level: "warn",
|
|
1801
|
+
message: "Failed to record run for fetch",
|
|
1802
|
+
meta: {
|
|
1803
|
+
url: redactUrl(url),
|
|
1804
|
+
capabilityId: effectiveCapabilityId,
|
|
1805
|
+
error: errorMessage2,
|
|
1806
|
+
errorCode: code
|
|
1807
|
+
}
|
|
1808
|
+
});
|
|
1809
|
+
}
|
|
1920
1810
|
}
|
|
1811
|
+
}
|
|
1812
|
+
const result = {
|
|
1813
|
+
runId,
|
|
1814
|
+
ok: response.ok,
|
|
1815
|
+
status: response.status,
|
|
1816
|
+
latencyMs,
|
|
1817
|
+
payment,
|
|
1818
|
+
outcome: computeOutcome(response.status, payment, warnings),
|
|
1819
|
+
body,
|
|
1820
|
+
bodyRaw
|
|
1921
1821
|
};
|
|
1822
|
+
if (bodyEncoding) result.bodyEncoding = bodyEncoding;
|
|
1823
|
+
if (!response.ok) {
|
|
1824
|
+
const candidate = bodyRaw !== null && bodyRaw.length > 0 && bodyEncoding !== "base64" ? bodyRaw.slice(0, 240) : null;
|
|
1825
|
+
result.error = candidate !== null && !requestBodyEchoed(candidate, opts.body) ? candidate : `HTTP ${response.status}`;
|
|
1826
|
+
if (upstreamErrorForRecord) result.upstreamError = upstreamErrorForRecord;
|
|
1827
|
+
}
|
|
1828
|
+
if (capabilityIdRequested && skipReasons.length > 0) {
|
|
1829
|
+
result.runTrackingSkipped = skipReasons;
|
|
1830
|
+
}
|
|
1831
|
+
if (warnings.length > 0) result.warnings = warnings;
|
|
1832
|
+
if (capabilityResolution !== void 0)
|
|
1833
|
+
result.capabilityResolution = capabilityResolution;
|
|
1834
|
+
return result;
|
|
1922
1835
|
};
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
}).nullable(),
|
|
1940
|
-
createdAt: z.coerce.date(),
|
|
1941
|
-
reviewed: z.boolean()
|
|
1836
|
+
|
|
1837
|
+
// package.json
|
|
1838
|
+
var package_default = {
|
|
1839
|
+
version: "0.7.0"};
|
|
1840
|
+
|
|
1841
|
+
// src/version.ts
|
|
1842
|
+
var SDK_VERSION = package_default.version;
|
|
1843
|
+
var userWalletDtoSchema = z.object({
|
|
1844
|
+
walletAddress: z.string(),
|
|
1845
|
+
source: z.enum(["self_custody", "privy_embedded"]),
|
|
1846
|
+
isPrimary: z.boolean(),
|
|
1847
|
+
linkedAt: z.union([z.string(), z.date()]).nullable().optional(),
|
|
1848
|
+
delegationGranted: z.boolean(),
|
|
1849
|
+
signerQuorumId: z.string().nullable(),
|
|
1850
|
+
// Per-transaction USDC cap in base units (6 dp). null = unlimited, 0 = free-only.
|
|
1851
|
+
maxTransactionUsdcBaseUnits: z.number().int().nullable()
|
|
1942
1852
|
});
|
|
1943
|
-
var
|
|
1944
|
-
|
|
1945
|
-
|
|
1853
|
+
var welcomeBonusSummarySchema = z.object({
|
|
1854
|
+
status: z.string(),
|
|
1855
|
+
amountUsd: z.number()
|
|
1946
1856
|
});
|
|
1947
|
-
var
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
slug: z.string(),
|
|
1963
|
-
name: z.string(),
|
|
1964
|
-
url: z.string(),
|
|
1965
|
-
method: z.string(),
|
|
1966
|
-
whatItDoes: z.string().nullable(),
|
|
1967
|
-
tags: z.array(z.string())
|
|
1857
|
+
var internalUserDtoSchema = z.object({
|
|
1858
|
+
id: z.string(),
|
|
1859
|
+
email: z.string().nullable(),
|
|
1860
|
+
createdAt: z.union([z.string(), z.date()]).optional(),
|
|
1861
|
+
lastLoginAt: z.union([z.string(), z.date()]).nullable().optional(),
|
|
1862
|
+
onboardingCompleted: z.boolean().optional(),
|
|
1863
|
+
delegationGranted: z.boolean().optional(),
|
|
1864
|
+
wallets: z.array(userWalletDtoSchema).optional(),
|
|
1865
|
+
balance: z.object({ amount: z.string(), asset: z.string() }).nullable().optional(),
|
|
1866
|
+
welcomeBonus: welcomeBonusSummarySchema.nullable().optional()
|
|
1867
|
+
});
|
|
1868
|
+
var publicUserDtoSchema = z.object({
|
|
1869
|
+
user: z.object({
|
|
1870
|
+
id: z.string(),
|
|
1871
|
+
email: z.string().nullable()
|
|
1968
1872
|
}),
|
|
1969
|
-
|
|
1873
|
+
walletAddress: z.string().nullable(),
|
|
1874
|
+
balance: z.object({ amount: z.string(), asset: z.string() }).nullable(),
|
|
1875
|
+
welcomeBonus: welcomeBonusSummarySchema.nullable()
|
|
1970
1876
|
});
|
|
1971
|
-
var
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
updated: z.boolean().optional()
|
|
1877
|
+
var signResponseSchema = z.object({
|
|
1878
|
+
signature: z.string().regex(/^0x[0-9a-fA-F]+$/, "must be 0x-prefixed hex"),
|
|
1879
|
+
walletAddress: z.string().regex(/^0x[0-9a-fA-F]{40}$/, "must be 0x-prefixed 40-char hex address")
|
|
1975
1880
|
});
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
),
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1881
|
+
|
|
1882
|
+
// src/schemas/device.ts
|
|
1883
|
+
var deviceStartResponseSchema = z.object({
|
|
1884
|
+
deviceCode: z.string(),
|
|
1885
|
+
userCode: z.string(),
|
|
1886
|
+
verificationUri: z.string(),
|
|
1887
|
+
pollInterval: z.number(),
|
|
1888
|
+
expiresAt: z.number()
|
|
1889
|
+
});
|
|
1890
|
+
var devicePollResponseSchema = z.union([
|
|
1891
|
+
// userCode + verificationUri are optional: older API deployments don't
|
|
1892
|
+
// echo them on pending.
|
|
1893
|
+
z.object({
|
|
1894
|
+
error: z.literal("authorization_pending"),
|
|
1895
|
+
userCode: z.string().optional(),
|
|
1896
|
+
verificationUri: z.string().optional()
|
|
1897
|
+
}),
|
|
1898
|
+
z.object({ error: z.literal("expired_token") }),
|
|
1899
|
+
z.object({
|
|
1900
|
+
accessToken: z.string(),
|
|
1901
|
+
refreshToken: z.string(),
|
|
1902
|
+
expiresIn: z.number(),
|
|
1903
|
+
user: internalUserDtoSchema
|
|
1997
1904
|
})
|
|
1905
|
+
]);
|
|
1906
|
+
var sessionExchangeResponseSchema = z.object({
|
|
1907
|
+
token: z.string(),
|
|
1908
|
+
expiresAt: z.number()
|
|
1909
|
+
});
|
|
1910
|
+
var refreshResponseSchema = z.object({
|
|
1911
|
+
accessToken: z.string(),
|
|
1912
|
+
refreshToken: z.string()
|
|
1913
|
+
});
|
|
1914
|
+
var tryRefreshSession = async (client, callerSignal) => {
|
|
1915
|
+
const credentials = client.getCredentials();
|
|
1916
|
+
if (credentials.kind !== "session") return false;
|
|
1917
|
+
if (!credentials.refreshToken) return false;
|
|
1918
|
+
const url = `${client.baseUrl.replace(/\/$/, "")}/v1/auth/refresh`;
|
|
1919
|
+
const timeoutSignal = AbortSignal.timeout(client.timeout);
|
|
1920
|
+
const signal = callerSignal ? AbortSignal.any([callerSignal, timeoutSignal]) : timeoutSignal;
|
|
1921
|
+
let response;
|
|
1922
|
+
try {
|
|
1923
|
+
response = await client.fetchImpl(url, {
|
|
1924
|
+
method: "POST",
|
|
1925
|
+
headers: { "content-type": "application/json" },
|
|
1926
|
+
body: JSON.stringify({ refreshToken: credentials.refreshToken }),
|
|
1927
|
+
signal
|
|
1928
|
+
});
|
|
1929
|
+
} catch {
|
|
1930
|
+
return false;
|
|
1931
|
+
}
|
|
1932
|
+
if (!response.ok) return false;
|
|
1933
|
+
let body;
|
|
1934
|
+
try {
|
|
1935
|
+
body = await response.json();
|
|
1936
|
+
} catch {
|
|
1937
|
+
return false;
|
|
1938
|
+
}
|
|
1939
|
+
const parsed = refreshResponseSchema.safeParse(body);
|
|
1940
|
+
if (!parsed.success) return false;
|
|
1941
|
+
await client.applyRefreshedTokens({
|
|
1942
|
+
accessToken: parsed.data.accessToken,
|
|
1943
|
+
refreshToken: parsed.data.refreshToken
|
|
1944
|
+
});
|
|
1945
|
+
return true;
|
|
1946
|
+
};
|
|
1947
|
+
var buildSessionAuthHeaders = (accessToken) => ({
|
|
1948
|
+
authorization: `Bearer ${accessToken}`
|
|
1998
1949
|
});
|
|
1950
|
+
var buildCanonicalMessage = (method, pathWithQuery, body, timestamp, nonce) => {
|
|
1951
|
+
const bodyHash = createHash("sha256").update(body ?? "").digest("hex");
|
|
1952
|
+
return `${method}:${pathWithQuery}:${bodyHash}:${timestamp}:${nonce}`;
|
|
1953
|
+
};
|
|
1954
|
+
var buildAccountAuthHeaders = async (account, method, pathWithQuery, body) => {
|
|
1955
|
+
const timestamp = Math.floor(Date.now() / 1e3).toString();
|
|
1956
|
+
const nonce = crypto.randomUUID();
|
|
1957
|
+
const message = buildCanonicalMessage(
|
|
1958
|
+
method,
|
|
1959
|
+
pathWithQuery,
|
|
1960
|
+
body,
|
|
1961
|
+
timestamp,
|
|
1962
|
+
nonce
|
|
1963
|
+
);
|
|
1964
|
+
const signature = await account.signMessage({ message });
|
|
1965
|
+
return {
|
|
1966
|
+
"x-zero-address": account.address,
|
|
1967
|
+
"x-zero-timestamp": timestamp,
|
|
1968
|
+
"x-zero-nonce": nonce,
|
|
1969
|
+
"x-zero-signature": signature
|
|
1970
|
+
};
|
|
1971
|
+
};
|
|
1972
|
+
|
|
1973
|
+
// src/transport/http.ts
|
|
1974
|
+
var RETRYABLE_STATUS = /* @__PURE__ */ new Set([408, 502, 503, 504]);
|
|
1975
|
+
var sleep = (ms, signal) => new Promise((resolve, reject) => {
|
|
1976
|
+
if (signal?.aborted) {
|
|
1977
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
1978
|
+
return;
|
|
1979
|
+
}
|
|
1980
|
+
const timer = setTimeout(() => {
|
|
1981
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1982
|
+
resolve();
|
|
1983
|
+
}, ms);
|
|
1984
|
+
const onAbort = () => {
|
|
1985
|
+
clearTimeout(timer);
|
|
1986
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
1987
|
+
};
|
|
1988
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1989
|
+
});
|
|
1990
|
+
var buildPathWithQuery = (path, query) => {
|
|
1991
|
+
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
|
1992
|
+
if (!query) return normalizedPath;
|
|
1993
|
+
const params = new URLSearchParams();
|
|
1994
|
+
for (const [key, value] of Object.entries(query)) {
|
|
1995
|
+
if (value !== void 0) params.set(key, String(value));
|
|
1996
|
+
}
|
|
1997
|
+
const qs = params.toString();
|
|
1998
|
+
return qs ? `${normalizedPath}?${qs}` : normalizedPath;
|
|
1999
|
+
};
|
|
2000
|
+
var joinUrl = (baseUrl, pathWithQuery) => {
|
|
2001
|
+
const base3 = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
|
|
2002
|
+
return `${base3}${pathWithQuery}`;
|
|
2003
|
+
};
|
|
2004
|
+
var buildHeaders = async (client, method, pathWithQuery, hasBody, bodyStr) => {
|
|
2005
|
+
const headers = { ...client.defaultHeaders };
|
|
2006
|
+
if (hasBody) headers["content-type"] = "application/json";
|
|
2007
|
+
headers["x-zero-sdk-version"] = SDK_VERSION;
|
|
2008
|
+
if (client.apiVersion) headers["x-zero-api-version"] = client.apiVersion;
|
|
2009
|
+
const credentials = client.getCredentials();
|
|
2010
|
+
if (credentials.kind === "session") {
|
|
2011
|
+
Object.assign(headers, buildSessionAuthHeaders(credentials.accessToken));
|
|
2012
|
+
} else if (credentials.kind === "account") {
|
|
2013
|
+
const signed = await buildAccountAuthHeaders(
|
|
2014
|
+
credentials.account,
|
|
2015
|
+
method,
|
|
2016
|
+
pathWithQuery,
|
|
2017
|
+
bodyStr
|
|
2018
|
+
);
|
|
2019
|
+
Object.assign(headers, signed);
|
|
2020
|
+
}
|
|
2021
|
+
return headers;
|
|
2022
|
+
};
|
|
2023
|
+
var parseErrorBody = async (response) => {
|
|
2024
|
+
try {
|
|
2025
|
+
return await response.clone().json();
|
|
2026
|
+
} catch {
|
|
2027
|
+
try {
|
|
2028
|
+
return await response.text();
|
|
2029
|
+
} catch {
|
|
2030
|
+
return void 0;
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
};
|
|
2034
|
+
var errorMessageFromBody = (body, status) => {
|
|
2035
|
+
if (body !== null && typeof body === "object" && "error" in body && typeof body.error === "string") {
|
|
2036
|
+
return body.error;
|
|
2037
|
+
}
|
|
2038
|
+
return `HTTP ${status}`;
|
|
2039
|
+
};
|
|
2040
|
+
var request = async (client, opts, schema) => {
|
|
2041
|
+
const pathWithQuery = buildPathWithQuery(opts.path, opts.query);
|
|
2042
|
+
const url = joinUrl(client.baseUrl, pathWithQuery);
|
|
2043
|
+
const hasBody = opts.body !== void 0;
|
|
2044
|
+
const bodyStr = hasBody ? JSON.stringify(
|
|
2045
|
+
opts.body,
|
|
2046
|
+
(_k, v) => typeof v === "bigint" ? v.toString() : v
|
|
2047
|
+
) : void 0;
|
|
2048
|
+
const configuredFetch = buildConfiguredFetch(client);
|
|
2049
|
+
const performOnce = async () => configuredFetch(url, {
|
|
2050
|
+
method: opts.method,
|
|
2051
|
+
headers: await buildHeaders(
|
|
2052
|
+
client,
|
|
2053
|
+
opts.method,
|
|
2054
|
+
pathWithQuery,
|
|
2055
|
+
hasBody,
|
|
2056
|
+
bodyStr
|
|
2057
|
+
),
|
|
2058
|
+
body: bodyStr,
|
|
2059
|
+
signal: opts.signal
|
|
2060
|
+
});
|
|
2061
|
+
let refreshAttempted = false;
|
|
2062
|
+
const executeWithRefresh = async () => {
|
|
2063
|
+
const response = await performOnce();
|
|
2064
|
+
if (response.status !== 401 || refreshAttempted) return response;
|
|
2065
|
+
const credentials = client.getCredentials();
|
|
2066
|
+
if (credentials.kind !== "session") return response;
|
|
2067
|
+
refreshAttempted = true;
|
|
2068
|
+
const refreshed = await client.refreshSessionOnce(opts.signal);
|
|
2069
|
+
if (!refreshed) return response;
|
|
2070
|
+
return await performOnce();
|
|
2071
|
+
};
|
|
2072
|
+
let lastNetworkError;
|
|
2073
|
+
for (let attempt = 0; attempt <= client.maxRetries; attempt++) {
|
|
2074
|
+
let response;
|
|
2075
|
+
try {
|
|
2076
|
+
response = await executeWithRefresh();
|
|
2077
|
+
} catch (e) {
|
|
2078
|
+
if (e instanceof ZeroError) throw e;
|
|
2079
|
+
lastNetworkError = e;
|
|
2080
|
+
if (attempt < client.maxRetries) {
|
|
2081
|
+
try {
|
|
2082
|
+
await sleep(2 ** attempt * 200, opts.signal);
|
|
2083
|
+
} catch (sleepErr) {
|
|
2084
|
+
throw new ZeroApiError("Request aborted by caller", {
|
|
2085
|
+
status: 0,
|
|
2086
|
+
cause: sleepErr
|
|
2087
|
+
});
|
|
2088
|
+
}
|
|
2089
|
+
continue;
|
|
2090
|
+
}
|
|
2091
|
+
throw new ZeroApiError("Network request failed", {
|
|
2092
|
+
status: 0,
|
|
2093
|
+
cause: e
|
|
2094
|
+
});
|
|
2095
|
+
}
|
|
2096
|
+
if (response.ok) {
|
|
2097
|
+
let text;
|
|
2098
|
+
try {
|
|
2099
|
+
text = await response.text();
|
|
2100
|
+
} catch (e) {
|
|
2101
|
+
throw new ZeroApiError("Response body read failed", {
|
|
2102
|
+
status: response.status,
|
|
2103
|
+
cause: e
|
|
2104
|
+
});
|
|
2105
|
+
}
|
|
2106
|
+
let json;
|
|
2107
|
+
try {
|
|
2108
|
+
json = JSON.parse(text);
|
|
2109
|
+
} catch {
|
|
2110
|
+
}
|
|
2111
|
+
const parsed = schema.safeParse(json);
|
|
2112
|
+
if (!parsed.success) {
|
|
2113
|
+
throw new ZeroValidationError(
|
|
2114
|
+
`Response did not match expected schema for ${opts.method} ${opts.path}`,
|
|
2115
|
+
{ cause: parsed.error, body: text }
|
|
2116
|
+
);
|
|
2117
|
+
}
|
|
2118
|
+
return parsed.data;
|
|
2119
|
+
}
|
|
2120
|
+
if (RETRYABLE_STATUS.has(response.status) && attempt < client.maxRetries) {
|
|
2121
|
+
try {
|
|
2122
|
+
await sleep(2 ** attempt * 200, opts.signal);
|
|
2123
|
+
} catch (sleepErr) {
|
|
2124
|
+
throw new ZeroApiError("Request aborted by caller", {
|
|
2125
|
+
status: 0,
|
|
2126
|
+
cause: sleepErr
|
|
2127
|
+
});
|
|
2128
|
+
}
|
|
2129
|
+
continue;
|
|
2130
|
+
}
|
|
2131
|
+
const errorBody = await parseErrorBody(response);
|
|
2132
|
+
const requestId = response.headers.get("x-request-id") ?? void 0;
|
|
2133
|
+
throw new ZeroApiError(errorMessageFromBody(errorBody, response.status), {
|
|
2134
|
+
status: response.status,
|
|
2135
|
+
requestId,
|
|
2136
|
+
body: errorBody
|
|
2137
|
+
});
|
|
2138
|
+
}
|
|
2139
|
+
throw new ZeroApiError("Exhausted retries", {
|
|
2140
|
+
status: 0,
|
|
2141
|
+
cause: lastNetworkError
|
|
2142
|
+
});
|
|
2143
|
+
};
|
|
1999
2144
|
|
|
2000
|
-
// src/namespaces/
|
|
2001
|
-
var
|
|
2145
|
+
// src/namespaces/auth-device.ts
|
|
2146
|
+
var AuthDevice = class {
|
|
2002
2147
|
constructor(client) {
|
|
2003
2148
|
this.client = client;
|
|
2004
2149
|
}
|
|
2005
|
-
|
|
2150
|
+
start = (opts = {}) => request(
|
|
2006
2151
|
this.client,
|
|
2007
2152
|
{
|
|
2008
2153
|
method: "POST",
|
|
2009
|
-
path: "/v1/
|
|
2010
|
-
body: input,
|
|
2154
|
+
path: "/v1/auth/device/start",
|
|
2011
2155
|
signal: opts.signal
|
|
2012
2156
|
},
|
|
2013
|
-
|
|
2157
|
+
deviceStartResponseSchema
|
|
2014
2158
|
);
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
if (opts.signal?.aborted) {
|
|
2018
|
-
return Promise.reject(
|
|
2019
|
-
new ZeroApiError("Request aborted by caller", { status: 0 })
|
|
2020
|
-
);
|
|
2021
|
-
}
|
|
2022
|
-
if (params.cursor === void 0) {
|
|
2023
|
-
return Promise.reject(
|
|
2024
|
-
new ZeroApiError(
|
|
2025
|
-
"runs.list({ limit: 0 }) requires a cursor \u2014 without one the empty page is indistinguishable from end-of-stream",
|
|
2026
|
-
{ status: 0 }
|
|
2027
|
-
)
|
|
2028
|
-
);
|
|
2029
|
-
}
|
|
2030
|
-
return Promise.resolve({
|
|
2031
|
-
runs: [],
|
|
2032
|
-
nextCursor: params.cursor
|
|
2033
|
-
});
|
|
2034
|
-
}
|
|
2035
|
-
const query = {};
|
|
2036
|
-
if (params.capabilityId !== void 0)
|
|
2037
|
-
query.capabilityId = params.capabilityId;
|
|
2038
|
-
if (params.unreviewed !== void 0) query.unreviewed = params.unreviewed;
|
|
2039
|
-
if (params.limit !== void 0) query.limit = params.limit;
|
|
2040
|
-
if (params.cursor !== void 0) query.cursor = params.cursor;
|
|
2041
|
-
return request(
|
|
2159
|
+
poll = async (deviceCode, opts = {}) => {
|
|
2160
|
+
const parsed = await request(
|
|
2042
2161
|
this.client,
|
|
2043
2162
|
{
|
|
2044
|
-
method: "
|
|
2045
|
-
path: "/v1/
|
|
2046
|
-
|
|
2163
|
+
method: "POST",
|
|
2164
|
+
path: "/v1/auth/device/poll",
|
|
2165
|
+
body: { deviceCode },
|
|
2047
2166
|
signal: opts.signal
|
|
2048
2167
|
},
|
|
2049
|
-
|
|
2168
|
+
devicePollResponseSchema
|
|
2050
2169
|
);
|
|
2170
|
+
if ("error" in parsed) {
|
|
2171
|
+
return parsed.error === "authorization_pending" ? {
|
|
2172
|
+
status: "pending",
|
|
2173
|
+
userCode: parsed.userCode,
|
|
2174
|
+
verificationUri: parsed.verificationUri
|
|
2175
|
+
} : { status: "expired" };
|
|
2176
|
+
}
|
|
2177
|
+
return {
|
|
2178
|
+
status: "ok",
|
|
2179
|
+
accessToken: parsed.accessToken,
|
|
2180
|
+
refreshToken: parsed.refreshToken,
|
|
2181
|
+
expiresIn: parsed.expiresIn,
|
|
2182
|
+
user: parsed.user
|
|
2183
|
+
};
|
|
2051
2184
|
};
|
|
2052
|
-
get = (runId, opts = {}) => request(
|
|
2053
|
-
this.client,
|
|
2054
|
-
{
|
|
2055
|
-
method: "GET",
|
|
2056
|
-
path: `/v1/runs/${runId}`,
|
|
2057
|
-
signal: opts.signal
|
|
2058
|
-
},
|
|
2059
|
-
runDetailSchema
|
|
2060
|
-
);
|
|
2061
|
-
review = (input, opts = {}) => request(
|
|
2062
|
-
this.client,
|
|
2063
|
-
{
|
|
2064
|
-
method: "POST",
|
|
2065
|
-
path: "/v1/reviews",
|
|
2066
|
-
body: input,
|
|
2067
|
-
signal: opts.signal
|
|
2068
|
-
},
|
|
2069
|
-
createReviewResponseSchema
|
|
2070
|
-
);
|
|
2071
|
-
// Bulk review submission. The CLI's bulk JSONL parser stays CLI-side —
|
|
2072
|
-
// SDK consumers pass already-parsed structured input.
|
|
2073
|
-
createReviewsBatch = (reviews, opts = {}) => request(
|
|
2074
|
-
this.client,
|
|
2075
|
-
{
|
|
2076
|
-
method: "POST",
|
|
2077
|
-
path: "/v1/reviews/batch",
|
|
2078
|
-
body: { reviews },
|
|
2079
|
-
signal: opts.signal
|
|
2080
|
-
},
|
|
2081
|
-
batchReviewResponseSchema
|
|
2082
|
-
);
|
|
2083
2185
|
};
|
|
2084
|
-
var
|
|
2085
|
-
var
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
id: TEMPO_CHAIN_ID2,
|
|
2090
|
-
name: "Tempo",
|
|
2091
|
-
nativeCurrency: { name: "USD", symbol: "USD", decimals: 18 },
|
|
2092
|
-
rpcUrls: {
|
|
2093
|
-
default: { http: ["https://rpc.tempo.xyz"] }
|
|
2094
|
-
}
|
|
2186
|
+
var userWalletsSchema = z.array(userWalletDtoSchema);
|
|
2187
|
+
var normalizeMessage = (message) => {
|
|
2188
|
+
if (typeof message === "string") return message;
|
|
2189
|
+
if (typeof message.raw === "string") return message.raw;
|
|
2190
|
+
return bytesToHex(message.raw);
|
|
2095
2191
|
};
|
|
2096
|
-
var
|
|
2097
|
-
{
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2192
|
+
var repackageSignFailure = (err, op) => {
|
|
2193
|
+
if (err instanceof ZeroApiError && err.status === 402) {
|
|
2194
|
+
const body = err.body;
|
|
2195
|
+
const detail = body && typeof body.error === "string" ? `: ${body.error}` : "";
|
|
2196
|
+
throw new ZeroPaymentError(
|
|
2197
|
+
`Sign endpoint refused ${op} (HTTP 402)${detail}`,
|
|
2198
|
+
{ cause: err }
|
|
2199
|
+
);
|
|
2103
2200
|
}
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
// src/namespaces/wallet.ts
|
|
2108
|
-
var fundingUrlResponseSchema = z.object({ url: z.string() });
|
|
2109
|
-
var userWalletListSchema = z.array(userWalletDtoSchema);
|
|
2110
|
-
var migrateAuthorizationResponseSchema = z.object({
|
|
2111
|
-
transactionHash: z.string()
|
|
2112
|
-
});
|
|
2113
|
-
var errorMessage = (reason) => reason instanceof Error ? reason.message : String(reason);
|
|
2114
|
-
var Wallet = class {
|
|
2201
|
+
throw err;
|
|
2202
|
+
};
|
|
2203
|
+
var Auth = class {
|
|
2115
2204
|
constructor(client) {
|
|
2116
2205
|
this.client = client;
|
|
2206
|
+
this.device = new AuthDevice(client);
|
|
2117
2207
|
}
|
|
2118
|
-
|
|
2119
|
-
// The
|
|
2120
|
-
//
|
|
2121
|
-
|
|
2122
|
-
const credentials = this.client.getCredentials();
|
|
2123
|
-
if (credentials.kind !== "account") return null;
|
|
2124
|
-
return credentials.account.address;
|
|
2125
|
-
}
|
|
2126
|
-
balance = async (opts = {}) => {
|
|
2127
|
-
const target = opts.address ?? this.address;
|
|
2128
|
-
if (!target) {
|
|
2129
|
-
throw new ZeroWalletError(
|
|
2130
|
-
"balance() requires either an `address` option or a client constructed with an account"
|
|
2131
|
-
);
|
|
2132
|
-
}
|
|
2133
|
-
const chain = opts.chain ?? "total";
|
|
2134
|
-
if (chain !== "total") {
|
|
2135
|
-
const raw = await this.readChainBalance(chain, target);
|
|
2136
|
-
return { amount: formatUnits(raw, USDC_DECIMALS), asset: "USDC" };
|
|
2137
|
-
}
|
|
2138
|
-
const chains = ["base", "tempo"];
|
|
2139
|
-
const settled = await Promise.allSettled(
|
|
2140
|
-
chains.map((c) => this.readChainBalance(c, target))
|
|
2141
|
-
);
|
|
2142
|
-
let sum = 0n;
|
|
2143
|
-
const errors = [];
|
|
2144
|
-
for (let i = 0; i < settled.length; i++) {
|
|
2145
|
-
const result = settled[i];
|
|
2146
|
-
const chainName = chains[i];
|
|
2147
|
-
if (!result || !chainName) continue;
|
|
2148
|
-
if (result.status === "fulfilled") {
|
|
2149
|
-
sum += result.value;
|
|
2150
|
-
} else {
|
|
2151
|
-
errors.push({ chain: chainName, message: errorMessage(result.reason) });
|
|
2152
|
-
}
|
|
2153
|
-
}
|
|
2154
|
-
if (errors.length === settled.length) {
|
|
2155
|
-
throw new ZeroWalletError(
|
|
2156
|
-
`balance() failed for every chain: ${errors.map((e) => `${e.chain} (${e.message})`).join(", ")}`
|
|
2157
|
-
);
|
|
2158
|
-
}
|
|
2159
|
-
const balance = {
|
|
2160
|
-
amount: formatUnits(sum, USDC_DECIMALS),
|
|
2161
|
-
asset: "USDC"
|
|
2162
|
-
};
|
|
2163
|
-
if (errors.length > 0) balance.errors = errors;
|
|
2164
|
-
return balance;
|
|
2165
|
-
};
|
|
2166
|
-
// All wallets linked to the authenticated session user. Session-only.
|
|
2167
|
-
list = (opts = {}) => request(
|
|
2208
|
+
device;
|
|
2209
|
+
// The internal (web-app facing) user profile. Richer than profile(): full
|
|
2210
|
+
// wallet list with per-wallet delegation/signer/limit metadata.
|
|
2211
|
+
me = (opts = {}) => request(
|
|
2168
2212
|
this.client,
|
|
2169
|
-
{
|
|
2170
|
-
|
|
2171
|
-
path: "/v1/users/me/wallets",
|
|
2172
|
-
signal: opts.signal
|
|
2173
|
-
},
|
|
2174
|
-
userWalletListSchema
|
|
2213
|
+
{ method: "GET", path: "/v1/users/me", signal: opts.signal },
|
|
2214
|
+
internalUserDtoSchema
|
|
2175
2215
|
);
|
|
2176
|
-
//
|
|
2177
|
-
//
|
|
2178
|
-
|
|
2216
|
+
// The public (agent-facing) profile — identity + primary wallet address +
|
|
2217
|
+
// balance + welcome-bonus, no per-wallet metadata. Same shape the MCP
|
|
2218
|
+
// get_profile tool returns; rendered by `zero auth whoami`.
|
|
2219
|
+
profile = (opts = {}) => request(
|
|
2220
|
+
this.client,
|
|
2221
|
+
{ method: "GET", path: "/v1/users/me/profile", signal: opts.signal },
|
|
2222
|
+
publicUserDtoSchema
|
|
2223
|
+
);
|
|
2224
|
+
// The user's wallets only. Lightweight sibling of `me()` for callers that
|
|
2225
|
+
// just need wallet addresses (e.g. managed-account resolution on the pay
|
|
2226
|
+
// path) — avoids the heavier internal-profile computation behind `me()`.
|
|
2227
|
+
wallets = (opts = {}) => request(
|
|
2179
2228
|
this.client,
|
|
2180
|
-
{
|
|
2181
|
-
|
|
2182
|
-
path: "/v1/users/me/wallets/provision",
|
|
2183
|
-
signal: opts.signal
|
|
2184
|
-
},
|
|
2185
|
-
userWalletDtoSchema
|
|
2229
|
+
{ method: "GET", path: "/v1/users/me/wallets", signal: opts.signal },
|
|
2230
|
+
userWalletsSchema
|
|
2186
2231
|
);
|
|
2187
|
-
//
|
|
2188
|
-
//
|
|
2189
|
-
//
|
|
2190
|
-
|
|
2191
|
-
// the signed payload. Session-only.
|
|
2192
|
-
migrateAuthorization = (authorization, opts = {}) => request(
|
|
2232
|
+
// Exchange an ephemeral session code (e.g. runner / sandbox handoff) for
|
|
2233
|
+
// a session access token. The returned token has no refresh path — pair
|
|
2234
|
+
// with `refreshToken: ""` when constructing the client.
|
|
2235
|
+
exchangeSessionCode = (code, opts = {}) => request(
|
|
2193
2236
|
this.client,
|
|
2194
2237
|
{
|
|
2195
2238
|
method: "POST",
|
|
2196
|
-
path: "/v1/
|
|
2197
|
-
body: {
|
|
2239
|
+
path: "/v1/session/exchange",
|
|
2240
|
+
body: { code },
|
|
2198
2241
|
signal: opts.signal
|
|
2199
2242
|
},
|
|
2200
|
-
|
|
2243
|
+
sessionExchangeResponseSchema
|
|
2201
2244
|
);
|
|
2202
|
-
//
|
|
2203
|
-
//
|
|
2204
|
-
//
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
"fundingUrl() requires a client constructed with an account or session credentials"
|
|
2211
|
-
);
|
|
2212
|
-
}
|
|
2213
|
-
const query = {
|
|
2214
|
-
provider: opts.provider ?? "coinbase"
|
|
2215
|
-
};
|
|
2216
|
-
if (opts.amount) query.amount = opts.amount;
|
|
2217
|
-
const { url } = await request(
|
|
2245
|
+
// Server-side revoke of the refresh token. Throws ZeroApiError on any
|
|
2246
|
+
// non-2xx — callers that have already cleared local state (e.g. CLI
|
|
2247
|
+
// logout) typically want to swallow with `.catch(() => {})` so a flaky
|
|
2248
|
+
// server doesn't surface as a user-visible failure for a sign-out that
|
|
2249
|
+
// already succeeded locally. Response body is opaque; schema is
|
|
2250
|
+
// `z.unknown()` and the resolved value is discarded.
|
|
2251
|
+
logout = async (refreshToken, opts = {}) => {
|
|
2252
|
+
await request(
|
|
2218
2253
|
this.client,
|
|
2219
|
-
{
|
|
2220
|
-
|
|
2254
|
+
{
|
|
2255
|
+
method: "POST",
|
|
2256
|
+
path: "/v1/auth/logout",
|
|
2257
|
+
body: { refreshToken },
|
|
2258
|
+
signal: opts.signal
|
|
2259
|
+
},
|
|
2260
|
+
z.unknown()
|
|
2221
2261
|
);
|
|
2222
|
-
return url;
|
|
2223
2262
|
};
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2263
|
+
// Force a session refresh. Routes through refreshSessionOnce() so a
|
|
2264
|
+
// proactive call doesn't race a 401-driven transport refresh —
|
|
2265
|
+
// concurrent POSTs with the same refresh token kill the session
|
|
2266
|
+
// family (RFC 6819 token-theft response).
|
|
2267
|
+
refresh = async () => {
|
|
2268
|
+
const credentials = this.client.getCredentials();
|
|
2269
|
+
if (credentials.kind !== "session") {
|
|
2270
|
+
throw new ZeroAuthError(
|
|
2271
|
+
"auth_no_credentials",
|
|
2272
|
+
"refresh() requires the client to be constructed with a session \u2014 got credentials.kind=" + credentials.kind
|
|
2273
|
+
);
|
|
2233
2274
|
}
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2275
|
+
const ok = await this.client.refreshSessionOnce();
|
|
2276
|
+
if (!ok) {
|
|
2277
|
+
throw new ZeroAuthError(
|
|
2278
|
+
"auth_session_expired",
|
|
2279
|
+
"Session refresh failed. The refresh token may be revoked or expired."
|
|
2280
|
+
);
|
|
2240
2281
|
}
|
|
2241
|
-
return this.clients.tempo;
|
|
2242
|
-
};
|
|
2243
|
-
/**
|
|
2244
|
-
* @internal
|
|
2245
|
-
*
|
|
2246
|
-
* Per-chain USDC balance read. Exposed (rather than private) so unit
|
|
2247
|
-
* tests can stub it without standing up a real viem public client —
|
|
2248
|
-
* viem's `http()` transport doesn't go through `client.fetchImpl`, so
|
|
2249
|
-
* the standard mock-fetch trick doesn't cover this path.
|
|
2250
|
-
*/
|
|
2251
|
-
readChainBalance = async (chain, address) => {
|
|
2252
|
-
const publicClient = this.getPublicClient(chain);
|
|
2253
|
-
const tokenAddress = chain === "base" ? USDC_BASE2 : USDC_TEMPO2;
|
|
2254
|
-
const balance = await publicClient.readContract({
|
|
2255
|
-
address: tokenAddress,
|
|
2256
|
-
abi: ERC20_BALANCE_ABI2,
|
|
2257
|
-
functionName: "balanceOf",
|
|
2258
|
-
args: [address]
|
|
2259
|
-
});
|
|
2260
|
-
return balance;
|
|
2261
2282
|
};
|
|
2262
|
-
}
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
var DEFAULT_TIMEOUT_MS = 18e4;
|
|
2267
|
-
var DEFAULT_MAX_RETRIES = 2;
|
|
2268
|
-
|
|
2269
|
-
// src/auth/credentials.ts
|
|
2270
|
-
var noopRefresh = async () => {
|
|
2271
|
-
};
|
|
2272
|
-
var MAX_402_PROBE_BYTES = 1024 * 1024;
|
|
2273
|
-
var MAX_RESPONSE_BYTES = 10 * 1024 * 1024;
|
|
2274
|
-
var TEXT_CONTENT_TYPES = [
|
|
2275
|
-
"application/json",
|
|
2276
|
-
"application/xml",
|
|
2277
|
-
"application/javascript",
|
|
2278
|
-
"application/ecmascript",
|
|
2279
|
-
"application/x-www-form-urlencoded",
|
|
2280
|
-
"text/",
|
|
2281
|
-
"+json",
|
|
2282
|
-
"+xml"
|
|
2283
|
-
];
|
|
2284
|
-
var MPP_SCHEME_TOKENS = /* @__PURE__ */ new Set(["payment", "mpp"]);
|
|
2285
|
-
var splitWwwAuthenticateChallenges = (header) => {
|
|
2286
|
-
const chunks = [];
|
|
2287
|
-
let buf = "";
|
|
2288
|
-
let inQuotes = false;
|
|
2289
|
-
for (let i = 0; i < header.length; i++) {
|
|
2290
|
-
const ch = header[i];
|
|
2291
|
-
if (inQuotes) {
|
|
2292
|
-
if (ch === "\\" && i + 1 < header.length) {
|
|
2293
|
-
buf += ch + header[i + 1];
|
|
2294
|
-
i++;
|
|
2295
|
-
continue;
|
|
2296
|
-
}
|
|
2297
|
-
if (ch === '"') inQuotes = false;
|
|
2298
|
-
buf += ch;
|
|
2299
|
-
continue;
|
|
2300
|
-
}
|
|
2301
|
-
if (ch === '"') {
|
|
2302
|
-
inQuotes = true;
|
|
2303
|
-
buf += ch;
|
|
2304
|
-
continue;
|
|
2283
|
+
signMessage = async (input, opts = {}) => {
|
|
2284
|
+
const credentials = this.client.getCredentials();
|
|
2285
|
+
if (credentials.kind === "account") {
|
|
2286
|
+
return await credentials.account.signMessage({ message: input.message });
|
|
2305
2287
|
}
|
|
2306
|
-
if (
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2288
|
+
if (credentials.kind === "session") {
|
|
2289
|
+
try {
|
|
2290
|
+
const parsed = await request(
|
|
2291
|
+
this.client,
|
|
2292
|
+
{
|
|
2293
|
+
method: "POST",
|
|
2294
|
+
path: "/v1/users/me/sign-message",
|
|
2295
|
+
body: { message: normalizeMessage(input.message) }
|
|
2296
|
+
},
|
|
2297
|
+
signResponseSchema
|
|
2298
|
+
);
|
|
2299
|
+
this.assertSignerMatches(parsed.walletAddress, opts.expectedAddress);
|
|
2300
|
+
return parsed.signature;
|
|
2301
|
+
} catch (err) {
|
|
2302
|
+
repackageSignFailure(err, "signMessage");
|
|
2303
|
+
}
|
|
2310
2304
|
}
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
};
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
);
|
|
2336
|
-
if (asString.length < ECHO_DETECT_MIN_LEN) return false;
|
|
2337
|
-
const scanLen = Math.min(asString.length, ECHO_DETECT_MAX_SCAN);
|
|
2338
|
-
for (let i = 0; i + ECHO_DETECT_MIN_LEN <= scanLen; i++) {
|
|
2339
|
-
if (candidate.includes(asString.slice(i, i + ECHO_DETECT_MIN_LEN))) {
|
|
2340
|
-
return true;
|
|
2305
|
+
throw new ZeroAuthError(
|
|
2306
|
+
"auth_no_credentials",
|
|
2307
|
+
"signMessage() requires the client to be constructed with `account` or `session`."
|
|
2308
|
+
);
|
|
2309
|
+
};
|
|
2310
|
+
// Managed-only: BYO accounts sign locally via viem's account.signTransaction.
|
|
2311
|
+
signTransaction = async (input, opts = {}) => {
|
|
2312
|
+
const credentials = this.client.getCredentials();
|
|
2313
|
+
if (credentials.kind === "session") {
|
|
2314
|
+
try {
|
|
2315
|
+
const parsed = await request(
|
|
2316
|
+
this.client,
|
|
2317
|
+
{
|
|
2318
|
+
method: "POST",
|
|
2319
|
+
path: "/v1/users/me/sign-transaction",
|
|
2320
|
+
body: { unsignedTransaction: input.unsignedTransaction }
|
|
2321
|
+
},
|
|
2322
|
+
signResponseSchema
|
|
2323
|
+
);
|
|
2324
|
+
this.assertSignerMatches(parsed.walletAddress, opts.expectedAddress);
|
|
2325
|
+
return parsed.signature;
|
|
2326
|
+
} catch (err) {
|
|
2327
|
+
repackageSignFailure(err, "signTransaction");
|
|
2328
|
+
}
|
|
2341
2329
|
}
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
try {
|
|
2347
|
-
response.body?.cancel().catch(() => {
|
|
2348
|
-
});
|
|
2349
|
-
} catch {
|
|
2350
|
-
}
|
|
2351
|
-
};
|
|
2352
|
-
var detectPaymentRequirement = async (response) => {
|
|
2353
|
-
if (response.status !== 402) return null;
|
|
2354
|
-
const x402Header = response.headers.get("payment-required") ?? response.headers.get("x-payment-required");
|
|
2355
|
-
if (x402Header) {
|
|
2356
|
-
cancelBody2(response);
|
|
2357
|
-
try {
|
|
2358
|
-
const decoded = JSON.parse(
|
|
2359
|
-
Buffer.from(x402Header, "base64").toString("utf8")
|
|
2330
|
+
if (credentials.kind === "account") {
|
|
2331
|
+
throw new ZeroAuthError(
|
|
2332
|
+
"auth_error",
|
|
2333
|
+
"signTransaction() is a managed-signing operation and is not supported in account mode. Sign locally with viem's account.signTransaction instead."
|
|
2360
2334
|
);
|
|
2361
|
-
return { protocol: "x402", raw: decoded };
|
|
2362
|
-
} catch {
|
|
2363
|
-
return { protocol: "x402", raw: { encoded: x402Header } };
|
|
2364
2335
|
}
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
}
|
|
2336
|
+
throw new ZeroAuthError(
|
|
2337
|
+
"auth_no_credentials",
|
|
2338
|
+
"signTransaction() requires session credentials (managed signing)."
|
|
2339
|
+
);
|
|
2340
|
+
};
|
|
2341
|
+
signTypedData = async (input, opts = {}) => {
|
|
2342
|
+
const credentials = this.client.getCredentials();
|
|
2343
|
+
if (credentials.kind === "account") {
|
|
2344
|
+
if (!credentials.account.signTypedData) {
|
|
2345
|
+
throw new ZeroAuthError(
|
|
2346
|
+
"auth_error",
|
|
2347
|
+
"The configured viem account does not implement signTypedData."
|
|
2348
|
+
);
|
|
2379
2349
|
}
|
|
2380
|
-
|
|
2350
|
+
return await credentials.account.signTypedData(input);
|
|
2381
2351
|
}
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2352
|
+
if (credentials.kind === "session") {
|
|
2353
|
+
try {
|
|
2354
|
+
const parsed = await request(
|
|
2355
|
+
this.client,
|
|
2356
|
+
{
|
|
2357
|
+
method: "POST",
|
|
2358
|
+
path: "/v1/users/me/sign-typed-data",
|
|
2359
|
+
body: { typedData: input }
|
|
2360
|
+
},
|
|
2361
|
+
signResponseSchema
|
|
2362
|
+
);
|
|
2363
|
+
this.assertSignerMatches(parsed.walletAddress, opts.expectedAddress);
|
|
2364
|
+
return parsed.signature;
|
|
2365
|
+
} catch (err) {
|
|
2366
|
+
repackageSignFailure(err, "signTypedData");
|
|
2367
|
+
}
|
|
2391
2368
|
}
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2369
|
+
throw new ZeroAuthError(
|
|
2370
|
+
"auth_no_credentials",
|
|
2371
|
+
"signTypedData() requires the client to be constructed with `account` or `session`."
|
|
2372
|
+
);
|
|
2373
|
+
};
|
|
2374
|
+
// Compare against the caller's pinned address (not shared cache) so
|
|
2375
|
+
// concurrent pay()s can't race past each other. Mismatch → clear + throw;
|
|
2376
|
+
// the caller's retry rebuilds against the new wallet.
|
|
2377
|
+
assertSignerMatches = (serverAddr, expectedAddr) => {
|
|
2378
|
+
if (!expectedAddr) return;
|
|
2379
|
+
const canonicalServer = getAddress(serverAddr);
|
|
2380
|
+
const canonicalExpected = getAddress(expectedAddr);
|
|
2381
|
+
if (canonicalExpected === canonicalServer) return;
|
|
2382
|
+
this.client.clearManagedAccount();
|
|
2383
|
+
throw new ZeroAuthError(
|
|
2384
|
+
"auth_error",
|
|
2385
|
+
`Managed signing wallet changed (caller expected ${canonicalExpected}, server signed as ${canonicalServer}). Cache cleared \u2014 retry to rebuild against the current wallet.`
|
|
2386
|
+
);
|
|
2387
|
+
};
|
|
2395
2388
|
};
|
|
2396
|
-
var
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2389
|
+
var BUG_REPORT_CATEGORIES = [
|
|
2390
|
+
"search_relevance",
|
|
2391
|
+
"ranking_issue",
|
|
2392
|
+
"missing_capability",
|
|
2393
|
+
"wrong_schema",
|
|
2394
|
+
"misleading_description",
|
|
2395
|
+
"broken_execution",
|
|
2396
|
+
"payment_failure",
|
|
2397
|
+
"billing_anomaly",
|
|
2398
|
+
"cli_bug",
|
|
2399
|
+
"security",
|
|
2400
|
+
"other"
|
|
2401
|
+
];
|
|
2402
|
+
var createBugReportResponseSchema = z.object({
|
|
2403
|
+
bugReportId: z.string(),
|
|
2404
|
+
status: z.string(),
|
|
2405
|
+
deduped: z.boolean(),
|
|
2406
|
+
category: z.enum(BUG_REPORT_CATEGORIES).nullable(),
|
|
2407
|
+
title: z.string().nullable(),
|
|
2408
|
+
attached: z.object({
|
|
2409
|
+
capabilityId: z.number().nullable(),
|
|
2410
|
+
runId: z.number().nullable(),
|
|
2411
|
+
searchId: z.number().nullable()
|
|
2412
|
+
})
|
|
2413
|
+
});
|
|
2414
|
+
|
|
2415
|
+
// src/namespaces/bug-reports.ts
|
|
2416
|
+
var BugReports = class {
|
|
2417
|
+
constructor(client) {
|
|
2418
|
+
this.client = client;
|
|
2422
2419
|
}
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2420
|
+
// POST /v1/bug-reports. Wallet-attributed server-side; the SDK client's
|
|
2421
|
+
// active credential drives attribution (account-mode → EIP-191 from the
|
|
2422
|
+
// wallet, session-mode → Bearer + server resolves the user's wallet).
|
|
2423
|
+
create = (input, opts = {}) => request(
|
|
2424
|
+
this.client,
|
|
2425
|
+
{
|
|
2426
|
+
method: "POST",
|
|
2427
|
+
path: "/v1/bug-reports",
|
|
2428
|
+
body: input,
|
|
2429
|
+
signal: opts.signal
|
|
2430
|
+
},
|
|
2431
|
+
createBugReportResponseSchema
|
|
2432
|
+
);
|
|
2428
2433
|
};
|
|
2429
|
-
var
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2434
|
+
var ratingSchema = z.object({
|
|
2435
|
+
score: z.string(),
|
|
2436
|
+
successRate: z.string(),
|
|
2437
|
+
reviews: z.number(),
|
|
2438
|
+
stars: z.string().nullable().optional(),
|
|
2439
|
+
state: z.enum(["unrated", "rated"]).optional()
|
|
2440
|
+
});
|
|
2441
|
+
|
|
2442
|
+
// src/schemas/capabilities.ts
|
|
2443
|
+
var capabilityResponseSchema = z.object({
|
|
2444
|
+
uid: z.string(),
|
|
2445
|
+
slug: z.string(),
|
|
2446
|
+
name: z.string(),
|
|
2447
|
+
description: z.string(),
|
|
2448
|
+
url: z.string(),
|
|
2449
|
+
urlTemplate: z.string().nullable().optional(),
|
|
2450
|
+
method: z.string(),
|
|
2451
|
+
headers: z.record(z.string(), z.string()).nullable(),
|
|
2452
|
+
bodySchema: z.record(z.string(), z.unknown()).nullable(),
|
|
2453
|
+
responseSchema: z.record(z.string(), z.unknown()).nullable(),
|
|
2454
|
+
example: z.object({ request: z.unknown(), response: z.unknown() }).nullable(),
|
|
2455
|
+
tags: z.array(z.string()).nullable(),
|
|
2456
|
+
exampleAgentPrompt: z.string().nullable().optional(),
|
|
2457
|
+
displayCostAmount: z.string(),
|
|
2458
|
+
displayCostAsset: z.string(),
|
|
2459
|
+
reviewCount: z.number(),
|
|
2460
|
+
rating: ratingSchema,
|
|
2461
|
+
priceObserved: z.object({
|
|
2462
|
+
minCents: z.string().nullable(),
|
|
2463
|
+
medianCents: z.string().nullable(),
|
|
2464
|
+
maxCents: z.string().nullable(),
|
|
2465
|
+
// Deprecated alias for maxCents; kept for back-compat with older
|
|
2466
|
+
// API versions that still emit it.
|
|
2467
|
+
p95Cents: z.string().nullable(),
|
|
2468
|
+
sampleCount: z.number(),
|
|
2469
|
+
varies: z.boolean(),
|
|
2470
|
+
failureChargeRate: z.number().nullable()
|
|
2471
|
+
}).nullable().optional(),
|
|
2472
|
+
paymentMethods: z.array(
|
|
2473
|
+
z.object({
|
|
2474
|
+
uid: z.string(),
|
|
2475
|
+
protocol: z.string(),
|
|
2476
|
+
methodType: z.string(),
|
|
2477
|
+
chain: z.string().nullable(),
|
|
2478
|
+
mode: z.string(),
|
|
2479
|
+
costAmount: z.string(),
|
|
2480
|
+
costPer: z.string(),
|
|
2481
|
+
priority: z.number()
|
|
2482
|
+
})
|
|
2483
|
+
).nullable(),
|
|
2484
|
+
availabilityStatus: z.enum(["healthy", "unknown", "down"]).nullable().optional(),
|
|
2485
|
+
/**
|
|
2486
|
+
* @deprecated The API no longer emits `displayStatus`; it has been replaced
|
|
2487
|
+
* by the consolidated 3-value {@link capabilityResponseSchema.shape.availabilityStatus}.
|
|
2488
|
+
* Kept as an optional field for one release for back-compat (ZERO-89 soft
|
|
2489
|
+
* migration) and will be removed in the next major version.
|
|
2490
|
+
*/
|
|
2491
|
+
displayStatus: z.enum(["healthy", "stable", "degraded", "unhealthy", "unknown"]).optional(),
|
|
2492
|
+
activationCount: z.number().optional(),
|
|
2493
|
+
lastUsedAt: z.string().nullable().optional(),
|
|
2494
|
+
lastSuccessfullyRanAt: z.string().nullable().optional()
|
|
2495
|
+
});
|
|
2496
|
+
var resolveCapabilityResponseSchema = z.object({
|
|
2497
|
+
capabilityId: z.string(),
|
|
2498
|
+
capabilitySlug: z.string(),
|
|
2499
|
+
matchedVia: z.enum(["exact", "template"])
|
|
2500
|
+
});
|
|
2501
|
+
|
|
2502
|
+
// src/namespaces/capabilities.ts
|
|
2503
|
+
var Capabilities = class {
|
|
2504
|
+
constructor(client) {
|
|
2505
|
+
this.client = client;
|
|
2443
2506
|
}
|
|
2444
|
-
|
|
2445
|
-
|
|
2507
|
+
get = (id, opts = {}) => request(
|
|
2508
|
+
this.client,
|
|
2509
|
+
{
|
|
2510
|
+
method: "GET",
|
|
2511
|
+
path: `/v1/capabilities/${encodeURIComponent(id)}`,
|
|
2512
|
+
query: opts.searchId ? { searchId: opts.searchId } : void 0,
|
|
2513
|
+
signal: opts.signal
|
|
2514
|
+
},
|
|
2515
|
+
capabilityResponseSchema
|
|
2516
|
+
);
|
|
2517
|
+
// Resolve a raw endpoint URL + method back to the capability that owns it,
|
|
2518
|
+
// for the `zero fetch <url>` case where the client has no local search
|
|
2519
|
+
// context to match against (a memoized/cross-session URL). Returns null on
|
|
2520
|
+
// 404 — i.e. no match or an ambiguous multi-match, both of which the server
|
|
2521
|
+
// reports as 404 (resolveByUrl fails closed rather than mis-attribute).
|
|
2522
|
+
// The URL travels in the POST body so it stays out of access logs and is
|
|
2523
|
+
// never stored; the caller uses the returned capabilityId to record an
|
|
2524
|
+
// attributed run without the URL ever touching the run row (ZERO-335).
|
|
2525
|
+
resolveByUrl = async (url, method, opts = {}) => {
|
|
2446
2526
|
try {
|
|
2447
|
-
return
|
|
2448
|
-
|
|
2527
|
+
return await request(
|
|
2528
|
+
this.client,
|
|
2529
|
+
{
|
|
2530
|
+
method: "POST",
|
|
2531
|
+
path: "/v1/capabilities/resolve",
|
|
2532
|
+
body: { url, method },
|
|
2533
|
+
signal: opts.signal
|
|
2534
|
+
},
|
|
2535
|
+
resolveCapabilityResponseSchema
|
|
2536
|
+
);
|
|
2537
|
+
} catch (err) {
|
|
2538
|
+
if (err instanceof ZeroApiError && err.status === 404) return null;
|
|
2539
|
+
throw err;
|
|
2449
2540
|
}
|
|
2450
|
-
}
|
|
2451
|
-
return { body: text, bodyRaw: text, truncated };
|
|
2452
|
-
};
|
|
2453
|
-
var UPSTREAM_ERROR_FIELDS = [
|
|
2454
|
-
"error",
|
|
2455
|
-
"message",
|
|
2456
|
-
"detail",
|
|
2457
|
-
"reason",
|
|
2458
|
-
"error_description"
|
|
2459
|
-
];
|
|
2460
|
-
var UPSTREAM_ERROR_MAX_LEN = 200;
|
|
2461
|
-
var ERROR_SNIPPET_BYTES = 500;
|
|
2462
|
-
var clampUpstreamMessage = (value) => {
|
|
2463
|
-
const trimmed = value.trim();
|
|
2464
|
-
if (!trimmed) return void 0;
|
|
2465
|
-
return trimmed.length > UPSTREAM_ERROR_MAX_LEN ? `${trimmed.slice(0, UPSTREAM_ERROR_MAX_LEN)}\u2026` : trimmed;
|
|
2466
|
-
};
|
|
2467
|
-
var tryParseJson = (text) => {
|
|
2468
|
-
try {
|
|
2469
|
-
return JSON.parse(text);
|
|
2470
|
-
} catch {
|
|
2471
|
-
return null;
|
|
2472
|
-
}
|
|
2541
|
+
};
|
|
2473
2542
|
};
|
|
2474
|
-
var
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2543
|
+
var createRunResponseSchema = z.object({
|
|
2544
|
+
runId: z.string()
|
|
2545
|
+
});
|
|
2546
|
+
var runListItemSchema = z.object({
|
|
2547
|
+
uid: z.string(),
|
|
2548
|
+
capabilityUid: z.string(),
|
|
2549
|
+
capabilitySlug: z.string(),
|
|
2550
|
+
capabilityName: z.string(),
|
|
2551
|
+
status: z.number().nullable(),
|
|
2552
|
+
latencyMs: z.number().nullable(),
|
|
2553
|
+
cost: z.object({ amount: z.string(), asset: z.string().nullable() }).nullable(),
|
|
2554
|
+
payment: z.object({
|
|
2555
|
+
protocol: z.string(),
|
|
2556
|
+
chain: z.string().nullable(),
|
|
2557
|
+
txHash: z.string().nullable(),
|
|
2558
|
+
mode: z.string().nullable()
|
|
2559
|
+
}).nullable(),
|
|
2560
|
+
createdAt: z.coerce.date(),
|
|
2561
|
+
reviewed: z.boolean()
|
|
2562
|
+
});
|
|
2563
|
+
var listRunsResponseSchema = z.object({
|
|
2564
|
+
runs: z.array(runListItemSchema),
|
|
2565
|
+
nextCursor: z.string().nullable()
|
|
2566
|
+
});
|
|
2567
|
+
var runDetailSchema = z.object({
|
|
2568
|
+
runId: z.string(),
|
|
2569
|
+
status: z.number().nullable(),
|
|
2570
|
+
latencyMs: z.number().nullable(),
|
|
2571
|
+
errorClass: z.string().nullable(),
|
|
2572
|
+
createdAt: z.coerce.date(),
|
|
2573
|
+
cost: z.object({ amount: z.string(), asset: z.string().nullable() }).nullable(),
|
|
2574
|
+
payment: z.object({
|
|
2575
|
+
protocol: z.string(),
|
|
2576
|
+
chain: z.string().nullable(),
|
|
2577
|
+
txHash: z.string().nullable(),
|
|
2578
|
+
mode: z.string().nullable()
|
|
2579
|
+
}).nullable(),
|
|
2580
|
+
capability: z.object({
|
|
2581
|
+
uid: z.string(),
|
|
2582
|
+
slug: z.string(),
|
|
2583
|
+
name: z.string(),
|
|
2584
|
+
url: z.string(),
|
|
2585
|
+
method: z.string(),
|
|
2586
|
+
whatItDoes: z.string().nullable(),
|
|
2587
|
+
tags: z.array(z.string())
|
|
2588
|
+
}),
|
|
2589
|
+
reviewed: z.boolean()
|
|
2590
|
+
});
|
|
2591
|
+
var createReviewResponseSchema = z.object({
|
|
2592
|
+
reviewId: z.string(),
|
|
2593
|
+
recorded: z.boolean(),
|
|
2594
|
+
updated: z.boolean().optional()
|
|
2595
|
+
});
|
|
2596
|
+
var batchReviewResponseSchema = z.object({
|
|
2597
|
+
results: z.array(
|
|
2598
|
+
z.union([
|
|
2599
|
+
z.object({
|
|
2600
|
+
runId: z.string(),
|
|
2601
|
+
ok: z.literal(true),
|
|
2602
|
+
reviewId: z.string(),
|
|
2603
|
+
updated: z.boolean()
|
|
2604
|
+
}),
|
|
2605
|
+
z.object({
|
|
2606
|
+
runId: z.string(),
|
|
2607
|
+
ok: z.literal(false),
|
|
2608
|
+
status: z.number(),
|
|
2609
|
+
error: z.string()
|
|
2610
|
+
})
|
|
2611
|
+
])
|
|
2612
|
+
),
|
|
2613
|
+
summary: z.object({
|
|
2614
|
+
total: z.number(),
|
|
2615
|
+
ok: z.number(),
|
|
2616
|
+
failed: z.number()
|
|
2617
|
+
})
|
|
2618
|
+
});
|
|
2619
|
+
|
|
2620
|
+
// src/namespaces/runs.ts
|
|
2621
|
+
var Runs = class {
|
|
2622
|
+
constructor(client) {
|
|
2623
|
+
this.client = client;
|
|
2478
2624
|
}
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2625
|
+
create = (input, opts = {}) => request(
|
|
2626
|
+
this.client,
|
|
2627
|
+
{
|
|
2628
|
+
method: "POST",
|
|
2629
|
+
path: "/v1/runs",
|
|
2630
|
+
body: input,
|
|
2631
|
+
signal: opts.signal
|
|
2632
|
+
},
|
|
2633
|
+
createRunResponseSchema
|
|
2634
|
+
);
|
|
2635
|
+
list = (params = {}, opts = {}) => {
|
|
2636
|
+
if (params.limit === 0) {
|
|
2637
|
+
if (opts.signal?.aborted) {
|
|
2638
|
+
return Promise.reject(
|
|
2639
|
+
new ZeroApiError("Request aborted by caller", { status: 0 })
|
|
2640
|
+
);
|
|
2641
|
+
}
|
|
2642
|
+
if (params.cursor === void 0) {
|
|
2643
|
+
return Promise.reject(
|
|
2644
|
+
new ZeroApiError(
|
|
2645
|
+
"runs.list({ limit: 0 }) requires a cursor \u2014 without one the empty page is indistinguishable from end-of-stream",
|
|
2646
|
+
{ status: 0 }
|
|
2647
|
+
)
|
|
2648
|
+
);
|
|
2489
2649
|
}
|
|
2650
|
+
return Promise.resolve({
|
|
2651
|
+
runs: [],
|
|
2652
|
+
nextCursor: params.cursor
|
|
2653
|
+
});
|
|
2490
2654
|
}
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
if (warnings.includes(FETCH_WARNINGS.mppSessionCloseFailed))
|
|
2508
|
-
return "payment_close_failed";
|
|
2509
|
-
if (status === 402 && payment !== null) return "payment_rejected";
|
|
2510
|
-
if (status === 402) return "payment_failed";
|
|
2511
|
-
if (status >= 400) return "server_error";
|
|
2512
|
-
return "success";
|
|
2513
|
-
};
|
|
2514
|
-
var errorFetchResult = (startedAt, error, skipReason, capabilityIdRequested, outcome, status = null) => {
|
|
2515
|
-
const result = {
|
|
2516
|
-
runId: null,
|
|
2517
|
-
ok: false,
|
|
2518
|
-
status,
|
|
2519
|
-
latencyMs: Date.now() - startedAt,
|
|
2520
|
-
payment: null,
|
|
2521
|
-
outcome,
|
|
2522
|
-
body: null,
|
|
2523
|
-
bodyRaw: null,
|
|
2524
|
-
error
|
|
2655
|
+
const query = {};
|
|
2656
|
+
if (params.capabilityId !== void 0)
|
|
2657
|
+
query.capabilityId = params.capabilityId;
|
|
2658
|
+
if (params.unreviewed !== void 0) query.unreviewed = params.unreviewed;
|
|
2659
|
+
if (params.limit !== void 0) query.limit = params.limit;
|
|
2660
|
+
if (params.cursor !== void 0) query.cursor = params.cursor;
|
|
2661
|
+
return request(
|
|
2662
|
+
this.client,
|
|
2663
|
+
{
|
|
2664
|
+
method: "GET",
|
|
2665
|
+
path: "/v1/runs",
|
|
2666
|
+
query,
|
|
2667
|
+
signal: opts.signal
|
|
2668
|
+
},
|
|
2669
|
+
listRunsResponseSchema
|
|
2670
|
+
);
|
|
2525
2671
|
};
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
}
|
|
2555
|
-
|
|
2556
|
-
const result = errorFetchResult(
|
|
2557
|
-
startedAt,
|
|
2558
|
-
err.message,
|
|
2559
|
-
"timeout",
|
|
2560
|
-
capabilityIdRequested,
|
|
2561
|
-
"network_error"
|
|
2672
|
+
get = (runId, opts = {}) => request(
|
|
2673
|
+
this.client,
|
|
2674
|
+
{
|
|
2675
|
+
method: "GET",
|
|
2676
|
+
path: `/v1/runs/${runId}`,
|
|
2677
|
+
signal: opts.signal
|
|
2678
|
+
},
|
|
2679
|
+
runDetailSchema
|
|
2680
|
+
);
|
|
2681
|
+
review = (input, opts = {}) => request(
|
|
2682
|
+
this.client,
|
|
2683
|
+
{
|
|
2684
|
+
method: "POST",
|
|
2685
|
+
path: "/v1/reviews",
|
|
2686
|
+
body: input,
|
|
2687
|
+
signal: opts.signal
|
|
2688
|
+
},
|
|
2689
|
+
createReviewResponseSchema
|
|
2690
|
+
);
|
|
2691
|
+
// Bulk review submission. The CLI's bulk JSONL parser stays CLI-side —
|
|
2692
|
+
// SDK consumers pass already-parsed structured input.
|
|
2693
|
+
createReviewsBatch = (reviews, opts = {}) => request(
|
|
2694
|
+
this.client,
|
|
2695
|
+
{
|
|
2696
|
+
method: "POST",
|
|
2697
|
+
path: "/v1/reviews/batch",
|
|
2698
|
+
body: { reviews },
|
|
2699
|
+
signal: opts.signal
|
|
2700
|
+
},
|
|
2701
|
+
batchReviewResponseSchema
|
|
2562
2702
|
);
|
|
2563
|
-
await recordErrorFetchRun(client, opts, result);
|
|
2564
2703
|
};
|
|
2565
|
-
var
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2704
|
+
var USDC_BASE2 = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913";
|
|
2705
|
+
var USDC_TEMPO2 = "0x20c000000000000000000000b9537d11c60e8b50";
|
|
2706
|
+
var TEMPO_CHAIN_ID2 = 4217;
|
|
2707
|
+
var baseChain = base;
|
|
2708
|
+
var tempoChain2 = {
|
|
2709
|
+
id: TEMPO_CHAIN_ID2,
|
|
2710
|
+
name: "Tempo",
|
|
2711
|
+
nativeCurrency: { name: "USD", symbol: "USD", decimals: 18 },
|
|
2712
|
+
rpcUrls: {
|
|
2713
|
+
default: { http: ["https://rpc.tempo.xyz"] }
|
|
2572
2714
|
}
|
|
2573
|
-
if (i >= buf.length) return false;
|
|
2574
|
-
return buf[i] === 123 || buf[i] === 91;
|
|
2575
2715
|
};
|
|
2576
|
-
var
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
if (!hasContentType) {
|
|
2584
|
-
next["content-type"] = typeof body === "string" || sniffJsonShapeBytes(body) ? "application/json" : "application/octet-stream";
|
|
2716
|
+
var ERC20_BALANCE_ABI2 = [
|
|
2717
|
+
{
|
|
2718
|
+
inputs: [{ name: "account", type: "address" }],
|
|
2719
|
+
name: "balanceOf",
|
|
2720
|
+
outputs: [{ name: "", type: "uint256" }],
|
|
2721
|
+
stateMutability: "view",
|
|
2722
|
+
type: "function"
|
|
2585
2723
|
}
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
client,
|
|
2600
|
-
opts
|
|
2601
|
-
);
|
|
2602
|
-
const resolvePromise = !capabilityIdRequested && canRecordForResolve ? client.capabilities.resolveByUrl(redactUrl(requestUrl), method).catch(() => null) : Promise.resolve(null);
|
|
2603
|
-
const probeHeaders = withDefaultContentType(opts.headers, requestBody);
|
|
2604
|
-
const requestInit = {
|
|
2605
|
-
method,
|
|
2606
|
-
headers: probeHeaders,
|
|
2607
|
-
body: requestBody,
|
|
2608
|
-
signal: opts.signal
|
|
2609
|
-
};
|
|
2610
|
-
const configuredFetch = buildConfiguredFetch(client, {
|
|
2611
|
-
timeoutMs: opts.timeoutMs
|
|
2612
|
-
});
|
|
2613
|
-
let response;
|
|
2614
|
-
let payment = null;
|
|
2615
|
-
const skipReasons = [];
|
|
2616
|
-
const warnings = [];
|
|
2617
|
-
try {
|
|
2618
|
-
response = await configuredFetch(requestUrl, requestInit);
|
|
2619
|
-
} catch (err) {
|
|
2620
|
-
if (err instanceof ZeroTimeoutError) {
|
|
2621
|
-
await recordTimeoutRun(
|
|
2622
|
-
client,
|
|
2623
|
-
opts,
|
|
2624
|
-
err,
|
|
2625
|
-
startedAt,
|
|
2626
|
-
capabilityIdRequested
|
|
2627
|
-
);
|
|
2628
|
-
throw err;
|
|
2629
|
-
}
|
|
2630
|
-
if (err instanceof ZeroError) throw err;
|
|
2631
|
-
const errMessage = err instanceof Error ? err.message : String(err);
|
|
2632
|
-
const result2 = errorFetchResult(
|
|
2633
|
-
startedAt,
|
|
2634
|
-
errMessage,
|
|
2635
|
-
"fetch_failed_before_response",
|
|
2636
|
-
capabilityIdRequested,
|
|
2637
|
-
"network_error"
|
|
2638
|
-
);
|
|
2639
|
-
await recordErrorFetchRun(client, opts, result2);
|
|
2640
|
-
return result2;
|
|
2724
|
+
];
|
|
2725
|
+
var USDC_DECIMALS = 6;
|
|
2726
|
+
|
|
2727
|
+
// src/namespaces/wallet.ts
|
|
2728
|
+
var fundingUrlResponseSchema = z.object({ url: z.string() });
|
|
2729
|
+
var userWalletListSchema = z.array(userWalletDtoSchema);
|
|
2730
|
+
var migrateAuthorizationResponseSchema = z.object({
|
|
2731
|
+
transactionHash: z.string()
|
|
2732
|
+
});
|
|
2733
|
+
var errorMessage = (reason) => reason instanceof Error ? reason.message : String(reason);
|
|
2734
|
+
var Wallet = class {
|
|
2735
|
+
constructor(client) {
|
|
2736
|
+
this.client = client;
|
|
2641
2737
|
}
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
method,
|
|
2650
|
-
// Fresh clone for the paid retry — same rationale as
|
|
2651
|
-
// the probe-side clone above. Re-derives the default
|
|
2652
|
-
// Content-Type so the paid leg matches the probe.
|
|
2653
|
-
headers: withDefaultContentType(opts.headers, requestBody),
|
|
2654
|
-
body: requestBody,
|
|
2655
|
-
maxPay: opts.maxPay,
|
|
2656
|
-
account: opts.account,
|
|
2657
|
-
signal: opts.signal,
|
|
2658
|
-
timeoutMs: opts.timeoutMs,
|
|
2659
|
-
...opts.displayCostAmount && {
|
|
2660
|
-
displayCostAmount: opts.displayCostAmount
|
|
2661
|
-
}
|
|
2662
|
-
});
|
|
2663
|
-
response = paid.response;
|
|
2664
|
-
payment = paid.payment;
|
|
2665
|
-
if (paid.warnings) warnings.push(...paid.warnings);
|
|
2666
|
-
} catch (err) {
|
|
2667
|
-
if (err instanceof ZeroSessionCloseFailedError) {
|
|
2668
|
-
response = err.response;
|
|
2669
|
-
payment = {
|
|
2670
|
-
protocol: "mpp",
|
|
2671
|
-
chain: tempoChainLabelFromId(err.session.chainId),
|
|
2672
|
-
txHash: null,
|
|
2673
|
-
amount: err.capturedAmount,
|
|
2674
|
-
asset: "USDC",
|
|
2675
|
-
session: err.session
|
|
2676
|
-
};
|
|
2677
|
-
warnings.push(FETCH_WARNINGS.mppSessionCloseFailed);
|
|
2678
|
-
} else if (err instanceof ZeroInsufficientFundsError) {
|
|
2679
|
-
return errorFetchResult(
|
|
2680
|
-
startedAt,
|
|
2681
|
-
err.message,
|
|
2682
|
-
FETCH_SKIP_REASONS.insufficientFunds,
|
|
2683
|
-
capabilityIdRequested,
|
|
2684
|
-
"insufficient_funds",
|
|
2685
|
-
null
|
|
2686
|
-
);
|
|
2687
|
-
} else if (err instanceof ZeroPaymentError || err instanceof ZeroAuthError) {
|
|
2688
|
-
const result2 = errorFetchResult(
|
|
2689
|
-
startedAt,
|
|
2690
|
-
err.message,
|
|
2691
|
-
"payment_failed",
|
|
2692
|
-
capabilityIdRequested,
|
|
2693
|
-
"payment_failed",
|
|
2694
|
-
402
|
|
2695
|
-
);
|
|
2696
|
-
await recordErrorFetchRun(client, opts, result2);
|
|
2697
|
-
return result2;
|
|
2698
|
-
} else if (err instanceof ZeroTimeoutError) {
|
|
2699
|
-
await recordTimeoutRun(
|
|
2700
|
-
client,
|
|
2701
|
-
opts,
|
|
2702
|
-
err,
|
|
2703
|
-
startedAt,
|
|
2704
|
-
capabilityIdRequested
|
|
2705
|
-
);
|
|
2706
|
-
throw err;
|
|
2707
|
-
} else {
|
|
2708
|
-
throw err;
|
|
2709
|
-
}
|
|
2710
|
-
}
|
|
2711
|
-
} else if (requirement?.protocol === "unknown") {
|
|
2712
|
-
const result2 = errorFetchResult(
|
|
2713
|
-
startedAt,
|
|
2714
|
-
"Capability returned 402 with an unrecognized payment protocol. Neither x402 nor MPP markers were detected in the response headers or body.",
|
|
2715
|
-
"unrecognized_402_protocol",
|
|
2716
|
-
capabilityIdRequested,
|
|
2717
|
-
"payment_failed",
|
|
2718
|
-
402
|
|
2719
|
-
);
|
|
2720
|
-
await recordErrorFetchRun(client, opts, result2);
|
|
2721
|
-
return result2;
|
|
2722
|
-
}
|
|
2738
|
+
clients = {};
|
|
2739
|
+
// The configured wallet address, or null if the client wasn't constructed
|
|
2740
|
+
// with an account. Stable read — does not hit the network.
|
|
2741
|
+
get address() {
|
|
2742
|
+
const credentials = this.client.getCredentials();
|
|
2743
|
+
if (credentials.kind !== "account") return null;
|
|
2744
|
+
return credentials.account.address;
|
|
2723
2745
|
}
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
const parsed = await parseResponseBody(response, maxResponseBytes);
|
|
2731
|
-
body = parsed.body;
|
|
2732
|
-
bodyRaw = parsed.bodyRaw;
|
|
2733
|
-
bodyEncoding = parsed.bodyEncoding;
|
|
2734
|
-
if (parsed.truncated) warnings.push(FETCH_WARNINGS.bodyTruncated);
|
|
2735
|
-
} catch (err) {
|
|
2736
|
-
if (err instanceof Error && err.name === "AbortError") {
|
|
2737
|
-
throw new ZeroApiError("Request aborted by caller", {
|
|
2738
|
-
status: 0,
|
|
2739
|
-
cause: err
|
|
2740
|
-
});
|
|
2746
|
+
balance = async (opts = {}) => {
|
|
2747
|
+
const target = opts.address ?? this.address;
|
|
2748
|
+
if (!target) {
|
|
2749
|
+
throw new ZeroWalletError(
|
|
2750
|
+
"balance() requires either an `address` option or a client constructed with an account"
|
|
2751
|
+
);
|
|
2741
2752
|
}
|
|
2742
|
-
const
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
warnings.push(FETCH_WARNINGS.paidButStill402NotRecorded);
|
|
2747
|
-
skips.push(FETCH_WARNINGS.paidButStill402NotRecorded);
|
|
2753
|
+
const chain = opts.chain ?? "total";
|
|
2754
|
+
if (chain !== "total") {
|
|
2755
|
+
const raw = await this.readChainBalance(chain, target);
|
|
2756
|
+
return { amount: formatUnits(raw, USDC_DECIMALS), asset: "USDC" };
|
|
2748
2757
|
}
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2758
|
+
const chains = ["base", "tempo"];
|
|
2759
|
+
const settled = await Promise.allSettled(
|
|
2760
|
+
chains.map((c) => this.readChainBalance(c, target))
|
|
2761
|
+
);
|
|
2762
|
+
let sum = 0n;
|
|
2763
|
+
const errors = [];
|
|
2764
|
+
for (let i = 0; i < settled.length; i++) {
|
|
2765
|
+
const result = settled[i];
|
|
2766
|
+
const chainName = chains[i];
|
|
2767
|
+
if (!result || !chainName) continue;
|
|
2768
|
+
if (result.status === "fulfilled") {
|
|
2769
|
+
sum += result.value;
|
|
2770
|
+
} else {
|
|
2771
|
+
errors.push({ chain: chainName, message: errorMessage(result.reason) });
|
|
2772
|
+
}
|
|
2752
2773
|
}
|
|
2753
|
-
if (
|
|
2754
|
-
|
|
2774
|
+
if (errors.length === settled.length) {
|
|
2775
|
+
throw new ZeroWalletError(
|
|
2776
|
+
`balance() failed for every chain: ${errors.map((e) => `${e.chain} (${e.message})`).join(", ")}`
|
|
2777
|
+
);
|
|
2755
2778
|
}
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
ok: false,
|
|
2760
|
-
status: response.status,
|
|
2761
|
-
latencyMs: Date.now() - startedAt,
|
|
2762
|
-
payment,
|
|
2763
|
-
outcome: computeOutcome(response.status, payment, warnings),
|
|
2764
|
-
body: null,
|
|
2765
|
-
bodyRaw: null,
|
|
2766
|
-
error: message
|
|
2779
|
+
const balance = {
|
|
2780
|
+
amount: formatUnits(sum, USDC_DECIMALS),
|
|
2781
|
+
asset: "USDC"
|
|
2767
2782
|
};
|
|
2768
|
-
if (
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
}
|
|
2781
|
-
let runId = null;
|
|
2782
|
-
const closeFailed = warnings.includes(FETCH_WARNINGS.mppSessionCloseFailed);
|
|
2783
|
-
const upstreamErrorForRecord = buildUpstreamError(
|
|
2784
|
-
response.status,
|
|
2785
|
-
bodyRaw,
|
|
2786
|
-
bodyEncoding
|
|
2783
|
+
if (errors.length > 0) balance.errors = errors;
|
|
2784
|
+
return balance;
|
|
2785
|
+
};
|
|
2786
|
+
// All wallets linked to the authenticated session user. Session-only.
|
|
2787
|
+
list = (opts = {}) => request(
|
|
2788
|
+
this.client,
|
|
2789
|
+
{
|
|
2790
|
+
method: "GET",
|
|
2791
|
+
path: "/v1/users/me/wallets",
|
|
2792
|
+
signal: opts.signal
|
|
2793
|
+
},
|
|
2794
|
+
userWalletListSchema
|
|
2787
2795
|
);
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
paymentProtocol: payment.protocol,
|
|
2843
|
-
paymentChain: payment.chain,
|
|
2844
|
-
paymentTxHash: payment.txHash ?? void 0,
|
|
2845
|
-
paymentMode: payment.session ? "session" : "charge"
|
|
2846
|
-
}
|
|
2847
|
-
});
|
|
2848
|
-
runId = result2.runId;
|
|
2849
|
-
} catch (err) {
|
|
2850
|
-
const errorMessage2 = err instanceof Error ? err.message : String(err);
|
|
2851
|
-
const code = err instanceof ZeroError ? err.code : void 0;
|
|
2852
|
-
const skipMsg = code ? `run_create_failed:${code}: ${errorMessage2}` : `run_create_failed: ${errorMessage2}`;
|
|
2853
|
-
skipReasons.push(skipMsg);
|
|
2854
|
-
client.logger?.({
|
|
2855
|
-
level: "warn",
|
|
2856
|
-
message: "Failed to record run for fetch",
|
|
2857
|
-
meta: {
|
|
2858
|
-
url: redactUrl(url),
|
|
2859
|
-
capabilityId: effectiveCapabilityId,
|
|
2860
|
-
error: errorMessage2,
|
|
2861
|
-
errorCode: code
|
|
2862
|
-
}
|
|
2796
|
+
// Provision a managed (Privy-embedded) wallet for the session user.
|
|
2797
|
+
// Idempotent server-side — repeat calls return the existing wallet.
|
|
2798
|
+
provision = (opts = {}) => request(
|
|
2799
|
+
this.client,
|
|
2800
|
+
{
|
|
2801
|
+
method: "POST",
|
|
2802
|
+
path: "/v1/users/me/wallets/provision",
|
|
2803
|
+
signal: opts.signal
|
|
2804
|
+
},
|
|
2805
|
+
userWalletDtoSchema
|
|
2806
|
+
);
|
|
2807
|
+
// Relays a BYO-wallet-signed EIP-3009 ReceiveWithAuthorization, sweeping
|
|
2808
|
+
// USDC from the BYO wallet to the user's Zero wallet on Base via the
|
|
2809
|
+
// gas-paying relayer. The SDK does not author the authorization (chain
|
|
2810
|
+
// nonce + EIP-712 domain coupling stays caller-side); it only forwards
|
|
2811
|
+
// the signed payload. Session-only.
|
|
2812
|
+
migrateAuthorization = (authorization, opts = {}) => request(
|
|
2813
|
+
this.client,
|
|
2814
|
+
{
|
|
2815
|
+
method: "POST",
|
|
2816
|
+
path: "/v1/users/me/wallets/migrate",
|
|
2817
|
+
body: { authorization },
|
|
2818
|
+
signal: opts.signal
|
|
2819
|
+
},
|
|
2820
|
+
migrateAuthorizationResponseSchema
|
|
2821
|
+
);
|
|
2822
|
+
// Routes by identity: account mode signs EIP-191 against /v1/wallet/fund-url,
|
|
2823
|
+
// session mode hits /v1/users/me/fund-url (Bearer-authed, wallet resolved
|
|
2824
|
+
// server-side). Both honor `provider` (coinbase | stripe).
|
|
2825
|
+
fundingUrl = async (opts = {}) => {
|
|
2826
|
+
const credentials = this.client.getCredentials();
|
|
2827
|
+
const path = credentials.kind === "account" ? "/v1/wallet/fund-url" : credentials.kind === "session" ? "/v1/users/me/fund-url" : null;
|
|
2828
|
+
if (!path) {
|
|
2829
|
+
throw new ZeroWalletError(
|
|
2830
|
+
"fundingUrl() requires a client constructed with an account or session credentials"
|
|
2831
|
+
);
|
|
2832
|
+
}
|
|
2833
|
+
const query = {
|
|
2834
|
+
provider: opts.provider ?? "coinbase"
|
|
2835
|
+
};
|
|
2836
|
+
if (opts.amount) query.amount = opts.amount;
|
|
2837
|
+
const { url } = await request(
|
|
2838
|
+
this.client,
|
|
2839
|
+
{ method: "GET", path, query, signal: opts.signal },
|
|
2840
|
+
fundingUrlResponseSchema
|
|
2841
|
+
);
|
|
2842
|
+
return url;
|
|
2843
|
+
};
|
|
2844
|
+
getPublicClient = (chain) => {
|
|
2845
|
+
if (chain === "base") {
|
|
2846
|
+
if (!this.clients.base) {
|
|
2847
|
+
this.clients.base = createPublicClient({
|
|
2848
|
+
chain: baseChain,
|
|
2849
|
+
transport: http()
|
|
2863
2850
|
});
|
|
2864
2851
|
}
|
|
2852
|
+
return this.clients.base;
|
|
2865
2853
|
}
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
body,
|
|
2875
|
-
bodyRaw
|
|
2854
|
+
if (!this.clients.tempo) {
|
|
2855
|
+
this.clients.tempo = createPublicClient({
|
|
2856
|
+
// biome-ignore lint/suspicious/noExplicitAny: tempo is a custom chain object that viem's stricter Chain type rejects shape-wise but accepts at runtime
|
|
2857
|
+
chain: tempoChain2,
|
|
2858
|
+
transport: http()
|
|
2859
|
+
});
|
|
2860
|
+
}
|
|
2861
|
+
return this.clients.tempo;
|
|
2876
2862
|
};
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2863
|
+
/**
|
|
2864
|
+
* @internal
|
|
2865
|
+
*
|
|
2866
|
+
* Per-chain USDC balance read. Exposed (rather than private) so unit
|
|
2867
|
+
* tests can stub it without standing up a real viem public client —
|
|
2868
|
+
* viem's `http()` transport doesn't go through `client.fetchImpl`, so
|
|
2869
|
+
* the standard mock-fetch trick doesn't cover this path.
|
|
2870
|
+
*/
|
|
2871
|
+
readChainBalance = async (chain, address) => {
|
|
2872
|
+
const publicClient = this.getPublicClient(chain);
|
|
2873
|
+
const tokenAddress = chain === "base" ? USDC_BASE2 : USDC_TEMPO2;
|
|
2874
|
+
const balance = await publicClient.readContract({
|
|
2875
|
+
address: tokenAddress,
|
|
2876
|
+
abi: ERC20_BALANCE_ABI2,
|
|
2877
|
+
functionName: "balanceOf",
|
|
2878
|
+
args: [address]
|
|
2879
|
+
});
|
|
2880
|
+
return balance;
|
|
2881
|
+
};
|
|
2882
|
+
};
|
|
2883
|
+
|
|
2884
|
+
// src/options.ts
|
|
2885
|
+
var DEFAULT_BASE_URL = "https://api.zero.xyz";
|
|
2886
|
+
var DEFAULT_TIMEOUT_MS = 18e4;
|
|
2887
|
+
var DEFAULT_MAX_RETRIES = 2;
|
|
2888
|
+
|
|
2889
|
+
// src/auth/credentials.ts
|
|
2890
|
+
var noopRefresh = async () => {
|
|
2890
2891
|
};
|
|
2891
2892
|
var searchResultSchema = z.object({
|
|
2892
2893
|
id: z.string(),
|
|
2894
|
+
// Opaque attribution token (format: `z_{shortId}.{position}`). Optional
|
|
2895
|
+
// so old API deploys that don't emit it still parse cleanly. Pass as
|
|
2896
|
+
// capabilityId to client.fetch() for exact search attribution.
|
|
2897
|
+
token: z.string().nullable().optional(),
|
|
2893
2898
|
position: z.number(),
|
|
2894
2899
|
slug: z.string(),
|
|
2895
2900
|
name: z.string(),
|
|
@@ -2908,6 +2913,9 @@ var searchResultSchema = z.object({
|
|
|
2908
2913
|
cost: z.object({ amount: z.string(), asset: z.string() }),
|
|
2909
2914
|
reviewCount: z.number().optional(),
|
|
2910
2915
|
rating: ratingSchema,
|
|
2916
|
+
// All-time activation count (non-deleted runs), mirroring capabilities.ts.
|
|
2917
|
+
// Optional so a response from an older API deploy still parses (ZERO-385).
|
|
2918
|
+
activationCount: z.number().optional(),
|
|
2911
2919
|
availabilityStatus: z.enum(["healthy", "unknown", "down"]).nullable().optional(),
|
|
2912
2920
|
/**
|
|
2913
2921
|
* @deprecated The API no longer emits `displayStatus`; it has been replaced
|
|
@@ -3299,6 +3307,6 @@ var ZeroClient = class _ZeroClient {
|
|
|
3299
3307
|
};
|
|
3300
3308
|
};
|
|
3301
3309
|
|
|
3302
|
-
export { Auth, AuthDevice, BUG_REPORT_CATEGORIES, BugReports, Capabilities, DEFAULT_BASE_URL, DEFAULT_MAX_RETRIES, DEFAULT_TIMEOUT_MS, FETCH_SKIP_REASONS, FETCH_WARNINGS, Payments, Runs, SDK_VERSION, TEMPO_CHAIN_ID, TEMPO_TESTNET_CHAIN_ID, Wallet, ZeroApiError, ZeroAuthError, ZeroClient, ZeroConfigurationError, ZeroError, ZeroPaymentError, ZeroSessionCloseFailedError, ZeroTimeoutError, ZeroValidationError, ZeroWalletError, asSchemaNode, coerceTempoChainId, createManagedAccount, extractInputEnvelope, normalizeTransportEnvelopeRequest, paymentHasAnchor, tempoChainLabelFromId, unwrapTransportEnvelope };
|
|
3303
|
-
//# sourceMappingURL=chunk-
|
|
3304
|
-
//# sourceMappingURL=chunk-
|
|
3310
|
+
export { Auth, AuthDevice, BUG_REPORT_CATEGORIES, BugReports, Capabilities, DEFAULT_BASE_URL, DEFAULT_MAX_RETRIES, DEFAULT_TIMEOUT_MS, FETCH_SKIP_REASONS, FETCH_WARNINGS, Payments, Runs, SDK_VERSION, TEMPO_CHAIN_ID, TEMPO_TESTNET_CHAIN_ID, Wallet, ZeroApiError, ZeroAuthError, ZeroClient, ZeroConfigurationError, ZeroError, ZeroPaymentError, ZeroSessionCloseFailedError, ZeroTimeoutError, ZeroValidationError, ZeroWalletError, asSchemaNode, coerceTempoChainId, createManagedAccount, extractInputEnvelope, isShortToken, normalizeTransportEnvelopeRequest, paymentHasAnchor, tempoChainLabelFromId, unwrapTransportEnvelope };
|
|
3311
|
+
//# sourceMappingURL=chunk-26IHUYNK.js.map
|
|
3312
|
+
//# sourceMappingURL=chunk-26IHUYNK.js.map
|