codai-sdk 0.4.0 → 0.6.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/README.md +75 -2
- package/dist/index.d.ts +2591 -1560
- package/dist/index.js +237 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1219,6 +1219,213 @@ var Projects = class {
|
|
|
1219
1219
|
}
|
|
1220
1220
|
};
|
|
1221
1221
|
|
|
1222
|
+
// src/resources/resolve.ts
|
|
1223
|
+
function verifyResolveWebhook(secret, rawBody, signatureHeader, opts = {}) {
|
|
1224
|
+
if (!secret || !signatureHeader) return false;
|
|
1225
|
+
let t;
|
|
1226
|
+
const v1s = [];
|
|
1227
|
+
for (const part of signatureHeader.split(",")) {
|
|
1228
|
+
const eq = part.indexOf("=");
|
|
1229
|
+
if (eq === -1) continue;
|
|
1230
|
+
const k = part.slice(0, eq).trim();
|
|
1231
|
+
const v = part.slice(eq + 1).trim();
|
|
1232
|
+
if (k === "t") t = v;
|
|
1233
|
+
else if (k === "v1") v1s.push(v.toLowerCase());
|
|
1234
|
+
}
|
|
1235
|
+
if (!t || !/^\d+$/.test(t) || v1s.length === 0) return false;
|
|
1236
|
+
const tolerance = opts.toleranceSeconds ?? 300;
|
|
1237
|
+
const now = opts.nowSeconds ?? Math.floor(Date.now() / 1e3);
|
|
1238
|
+
if (Math.abs(now - Number(t)) > tolerance) return false;
|
|
1239
|
+
const { createHmac, timingSafeEqual } = nodeCrypto();
|
|
1240
|
+
const enc2 = new TextEncoder();
|
|
1241
|
+
const body = typeof rawBody === "string" ? enc2.encode(rawBody) : rawBody;
|
|
1242
|
+
const expected = createHmac("sha256", secret).update(enc2.encode(`${t}.`)).update(body).digest();
|
|
1243
|
+
return v1s.some((v1) => {
|
|
1244
|
+
if (!/^[0-9a-f]{64}$/.test(v1)) return false;
|
|
1245
|
+
return timingSafeEqual(hexToBytes(v1), expected);
|
|
1246
|
+
});
|
|
1247
|
+
}
|
|
1248
|
+
function nodeCrypto() {
|
|
1249
|
+
const proc = globalThis.process;
|
|
1250
|
+
const mod = proc?.getBuiltinModule?.("node:crypto");
|
|
1251
|
+
if (!mod) {
|
|
1252
|
+
throw new Error(
|
|
1253
|
+
"verifyResolveWebhook needs node:crypto (Node 20.16+/22.3+, Bun or Deno); run it server-side"
|
|
1254
|
+
);
|
|
1255
|
+
}
|
|
1256
|
+
return mod;
|
|
1257
|
+
}
|
|
1258
|
+
function hexToBytes(hex) {
|
|
1259
|
+
const out = new Uint8Array(hex.length / 2);
|
|
1260
|
+
for (let i = 0; i < out.length; i++) out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
1261
|
+
return out;
|
|
1262
|
+
}
|
|
1263
|
+
var RESOLVE_TERMINAL_STATUSES = /* @__PURE__ */ new Set([
|
|
1264
|
+
"resolved",
|
|
1265
|
+
"failed",
|
|
1266
|
+
"declined",
|
|
1267
|
+
"error"
|
|
1268
|
+
]);
|
|
1269
|
+
var ResolveError = class extends CodaiError {
|
|
1270
|
+
/** The `error` field of the body: a message, or Zod `flatten()` details on a 400. */
|
|
1271
|
+
detail;
|
|
1272
|
+
constructor(message, status, body, meta = {}) {
|
|
1273
|
+
super(message, status, body, meta);
|
|
1274
|
+
this.name = "ResolveError";
|
|
1275
|
+
this.detail = extractDetail(body);
|
|
1276
|
+
}
|
|
1277
|
+
};
|
|
1278
|
+
var ResolveInsufficientFundsError = class extends ResolveError {
|
|
1279
|
+
/** Wallet balance, in `currency` units (EUR micros). */
|
|
1280
|
+
balance;
|
|
1281
|
+
/** Amount the hold needs, in `currency` units (EUR micros). */
|
|
1282
|
+
required;
|
|
1283
|
+
currency;
|
|
1284
|
+
/** pay.codai.ro checkout link for the shortfall — open it, then retry `accept()`. */
|
|
1285
|
+
topupUrl;
|
|
1286
|
+
constructor(message, body, meta = {}) {
|
|
1287
|
+
super(message, 402, body, meta);
|
|
1288
|
+
this.name = "ResolveInsufficientFundsError";
|
|
1289
|
+
this.balance = body.balance;
|
|
1290
|
+
this.required = body.required;
|
|
1291
|
+
this.currency = body.currency;
|
|
1292
|
+
this.topupUrl = body.topup_url;
|
|
1293
|
+
}
|
|
1294
|
+
};
|
|
1295
|
+
var Resolve = class {
|
|
1296
|
+
constructor(http) {
|
|
1297
|
+
this.http = http;
|
|
1298
|
+
}
|
|
1299
|
+
http;
|
|
1300
|
+
/**
|
|
1301
|
+
* `POST /v1/resolve/jobs` — submit a public GitHub repo + issue for triage.
|
|
1302
|
+
* Returns a quote (`status: 'quoted'`), a free-tier auto-accept
|
|
1303
|
+
* (`status: 'accepted'`) or a decline (`status: 'declined'`). Never retried:
|
|
1304
|
+
* a retry could create a second job.
|
|
1305
|
+
*/
|
|
1306
|
+
submit(body, opts = {}) {
|
|
1307
|
+
if (!body.issue_url && !body.issue_text)
|
|
1308
|
+
throw new TypeError("resolve.submit: one of issue_url or issue_text is required");
|
|
1309
|
+
return this.call("/v1/resolve/jobs", {
|
|
1310
|
+
method: "POST",
|
|
1311
|
+
body,
|
|
1312
|
+
noRetry: true,
|
|
1313
|
+
signal: opts.signal
|
|
1314
|
+
});
|
|
1315
|
+
}
|
|
1316
|
+
/** `GET /v1/resolve/jobs/{id}` — status, quote, reasons, cost and attestation pointer. */
|
|
1317
|
+
get(id, opts = {}) {
|
|
1318
|
+
return this.call(`/v1/resolve/jobs/${encodeURIComponent(id)}`, {
|
|
1319
|
+
signal: opts.signal
|
|
1320
|
+
});
|
|
1321
|
+
}
|
|
1322
|
+
/**
|
|
1323
|
+
* `POST /v1/resolve/jobs/{id}/accept` — accept a paid quote and start the job.
|
|
1324
|
+
* Throws `ResolveInsufficientFundsError` (402, carries `topupUrl`) when the
|
|
1325
|
+
* wallet is short, `ResolveError` 409 when the job is not `quoted`.
|
|
1326
|
+
*/
|
|
1327
|
+
accept(id, opts = {}) {
|
|
1328
|
+
return this.call(`/v1/resolve/jobs/${encodeURIComponent(id)}/accept`, {
|
|
1329
|
+
method: "POST",
|
|
1330
|
+
rawBody: "",
|
|
1331
|
+
// no body — sent with content-length: 0
|
|
1332
|
+
noRetry: true,
|
|
1333
|
+
signal: opts.signal
|
|
1334
|
+
});
|
|
1335
|
+
}
|
|
1336
|
+
/**
|
|
1337
|
+
* `GET /v1/resolve/attestations/{id}` — public execution proof (no auth needed
|
|
1338
|
+
* server-side). Accepts the attestation id or the job's `attestation_url`.
|
|
1339
|
+
*/
|
|
1340
|
+
attestation(idOrUrl, opts = {}) {
|
|
1341
|
+
const id = idOrUrl.replace(/^.*\/v1\/resolve\/attestations\//, "");
|
|
1342
|
+
return this.call(`/v1/resolve/attestations/${encodeURIComponent(id)}`, {
|
|
1343
|
+
signal: opts.signal
|
|
1344
|
+
});
|
|
1345
|
+
}
|
|
1346
|
+
/** `GET /health` — liveness of the Resolve service. */
|
|
1347
|
+
health(opts = {}) {
|
|
1348
|
+
return this.call("/health", { signal: opts.signal });
|
|
1349
|
+
}
|
|
1350
|
+
/**
|
|
1351
|
+
* Poll `get(id)` until the job reaches a terminal status (`resolved`,
|
|
1352
|
+
* `failed`, `declined`, `error`) or one of `opts.until`. A `quoted` job
|
|
1353
|
+
* does not move until you `accept()` it.
|
|
1354
|
+
*/
|
|
1355
|
+
async waitFor(id, opts = {}) {
|
|
1356
|
+
const intervalMs = opts.intervalMs ?? 5e3;
|
|
1357
|
+
const timeoutMs = opts.timeoutMs ?? 18e5;
|
|
1358
|
+
const until = new Set(opts.until ?? RESOLVE_TERMINAL_STATUSES);
|
|
1359
|
+
const deadline = Date.now() + timeoutMs;
|
|
1360
|
+
for (; ; ) {
|
|
1361
|
+
const job = await this.get(id, { signal: opts.signal });
|
|
1362
|
+
opts.onPoll?.(job);
|
|
1363
|
+
if (until.has(job.status)) return job;
|
|
1364
|
+
const left = deadline - Date.now();
|
|
1365
|
+
if (left <= 0)
|
|
1366
|
+
throw new ResolveError(
|
|
1367
|
+
`codai-resolve job ${id} still ${job.status} after ${timeoutMs} ms`,
|
|
1368
|
+
0,
|
|
1369
|
+
job
|
|
1370
|
+
);
|
|
1371
|
+
await abortableSleep(Math.min(intervalMs, left), opts.signal);
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
async call(path, init) {
|
|
1375
|
+
try {
|
|
1376
|
+
return await this.http.json(path, init);
|
|
1377
|
+
} catch (err) {
|
|
1378
|
+
throw toResolveError(path, err);
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
};
|
|
1382
|
+
function extractDetail(body) {
|
|
1383
|
+
if (body && typeof body === "object" && "error" in body) {
|
|
1384
|
+
const e = body.error;
|
|
1385
|
+
if (typeof e === "string") return e;
|
|
1386
|
+
if (e && typeof e === "object") return e;
|
|
1387
|
+
}
|
|
1388
|
+
return null;
|
|
1389
|
+
}
|
|
1390
|
+
function isInsufficientFunds(body) {
|
|
1391
|
+
return !!body && typeof body === "object" && body.error === "insufficient_funds" && typeof body.topup_url === "string";
|
|
1392
|
+
}
|
|
1393
|
+
function toResolveError(path, err) {
|
|
1394
|
+
if (!(err instanceof CodaiError) || err instanceof ResolveError) return err;
|
|
1395
|
+
if (err.status === 0) return err;
|
|
1396
|
+
const meta = { requestId: err.requestId, retryAfter: err.retryAfter };
|
|
1397
|
+
if (err.status === 402 && isInsufficientFunds(err.body)) {
|
|
1398
|
+
const b = err.body;
|
|
1399
|
+
return new ResolveInsufficientFundsError(
|
|
1400
|
+
`codai-resolve ${path} \u2192 402: insufficient funds (balance ${b.balance}, required ${b.required} ${b.currency}); top up at ${b.topup_url}`,
|
|
1401
|
+
b,
|
|
1402
|
+
meta
|
|
1403
|
+
);
|
|
1404
|
+
}
|
|
1405
|
+
const detail = extractDetail(err.body);
|
|
1406
|
+
const text = detail === null ? "" : `: ${typeof detail === "string" ? detail : JSON.stringify(detail)}`;
|
|
1407
|
+
return new ResolveError(
|
|
1408
|
+
`codai-resolve ${path} \u2192 ${err.status}${text}`,
|
|
1409
|
+
err.status,
|
|
1410
|
+
err.body,
|
|
1411
|
+
meta
|
|
1412
|
+
);
|
|
1413
|
+
}
|
|
1414
|
+
function abortableSleep(ms, signal) {
|
|
1415
|
+
return new Promise((resolve, reject) => {
|
|
1416
|
+
if (signal?.aborted) return reject(signal.reason);
|
|
1417
|
+
const onAbort = () => {
|
|
1418
|
+
clearTimeout(t);
|
|
1419
|
+
reject(signal?.reason);
|
|
1420
|
+
};
|
|
1421
|
+
const t = setTimeout(() => {
|
|
1422
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1423
|
+
resolve();
|
|
1424
|
+
}, ms);
|
|
1425
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1426
|
+
});
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1222
1429
|
// src/resources/responses.ts
|
|
1223
1430
|
var Responses = class {
|
|
1224
1431
|
constructor(http) {
|
|
@@ -1663,6 +1870,14 @@ var Notifications = class {
|
|
|
1663
1870
|
markRead(ids, opts = {}) {
|
|
1664
1871
|
return this.http.json("/v1/notifications/read", { method: "POST", body: ids ? { ids } : {}, ext: opts.ext, signal: opts.signal });
|
|
1665
1872
|
}
|
|
1873
|
+
/** `DELETE /v1/notifications/:id` — `{ deleted: 0 }` when the id is missing or not yours. */
|
|
1874
|
+
delete(id, opts = {}) {
|
|
1875
|
+
return this.http.json(`/v1/notifications/${encodeURIComponent(String(id))}`, { method: "DELETE", ext: opts.ext, signal: opts.signal });
|
|
1876
|
+
}
|
|
1877
|
+
/** `POST /v1/notifications/delete` — 1..200 ids; ids of other users are skipped. */
|
|
1878
|
+
deleteMany(ids, opts = {}) {
|
|
1879
|
+
return this.http.json("/v1/notifications/delete", { method: "POST", body: { ids }, ext: opts.ext, signal: opts.signal });
|
|
1880
|
+
}
|
|
1666
1881
|
};
|
|
1667
1882
|
|
|
1668
1883
|
// src/resources/tokens.ts
|
|
@@ -1756,6 +1971,8 @@ var OPERATION_METHODS = {
|
|
|
1756
1971
|
receiveTriggerHook: "triggers.fireHook",
|
|
1757
1972
|
listNotifications: "notifications.list",
|
|
1758
1973
|
markNotificationsRead: "notifications.markRead",
|
|
1974
|
+
deleteNotification: "notifications.delete",
|
|
1975
|
+
deleteNotifications: "notifications.deleteMany",
|
|
1759
1976
|
// Sessions
|
|
1760
1977
|
createSession: "sessions.create",
|
|
1761
1978
|
listSessions: "sessions.list",
|
|
@@ -1837,6 +2054,13 @@ var STREAMING_OPERATIONS = /* @__PURE__ */ new Set([
|
|
|
1837
2054
|
"streamSession",
|
|
1838
2055
|
"streamHost"
|
|
1839
2056
|
]);
|
|
2057
|
+
var RESOLVE_OPERATION_METHODS = {
|
|
2058
|
+
resolveHealth: "resolve.health",
|
|
2059
|
+
createResolveJob: "resolve.submit",
|
|
2060
|
+
getResolveJob: "resolve.get",
|
|
2061
|
+
acceptResolveJob: "resolve.accept",
|
|
2062
|
+
getResolveAttestation: "resolve.attestation"
|
|
2063
|
+
};
|
|
1840
2064
|
|
|
1841
2065
|
// src/index.ts
|
|
1842
2066
|
var Codai = class {
|
|
@@ -1869,6 +2093,8 @@ var Codai = class {
|
|
|
1869
2093
|
receipt;
|
|
1870
2094
|
feedback;
|
|
1871
2095
|
phoneModels;
|
|
2096
|
+
/** codai Resolve — execution-verified bug fixes (separate host, `resolveBaseUrl`). */
|
|
2097
|
+
resolve;
|
|
1872
2098
|
constructor(opts) {
|
|
1873
2099
|
if (!opts.apiKey) throw new Error("Codai: apiKey is required");
|
|
1874
2100
|
this.http = new HttpClient({
|
|
@@ -1907,6 +2133,16 @@ var Codai = class {
|
|
|
1907
2133
|
this.account = new Account(http);
|
|
1908
2134
|
this.receipt = new Receipts(http);
|
|
1909
2135
|
this.phoneModels = new PhoneModels(http);
|
|
2136
|
+
this.resolve = new Resolve(
|
|
2137
|
+
new HttpClient({
|
|
2138
|
+
apiKey: opts.apiKey,
|
|
2139
|
+
baseUrl: opts.resolveBaseUrl ?? "https://resolve.codai.ro",
|
|
2140
|
+
timeoutMs: opts.timeoutMs ?? 12e4,
|
|
2141
|
+
maxRetries: opts.maxRetries ?? 2,
|
|
2142
|
+
fetch: opts.fetch,
|
|
2143
|
+
defaults: opts.client ? { client: opts.client } : {}
|
|
2144
|
+
})
|
|
2145
|
+
);
|
|
1910
2146
|
this.chat = callable(
|
|
1911
2147
|
new Chat(http),
|
|
1912
2148
|
(o) => this.chat.completions.create(this.legacyBody(o), {
|
|
@@ -1991,6 +2227,6 @@ function allKeys(obj) {
|
|
|
1991
2227
|
}
|
|
1992
2228
|
var index_default = Codai;
|
|
1993
2229
|
|
|
1994
|
-
export { Account, AgentRuns, Agents, Audio, Chat, ChatCompletions, Codai, CodaiError, Devices, Embeddings, EnvironmentMembers, EnvironmentPorts, EnvironmentSecrets, EnvironmentTasks, Environments, Feedback, Health, Hosts, HttpClient, Messages, Models, Notifications, OPERATION_METHODS, OrgMembers, Orgs, PhoneModels, Projects, Receipts, Responses, STREAMING_OPERATIONS, SessionControls, SessionEvents, SessionLease, SessionShares, Sessions, Tasks, Tokens, Tools, Triggers, index_default as default, extensionHeaders, frameJson, mergeToolCall, readSse };
|
|
2230
|
+
export { Account, AgentRuns, Agents, Audio, Chat, ChatCompletions, Codai, CodaiError, Devices, Embeddings, EnvironmentMembers, EnvironmentPorts, EnvironmentSecrets, EnvironmentTasks, Environments, Feedback, Health, Hosts, HttpClient, Messages, Models, Notifications, OPERATION_METHODS, OrgMembers, Orgs, PhoneModels, Projects, RESOLVE_OPERATION_METHODS, RESOLVE_TERMINAL_STATUSES, Receipts, Resolve, ResolveError, ResolveInsufficientFundsError, Responses, STREAMING_OPERATIONS, SessionControls, SessionEvents, SessionLease, SessionShares, Sessions, Tasks, Tokens, Tools, Triggers, index_default as default, extensionHeaders, frameJson, mergeToolCall, readSse, verifyResolveWebhook };
|
|
1995
2231
|
//# sourceMappingURL=index.js.map
|
|
1996
2232
|
//# sourceMappingURL=index.js.map
|