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