codai-sdk 0.2.0 → 0.3.1
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 +181 -4
- package/dist/index.d.ts +7551 -128
- package/dist/index.js +1446 -251
- package/dist/index.js.map +1 -1
- package/package.json +12 -7
package/dist/index.js
CHANGED
|
@@ -1,15 +1,483 @@
|
|
|
1
|
-
// src/
|
|
1
|
+
// src/http.ts
|
|
2
2
|
var CodaiError = class extends Error {
|
|
3
|
-
constructor(message, status, body) {
|
|
3
|
+
constructor(message, status, body, meta = {}) {
|
|
4
4
|
super(message);
|
|
5
5
|
this.status = status;
|
|
6
6
|
this.body = body;
|
|
7
7
|
this.name = "CodaiError";
|
|
8
|
+
this.code = meta.code ?? extractCode(body);
|
|
9
|
+
this.requestId = meta.requestId ?? null;
|
|
10
|
+
this.retryAfter = meta.retryAfter ?? null;
|
|
8
11
|
}
|
|
9
12
|
status;
|
|
10
13
|
body;
|
|
14
|
+
/** Stable gateway error code (`invalid_api_key`, `rate_limit_exceeded`, …) when the body was an error envelope. */
|
|
15
|
+
code;
|
|
16
|
+
/** `x-codai-trace-id` (or `x-request-id`) of the failed response, when present. */
|
|
17
|
+
requestId;
|
|
18
|
+
/** Seconds to wait before retrying, from `Retry-After` on 429s. */
|
|
19
|
+
retryAfter;
|
|
11
20
|
};
|
|
21
|
+
function extractCode(body) {
|
|
22
|
+
if (body && typeof body === "object") {
|
|
23
|
+
const err = body.error;
|
|
24
|
+
if (err && typeof err === "object" && typeof err.code === "string")
|
|
25
|
+
return err.code;
|
|
26
|
+
}
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
function extractMessage(body) {
|
|
30
|
+
if (body && typeof body === "object") {
|
|
31
|
+
const err = body.error;
|
|
32
|
+
if (err && typeof err === "object" && typeof err.message === "string") return err.message;
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
12
36
|
var RETRYABLE = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
|
|
37
|
+
var EXTENSION_HEADERS = {
|
|
38
|
+
sessionId: "x-codai-session-id",
|
|
39
|
+
requestId: "x-request-id",
|
|
40
|
+
effort: "x-codai-effort",
|
|
41
|
+
thinking: "x-codai-thinking",
|
|
42
|
+
thinkingBudget: "x-codai-thinking-budget",
|
|
43
|
+
thinkingPin: "x-codai-thinking-pin",
|
|
44
|
+
cache: "x-codai-cache",
|
|
45
|
+
noTask: "x-codai-no-task",
|
|
46
|
+
taskId: "x-codai-task-id",
|
|
47
|
+
taskOutcome: "x-codai-task-outcome",
|
|
48
|
+
taskEvidence: "x-codai-task-evidence",
|
|
49
|
+
client: "x-codai-client",
|
|
50
|
+
incognito: "x-codai-incognito",
|
|
51
|
+
noRecall: "x-codai-no-recall",
|
|
52
|
+
provenOnly: "x-codai-proven-only",
|
|
53
|
+
newSession: "x-codai-new-session",
|
|
54
|
+
repo: "x-codai-repo",
|
|
55
|
+
agentId: "x-codai-agent-id",
|
|
56
|
+
disableSubagents: "x-codai-disable-subagents",
|
|
57
|
+
mode: "x-codai-mode",
|
|
58
|
+
serverTools: "x-codai-server-tools",
|
|
59
|
+
orchestrate: "x-codai-orchestrate",
|
|
60
|
+
cascade: "x-codai-cascade",
|
|
61
|
+
bestOf: "x-codai-best-of",
|
|
62
|
+
bestOfDepth: "x-codai-best-of-depth",
|
|
63
|
+
reflect: "x-codai-reflect",
|
|
64
|
+
stepVerify: "x-codai-step-verify",
|
|
65
|
+
plan: "x-codai-plan",
|
|
66
|
+
consensus: "x-codai-consensus",
|
|
67
|
+
compact: "x-codai-compact",
|
|
68
|
+
retrieval: "x-codai-retrieval",
|
|
69
|
+
heuristics: "x-codai-heuristics",
|
|
70
|
+
identity: "x-codai-identity",
|
|
71
|
+
playbook: "x-codai-playbook",
|
|
72
|
+
noPlaybook: "x-codai-no-playbook",
|
|
73
|
+
debug: "x-codai-debug",
|
|
74
|
+
device: "x-codai-device",
|
|
75
|
+
deviceName: "x-codai-device-name",
|
|
76
|
+
devicePlatform: "x-codai-device-platform",
|
|
77
|
+
pushToken: "x-codai-push-token",
|
|
78
|
+
shareToken: "x-codai-share-token"
|
|
79
|
+
};
|
|
80
|
+
function extensionHeaders(ext) {
|
|
81
|
+
const out = {};
|
|
82
|
+
if (!ext) return out;
|
|
83
|
+
for (const [key, header] of Object.entries(EXTENSION_HEADERS)) {
|
|
84
|
+
const value = ext[key];
|
|
85
|
+
if (value === void 0 || value === null) continue;
|
|
86
|
+
if (typeof value === "boolean") out[header] = value ? "1" : "0";
|
|
87
|
+
else if (key === "compact" && value === "auto") out[header] = "1";
|
|
88
|
+
else out[header] = String(value);
|
|
89
|
+
}
|
|
90
|
+
if (ext.headers) Object.assign(out, ext.headers);
|
|
91
|
+
return out;
|
|
92
|
+
}
|
|
93
|
+
var HttpClient = class {
|
|
94
|
+
apiKey;
|
|
95
|
+
baseUrl;
|
|
96
|
+
timeoutMs;
|
|
97
|
+
maxRetries;
|
|
98
|
+
defaults;
|
|
99
|
+
fetchImpl;
|
|
100
|
+
constructor(opts) {
|
|
101
|
+
this.apiKey = opts.apiKey;
|
|
102
|
+
this.baseUrl = opts.baseUrl.replace(/\/$/, "");
|
|
103
|
+
this.timeoutMs = opts.timeoutMs;
|
|
104
|
+
this.maxRetries = opts.maxRetries;
|
|
105
|
+
this.defaults = opts.defaults ?? {};
|
|
106
|
+
this.fetchImpl = opts.fetch ?? ((input, init) => fetch(input, init));
|
|
107
|
+
}
|
|
108
|
+
url(path, query) {
|
|
109
|
+
const url = new URL(`${this.baseUrl}${path}`);
|
|
110
|
+
if (query) {
|
|
111
|
+
for (const [k, v] of Object.entries(query)) {
|
|
112
|
+
if (v === void 0 || v === null) continue;
|
|
113
|
+
url.searchParams.set(k, typeof v === "boolean" ? v ? "1" : "0" : String(v));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return url.toString();
|
|
117
|
+
}
|
|
118
|
+
headers(ext, json = true) {
|
|
119
|
+
return {
|
|
120
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
121
|
+
...json ? { "Content-Type": "application/json" } : {},
|
|
122
|
+
...extensionHeaders(this.defaults),
|
|
123
|
+
...extensionHeaders(ext)
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
/** Perform a request; throws `CodaiError` on non-2xx (after retries on 429/5xx). */
|
|
127
|
+
async request(path, init = {}) {
|
|
128
|
+
const url = this.url(path, init.query);
|
|
129
|
+
const hasJson = init.body !== void 0;
|
|
130
|
+
const headers = this.headers(init.ext, hasJson);
|
|
131
|
+
const method = init.method ?? (hasJson || init.rawBody !== void 0 ? "POST" : "GET");
|
|
132
|
+
const retries = init.noRetry || init.rawBody !== void 0 ? 0 : this.maxRetries;
|
|
133
|
+
const timeout = init.timeoutMs ?? this.timeoutMs;
|
|
134
|
+
const accept = new Set(init.acceptStatus ?? []);
|
|
135
|
+
let lastErr;
|
|
136
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
137
|
+
const signal = init.signal ?? (timeout > 0 ? AbortSignal.timeout(timeout) : void 0);
|
|
138
|
+
try {
|
|
139
|
+
const res = await this.fetchImpl(url, {
|
|
140
|
+
method,
|
|
141
|
+
headers,
|
|
142
|
+
...hasJson ? { body: JSON.stringify(init.body) } : {},
|
|
143
|
+
...init.rawBody !== void 0 ? { body: init.rawBody } : {},
|
|
144
|
+
...signal ? { signal } : {}
|
|
145
|
+
});
|
|
146
|
+
if (res.ok || accept.has(res.status)) return res;
|
|
147
|
+
if (RETRYABLE.has(res.status) && attempt < retries) {
|
|
148
|
+
await sleep(500 * 2 ** attempt + Math.random() * 250);
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
throw await this.toError(path, res);
|
|
152
|
+
} catch (err) {
|
|
153
|
+
if (err instanceof CodaiError) throw err;
|
|
154
|
+
lastErr = err;
|
|
155
|
+
if (init.signal?.aborted) throw err;
|
|
156
|
+
if (attempt < retries) {
|
|
157
|
+
await sleep(500 * 2 ** attempt);
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
throw new CodaiError(`codai ${path} failed after retries: ${String(lastErr)}`, 0, null);
|
|
163
|
+
}
|
|
164
|
+
async json(path, init = {}) {
|
|
165
|
+
const res = await this.request(path, init);
|
|
166
|
+
return await res.json();
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Open a `text/event-stream` and iterate parsed frames. Frames are
|
|
170
|
+
* `\n\n`-separated blocks; each yields `{ event, data }` where `event`
|
|
171
|
+
* defaults to `message` and `data` is the joined `data:` payload (raw
|
|
172
|
+
* string — the caller decides whether it is JSON or `[DONE]`). Comment
|
|
173
|
+
* lines (`: ping`) are skipped. The stream is retried like any request
|
|
174
|
+
* up to the first byte; after that errors propagate.
|
|
175
|
+
*/
|
|
176
|
+
async *sse(path, init = {}) {
|
|
177
|
+
const res = await this.request(path, {
|
|
178
|
+
...init,
|
|
179
|
+
timeoutMs: init.timeoutMs ?? 0,
|
|
180
|
+
noRetry: true
|
|
181
|
+
});
|
|
182
|
+
yield* readSse(res, init.signal);
|
|
183
|
+
return res;
|
|
184
|
+
}
|
|
185
|
+
async toError(path, res) {
|
|
186
|
+
let body = null;
|
|
187
|
+
try {
|
|
188
|
+
body = await res.json();
|
|
189
|
+
} catch {
|
|
190
|
+
}
|
|
191
|
+
const retryAfterRaw = res.headers.get("retry-after");
|
|
192
|
+
const retryAfter = retryAfterRaw && /^\d+$/.test(retryAfterRaw) ? Number(retryAfterRaw) : null;
|
|
193
|
+
const message = extractMessage(body);
|
|
194
|
+
return new CodaiError(
|
|
195
|
+
message ? `codai ${path} \u2192 ${res.status}: ${message}` : `codai ${path} \u2192 ${res.status}`,
|
|
196
|
+
res.status,
|
|
197
|
+
body,
|
|
198
|
+
{
|
|
199
|
+
requestId: res.headers.get("x-codai-trace-id") ?? res.headers.get("x-request-id"),
|
|
200
|
+
retryAfter
|
|
201
|
+
}
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
async function* readSse(res, signal) {
|
|
206
|
+
const reader = res.body?.getReader();
|
|
207
|
+
if (!reader) throw new CodaiError("no response body", res.status, null);
|
|
208
|
+
const decoder = new TextDecoder();
|
|
209
|
+
let buffer = "";
|
|
210
|
+
const onAbort = () => void reader.cancel().catch(() => {
|
|
211
|
+
});
|
|
212
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
213
|
+
try {
|
|
214
|
+
for (; ; ) {
|
|
215
|
+
const { done, value } = await reader.read();
|
|
216
|
+
if (done) break;
|
|
217
|
+
buffer += decoder.decode(value, { stream: true });
|
|
218
|
+
buffer = buffer.replace(/\r\n/g, "\n");
|
|
219
|
+
const blocks = buffer.split("\n\n");
|
|
220
|
+
buffer = blocks.pop() ?? "";
|
|
221
|
+
for (const block of blocks) {
|
|
222
|
+
const frame = parseSseBlock(block);
|
|
223
|
+
if (frame) yield frame;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
const tail = buffer.trim() ? parseSseBlock(buffer) : null;
|
|
227
|
+
if (tail) yield tail;
|
|
228
|
+
} finally {
|
|
229
|
+
signal?.removeEventListener("abort", onAbort);
|
|
230
|
+
reader.releaseLock?.();
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
function parseSseBlock(block) {
|
|
234
|
+
let event = "message";
|
|
235
|
+
const data = [];
|
|
236
|
+
for (const line of block.split("\n")) {
|
|
237
|
+
if (!line || line.startsWith(":")) continue;
|
|
238
|
+
if (line.startsWith("event:")) event = line.slice(6).trim();
|
|
239
|
+
else if (line.startsWith("data:")) data.push(line.slice(5).replace(/^ /, ""));
|
|
240
|
+
}
|
|
241
|
+
if (data.length === 0) return null;
|
|
242
|
+
return { event, data: data.join("\n") };
|
|
243
|
+
}
|
|
244
|
+
function frameJson(frame) {
|
|
245
|
+
if (frame.data === "[DONE]") return null;
|
|
246
|
+
try {
|
|
247
|
+
return JSON.parse(frame.data);
|
|
248
|
+
} catch {
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
function sleep(ms) {
|
|
253
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// src/resources/account.ts
|
|
257
|
+
var Account = class {
|
|
258
|
+
constructor(http) {
|
|
259
|
+
this.http = http;
|
|
260
|
+
}
|
|
261
|
+
http;
|
|
262
|
+
/** `GET /v1/account` — the full account card. */
|
|
263
|
+
get(opts = {}) {
|
|
264
|
+
return this.http.json("/v1/account", { ext: opts.ext, signal: opts.signal });
|
|
265
|
+
}
|
|
266
|
+
/** `PATCH /v1/account` — consents, username, referral (at least one field). */
|
|
267
|
+
update(body, opts = {}) {
|
|
268
|
+
return this.http.json("/v1/account", {
|
|
269
|
+
method: "PATCH",
|
|
270
|
+
body,
|
|
271
|
+
ext: opts.ext,
|
|
272
|
+
signal: opts.signal
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
var Receipts = class {
|
|
277
|
+
constructor(http) {
|
|
278
|
+
this.http = http;
|
|
279
|
+
}
|
|
280
|
+
http;
|
|
281
|
+
/**
|
|
282
|
+
* `GET /v1/receipt` — spend receipt. `sessionId` wins over `since`; with
|
|
283
|
+
* neither, the last 24 hours.
|
|
284
|
+
*/
|
|
285
|
+
get(query = {}, opts = {}) {
|
|
286
|
+
return this.http.json("/v1/receipt", {
|
|
287
|
+
query: {
|
|
288
|
+
session_id: query.sessionId,
|
|
289
|
+
since: query.since instanceof Date ? query.since.toISOString() : query.since
|
|
290
|
+
},
|
|
291
|
+
ext: opts.ext,
|
|
292
|
+
signal: opts.signal
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
var Feedback = class {
|
|
297
|
+
constructor(http) {
|
|
298
|
+
this.http = http;
|
|
299
|
+
}
|
|
300
|
+
http;
|
|
301
|
+
/**
|
|
302
|
+
* `POST /v1/feedback` — rate one turn. Exactly one of `event_id`,
|
|
303
|
+
* `session_id`, `client_request_id` must be present.
|
|
304
|
+
*/
|
|
305
|
+
submit(body, opts = {}) {
|
|
306
|
+
return this.http.json("/v1/feedback", {
|
|
307
|
+
body,
|
|
308
|
+
ext: opts.ext,
|
|
309
|
+
signal: opts.signal
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
var PhoneModels = class {
|
|
314
|
+
constructor(http) {
|
|
315
|
+
this.http = http;
|
|
316
|
+
}
|
|
317
|
+
http;
|
|
318
|
+
/** `GET /v1/phone/models` — on-device model catalog with signed URLs. */
|
|
319
|
+
async list(opts = {}) {
|
|
320
|
+
const raw = await this.http.json("/v1/phone/models", {
|
|
321
|
+
ext: opts.ext,
|
|
322
|
+
signal: opts.signal
|
|
323
|
+
});
|
|
324
|
+
return [...raw.models ?? []];
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
// src/resources/agents.ts
|
|
329
|
+
var AgentRuns = class {
|
|
330
|
+
constructor(http) {
|
|
331
|
+
this.http = http;
|
|
332
|
+
}
|
|
333
|
+
http;
|
|
334
|
+
/** `POST /v1/agents/runs` — returns 202 with the run id. */
|
|
335
|
+
create(body, opts = {}) {
|
|
336
|
+
return this.http.json("/v1/agents/runs", {
|
|
337
|
+
body,
|
|
338
|
+
ext: opts.ext,
|
|
339
|
+
signal: opts.signal
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
/** `GET /v1/agents/runs/{id}` */
|
|
343
|
+
get(id, opts = {}) {
|
|
344
|
+
return this.http.json(`/v1/agents/runs/${encodeURIComponent(id)}`, {
|
|
345
|
+
ext: opts.ext,
|
|
346
|
+
signal: opts.signal
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
/** `GET /v1/agents/runs/{id}/steps` — newest first. */
|
|
350
|
+
async steps(id, opts = {}) {
|
|
351
|
+
const raw = await this.http.json(
|
|
352
|
+
`/v1/agents/runs/${encodeURIComponent(id)}/steps`,
|
|
353
|
+
{ ext: opts.ext, signal: opts.signal }
|
|
354
|
+
);
|
|
355
|
+
return [...raw.steps ?? []];
|
|
356
|
+
}
|
|
357
|
+
/** `GET /v1/agents/runs/stats?days=` */
|
|
358
|
+
stats(days, opts = {}) {
|
|
359
|
+
return this.http.json("/v1/agents/runs/stats", {
|
|
360
|
+
query: { days },
|
|
361
|
+
ext: opts.ext,
|
|
362
|
+
signal: opts.signal
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
/** `POST /v1/agents/runs/{id}/cancel` */
|
|
366
|
+
cancel(id, opts = {}) {
|
|
367
|
+
return this.http.json(
|
|
368
|
+
`/v1/agents/runs/${encodeURIComponent(id)}/cancel`,
|
|
369
|
+
{ method: "POST", body: {}, ext: opts.ext, signal: opts.signal }
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* `GET /v1/agents/runs/{id}/stream` — async iterator over `step` / `done` /
|
|
374
|
+
* `timeout` events. The iterator ends after `done`; on `timeout` (30 min)
|
|
375
|
+
* it also ends and you may reconnect (full replay from seq 0).
|
|
376
|
+
*/
|
|
377
|
+
async *stream(id, opts = {}) {
|
|
378
|
+
const frames = this.http.sse(`/v1/agents/runs/${encodeURIComponent(id)}/stream`, {
|
|
379
|
+
ext: opts.ext,
|
|
380
|
+
signal: opts.signal
|
|
381
|
+
});
|
|
382
|
+
for await (const frame of frames) {
|
|
383
|
+
const data = frameJson(frame);
|
|
384
|
+
if (!data) continue;
|
|
385
|
+
const ev = { event: frame.event, data };
|
|
386
|
+
yield ev;
|
|
387
|
+
if (ev.event === "done" || ev.event === "timeout") return;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
};
|
|
391
|
+
var Agents = class {
|
|
392
|
+
constructor(http) {
|
|
393
|
+
this.http = http;
|
|
394
|
+
this.runs = new AgentRuns(http);
|
|
395
|
+
}
|
|
396
|
+
http;
|
|
397
|
+
runs;
|
|
398
|
+
/** `POST /v1/agents/run` — blocks until the agent loop finishes. */
|
|
399
|
+
async run(body, opts = {}) {
|
|
400
|
+
const res = await this.http.request("/v1/agents/run", {
|
|
401
|
+
body,
|
|
402
|
+
ext: opts.ext,
|
|
403
|
+
signal: opts.signal
|
|
404
|
+
});
|
|
405
|
+
const raw = await res.json();
|
|
406
|
+
return {
|
|
407
|
+
result: String(raw.result ?? ""),
|
|
408
|
+
model: String(raw.model ?? ""),
|
|
409
|
+
eventId: raw.event_id ?? res.headers.get("x-codai-event-id"),
|
|
410
|
+
usage: raw.usage ?? null,
|
|
411
|
+
raw,
|
|
412
|
+
headers: res.headers
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
// src/resources/audio.ts
|
|
418
|
+
var Audio = class {
|
|
419
|
+
constructor(http) {
|
|
420
|
+
this.http = http;
|
|
421
|
+
}
|
|
422
|
+
http;
|
|
423
|
+
/** Speech-to-text (multipart upload; never retried). Returns the transcript (0.2.x shape). */
|
|
424
|
+
async transcribe(opts) {
|
|
425
|
+
return (await this.transcribeDetailed(opts)).text;
|
|
426
|
+
}
|
|
427
|
+
/** Speech-to-text with the raw upstream JSON and routing headers. */
|
|
428
|
+
async transcribeDetailed(opts) {
|
|
429
|
+
const form = new FormData();
|
|
430
|
+
const blob = opts.file instanceof Blob ? opts.file : new Blob([opts.file]);
|
|
431
|
+
form.append("file", blob, opts.filename ?? "audio.webm");
|
|
432
|
+
form.append("model", opts.model ?? "codai-transcribe");
|
|
433
|
+
if (opts.language) form.append("language", opts.language);
|
|
434
|
+
if (opts.prompt) form.append("prompt", opts.prompt);
|
|
435
|
+
if (opts.responseFormat) form.append("response_format", opts.responseFormat);
|
|
436
|
+
if (opts.temperature !== void 0) form.append("temperature", String(opts.temperature));
|
|
437
|
+
const res = await this.http.request("/v1/audio/transcriptions", {
|
|
438
|
+
method: "POST",
|
|
439
|
+
rawBody: form,
|
|
440
|
+
ext: opts.ext,
|
|
441
|
+
signal: opts.signal
|
|
442
|
+
});
|
|
443
|
+
const raw = await res.json();
|
|
444
|
+
return {
|
|
445
|
+
text: typeof raw.text === "string" ? raw.text : "",
|
|
446
|
+
raw,
|
|
447
|
+
routedTo: res.headers.get("x-codai-routed-to"),
|
|
448
|
+
headers: res.headers
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
/** Text-to-speech. Returns the raw audio bytes (0.2.x shape); see `speechDetailed()` for headers. */
|
|
452
|
+
async speech(body, opts = {}) {
|
|
453
|
+
return (await this.speechDetailed(body, opts)).audio;
|
|
454
|
+
}
|
|
455
|
+
/** Text-to-speech with the upstream `content-type` and routing headers. */
|
|
456
|
+
async speechDetailed(body, opts = {}) {
|
|
457
|
+
const res = await this.http.request("/v1/audio/speech", {
|
|
458
|
+
body: { ...body, model: body.model ?? "codai-tts", voice: body.voice ?? "alloy" },
|
|
459
|
+
ext: opts.ext,
|
|
460
|
+
signal: opts.signal
|
|
461
|
+
});
|
|
462
|
+
return {
|
|
463
|
+
audio: await res.arrayBuffer(),
|
|
464
|
+
contentType: res.headers.get("content-type") ?? "audio/mpeg",
|
|
465
|
+
routedTo: res.headers.get("x-codai-routed-to"),
|
|
466
|
+
headers: res.headers
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
};
|
|
470
|
+
|
|
471
|
+
// src/resources/chat.ts
|
|
472
|
+
function toUsage(u) {
|
|
473
|
+
if (!u) return null;
|
|
474
|
+
const cached = u.prompt_tokens_details?.cached_tokens;
|
|
475
|
+
return {
|
|
476
|
+
promptTokens: u.prompt_tokens ?? 0,
|
|
477
|
+
completionTokens: u.completion_tokens ?? 0,
|
|
478
|
+
...cached !== void 0 ? { cachedTokens: cached } : {}
|
|
479
|
+
};
|
|
480
|
+
}
|
|
13
481
|
function mergeToolCall(acc, index, fragment) {
|
|
14
482
|
const existing = acc.get(index) ?? { index };
|
|
15
483
|
for (const [key, val] of Object.entries(fragment)) {
|
|
@@ -17,11 +485,8 @@ function mergeToolCall(acc, index, fragment) {
|
|
|
17
485
|
if (key === "function" && val && typeof val === "object") {
|
|
18
486
|
const fn = existing.function ?? {};
|
|
19
487
|
for (const [fk, fv] of Object.entries(val)) {
|
|
20
|
-
if (fk === "arguments")
|
|
21
|
-
|
|
22
|
-
} else {
|
|
23
|
-
fn[fk] = fv;
|
|
24
|
-
}
|
|
488
|
+
if (fk === "arguments") fn.arguments = String(fn.arguments ?? "") + String(fv ?? "");
|
|
489
|
+
else fn[fk] = fv;
|
|
25
490
|
}
|
|
26
491
|
existing.function = fn;
|
|
27
492
|
} else {
|
|
@@ -30,49 +495,302 @@ function mergeToolCall(acc, index, fragment) {
|
|
|
30
495
|
}
|
|
31
496
|
acc.set(index, existing);
|
|
32
497
|
}
|
|
33
|
-
var
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
// ---------------------------------------------------------------- chat
|
|
48
|
-
async chat(opts) {
|
|
49
|
-
const res = await this.request("/v1/chat/completions", this.chatBody(opts, false), {
|
|
50
|
-
signal: opts.signal,
|
|
51
|
-
headers: this.chatHeaders(opts)
|
|
498
|
+
var ChatCompletions = class {
|
|
499
|
+
constructor(http) {
|
|
500
|
+
this.http = http;
|
|
501
|
+
}
|
|
502
|
+
http;
|
|
503
|
+
/**
|
|
504
|
+
* Non-streaming completion. `stream` is forced to `false`; use
|
|
505
|
+
* `stream()` for SSE.
|
|
506
|
+
*/
|
|
507
|
+
async create(body, opts = {}) {
|
|
508
|
+
const res = await this.http.request("/v1/chat/completions", {
|
|
509
|
+
body: { ...body, model: body.model ?? "codai", stream: false },
|
|
510
|
+
ext: opts.ext,
|
|
511
|
+
signal: opts.signal
|
|
52
512
|
});
|
|
53
513
|
const raw = await res.json();
|
|
54
|
-
const
|
|
514
|
+
const first = raw.choices?.[0];
|
|
55
515
|
const usage = raw.usage;
|
|
56
516
|
return {
|
|
57
|
-
content:
|
|
517
|
+
content: typeof first?.message?.content === "string" ? first.message.content : "",
|
|
518
|
+
toolCalls: [...first?.message?.tool_calls ?? []],
|
|
58
519
|
raw,
|
|
59
|
-
requestId: res.headers.get("x-request-id"),
|
|
520
|
+
requestId: res.headers.get("x-codai-trace-id") ?? res.headers.get("x-request-id"),
|
|
521
|
+
eventId: res.headers.get("x-codai-event-id"),
|
|
60
522
|
routedTo: res.headers.get("x-codai-routed-to"),
|
|
61
523
|
execVerify: res.headers.get("x-codai-exec-verify"),
|
|
62
|
-
usage: usage
|
|
63
|
-
|
|
64
|
-
completionTokens: usage.completion_tokens ?? 0
|
|
65
|
-
} : null
|
|
524
|
+
usage: toUsage(usage),
|
|
525
|
+
headers: res.headers
|
|
66
526
|
};
|
|
67
527
|
}
|
|
528
|
+
/** Streaming completion (`stream: true`). */
|
|
529
|
+
stream(body, opts = {}) {
|
|
530
|
+
const requestPromise = this.http.request("/v1/chat/completions", {
|
|
531
|
+
body: { ...body, model: body.model ?? "codai", stream: true },
|
|
532
|
+
ext: opts.ext,
|
|
533
|
+
signal: opts.signal,
|
|
534
|
+
timeoutMs: 0,
|
|
535
|
+
noRetry: true
|
|
536
|
+
});
|
|
537
|
+
let resolveFinal;
|
|
538
|
+
let rejectFinal;
|
|
539
|
+
const final = new Promise((resolve, reject) => {
|
|
540
|
+
resolveFinal = resolve;
|
|
541
|
+
rejectFinal = reject;
|
|
542
|
+
});
|
|
543
|
+
final.catch(() => {
|
|
544
|
+
});
|
|
545
|
+
const toolCallsByIndex = /* @__PURE__ */ new Map();
|
|
546
|
+
let content = "";
|
|
547
|
+
let usage = null;
|
|
548
|
+
let finishReason = null;
|
|
549
|
+
let res = null;
|
|
550
|
+
const buildResult = () => ({
|
|
551
|
+
content,
|
|
552
|
+
toolCalls: [...toolCallsByIndex.values()],
|
|
553
|
+
requestId: res?.headers.get("x-codai-trace-id") ?? res?.headers.get("x-request-id") ?? null,
|
|
554
|
+
routedTo: res?.headers.get("x-codai-routed-to") ?? null,
|
|
555
|
+
execVerify: res?.headers.get("x-codai-exec-verify") ?? null,
|
|
556
|
+
usage,
|
|
557
|
+
finishReason,
|
|
558
|
+
headers: res?.headers ?? null
|
|
559
|
+
});
|
|
560
|
+
async function* chunks() {
|
|
561
|
+
try {
|
|
562
|
+
res = await requestPromise;
|
|
563
|
+
for await (const frame of readSse(res, opts.signal)) {
|
|
564
|
+
if (frame.data === "[DONE]") break;
|
|
565
|
+
const chunk = frameJson(frame);
|
|
566
|
+
if (!chunk) continue;
|
|
567
|
+
if (chunk.usage) usage = toUsage(chunk.usage);
|
|
568
|
+
const choice = chunk.choices?.[0];
|
|
569
|
+
if (choice?.finish_reason) finishReason = choice.finish_reason;
|
|
570
|
+
const delta = choice?.delta;
|
|
571
|
+
if (delta?.tool_calls) {
|
|
572
|
+
for (const tc of delta.tool_calls) {
|
|
573
|
+
mergeToolCall(toolCallsByIndex, typeof tc.index === "number" ? tc.index : 0, tc);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
if (delta?.content) content += delta.content;
|
|
577
|
+
yield chunk;
|
|
578
|
+
}
|
|
579
|
+
resolveFinal(buildResult());
|
|
580
|
+
} catch (err) {
|
|
581
|
+
rejectFinal(err);
|
|
582
|
+
throw err;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
async function* text() {
|
|
586
|
+
for await (const chunk of chunks()) {
|
|
587
|
+
const delta = chunk.choices?.[0]?.delta?.content;
|
|
588
|
+
if (delta) yield delta;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
return {
|
|
592
|
+
[Symbol.asyncIterator]: () => text(),
|
|
593
|
+
chunks: () => ({ [Symbol.asyncIterator]: () => chunks() }),
|
|
594
|
+
final
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
};
|
|
598
|
+
var Chat = class {
|
|
599
|
+
completions;
|
|
600
|
+
constructor(http) {
|
|
601
|
+
this.completions = new ChatCompletions(http);
|
|
602
|
+
}
|
|
603
|
+
};
|
|
604
|
+
|
|
605
|
+
// src/resources/devices.ts
|
|
606
|
+
var Devices = class {
|
|
607
|
+
constructor(http) {
|
|
608
|
+
this.http = http;
|
|
609
|
+
}
|
|
610
|
+
http;
|
|
611
|
+
/** `GET /v1/devices` — most recently seen first. */
|
|
612
|
+
async list(opts = {}) {
|
|
613
|
+
const raw = await this.http.json("/v1/devices", {
|
|
614
|
+
ext: opts.ext,
|
|
615
|
+
signal: opts.signal
|
|
616
|
+
});
|
|
617
|
+
return [...raw.devices ?? []];
|
|
618
|
+
}
|
|
619
|
+
/** `PATCH /v1/devices/{id}` — push token and/or capabilities. */
|
|
620
|
+
update(id, body, opts = {}) {
|
|
621
|
+
return this.http.json(`/v1/devices/${encodeURIComponent(id)}`, {
|
|
622
|
+
method: "PATCH",
|
|
623
|
+
body,
|
|
624
|
+
ext: opts.ext,
|
|
625
|
+
signal: opts.signal
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
/** `DELETE /v1/devices/{id}` */
|
|
629
|
+
delete(id, opts = {}) {
|
|
630
|
+
return this.http.json(`/v1/devices/${encodeURIComponent(id)}`, {
|
|
631
|
+
method: "DELETE",
|
|
632
|
+
ext: opts.ext,
|
|
633
|
+
signal: opts.signal
|
|
634
|
+
});
|
|
635
|
+
}
|
|
68
636
|
/**
|
|
69
|
-
*
|
|
70
|
-
*
|
|
637
|
+
* `GET /v1/devices/me/dispatch?limit=` — pending controls dispatched at the
|
|
638
|
+
* calling device (`x-codai-device` required) across all owned sessions.
|
|
71
639
|
*/
|
|
72
|
-
|
|
73
|
-
|
|
640
|
+
dispatchInbox(query = {}, opts = {}) {
|
|
641
|
+
return this.http.json("/v1/devices/me/dispatch", {
|
|
642
|
+
query: { limit: query.limit },
|
|
643
|
+
ext: opts.ext,
|
|
644
|
+
signal: opts.signal
|
|
645
|
+
});
|
|
646
|
+
}
|
|
647
|
+
};
|
|
648
|
+
|
|
649
|
+
// src/resources/embeddings.ts
|
|
650
|
+
var Embeddings = class {
|
|
651
|
+
constructor(http) {
|
|
652
|
+
this.http = http;
|
|
653
|
+
}
|
|
654
|
+
http;
|
|
655
|
+
async create(body, opts = {}) {
|
|
656
|
+
const res = await this.http.request("/v1/embeddings", {
|
|
657
|
+
body: { ...body, model: body.model ?? "codai-embed" },
|
|
658
|
+
ext: opts.ext,
|
|
659
|
+
signal: opts.signal
|
|
660
|
+
});
|
|
661
|
+
const raw = await res.json();
|
|
662
|
+
return {
|
|
663
|
+
embeddings: (raw.data ?? []).map((d) => [...d.embedding]),
|
|
664
|
+
raw,
|
|
665
|
+
routedTo: res.headers.get("x-codai-routed-to"),
|
|
666
|
+
fallback: res.headers.get("x-codai-embed-fallback"),
|
|
667
|
+
headers: res.headers
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
};
|
|
671
|
+
|
|
672
|
+
// src/resources/health.ts
|
|
673
|
+
var Health = class {
|
|
674
|
+
constructor(http) {
|
|
675
|
+
this.http = http;
|
|
676
|
+
}
|
|
677
|
+
http;
|
|
678
|
+
/** Liveness probe. */
|
|
679
|
+
get(opts = {}) {
|
|
680
|
+
return this.http.json("/health", { signal: opts.signal, noRetry: true });
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* Deep readiness probe. A 503 is returned as `{ ok: false, ... }` rather
|
|
684
|
+
* than thrown, so callers can read the failing check.
|
|
685
|
+
*/
|
|
686
|
+
async ready(opts = {}) {
|
|
687
|
+
const res = await this.http.request("/health/ready", {
|
|
688
|
+
signal: opts.signal,
|
|
689
|
+
noRetry: true,
|
|
690
|
+
acceptStatus: [503]
|
|
691
|
+
});
|
|
692
|
+
const body = await res.json();
|
|
693
|
+
return { ...body, httpStatus: res.status };
|
|
694
|
+
}
|
|
695
|
+
/** Registry snapshot (aliases + provider health). */
|
|
696
|
+
status(opts = {}) {
|
|
697
|
+
return this.http.json("/status", { signal: opts.signal, noRetry: true });
|
|
698
|
+
}
|
|
699
|
+
};
|
|
700
|
+
|
|
701
|
+
// src/resources/hosts.ts
|
|
702
|
+
var Hosts = class {
|
|
703
|
+
constructor(http) {
|
|
704
|
+
this.http = http;
|
|
705
|
+
}
|
|
706
|
+
http;
|
|
707
|
+
/** `GET /v1/hosts` — your devices currently connected as hosts. */
|
|
708
|
+
async list(opts = {}) {
|
|
709
|
+
const raw = await this.http.json("/v1/hosts", {
|
|
710
|
+
ext: opts.ext,
|
|
711
|
+
signal: opts.signal
|
|
712
|
+
});
|
|
713
|
+
return [...raw.hosts ?? []];
|
|
714
|
+
}
|
|
715
|
+
/**
|
|
716
|
+
* `POST /v1/hosts/{deviceId}/exec` — run one op on a connected host and
|
|
717
|
+
* block until it answers (or `timeout_ms`, default 60 s).
|
|
718
|
+
*/
|
|
719
|
+
exec(deviceId, op, args = {}, extra = {}) {
|
|
720
|
+
const body = {
|
|
721
|
+
op,
|
|
722
|
+
args,
|
|
723
|
+
...extra.timeoutMs !== void 0 ? { timeout_ms: extra.timeoutMs } : {},
|
|
724
|
+
...extra.label !== void 0 ? { label: extra.label } : {}
|
|
725
|
+
};
|
|
726
|
+
return this.http.json(`/v1/hosts/${encodeURIComponent(deviceId)}/exec`, {
|
|
727
|
+
body,
|
|
728
|
+
ext: extra.ext,
|
|
729
|
+
signal: extra.signal,
|
|
730
|
+
// The gateway itself waits up to timeout_ms + 2 s; give the HTTP layer headroom.
|
|
731
|
+
timeoutMs: (extra.timeoutMs ?? 6e4) + 1e4,
|
|
732
|
+
noRetry: true
|
|
733
|
+
});
|
|
734
|
+
}
|
|
735
|
+
/** `POST /v1/hosts/exec/{reqId}/result` — host side: deliver an exec result. */
|
|
736
|
+
postResult(reqId, body, opts = {}) {
|
|
737
|
+
return this.http.json(
|
|
738
|
+
`/v1/hosts/exec/${encodeURIComponent(reqId)}/result`,
|
|
739
|
+
{ body, ext: opts.ext, signal: opts.signal }
|
|
740
|
+
);
|
|
741
|
+
}
|
|
742
|
+
/**
|
|
743
|
+
* `GET /v1/hosts/stream?os=&hostname=&roots=` — host side: announce this
|
|
744
|
+
* device and receive `hello` / `ping` / `exec` events. Closed by the server
|
|
745
|
+
* after 6 h; reconnect to re-announce presence.
|
|
746
|
+
*/
|
|
747
|
+
async *stream(query = {}, opts = {}) {
|
|
748
|
+
const frames = this.http.sse("/v1/hosts/stream", {
|
|
749
|
+
query: {
|
|
750
|
+
os: query.os,
|
|
751
|
+
hostname: query.hostname,
|
|
752
|
+
roots: query.roots?.length ? query.roots.join(",") : void 0
|
|
753
|
+
},
|
|
754
|
+
ext: opts.ext,
|
|
755
|
+
signal: opts.signal
|
|
756
|
+
});
|
|
757
|
+
for await (const frame of frames) {
|
|
758
|
+
const data = frameJson(frame);
|
|
759
|
+
if (!data) continue;
|
|
760
|
+
yield { event: frame.event, data };
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
};
|
|
764
|
+
|
|
765
|
+
// src/resources/messages.ts
|
|
766
|
+
var Messages = class {
|
|
767
|
+
constructor(http) {
|
|
768
|
+
this.http = http;
|
|
769
|
+
}
|
|
770
|
+
http;
|
|
771
|
+
async create(body, opts = {}) {
|
|
772
|
+
const res = await this.http.request("/v1/messages", {
|
|
773
|
+
body: { ...body, model: body.model ?? "codai", stream: false },
|
|
774
|
+
ext: opts.ext,
|
|
775
|
+
signal: opts.signal
|
|
776
|
+
});
|
|
777
|
+
const raw = await res.json();
|
|
778
|
+
const text = (raw.content ?? []).map((b) => b.type === "text" && typeof b.text === "string" ? b.text : "").join("");
|
|
779
|
+
return {
|
|
780
|
+
text,
|
|
781
|
+
raw,
|
|
782
|
+
requestId: res.headers.get("x-codai-trace-id") ?? res.headers.get("x-request-id"),
|
|
783
|
+
routedTo: res.headers.get("x-codai-routed-to"),
|
|
784
|
+
headers: res.headers
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
stream(body, opts = {}) {
|
|
788
|
+
const requestPromise = this.http.request("/v1/messages", {
|
|
789
|
+
body: { ...body, model: body.model ?? "codai", stream: true },
|
|
790
|
+
ext: opts.ext,
|
|
74
791
|
signal: opts.signal,
|
|
75
|
-
|
|
792
|
+
timeoutMs: 0,
|
|
793
|
+
noRetry: true
|
|
76
794
|
});
|
|
77
795
|
let resolveFinal;
|
|
78
796
|
let rejectFinal;
|
|
@@ -82,258 +800,735 @@ var Codai = class {
|
|
|
82
800
|
});
|
|
83
801
|
final.catch(() => {
|
|
84
802
|
});
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
803
|
+
let text = "";
|
|
804
|
+
let stopReason = null;
|
|
805
|
+
let inputTokens = null;
|
|
806
|
+
let outputTokens = null;
|
|
807
|
+
let res = null;
|
|
808
|
+
async function* events() {
|
|
90
809
|
try {
|
|
91
810
|
res = await requestPromise;
|
|
811
|
+
for await (const frame of readSse(res, opts.signal)) {
|
|
812
|
+
const ev = frameJson(frame);
|
|
813
|
+
if (!ev) continue;
|
|
814
|
+
if (typeof ev.type !== "string") ev.type = frame.event;
|
|
815
|
+
if (ev.type === "message_start") {
|
|
816
|
+
const usage = ev.message?.usage;
|
|
817
|
+
if (usage?.input_tokens !== void 0) inputTokens = usage.input_tokens;
|
|
818
|
+
} else if (ev.type === "content_block_delta") {
|
|
819
|
+
const delta = ev.delta;
|
|
820
|
+
if (delta?.type === "text_delta" && typeof delta.text === "string") text += delta.text;
|
|
821
|
+
} else if (ev.type === "message_delta") {
|
|
822
|
+
const delta = ev.delta;
|
|
823
|
+
if (delta?.stop_reason) stopReason = delta.stop_reason;
|
|
824
|
+
const usage = ev.usage;
|
|
825
|
+
if (usage?.output_tokens !== void 0) outputTokens = usage.output_tokens;
|
|
826
|
+
}
|
|
827
|
+
yield ev;
|
|
828
|
+
if (ev.type === "message_stop") break;
|
|
829
|
+
}
|
|
830
|
+
resolveFinal({
|
|
831
|
+
text,
|
|
832
|
+
stopReason,
|
|
833
|
+
usage: inputTokens !== null || outputTokens !== null ? { inputTokens: inputTokens ?? 0, outputTokens: outputTokens ?? 0 } : null,
|
|
834
|
+
requestId: res.headers.get("x-codai-trace-id") ?? res.headers.get("x-request-id") ?? null,
|
|
835
|
+
routedTo: res.headers.get("x-codai-routed-to"),
|
|
836
|
+
headers: res.headers
|
|
837
|
+
});
|
|
92
838
|
} catch (err) {
|
|
93
839
|
rejectFinal(err);
|
|
94
840
|
throw err;
|
|
95
841
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
842
|
+
}
|
|
843
|
+
async function* textOnly() {
|
|
844
|
+
for await (const ev of events()) {
|
|
845
|
+
if (ev.type !== "content_block_delta") continue;
|
|
846
|
+
const delta = ev.delta;
|
|
847
|
+
if (delta?.type === "text_delta" && delta.text) yield delta.text;
|
|
101
848
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
849
|
+
}
|
|
850
|
+
return {
|
|
851
|
+
[Symbol.asyncIterator]: () => events(),
|
|
852
|
+
text: () => ({ [Symbol.asyncIterator]: () => textOnly() }),
|
|
853
|
+
final
|
|
854
|
+
};
|
|
855
|
+
}
|
|
856
|
+
};
|
|
857
|
+
|
|
858
|
+
// src/resources/models.ts
|
|
859
|
+
var Models = class {
|
|
860
|
+
constructor(http) {
|
|
861
|
+
this.http = http;
|
|
862
|
+
}
|
|
863
|
+
http;
|
|
864
|
+
/** Every model the calling key may address (OpenAI list shape, `data[]`). */
|
|
865
|
+
async list(opts = {}) {
|
|
866
|
+
const raw = await this.http.json("/v1/models", {
|
|
867
|
+
ext: opts.ext,
|
|
868
|
+
signal: opts.signal
|
|
869
|
+
});
|
|
870
|
+
return [...raw.data ?? []];
|
|
871
|
+
}
|
|
872
|
+
};
|
|
873
|
+
|
|
874
|
+
// src/resources/orgs.ts
|
|
875
|
+
var OrgMembers = class {
|
|
876
|
+
constructor(http) {
|
|
877
|
+
this.http = http;
|
|
878
|
+
}
|
|
879
|
+
http;
|
|
880
|
+
/** `GET /v1/orgs/{id}/members` */
|
|
881
|
+
async list(orgId, opts = {}) {
|
|
882
|
+
const raw = await this.http.json(
|
|
883
|
+
`/v1/orgs/${encodeURIComponent(orgId)}/members`,
|
|
884
|
+
{ ext: opts.ext, signal: opts.signal }
|
|
885
|
+
);
|
|
886
|
+
return [...raw.members ?? []];
|
|
887
|
+
}
|
|
888
|
+
/** `POST /v1/orgs/{id}/members` — add or re-role (owner/admin). */
|
|
889
|
+
add(orgId, body, opts = {}) {
|
|
890
|
+
return this.http.json(
|
|
891
|
+
`/v1/orgs/${encodeURIComponent(orgId)}/members`,
|
|
892
|
+
{ body, ext: opts.ext, signal: opts.signal }
|
|
893
|
+
);
|
|
894
|
+
}
|
|
895
|
+
/** `DELETE /v1/orgs/{id}/members/{userId}` */
|
|
896
|
+
remove(orgId, userId, opts = {}) {
|
|
897
|
+
return this.http.json(
|
|
898
|
+
`/v1/orgs/${encodeURIComponent(orgId)}/members/${encodeURIComponent(userId)}`,
|
|
899
|
+
{ method: "DELETE", ext: opts.ext, signal: opts.signal }
|
|
900
|
+
);
|
|
901
|
+
}
|
|
902
|
+
};
|
|
903
|
+
var Orgs = class {
|
|
904
|
+
constructor(http) {
|
|
905
|
+
this.http = http;
|
|
906
|
+
this.members = new OrgMembers(http);
|
|
907
|
+
}
|
|
908
|
+
http;
|
|
909
|
+
members;
|
|
910
|
+
/** `POST /v1/orgs` */
|
|
911
|
+
create(name, opts = {}) {
|
|
912
|
+
return this.http.json("/v1/orgs", { body: { name }, ext: opts.ext, signal: opts.signal });
|
|
913
|
+
}
|
|
914
|
+
/** `GET /v1/orgs` */
|
|
915
|
+
async list(opts = {}) {
|
|
916
|
+
const raw = await this.http.json("/v1/orgs", {
|
|
917
|
+
ext: opts.ext,
|
|
918
|
+
signal: opts.signal
|
|
919
|
+
});
|
|
920
|
+
return [...raw.orgs ?? []];
|
|
921
|
+
}
|
|
922
|
+
};
|
|
923
|
+
|
|
924
|
+
// src/resources/responses.ts
|
|
925
|
+
var Responses = class {
|
|
926
|
+
constructor(http) {
|
|
927
|
+
this.http = http;
|
|
928
|
+
}
|
|
929
|
+
http;
|
|
930
|
+
async create(body, opts = {}) {
|
|
931
|
+
const res = await this.http.request("/v1/responses", {
|
|
932
|
+
body: { ...body, model: body.model ?? "codai", stream: false },
|
|
933
|
+
ext: opts.ext,
|
|
934
|
+
signal: opts.signal
|
|
935
|
+
});
|
|
936
|
+
const raw = await res.json();
|
|
937
|
+
return {
|
|
938
|
+
outputText: typeof raw.output_text === "string" ? raw.output_text : "",
|
|
939
|
+
raw,
|
|
940
|
+
requestId: res.headers.get("x-codai-trace-id") ?? res.headers.get("x-request-id"),
|
|
941
|
+
routedTo: res.headers.get("x-codai-routed-to"),
|
|
942
|
+
headers: res.headers
|
|
943
|
+
};
|
|
944
|
+
}
|
|
945
|
+
stream(body, opts = {}) {
|
|
946
|
+
const requestPromise = this.http.request("/v1/responses", {
|
|
947
|
+
body: { ...body, model: body.model ?? "codai", stream: true },
|
|
948
|
+
ext: opts.ext,
|
|
949
|
+
signal: opts.signal,
|
|
950
|
+
timeoutMs: 0,
|
|
951
|
+
noRetry: true
|
|
952
|
+
});
|
|
953
|
+
let resolveFinal;
|
|
954
|
+
let rejectFinal;
|
|
955
|
+
const final = new Promise((resolve, reject) => {
|
|
956
|
+
resolveFinal = resolve;
|
|
957
|
+
rejectFinal = reject;
|
|
958
|
+
});
|
|
959
|
+
final.catch(() => {
|
|
960
|
+
});
|
|
961
|
+
let outputText = "";
|
|
962
|
+
let response = null;
|
|
963
|
+
let res = null;
|
|
964
|
+
async function* events() {
|
|
113
965
|
try {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
const data = line.slice(5).trim();
|
|
124
|
-
if (data === "[DONE]") {
|
|
125
|
-
finished = true;
|
|
126
|
-
break;
|
|
127
|
-
}
|
|
128
|
-
let chunk;
|
|
129
|
-
try {
|
|
130
|
-
chunk = JSON.parse(data);
|
|
131
|
-
} catch {
|
|
132
|
-
continue;
|
|
133
|
-
}
|
|
134
|
-
if (chunk.usage) {
|
|
135
|
-
usage = {
|
|
136
|
-
promptTokens: chunk.usage.prompt_tokens ?? 0,
|
|
137
|
-
completionTokens: chunk.usage.completion_tokens ?? 0
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
const delta = chunk.choices?.[0]?.delta;
|
|
141
|
-
if (delta?.tool_calls) {
|
|
142
|
-
for (const tc of delta.tool_calls) {
|
|
143
|
-
const idx = typeof tc.index === "number" ? tc.index : 0;
|
|
144
|
-
mergeToolCall(toolCallsByIndex, idx, tc);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
if (delta?.content) {
|
|
148
|
-
content += delta.content;
|
|
149
|
-
yield delta.content;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
if (finished) break;
|
|
966
|
+
res = await requestPromise;
|
|
967
|
+
for await (const frame of readSse(res, opts.signal)) {
|
|
968
|
+
const ev = frameJson(frame);
|
|
969
|
+
if (!ev) continue;
|
|
970
|
+
if (typeof ev.type !== "string") ev.type = frame.event;
|
|
971
|
+
if (ev.type === "response.output_text.delta" && typeof ev.delta === "string") {
|
|
972
|
+
outputText += ev.delta;
|
|
973
|
+
} else if (ev.type === "response.completed" && ev.response) {
|
|
974
|
+
response = ev.response;
|
|
153
975
|
}
|
|
154
|
-
|
|
976
|
+
yield ev;
|
|
977
|
+
if (ev.type === "response.completed") break;
|
|
155
978
|
}
|
|
156
|
-
resolveFinal(
|
|
979
|
+
resolveFinal({
|
|
980
|
+
outputText,
|
|
981
|
+
response,
|
|
982
|
+
requestId: res.headers.get("x-codai-trace-id") ?? res.headers.get("x-request-id") ?? null,
|
|
983
|
+
routedTo: res.headers.get("x-codai-routed-to"),
|
|
984
|
+
headers: res.headers
|
|
985
|
+
});
|
|
157
986
|
} catch (err) {
|
|
158
987
|
rejectFinal(err);
|
|
159
988
|
throw err;
|
|
160
|
-
} finally {
|
|
161
|
-
reader.releaseLock?.();
|
|
162
989
|
}
|
|
163
990
|
}
|
|
164
|
-
|
|
991
|
+
async function* textOnly() {
|
|
992
|
+
for await (const ev of events()) {
|
|
993
|
+
if (ev.type === "response.output_text.delta" && typeof ev.delta === "string")
|
|
994
|
+
yield ev.delta;
|
|
995
|
+
}
|
|
996
|
+
}
|
|
165
997
|
return {
|
|
166
|
-
[Symbol.asyncIterator]: () =>
|
|
998
|
+
[Symbol.asyncIterator]: () => events(),
|
|
999
|
+
text: () => ({ [Symbol.asyncIterator]: () => textOnly() }),
|
|
167
1000
|
final
|
|
168
1001
|
};
|
|
169
1002
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
1003
|
+
};
|
|
1004
|
+
|
|
1005
|
+
// src/resources/sessions.ts
|
|
1006
|
+
var enc = encodeURIComponent;
|
|
1007
|
+
var SessionLease = class {
|
|
1008
|
+
constructor(http) {
|
|
1009
|
+
this.http = http;
|
|
1010
|
+
}
|
|
1011
|
+
http;
|
|
1012
|
+
/** `POST /v1/sessions/{id}/lease` — CAS claim; `force: true` takes over a live lease. */
|
|
1013
|
+
acquire(sessionId, body = {}, opts = {}) {
|
|
1014
|
+
return this.http.json(`/v1/sessions/${enc(sessionId)}/lease`, {
|
|
1015
|
+
method: "POST",
|
|
1016
|
+
body,
|
|
1017
|
+
ext: opts.ext,
|
|
1018
|
+
signal: opts.signal
|
|
1019
|
+
});
|
|
1020
|
+
}
|
|
1021
|
+
/** `PUT /v1/sessions/{id}/lease` — heartbeat every 10 s (TTL 30 s). */
|
|
1022
|
+
renew(sessionId, opts = {}) {
|
|
1023
|
+
return this.http.json(`/v1/sessions/${enc(sessionId)}/lease`, {
|
|
1024
|
+
method: "PUT",
|
|
1025
|
+
ext: opts.ext,
|
|
1026
|
+
signal: opts.signal
|
|
1027
|
+
});
|
|
1028
|
+
}
|
|
1029
|
+
/** `DELETE /v1/sessions/{id}/lease` */
|
|
1030
|
+
release(sessionId, opts = {}) {
|
|
1031
|
+
return this.http.json(`/v1/sessions/${enc(sessionId)}/lease`, {
|
|
1032
|
+
method: "DELETE",
|
|
1033
|
+
ext: opts.ext,
|
|
1034
|
+
signal: opts.signal
|
|
1035
|
+
});
|
|
1036
|
+
}
|
|
1037
|
+
};
|
|
1038
|
+
var SessionEvents = class {
|
|
1039
|
+
constructor(http) {
|
|
1040
|
+
this.http = http;
|
|
1041
|
+
}
|
|
1042
|
+
http;
|
|
1043
|
+
/** `GET /v1/sessions/{id}/events?after=&limit=` */
|
|
1044
|
+
list(sessionId, query = {}, opts = {}) {
|
|
1045
|
+
return this.http.json(`/v1/sessions/${enc(sessionId)}/events`, {
|
|
1046
|
+
query: { after: query.after, limit: query.limit },
|
|
1047
|
+
ext: opts.ext,
|
|
1048
|
+
signal: opts.signal
|
|
1049
|
+
});
|
|
1050
|
+
}
|
|
1051
|
+
/** `POST /v1/sessions/{id}/events` — executor only (needs the live lease). */
|
|
1052
|
+
append(sessionId, body, opts = {}) {
|
|
1053
|
+
return this.http.json(`/v1/sessions/${enc(sessionId)}/events`, {
|
|
1054
|
+
body,
|
|
1055
|
+
ext: opts.ext,
|
|
1056
|
+
signal: opts.signal
|
|
1057
|
+
});
|
|
1058
|
+
}
|
|
1059
|
+
};
|
|
1060
|
+
var SessionControls = class {
|
|
1061
|
+
constructor(http) {
|
|
1062
|
+
this.http = http;
|
|
1063
|
+
}
|
|
1064
|
+
http;
|
|
1065
|
+
/** `GET /v1/sessions/{id}/controls?applied=&target=` — drain the control queue. */
|
|
1066
|
+
async list(sessionId, query = {}, opts = {}) {
|
|
1067
|
+
const raw = await this.http.json(
|
|
1068
|
+
`/v1/sessions/${enc(sessionId)}/controls`,
|
|
1069
|
+
{
|
|
1070
|
+
query: { applied: query.applied, target: query.target },
|
|
1071
|
+
ext: opts.ext,
|
|
1072
|
+
signal: opts.signal
|
|
1073
|
+
}
|
|
1074
|
+
);
|
|
1075
|
+
return [...raw.controls ?? []];
|
|
1076
|
+
}
|
|
1077
|
+
/** `POST /v1/sessions/{id}/control` — send a control to the executor (editor+). */
|
|
1078
|
+
submit(sessionId, body, opts = {}) {
|
|
1079
|
+
return this.http.json(`/v1/sessions/${enc(sessionId)}/control`, {
|
|
1080
|
+
body,
|
|
1081
|
+
ext: opts.ext,
|
|
1082
|
+
signal: opts.signal
|
|
1083
|
+
});
|
|
1084
|
+
}
|
|
1085
|
+
/** `POST /v1/sessions/{id}/control/{cid}/applied` — executor only. */
|
|
1086
|
+
markApplied(sessionId, controlId, opts = {}) {
|
|
1087
|
+
return this.http.json(
|
|
1088
|
+
`/v1/sessions/${enc(sessionId)}/control/${enc(controlId)}/applied`,
|
|
1089
|
+
{ method: "POST", ext: opts.ext, signal: opts.signal }
|
|
1090
|
+
);
|
|
1091
|
+
}
|
|
1092
|
+
};
|
|
1093
|
+
var SessionShares = class {
|
|
1094
|
+
constructor(http) {
|
|
1095
|
+
this.http = http;
|
|
1096
|
+
}
|
|
1097
|
+
http;
|
|
1098
|
+
/** `GET /v1/sessions/{id}/shares` — owner only. */
|
|
1099
|
+
async list(sessionId, opts = {}) {
|
|
1100
|
+
const raw = await this.http.json(
|
|
1101
|
+
`/v1/sessions/${enc(sessionId)}/shares`,
|
|
1102
|
+
{ ext: opts.ext, signal: opts.signal }
|
|
1103
|
+
);
|
|
1104
|
+
return [...raw.shares ?? []];
|
|
1105
|
+
}
|
|
1106
|
+
/** `POST /v1/sessions/{id}/shares` — `token` is returned once for link shares. */
|
|
1107
|
+
create(sessionId, body, opts = {}) {
|
|
1108
|
+
return this.http.json(`/v1/sessions/${enc(sessionId)}/shares`, {
|
|
1109
|
+
body,
|
|
1110
|
+
ext: opts.ext,
|
|
1111
|
+
signal: opts.signal
|
|
1112
|
+
});
|
|
1113
|
+
}
|
|
1114
|
+
/** `DELETE /v1/sessions/{id}/shares/{shareId}` */
|
|
1115
|
+
delete(sessionId, shareId, opts = {}) {
|
|
1116
|
+
return this.http.json(
|
|
1117
|
+
`/v1/sessions/${enc(sessionId)}/shares/${enc(shareId)}`,
|
|
1118
|
+
{ method: "DELETE", ext: opts.ext, signal: opts.signal }
|
|
1119
|
+
);
|
|
1120
|
+
}
|
|
1121
|
+
/** `GET /v1/sessions/shared-with-me` */
|
|
1122
|
+
async sharedWithMe(query = {}, opts = {}) {
|
|
1123
|
+
const raw = await this.http.json(
|
|
1124
|
+
"/v1/sessions/shared-with-me",
|
|
1125
|
+
{
|
|
1126
|
+
query: { archived: query.archived ? "1" : void 0 },
|
|
1127
|
+
ext: opts.ext,
|
|
1128
|
+
signal: opts.signal
|
|
1129
|
+
}
|
|
1130
|
+
);
|
|
1131
|
+
return [...raw.sessions ?? []];
|
|
1132
|
+
}
|
|
1133
|
+
};
|
|
1134
|
+
var Sessions = class {
|
|
1135
|
+
constructor(http) {
|
|
1136
|
+
this.http = http;
|
|
1137
|
+
this.events = new SessionEvents(http);
|
|
1138
|
+
this.controls = new SessionControls(http);
|
|
1139
|
+
this.lease = new SessionLease(http);
|
|
1140
|
+
this.shares = new SessionShares(http);
|
|
1141
|
+
}
|
|
1142
|
+
http;
|
|
1143
|
+
events;
|
|
1144
|
+
controls;
|
|
1145
|
+
lease;
|
|
1146
|
+
shares;
|
|
1147
|
+
/** `POST /v1/sessions` — idempotent on `session_key` (200 existing / 201 created). */
|
|
1148
|
+
async create(body = {}, opts = {}) {
|
|
1149
|
+
const res = await this.http.request("/v1/sessions", {
|
|
1150
|
+
body,
|
|
1151
|
+
ext: opts.ext,
|
|
1152
|
+
signal: opts.signal
|
|
1153
|
+
});
|
|
1154
|
+
const session = await res.json();
|
|
1155
|
+
return { ...session, created: res.status === 201 };
|
|
1156
|
+
}
|
|
1157
|
+
/** `GET /v1/sessions?limit=&archived=` — own sessions first, then shared. */
|
|
1158
|
+
async list(query = {}, opts = {}) {
|
|
1159
|
+
const raw = await this.http.json("/v1/sessions", {
|
|
1160
|
+
query: { limit: query.limit, archived: query.archived ? "1" : void 0, v: "2" },
|
|
1161
|
+
ext: opts.ext,
|
|
1162
|
+
signal: opts.signal
|
|
1163
|
+
});
|
|
1164
|
+
return [...raw.sessions ?? []];
|
|
1165
|
+
}
|
|
1166
|
+
/** `GET /v1/sessions/{id}` — detail with members, lease and presence. */
|
|
1167
|
+
get(sessionId, query = {}, opts = {}) {
|
|
1168
|
+
return this.http.json(`/v1/sessions/${enc(sessionId)}`, {
|
|
1169
|
+
query: { share: query.share, v: "2" },
|
|
1170
|
+
ext: opts.ext,
|
|
1171
|
+
signal: opts.signal
|
|
1172
|
+
});
|
|
1173
|
+
}
|
|
1174
|
+
/** `PATCH /v1/sessions/{id}` — rename / archive (owner). */
|
|
1175
|
+
update(sessionId, body, opts = {}) {
|
|
1176
|
+
return this.http.json(`/v1/sessions/${enc(sessionId)}`, {
|
|
1177
|
+
method: "PATCH",
|
|
1178
|
+
body,
|
|
1179
|
+
ext: opts.ext,
|
|
1180
|
+
signal: opts.signal
|
|
1181
|
+
});
|
|
1182
|
+
}
|
|
1183
|
+
/** `DELETE /v1/sessions/{id}` — irreversible (owner). */
|
|
1184
|
+
delete(sessionId, opts = {}) {
|
|
1185
|
+
return this.http.json(`/v1/sessions/${enc(sessionId)}`, {
|
|
1186
|
+
method: "DELETE",
|
|
1187
|
+
ext: opts.ext,
|
|
1188
|
+
signal: opts.signal
|
|
1189
|
+
});
|
|
1190
|
+
}
|
|
1191
|
+
/** `POST /v1/sessions/{id}/dispatch` — queue a `send` for one device and wake it over FCM. */
|
|
1192
|
+
dispatch(sessionId, body, opts = {}) {
|
|
1193
|
+
return this.http.json(`/v1/sessions/${enc(sessionId)}/dispatch`, {
|
|
1194
|
+
body,
|
|
1195
|
+
ext: opts.ext,
|
|
1196
|
+
signal: opts.signal
|
|
1197
|
+
});
|
|
1198
|
+
}
|
|
1199
|
+
/**
|
|
1200
|
+
* `GET /v1/sessions/{id}/stream?after=` — live SSE of `event` / `control` /
|
|
1201
|
+
* `lease` / `presence` frames. `: ping` heartbeats are skipped. The server
|
|
1202
|
+
* closes after 30 min; reconnect with `after` = highest `seq` seen.
|
|
1203
|
+
*/
|
|
1204
|
+
async *stream(sessionId, query = {}, opts = {}) {
|
|
1205
|
+
const frames = this.http.sse(`/v1/sessions/${enc(sessionId)}/stream`, {
|
|
1206
|
+
query: { after: query.after, share: query.share },
|
|
1207
|
+
ext: opts.ext,
|
|
1208
|
+
signal: opts.signal
|
|
1209
|
+
});
|
|
1210
|
+
for await (const frame of frames) {
|
|
1211
|
+
const data = frameJson(frame);
|
|
1212
|
+
if (!data) continue;
|
|
1213
|
+
yield { event: frame.event, data };
|
|
190
1214
|
}
|
|
191
|
-
};
|
|
192
|
-
// ------------------------------------------------------------ feedback
|
|
193
|
-
/** Rate a completed request: +1 (helpful) or -1 (not helpful). */
|
|
194
|
-
async feedback(requestId, rating, comment) {
|
|
195
|
-
await this.request("/v1/feedback", {
|
|
196
|
-
event_id: requestId,
|
|
197
|
-
rating,
|
|
198
|
-
...comment ? { comment } : {}
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
|
-
// -------------------------------------------------------------- models
|
|
202
|
-
async models() {
|
|
203
|
-
const res = await this.request("/v1/models", null, { method: "GET" });
|
|
204
|
-
const body = await res.json();
|
|
205
|
-
return body.data ?? [];
|
|
206
1215
|
}
|
|
207
|
-
|
|
1216
|
+
};
|
|
1217
|
+
|
|
1218
|
+
// src/resources/tasks.ts
|
|
1219
|
+
var Tasks = class {
|
|
1220
|
+
constructor(http) {
|
|
1221
|
+
this.http = http;
|
|
1222
|
+
}
|
|
1223
|
+
http;
|
|
1224
|
+
/** `GET /v1/tasks` — newest first; page with `cursor` = previous `next_cursor`. */
|
|
1225
|
+
list(query = {}, opts = {}) {
|
|
1226
|
+
return this.http.json("/v1/tasks", {
|
|
1227
|
+
query: { outcome: query.outcome, limit: query.limit, cursor: query.cursor },
|
|
1228
|
+
ext: opts.ext,
|
|
1229
|
+
signal: opts.signal
|
|
1230
|
+
});
|
|
1231
|
+
}
|
|
1232
|
+
/** `GET /v1/tasks/pending` — tasks awaiting your confirmation (max 100). */
|
|
1233
|
+
async pending(opts = {}) {
|
|
1234
|
+
const raw = await this.http.json("/v1/tasks/pending", {
|
|
1235
|
+
ext: opts.ext,
|
|
1236
|
+
signal: opts.signal
|
|
1237
|
+
});
|
|
1238
|
+
return [...raw.tasks ?? []];
|
|
1239
|
+
}
|
|
1240
|
+
/** `GET /v1/tasks/stats?since=` */
|
|
1241
|
+
stats(since, opts = {}) {
|
|
1242
|
+
return this.http.json("/v1/tasks/stats", {
|
|
1243
|
+
query: { since: since instanceof Date ? since.toISOString() : since },
|
|
1244
|
+
ext: opts.ext,
|
|
1245
|
+
signal: opts.signal
|
|
1246
|
+
});
|
|
1247
|
+
}
|
|
1248
|
+
/** `GET /v1/tasks/{id}` */
|
|
1249
|
+
get(id, opts = {}) {
|
|
1250
|
+
return this.http.json(`/v1/tasks/${encodeURIComponent(id)}`, {
|
|
1251
|
+
ext: opts.ext,
|
|
1252
|
+
signal: opts.signal
|
|
1253
|
+
});
|
|
1254
|
+
}
|
|
1255
|
+
/** `POST /v1/tasks/{id}/confirm` — answer "Did codai solve it?". */
|
|
1256
|
+
confirm(id, body, opts = {}) {
|
|
1257
|
+
return this.http.json(`/v1/tasks/${encodeURIComponent(id)}/confirm`, {
|
|
1258
|
+
body,
|
|
1259
|
+
ext: opts.ext,
|
|
1260
|
+
signal: opts.signal
|
|
1261
|
+
});
|
|
1262
|
+
}
|
|
1263
|
+
};
|
|
1264
|
+
|
|
1265
|
+
// src/resources/tokens.ts
|
|
1266
|
+
var Tokens = class {
|
|
1267
|
+
constructor(http) {
|
|
1268
|
+
this.http = http;
|
|
1269
|
+
}
|
|
1270
|
+
http;
|
|
208
1271
|
/**
|
|
209
|
-
* Mint a short-lived
|
|
210
|
-
*
|
|
1272
|
+
* Mint a short-lived token (60–3600 s, default 600) bound to one surface.
|
|
1273
|
+
* Only a real `codai_` key may mint.
|
|
211
1274
|
*/
|
|
212
|
-
async
|
|
213
|
-
const
|
|
214
|
-
scope,
|
|
215
|
-
|
|
1275
|
+
async create(scope, ttlSeconds, opts = {}) {
|
|
1276
|
+
const raw = await this.http.json("/v1/tokens", {
|
|
1277
|
+
body: { scope, ...ttlSeconds !== void 0 ? { ttl_seconds: ttlSeconds } : {} },
|
|
1278
|
+
ext: opts.ext,
|
|
1279
|
+
signal: opts.signal
|
|
216
1280
|
});
|
|
217
|
-
|
|
218
|
-
return { token: body.token, expiresAt: body.expires_at, scope: body.scope };
|
|
1281
|
+
return { token: raw.token, expiresAt: raw.expires_at, scope: raw.scope, raw };
|
|
219
1282
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
1283
|
+
};
|
|
1284
|
+
|
|
1285
|
+
// src/resources/tools.ts
|
|
1286
|
+
var Tools = class {
|
|
1287
|
+
constructor(http) {
|
|
1288
|
+
this.http = http;
|
|
1289
|
+
}
|
|
1290
|
+
http;
|
|
1291
|
+
/** `POST /v1/tools/search` — verified web search (never falls back to an unverified provider). */
|
|
1292
|
+
search(body, opts = {}) {
|
|
1293
|
+
return this.http.json("/v1/tools/search", {
|
|
1294
|
+
body,
|
|
1295
|
+
ext: opts.ext,
|
|
1296
|
+
signal: opts.signal
|
|
1297
|
+
});
|
|
1298
|
+
}
|
|
1299
|
+
/** `POST /v1/tools/fetch` — fetch + extract a public page (SSRF-guarded). */
|
|
1300
|
+
fetch(body, opts = {}) {
|
|
1301
|
+
return this.http.json("/v1/tools/fetch", {
|
|
1302
|
+
body,
|
|
1303
|
+
ext: opts.ext,
|
|
1304
|
+
signal: opts.signal
|
|
1305
|
+
});
|
|
1306
|
+
}
|
|
1307
|
+
};
|
|
1308
|
+
|
|
1309
|
+
// src/operations.ts
|
|
1310
|
+
var OPERATION_METHODS = {
|
|
1311
|
+
// Chat & Completions
|
|
1312
|
+
createChatCompletion: "chat.completions.create",
|
|
1313
|
+
createResponse: "responses.create",
|
|
1314
|
+
createMessage: "messages.create",
|
|
1315
|
+
// Embeddings & Audio
|
|
1316
|
+
createEmbeddings: "embeddings.create",
|
|
1317
|
+
createTranscription: "audio.transcribe",
|
|
1318
|
+
createSpeech: "audio.speech",
|
|
1319
|
+
createEphemeralToken: "tokens.create",
|
|
1320
|
+
// Models
|
|
1321
|
+
listModels: "models.list",
|
|
1322
|
+
// Health
|
|
1323
|
+
getHealth: "health.get",
|
|
1324
|
+
getHealthReady: "health.ready",
|
|
1325
|
+
getStatus: "health.status",
|
|
1326
|
+
// Agents
|
|
1327
|
+
runAgentSync: "agents.run",
|
|
1328
|
+
createAgentRun: "agents.runs.create",
|
|
1329
|
+
getAgentRunStats: "agents.runs.stats",
|
|
1330
|
+
getAgentRun: "agents.runs.get",
|
|
1331
|
+
listAgentRunSteps: "agents.runs.steps",
|
|
1332
|
+
streamAgentRun: "agents.runs.stream",
|
|
1333
|
+
cancelAgentRun: "agents.runs.cancel",
|
|
1334
|
+
// Tools
|
|
1335
|
+
toolsSearch: "tools.search",
|
|
1336
|
+
toolsFetch: "tools.fetch",
|
|
1337
|
+
// Tasks
|
|
1338
|
+
listTasks: "tasks.list",
|
|
1339
|
+
listPendingTasks: "tasks.pending",
|
|
1340
|
+
getTaskStats: "tasks.stats",
|
|
1341
|
+
getTask: "tasks.get",
|
|
1342
|
+
confirmTask: "tasks.confirm",
|
|
1343
|
+
// Sessions
|
|
1344
|
+
createSession: "sessions.create",
|
|
1345
|
+
listSessions: "sessions.list",
|
|
1346
|
+
getSession: "sessions.get",
|
|
1347
|
+
updateSession: "sessions.update",
|
|
1348
|
+
deleteSession: "sessions.delete",
|
|
1349
|
+
listSessionEvents: "sessions.events.list",
|
|
1350
|
+
appendSessionEvents: "sessions.events.append",
|
|
1351
|
+
listSessionControls: "sessions.controls.list",
|
|
1352
|
+
submitSessionControl: "sessions.controls.submit",
|
|
1353
|
+
markSessionControlApplied: "sessions.controls.markApplied",
|
|
1354
|
+
dispatchToDevice: "sessions.dispatch",
|
|
1355
|
+
claimSessionLease: "sessions.lease.acquire",
|
|
1356
|
+
heartbeatSessionLease: "sessions.lease.renew",
|
|
1357
|
+
releaseSessionLease: "sessions.lease.release",
|
|
1358
|
+
streamSession: "sessions.stream",
|
|
1359
|
+
// Devices
|
|
1360
|
+
getMyDispatchInbox: "devices.dispatchInbox",
|
|
1361
|
+
listDevices: "devices.list",
|
|
1362
|
+
updateDevice: "devices.update",
|
|
1363
|
+
deleteDevice: "devices.delete",
|
|
1364
|
+
// Hosts
|
|
1365
|
+
listHosts: "hosts.list",
|
|
1366
|
+
streamHost: "hosts.stream",
|
|
1367
|
+
execOnHost: "hosts.exec",
|
|
1368
|
+
postHostExecResult: "hosts.postResult",
|
|
1369
|
+
// Orgs & Sharing
|
|
1370
|
+
listSessionsSharedWithMe: "sessions.shares.sharedWithMe",
|
|
1371
|
+
listSessionShares: "sessions.shares.list",
|
|
1372
|
+
createSessionShare: "sessions.shares.create",
|
|
1373
|
+
deleteSessionShare: "sessions.shares.delete",
|
|
1374
|
+
createOrg: "orgs.create",
|
|
1375
|
+
listOrgs: "orgs.list",
|
|
1376
|
+
listOrgMembers: "orgs.members.list",
|
|
1377
|
+
addOrgMember: "orgs.members.add",
|
|
1378
|
+
removeOrgMember: "orgs.members.remove",
|
|
1379
|
+
// Account
|
|
1380
|
+
getAccount: "account.get",
|
|
1381
|
+
patchAccount: "account.update",
|
|
1382
|
+
getReceipt: "receipt.get",
|
|
1383
|
+
submitFeedback: "feedback.submit",
|
|
1384
|
+
listPhoneModels: "phoneModels.list"
|
|
1385
|
+
};
|
|
1386
|
+
var STREAMING_OPERATIONS = /* @__PURE__ */ new Set([
|
|
1387
|
+
"streamAgentRun",
|
|
1388
|
+
"streamSession",
|
|
1389
|
+
"streamHost"
|
|
1390
|
+
]);
|
|
1391
|
+
|
|
1392
|
+
// src/index.ts
|
|
1393
|
+
var Codai = class {
|
|
1394
|
+
http;
|
|
1395
|
+
/** Resource group (`chat.completions.create/stream`) that is also callable with the 0.2.x `chat({ messages })` shape. */
|
|
1396
|
+
chat;
|
|
1397
|
+
messages;
|
|
1398
|
+
responses;
|
|
1399
|
+
embeddings;
|
|
1400
|
+
audio;
|
|
1401
|
+
tokens;
|
|
1402
|
+
models;
|
|
1403
|
+
health;
|
|
1404
|
+
agents;
|
|
1405
|
+
tools;
|
|
1406
|
+
tasks;
|
|
1407
|
+
sessions;
|
|
1408
|
+
devices;
|
|
1409
|
+
hosts;
|
|
1410
|
+
orgs;
|
|
1411
|
+
account;
|
|
1412
|
+
receipt;
|
|
1413
|
+
feedback;
|
|
1414
|
+
phoneModels;
|
|
1415
|
+
constructor(opts) {
|
|
1416
|
+
if (!opts.apiKey) throw new Error("Codai: apiKey is required");
|
|
1417
|
+
this.http = new HttpClient({
|
|
1418
|
+
apiKey: opts.apiKey,
|
|
1419
|
+
baseUrl: opts.baseUrl ?? "https://ai.codai.ro",
|
|
1420
|
+
timeoutMs: opts.timeoutMs ?? 12e4,
|
|
1421
|
+
maxRetries: opts.maxRetries ?? 2,
|
|
1422
|
+
fetch: opts.fetch,
|
|
1423
|
+
defaults: {
|
|
1424
|
+
...opts.defaults,
|
|
1425
|
+
...opts.sessionId ? { sessionId: opts.sessionId } : {},
|
|
1426
|
+
...opts.device ? { device: opts.device } : {},
|
|
1427
|
+
...opts.deviceName ? { deviceName: opts.deviceName } : {},
|
|
1428
|
+
...opts.devicePlatform ? { devicePlatform: opts.devicePlatform } : {},
|
|
1429
|
+
...opts.client ? { client: opts.client } : {}
|
|
255
1430
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
1431
|
+
});
|
|
1432
|
+
const http = this.http;
|
|
1433
|
+
this.messages = new Messages(http);
|
|
1434
|
+
this.responses = new Responses(http);
|
|
1435
|
+
this.audio = new Audio(http);
|
|
1436
|
+
this.tokens = new Tokens(http);
|
|
1437
|
+
this.health = new Health(http);
|
|
1438
|
+
this.agents = new Agents(http);
|
|
1439
|
+
this.tools = new Tools(http);
|
|
1440
|
+
this.tasks = new Tasks(http);
|
|
1441
|
+
this.sessions = new Sessions(http);
|
|
1442
|
+
this.devices = new Devices(http);
|
|
1443
|
+
this.hosts = new Hosts(http);
|
|
1444
|
+
this.orgs = new Orgs(http);
|
|
1445
|
+
this.account = new Account(http);
|
|
1446
|
+
this.receipt = new Receipts(http);
|
|
1447
|
+
this.phoneModels = new PhoneModels(http);
|
|
1448
|
+
this.chat = callable(
|
|
1449
|
+
new Chat(http),
|
|
1450
|
+
(o) => this.chat.completions.create(this.legacyBody(o), {
|
|
1451
|
+
ext: this.legacyExt(o),
|
|
1452
|
+
signal: o.signal
|
|
1453
|
+
})
|
|
1454
|
+
);
|
|
1455
|
+
this.embeddings = callable(new Embeddings(http), async (o) => {
|
|
1456
|
+
const r = await this.embeddings.create(
|
|
1457
|
+
{ input: o.input, model: o.model, dimensions: o.dimensions },
|
|
1458
|
+
{ signal: o.signal }
|
|
269
1459
|
);
|
|
270
|
-
return
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
1460
|
+
return { embeddings: r.embeddings, raw: r.raw };
|
|
1461
|
+
});
|
|
1462
|
+
this.models = callable(
|
|
1463
|
+
new Models(http),
|
|
1464
|
+
async () => (await this.models.list()).map((m) => ({ id: m.id }))
|
|
1465
|
+
);
|
|
1466
|
+
this.feedback = callable(
|
|
1467
|
+
new Feedback(http),
|
|
1468
|
+
async (requestId, rating, comment) => {
|
|
1469
|
+
await this.feedback.submit({
|
|
1470
|
+
event_id: requestId,
|
|
1471
|
+
rating,
|
|
1472
|
+
...comment ? { comment } : {}
|
|
1473
|
+
});
|
|
1474
|
+
}
|
|
1475
|
+
);
|
|
1476
|
+
}
|
|
1477
|
+
// ------------------------------------------------- 0.2.x compatibility aliases
|
|
1478
|
+
/** Alias of `chat.completions.stream()` with the 0.2.x option names. */
|
|
1479
|
+
chatStream(opts) {
|
|
1480
|
+
return this.chat.completions.stream(this.legacyBody(opts), {
|
|
1481
|
+
ext: this.legacyExt(opts),
|
|
1482
|
+
signal: opts.signal
|
|
1483
|
+
});
|
|
1484
|
+
}
|
|
1485
|
+
/** Alias of `tokens.create()`. */
|
|
1486
|
+
mintToken(scope, ttlSeconds) {
|
|
1487
|
+
return this.tokens.create(scope, ttlSeconds);
|
|
1488
|
+
}
|
|
1489
|
+
legacyBody(opts) {
|
|
275
1490
|
return {
|
|
276
1491
|
model: opts.model ?? "codai",
|
|
277
1492
|
messages: opts.messages,
|
|
278
|
-
stream,
|
|
279
1493
|
...opts.temperature !== void 0 ? { temperature: opts.temperature } : {},
|
|
280
1494
|
...opts.maxTokens !== void 0 ? { max_tokens: opts.maxTokens } : {},
|
|
281
1495
|
...opts.tools ? { tools: opts.tools } : {}
|
|
282
1496
|
};
|
|
283
1497
|
}
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
}
|
|
294
|
-
async request(path, body, init = {}) {
|
|
295
|
-
const url = `${this.baseUrl}${path}`;
|
|
296
|
-
let lastErr;
|
|
297
|
-
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
298
|
-
try {
|
|
299
|
-
const res = await fetch(url, {
|
|
300
|
-
method: init.method ?? "POST",
|
|
301
|
-
headers: {
|
|
302
|
-
Authorization: `Bearer ${this.apiKey}`,
|
|
303
|
-
...body !== null ? { "Content-Type": "application/json" } : {},
|
|
304
|
-
...init.headers
|
|
305
|
-
},
|
|
306
|
-
...body !== null ? { body: JSON.stringify(body) } : {},
|
|
307
|
-
signal: init.signal ?? AbortSignal.timeout(this.timeoutMs)
|
|
308
|
-
});
|
|
309
|
-
if (res.ok) return res;
|
|
310
|
-
if (RETRYABLE.has(res.status) && attempt < this.maxRetries) {
|
|
311
|
-
await sleep(500 * 2 ** attempt + Math.random() * 250);
|
|
312
|
-
continue;
|
|
313
|
-
}
|
|
314
|
-
let errBody = null;
|
|
315
|
-
try {
|
|
316
|
-
errBody = await res.json();
|
|
317
|
-
} catch {
|
|
318
|
-
}
|
|
319
|
-
throw new CodaiError(`codai ${path} \u2192 ${res.status}`, res.status, errBody);
|
|
320
|
-
} catch (err) {
|
|
321
|
-
if (err instanceof CodaiError) throw err;
|
|
322
|
-
lastErr = err;
|
|
323
|
-
if (attempt < this.maxRetries) {
|
|
324
|
-
await sleep(500 * 2 ** attempt);
|
|
325
|
-
continue;
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
throw new CodaiError(`codai ${path} failed after retries: ${String(lastErr)}`, 0, null);
|
|
1498
|
+
legacyExt(opts) {
|
|
1499
|
+
return {
|
|
1500
|
+
...opts.ext,
|
|
1501
|
+
...opts.sessionId ? { sessionId: opts.sessionId } : {},
|
|
1502
|
+
...opts.agentMode ? { mode: "agent" } : {},
|
|
1503
|
+
...opts.compact ? { compact: opts.compact } : {},
|
|
1504
|
+
...opts.bestOf !== void 0 ? { bestOf: opts.bestOf } : {},
|
|
1505
|
+
...opts.agentId ? { agentId: opts.agentId } : {}
|
|
1506
|
+
};
|
|
330
1507
|
}
|
|
331
1508
|
};
|
|
332
|
-
function
|
|
333
|
-
|
|
1509
|
+
function callable(target, fn) {
|
|
1510
|
+
const f = ((...args) => fn(...args));
|
|
1511
|
+
for (const key of allKeys(target)) {
|
|
1512
|
+
const value = target[key];
|
|
1513
|
+
Object.defineProperty(f, key, {
|
|
1514
|
+
value: typeof value === "function" ? value.bind(target) : value,
|
|
1515
|
+
enumerable: true,
|
|
1516
|
+
writable: false
|
|
1517
|
+
});
|
|
1518
|
+
}
|
|
1519
|
+
return f;
|
|
1520
|
+
}
|
|
1521
|
+
function allKeys(obj) {
|
|
1522
|
+
const keys = /* @__PURE__ */ new Set();
|
|
1523
|
+
let cur = obj;
|
|
1524
|
+
while (cur && cur !== Object.prototype) {
|
|
1525
|
+
for (const k of Object.getOwnPropertyNames(cur)) if (k !== "constructor") keys.add(k);
|
|
1526
|
+
cur = Object.getPrototypeOf(cur);
|
|
1527
|
+
}
|
|
1528
|
+
return [...keys];
|
|
334
1529
|
}
|
|
335
1530
|
var index_default = Codai;
|
|
336
1531
|
|
|
337
|
-
export { Codai, CodaiError, index_default as default };
|
|
1532
|
+
export { Account, AgentRuns, Agents, Audio, Chat, ChatCompletions, Codai, CodaiError, Devices, Embeddings, Feedback, Health, Hosts, HttpClient, Messages, Models, OPERATION_METHODS, OrgMembers, Orgs, PhoneModels, Receipts, Responses, STREAMING_OPERATIONS, SessionControls, SessionEvents, SessionLease, SessionShares, Sessions, Tasks, Tokens, Tools, index_default as default, extensionHeaders, frameJson, mergeToolCall, readSse };
|
|
338
1533
|
//# sourceMappingURL=index.js.map
|
|
339
1534
|
//# sourceMappingURL=index.js.map
|