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