codai-sdk 0.2.0 → 0.4.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 +181 -4
- package/dist/index.d.ts +11101 -128
- package/dist/index.js +1921 -264
- 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,44 @@ 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
|
}
|
|
68
|
-
/**
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const requestPromise = this.request("/v1/chat/completions", this.chatBody(opts, true), {
|
|
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,
|
|
74
533
|
signal: opts.signal,
|
|
75
|
-
|
|
534
|
+
timeoutMs: 0,
|
|
535
|
+
noRetry: true
|
|
76
536
|
});
|
|
77
537
|
let resolveFinal;
|
|
78
538
|
let rejectFinal;
|
|
@@ -83,257 +543,1454 @@ var Codai = class {
|
|
|
83
543
|
final.catch(() => {
|
|
84
544
|
});
|
|
85
545
|
const toolCallsByIndex = /* @__PURE__ */ new Map();
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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() {
|
|
90
561
|
try {
|
|
91
562
|
res = await requestPromise;
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
let buffer = "";
|
|
104
|
-
let finished = false;
|
|
105
|
-
const buildResult = () => ({
|
|
106
|
-
content,
|
|
107
|
-
toolCalls: [...toolCallsByIndex.values()],
|
|
108
|
-
requestId: res.headers.get("x-request-id"),
|
|
109
|
-
routedTo: res.headers.get("x-codai-routed-to"),
|
|
110
|
-
execVerify: res.headers.get("x-codai-exec-verify"),
|
|
111
|
-
usage
|
|
112
|
-
});
|
|
113
|
-
try {
|
|
114
|
-
for (; ; ) {
|
|
115
|
-
const { done, value } = await reader.read();
|
|
116
|
-
if (done) break;
|
|
117
|
-
buffer += decoder.decode(value, { stream: true });
|
|
118
|
-
const frames = buffer.split("\n\n");
|
|
119
|
-
buffer = frames.pop() ?? "";
|
|
120
|
-
for (const frame of frames) {
|
|
121
|
-
for (const line of frame.split("\n")) {
|
|
122
|
-
if (!line.startsWith("data:")) continue;
|
|
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
|
-
}
|
|
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);
|
|
151
574
|
}
|
|
152
|
-
if (finished) break;
|
|
153
575
|
}
|
|
154
|
-
if (
|
|
576
|
+
if (delta?.content) content += delta.content;
|
|
577
|
+
yield chunk;
|
|
155
578
|
}
|
|
156
579
|
resolveFinal(buildResult());
|
|
157
580
|
} catch (err) {
|
|
158
581
|
rejectFinal(err);
|
|
159
582
|
throw err;
|
|
160
|
-
} finally {
|
|
161
|
-
reader.releaseLock?.();
|
|
162
583
|
}
|
|
163
584
|
}
|
|
164
|
-
|
|
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
|
+
}
|
|
165
591
|
return {
|
|
166
|
-
[Symbol.asyncIterator]: () =>
|
|
592
|
+
[Symbol.asyncIterator]: () => text(),
|
|
593
|
+
chunks: () => ({ [Symbol.asyncIterator]: () => chunks() }),
|
|
167
594
|
final
|
|
168
595
|
};
|
|
169
596
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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
|
+
});
|
|
206
635
|
}
|
|
207
|
-
// -------------------------------------------------------------- tokens
|
|
208
636
|
/**
|
|
209
|
-
*
|
|
210
|
-
*
|
|
637
|
+
* `GET /v1/devices/me/dispatch?limit=` — pending controls dispatched at the
|
|
638
|
+
* calling device (`x-codai-device` required) across all owned sessions.
|
|
211
639
|
*/
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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
|
|
216
645
|
});
|
|
217
|
-
const body = await res.json();
|
|
218
|
-
return { token: body.token, expiresAt: body.expires_at, scope: body.scope };
|
|
219
646
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
audio = {
|
|
236
|
-
/** Speech-to-text (multipart upload, max 25 MB). Returns the transcript. */
|
|
237
|
-
transcribe: async (opts) => {
|
|
238
|
-
const form = new FormData();
|
|
239
|
-
const blob = opts.file instanceof Blob ? opts.file : new Blob([opts.file]);
|
|
240
|
-
form.append("file", blob, opts.filename ?? "audio.webm");
|
|
241
|
-
form.append("model", opts.model ?? "codai-transcribe");
|
|
242
|
-
const res = await fetch(`${this.baseUrl}/v1/audio/transcriptions`, {
|
|
243
|
-
method: "POST",
|
|
244
|
-
headers: { Authorization: `Bearer ${this.apiKey}` },
|
|
245
|
-
body: form,
|
|
246
|
-
signal: opts.signal ?? AbortSignal.timeout(this.timeoutMs)
|
|
247
|
-
});
|
|
248
|
-
if (!res.ok) {
|
|
249
|
-
let errBody = null;
|
|
250
|
-
try {
|
|
251
|
-
errBody = await res.json();
|
|
252
|
-
} catch {
|
|
253
|
-
}
|
|
254
|
-
throw new CodaiError(`codai /v1/audio/transcriptions \u2192 ${res.status}`, res.status, errBody);
|
|
255
|
-
}
|
|
256
|
-
const body = await res.json();
|
|
257
|
-
return body.text ?? "";
|
|
258
|
-
},
|
|
259
|
-
/** Text-to-speech. Returns the audio bytes (default mp3). */
|
|
260
|
-
speech: async (opts) => {
|
|
261
|
-
const res = await this.request(
|
|
262
|
-
"/v1/audio/speech",
|
|
263
|
-
{
|
|
264
|
-
model: opts.model ?? "codai-tts",
|
|
265
|
-
input: opts.input,
|
|
266
|
-
voice: opts.voice ?? "alloy"
|
|
267
|
-
},
|
|
268
|
-
{ signal: opts.signal }
|
|
269
|
-
);
|
|
270
|
-
return res.arrayBuffer();
|
|
271
|
-
}
|
|
272
|
-
};
|
|
273
|
-
// ------------------------------------------------------------ internal
|
|
274
|
-
chatBody(opts, stream) {
|
|
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();
|
|
275
662
|
return {
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
...opts.tools ? { tools: opts.tools } : {}
|
|
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
|
|
282
668
|
};
|
|
283
669
|
}
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
}
|
|
294
|
-
async
|
|
295
|
-
const
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
670
|
+
};
|
|
671
|
+
|
|
672
|
+
// src/resources/environments.ts
|
|
673
|
+
var envPath = (id, suffix = "") => `/v1/environments/${encodeURIComponent(id)}${suffix}`;
|
|
674
|
+
var EnvironmentMembers = class {
|
|
675
|
+
constructor(http) {
|
|
676
|
+
this.http = http;
|
|
677
|
+
}
|
|
678
|
+
http;
|
|
679
|
+
/** `GET /v1/environments/{id}/members` — any role. */
|
|
680
|
+
async list(envId, opts = {}) {
|
|
681
|
+
const raw = await this.http.json(envPath(envId, "/members"), {
|
|
682
|
+
ext: opts.ext,
|
|
683
|
+
signal: opts.signal
|
|
684
|
+
});
|
|
685
|
+
return [...raw.members ?? []];
|
|
686
|
+
}
|
|
687
|
+
/** `POST /v1/environments/{id}/members` — exactly one of `user_id` / `email`. */
|
|
688
|
+
async add(envId, body, opts = {}) {
|
|
689
|
+
const raw = await this.http.json(
|
|
690
|
+
envPath(envId, "/members"),
|
|
691
|
+
{ body, ext: opts.ext, signal: opts.signal }
|
|
692
|
+
);
|
|
693
|
+
return raw.member;
|
|
694
|
+
}
|
|
695
|
+
/** `PUT /v1/environments/{id}/members/me/ssh-key` — one `authorized_keys` line. */
|
|
696
|
+
setSshKey(envId, publicKey, opts = {}) {
|
|
697
|
+
const body = { public_key: publicKey };
|
|
698
|
+
return this.http.json(envPath(envId, "/members/me/ssh-key"), {
|
|
699
|
+
method: "PUT",
|
|
700
|
+
body,
|
|
701
|
+
ext: opts.ext,
|
|
702
|
+
signal: opts.signal
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
/** `PATCH /v1/environments/{id}/members/{userId}` — 409 `last_owner` on demoting the last owner. */
|
|
706
|
+
async setRole(envId, userId, role, opts = {}) {
|
|
707
|
+
const raw = await this.http.json(
|
|
708
|
+
envPath(envId, `/members/${encodeURIComponent(userId)}`),
|
|
709
|
+
{ method: "PATCH", body: { role }, ext: opts.ext, signal: opts.signal }
|
|
710
|
+
);
|
|
711
|
+
return raw.member;
|
|
712
|
+
}
|
|
713
|
+
/** `DELETE /v1/environments/{id}/members/{userId}` — pass your own id to leave. */
|
|
714
|
+
remove(envId, userId, opts = {}) {
|
|
715
|
+
return this.http.json(
|
|
716
|
+
envPath(envId, `/members/${encodeURIComponent(userId)}`),
|
|
717
|
+
{ method: "DELETE", ext: opts.ext, signal: opts.signal }
|
|
718
|
+
);
|
|
719
|
+
}
|
|
720
|
+
/** `POST /v1/environments/{id}/members/me/ssh-cert` — 12 h OpenSSH user certificate for your key. */
|
|
721
|
+
sshCert(envId, body, opts = {}) {
|
|
722
|
+
return this.http.json(envPath(envId, "/members/me/ssh-cert"), {
|
|
723
|
+
method: "POST",
|
|
724
|
+
body,
|
|
725
|
+
ext: opts.ext,
|
|
726
|
+
signal: opts.signal,
|
|
727
|
+
noRetry: true
|
|
728
|
+
});
|
|
330
729
|
}
|
|
331
730
|
};
|
|
332
|
-
|
|
333
|
-
|
|
731
|
+
var EnvironmentTasks = class {
|
|
732
|
+
constructor(http) {
|
|
733
|
+
this.http = http;
|
|
734
|
+
}
|
|
735
|
+
http;
|
|
736
|
+
/** `GET /v1/environments/{id}/tasks` */
|
|
737
|
+
list(envId, opts = {}) {
|
|
738
|
+
return this.http.json(envPath(envId, "/tasks"), { ext: opts.ext, signal: opts.signal });
|
|
739
|
+
}
|
|
740
|
+
/** `POST /v1/environments/{id}/tasks` — queued; you get a notification when it finishes. */
|
|
741
|
+
create(envId, body, opts = {}) {
|
|
742
|
+
return this.http.json(envPath(envId, "/tasks"), {
|
|
743
|
+
method: "POST",
|
|
744
|
+
body,
|
|
745
|
+
ext: opts.ext,
|
|
746
|
+
signal: opts.signal,
|
|
747
|
+
noRetry: true
|
|
748
|
+
});
|
|
749
|
+
}
|
|
750
|
+
/** `GET /v1/environments/{id}/tasks/{taskId}` */
|
|
751
|
+
get(envId, taskId, opts = {}) {
|
|
752
|
+
return this.http.json(envPath(envId, `/tasks/${encodeURIComponent(taskId)}`), { ext: opts.ext, signal: opts.signal });
|
|
753
|
+
}
|
|
754
|
+
};
|
|
755
|
+
var EnvironmentPorts = class {
|
|
756
|
+
constructor(http) {
|
|
757
|
+
this.http = http;
|
|
758
|
+
}
|
|
759
|
+
http;
|
|
760
|
+
/** `GET /v1/environments/{id}/ports` — published (forwarded) ports. */
|
|
761
|
+
async list(envId, opts = {}) {
|
|
762
|
+
const raw = await this.http.json(envPath(envId, "/ports"), {
|
|
763
|
+
ext: opts.ext,
|
|
764
|
+
signal: opts.signal
|
|
765
|
+
});
|
|
766
|
+
return [...raw.ports ?? []];
|
|
767
|
+
}
|
|
768
|
+
/** `PUT /v1/environments/{id}/ports/{port}` — publish or change visibility (upsert). */
|
|
769
|
+
async set(envId, port, body, opts = {}) {
|
|
770
|
+
const raw = await this.http.json(
|
|
771
|
+
envPath(envId, `/ports/${encodeURIComponent(String(port))}`),
|
|
772
|
+
{ method: "PUT", body, ext: opts.ext, signal: opts.signal }
|
|
773
|
+
);
|
|
774
|
+
return raw.port;
|
|
775
|
+
}
|
|
776
|
+
/** `DELETE /v1/environments/{id}/ports/{port}` — unpublish. */
|
|
777
|
+
remove(envId, port, opts = {}) {
|
|
778
|
+
return this.http.json(
|
|
779
|
+
envPath(envId, `/ports/${encodeURIComponent(String(port))}`),
|
|
780
|
+
{ method: "DELETE", ext: opts.ext, signal: opts.signal }
|
|
781
|
+
);
|
|
782
|
+
}
|
|
783
|
+
};
|
|
784
|
+
var EnvironmentSecrets = class {
|
|
785
|
+
constructor(http) {
|
|
786
|
+
this.http = http;
|
|
787
|
+
}
|
|
788
|
+
http;
|
|
789
|
+
/** `GET /v1/environments/{id}/secrets` — names and timestamps only, never values. */
|
|
790
|
+
async list(envId, opts = {}) {
|
|
791
|
+
const raw = await this.http.json(envPath(envId, "/secrets"), {
|
|
792
|
+
ext: opts.ext,
|
|
793
|
+
signal: opts.signal
|
|
794
|
+
});
|
|
795
|
+
return [...raw.secrets ?? []];
|
|
796
|
+
}
|
|
797
|
+
/** `PUT /v1/environments/{id}/secrets/{name}` — upsert; `name` is `ENV_STYLE`. */
|
|
798
|
+
set(envId, name, value, opts = {}) {
|
|
799
|
+
const body = { value };
|
|
800
|
+
return this.http.json(
|
|
801
|
+
envPath(envId, `/secrets/${encodeURIComponent(name)}`),
|
|
802
|
+
{ method: "PUT", body, ext: opts.ext, signal: opts.signal }
|
|
803
|
+
);
|
|
804
|
+
}
|
|
805
|
+
/** `DELETE /v1/environments/{id}/secrets/{name}` */
|
|
806
|
+
remove(envId, name, opts = {}) {
|
|
807
|
+
return this.http.json(
|
|
808
|
+
envPath(envId, `/secrets/${encodeURIComponent(name)}`),
|
|
809
|
+
{ method: "DELETE", ext: opts.ext, signal: opts.signal }
|
|
810
|
+
);
|
|
811
|
+
}
|
|
812
|
+
};
|
|
813
|
+
var Environments = class {
|
|
814
|
+
constructor(http) {
|
|
815
|
+
this.http = http;
|
|
816
|
+
this.members = new EnvironmentMembers(http);
|
|
817
|
+
this.ports = new EnvironmentPorts(http);
|
|
818
|
+
this.secrets = new EnvironmentSecrets(http);
|
|
819
|
+
this.tasks = new EnvironmentTasks(http);
|
|
820
|
+
}
|
|
821
|
+
http;
|
|
822
|
+
members;
|
|
823
|
+
ports;
|
|
824
|
+
secrets;
|
|
825
|
+
tasks;
|
|
826
|
+
/** `GET /v1/environments/{id}/ssh-ca` — CA public keys to trust (`@cert-authority` lines). */
|
|
827
|
+
sshCa(envId, opts = {}) {
|
|
828
|
+
return this.http.json(envPath(envId, "/ssh-ca"), { ext: opts.ext, signal: opts.signal });
|
|
829
|
+
}
|
|
830
|
+
/** `GET /v1/environments?project_id=&for_user_id=me` — newest first, every state. */
|
|
831
|
+
async list(query = {}, opts = {}) {
|
|
832
|
+
const raw = await this.http.json("/v1/environments", {
|
|
833
|
+
query: { project_id: query.project_id, for_user_id: query.for_user_id },
|
|
834
|
+
ext: opts.ext,
|
|
835
|
+
signal: opts.signal
|
|
836
|
+
});
|
|
837
|
+
return [...raw.environments ?? []];
|
|
838
|
+
}
|
|
839
|
+
/**
|
|
840
|
+
* `POST /v1/environments` — `byo` starts `pending`; managed providers start
|
|
841
|
+
* `creating`. 409 `already_has_env` in a `vm_per_dev` project (the error
|
|
842
|
+
* body carries the `environment_id` to reuse).
|
|
843
|
+
*/
|
|
844
|
+
async create(body, opts = {}) {
|
|
845
|
+
const raw = await this.http.json("/v1/environments", {
|
|
846
|
+
body,
|
|
847
|
+
ext: opts.ext,
|
|
848
|
+
signal: opts.signal,
|
|
849
|
+
noRetry: true
|
|
850
|
+
});
|
|
851
|
+
return raw.environment;
|
|
852
|
+
}
|
|
853
|
+
/**
|
|
854
|
+
* `POST /v1/environments/enroll` — daemon side. The one-time `codai_env_…`
|
|
855
|
+
* token in the body is the only credential; the client's API key header is
|
|
856
|
+
* ignored by this route. Managed environments additionally receive a
|
|
857
|
+
* freshly minted `api_key`, returned exactly once.
|
|
858
|
+
*/
|
|
859
|
+
enroll(body, opts = {}) {
|
|
860
|
+
return this.http.json("/v1/environments/enroll", {
|
|
861
|
+
body,
|
|
862
|
+
ext: opts.ext,
|
|
863
|
+
signal: opts.signal,
|
|
864
|
+
// The token is consumed on success; a retried POST would 401.
|
|
865
|
+
noRetry: true
|
|
866
|
+
});
|
|
867
|
+
}
|
|
868
|
+
/** `GET /v1/environments/{id}` — environment, your role, members, ports, recent events. */
|
|
869
|
+
get(envId, opts = {}) {
|
|
870
|
+
return this.http.json(envPath(envId), {
|
|
871
|
+
ext: opts.ext,
|
|
872
|
+
signal: opts.signal
|
|
873
|
+
});
|
|
874
|
+
}
|
|
875
|
+
/** `PATCH /v1/environments/{id}` — `name` and/or `idle_timeout_minutes` (5..1440). */
|
|
876
|
+
async update(envId, body, opts = {}) {
|
|
877
|
+
const raw = await this.http.json(envPath(envId), {
|
|
878
|
+
method: "PATCH",
|
|
879
|
+
body,
|
|
880
|
+
ext: opts.ext,
|
|
881
|
+
signal: opts.signal
|
|
882
|
+
});
|
|
883
|
+
return raw.environment;
|
|
884
|
+
}
|
|
885
|
+
/** `POST /v1/environments/{id}/start` — managed providers only (`stopped` → `starting`). */
|
|
886
|
+
start(envId, opts = {}) {
|
|
887
|
+
return this.transition(envId, "start", opts);
|
|
888
|
+
}
|
|
889
|
+
/** `POST /v1/environments/{id}/stop` — managed providers only (`running` → `stopping`). */
|
|
890
|
+
stop(envId, opts = {}) {
|
|
891
|
+
return this.transition(envId, "stop", opts);
|
|
892
|
+
}
|
|
893
|
+
/** `POST /v1/environments/{id}/archive` — keeps history, releases the machine. */
|
|
894
|
+
archive(envId, opts = {}) {
|
|
895
|
+
return this.transition(envId, "archive", opts);
|
|
896
|
+
}
|
|
897
|
+
/** `POST /v1/environments/{id}/destroy` — owner only; terminal. */
|
|
898
|
+
destroy(envId, opts = {}) {
|
|
899
|
+
return this.transition(envId, "destroy", opts);
|
|
900
|
+
}
|
|
901
|
+
/**
|
|
902
|
+
* `POST /v1/environments/{id}/enroll-token` — `byo` only; a 30-minute
|
|
903
|
+
* one-time token for `enroll()`. Minting again replaces the previous one.
|
|
904
|
+
*/
|
|
905
|
+
enrollToken(envId, opts = {}) {
|
|
906
|
+
return this.http.json(envPath(envId, "/enroll-token"), {
|
|
907
|
+
method: "POST",
|
|
908
|
+
ext: opts.ext,
|
|
909
|
+
signal: opts.signal,
|
|
910
|
+
noRetry: true
|
|
911
|
+
});
|
|
912
|
+
}
|
|
913
|
+
transition(envId, action, opts) {
|
|
914
|
+
return this.http.json(envPath(envId, `/${action}`), {
|
|
915
|
+
method: "POST",
|
|
916
|
+
ext: opts.ext,
|
|
917
|
+
signal: opts.signal
|
|
918
|
+
});
|
|
919
|
+
}
|
|
920
|
+
};
|
|
921
|
+
|
|
922
|
+
// src/resources/health.ts
|
|
923
|
+
var Health = class {
|
|
924
|
+
constructor(http) {
|
|
925
|
+
this.http = http;
|
|
926
|
+
}
|
|
927
|
+
http;
|
|
928
|
+
/** Liveness probe. */
|
|
929
|
+
get(opts = {}) {
|
|
930
|
+
return this.http.json("/health", { signal: opts.signal, noRetry: true });
|
|
931
|
+
}
|
|
932
|
+
/**
|
|
933
|
+
* Deep readiness probe. A 503 is returned as `{ ok: false, ... }` rather
|
|
934
|
+
* than thrown, so callers can read the failing check.
|
|
935
|
+
*/
|
|
936
|
+
async ready(opts = {}) {
|
|
937
|
+
const res = await this.http.request("/health/ready", {
|
|
938
|
+
signal: opts.signal,
|
|
939
|
+
noRetry: true,
|
|
940
|
+
acceptStatus: [503]
|
|
941
|
+
});
|
|
942
|
+
const body = await res.json();
|
|
943
|
+
return { ...body, httpStatus: res.status };
|
|
944
|
+
}
|
|
945
|
+
/** Registry snapshot (aliases + provider health). */
|
|
946
|
+
status(opts = {}) {
|
|
947
|
+
return this.http.json("/status", { signal: opts.signal, noRetry: true });
|
|
948
|
+
}
|
|
949
|
+
};
|
|
950
|
+
|
|
951
|
+
// src/resources/hosts.ts
|
|
952
|
+
var Hosts = class {
|
|
953
|
+
constructor(http) {
|
|
954
|
+
this.http = http;
|
|
955
|
+
}
|
|
956
|
+
http;
|
|
957
|
+
/** `GET /v1/hosts` — your devices currently connected as hosts. */
|
|
958
|
+
async list(opts = {}) {
|
|
959
|
+
const raw = await this.http.json("/v1/hosts", {
|
|
960
|
+
ext: opts.ext,
|
|
961
|
+
signal: opts.signal
|
|
962
|
+
});
|
|
963
|
+
return [...raw.hosts ?? []];
|
|
964
|
+
}
|
|
965
|
+
/**
|
|
966
|
+
* `POST /v1/hosts/{deviceId}/exec` — run one op on a connected host and
|
|
967
|
+
* block until it answers (or `timeout_ms`, default 60 s).
|
|
968
|
+
*/
|
|
969
|
+
exec(deviceId, op, args = {}, extra = {}) {
|
|
970
|
+
const body = {
|
|
971
|
+
op,
|
|
972
|
+
args,
|
|
973
|
+
...extra.timeoutMs !== void 0 ? { timeout_ms: extra.timeoutMs } : {},
|
|
974
|
+
...extra.label !== void 0 ? { label: extra.label } : {}
|
|
975
|
+
};
|
|
976
|
+
return this.http.json(`/v1/hosts/${encodeURIComponent(deviceId)}/exec`, {
|
|
977
|
+
body,
|
|
978
|
+
ext: extra.ext,
|
|
979
|
+
signal: extra.signal,
|
|
980
|
+
// The gateway itself waits up to timeout_ms + 2 s; give the HTTP layer headroom.
|
|
981
|
+
timeoutMs: (extra.timeoutMs ?? 6e4) + 1e4,
|
|
982
|
+
noRetry: true
|
|
983
|
+
});
|
|
984
|
+
}
|
|
985
|
+
/** `POST /v1/hosts/exec/{reqId}/result` — host side: deliver an exec result. */
|
|
986
|
+
postResult(reqId, body, opts = {}) {
|
|
987
|
+
return this.http.json(
|
|
988
|
+
`/v1/hosts/exec/${encodeURIComponent(reqId)}/result`,
|
|
989
|
+
{ body, ext: opts.ext, signal: opts.signal }
|
|
990
|
+
);
|
|
991
|
+
}
|
|
992
|
+
/**
|
|
993
|
+
* `GET /v1/hosts/stream?os=&hostname=&roots=` — host side: announce this
|
|
994
|
+
* device and receive `hello` / `ping` / `exec` events. Closed by the server
|
|
995
|
+
* after 6 h; reconnect to re-announce presence.
|
|
996
|
+
*/
|
|
997
|
+
async *stream(query = {}, opts = {}) {
|
|
998
|
+
const frames = this.http.sse("/v1/hosts/stream", {
|
|
999
|
+
query: {
|
|
1000
|
+
os: query.os,
|
|
1001
|
+
hostname: query.hostname,
|
|
1002
|
+
roots: query.roots?.length ? query.roots.join(",") : void 0
|
|
1003
|
+
},
|
|
1004
|
+
ext: opts.ext,
|
|
1005
|
+
signal: opts.signal
|
|
1006
|
+
});
|
|
1007
|
+
for await (const frame of frames) {
|
|
1008
|
+
const data = frameJson(frame);
|
|
1009
|
+
if (!data) continue;
|
|
1010
|
+
yield { event: frame.event, data };
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
};
|
|
1014
|
+
|
|
1015
|
+
// src/resources/messages.ts
|
|
1016
|
+
var Messages = class {
|
|
1017
|
+
constructor(http) {
|
|
1018
|
+
this.http = http;
|
|
1019
|
+
}
|
|
1020
|
+
http;
|
|
1021
|
+
async create(body, opts = {}) {
|
|
1022
|
+
const res = await this.http.request("/v1/messages", {
|
|
1023
|
+
body: { ...body, model: body.model ?? "codai", stream: false },
|
|
1024
|
+
ext: opts.ext,
|
|
1025
|
+
signal: opts.signal
|
|
1026
|
+
});
|
|
1027
|
+
const raw = await res.json();
|
|
1028
|
+
const text = (raw.content ?? []).map((b) => b.type === "text" && typeof b.text === "string" ? b.text : "").join("");
|
|
1029
|
+
return {
|
|
1030
|
+
text,
|
|
1031
|
+
raw,
|
|
1032
|
+
requestId: res.headers.get("x-codai-trace-id") ?? res.headers.get("x-request-id"),
|
|
1033
|
+
routedTo: res.headers.get("x-codai-routed-to"),
|
|
1034
|
+
headers: res.headers
|
|
1035
|
+
};
|
|
1036
|
+
}
|
|
1037
|
+
stream(body, opts = {}) {
|
|
1038
|
+
const requestPromise = this.http.request("/v1/messages", {
|
|
1039
|
+
body: { ...body, model: body.model ?? "codai", stream: true },
|
|
1040
|
+
ext: opts.ext,
|
|
1041
|
+
signal: opts.signal,
|
|
1042
|
+
timeoutMs: 0,
|
|
1043
|
+
noRetry: true
|
|
1044
|
+
});
|
|
1045
|
+
let resolveFinal;
|
|
1046
|
+
let rejectFinal;
|
|
1047
|
+
const final = new Promise((resolve, reject) => {
|
|
1048
|
+
resolveFinal = resolve;
|
|
1049
|
+
rejectFinal = reject;
|
|
1050
|
+
});
|
|
1051
|
+
final.catch(() => {
|
|
1052
|
+
});
|
|
1053
|
+
let text = "";
|
|
1054
|
+
let stopReason = null;
|
|
1055
|
+
let inputTokens = null;
|
|
1056
|
+
let outputTokens = null;
|
|
1057
|
+
let res = null;
|
|
1058
|
+
async function* events() {
|
|
1059
|
+
try {
|
|
1060
|
+
res = await requestPromise;
|
|
1061
|
+
for await (const frame of readSse(res, opts.signal)) {
|
|
1062
|
+
const ev = frameJson(frame);
|
|
1063
|
+
if (!ev) continue;
|
|
1064
|
+
if (typeof ev.type !== "string") ev.type = frame.event;
|
|
1065
|
+
if (ev.type === "message_start") {
|
|
1066
|
+
const usage = ev.message?.usage;
|
|
1067
|
+
if (usage?.input_tokens !== void 0) inputTokens = usage.input_tokens;
|
|
1068
|
+
} else if (ev.type === "content_block_delta") {
|
|
1069
|
+
const delta = ev.delta;
|
|
1070
|
+
if (delta?.type === "text_delta" && typeof delta.text === "string") text += delta.text;
|
|
1071
|
+
} else if (ev.type === "message_delta") {
|
|
1072
|
+
const delta = ev.delta;
|
|
1073
|
+
if (delta?.stop_reason) stopReason = delta.stop_reason;
|
|
1074
|
+
const usage = ev.usage;
|
|
1075
|
+
if (usage?.output_tokens !== void 0) outputTokens = usage.output_tokens;
|
|
1076
|
+
}
|
|
1077
|
+
yield ev;
|
|
1078
|
+
if (ev.type === "message_stop") break;
|
|
1079
|
+
}
|
|
1080
|
+
resolveFinal({
|
|
1081
|
+
text,
|
|
1082
|
+
stopReason,
|
|
1083
|
+
usage: inputTokens !== null || outputTokens !== null ? { inputTokens: inputTokens ?? 0, outputTokens: outputTokens ?? 0 } : null,
|
|
1084
|
+
requestId: res.headers.get("x-codai-trace-id") ?? res.headers.get("x-request-id") ?? null,
|
|
1085
|
+
routedTo: res.headers.get("x-codai-routed-to"),
|
|
1086
|
+
headers: res.headers
|
|
1087
|
+
});
|
|
1088
|
+
} catch (err) {
|
|
1089
|
+
rejectFinal(err);
|
|
1090
|
+
throw err;
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
async function* textOnly() {
|
|
1094
|
+
for await (const ev of events()) {
|
|
1095
|
+
if (ev.type !== "content_block_delta") continue;
|
|
1096
|
+
const delta = ev.delta;
|
|
1097
|
+
if (delta?.type === "text_delta" && delta.text) yield delta.text;
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
return {
|
|
1101
|
+
[Symbol.asyncIterator]: () => events(),
|
|
1102
|
+
text: () => ({ [Symbol.asyncIterator]: () => textOnly() }),
|
|
1103
|
+
final
|
|
1104
|
+
};
|
|
1105
|
+
}
|
|
1106
|
+
};
|
|
1107
|
+
|
|
1108
|
+
// src/resources/models.ts
|
|
1109
|
+
var Models = class {
|
|
1110
|
+
constructor(http) {
|
|
1111
|
+
this.http = http;
|
|
1112
|
+
}
|
|
1113
|
+
http;
|
|
1114
|
+
/** Every model the calling key may address (OpenAI list shape, `data[]`). */
|
|
1115
|
+
async list(opts = {}) {
|
|
1116
|
+
const raw = await this.http.json("/v1/models", {
|
|
1117
|
+
ext: opts.ext,
|
|
1118
|
+
signal: opts.signal
|
|
1119
|
+
});
|
|
1120
|
+
return [...raw.data ?? []];
|
|
1121
|
+
}
|
|
1122
|
+
};
|
|
1123
|
+
|
|
1124
|
+
// src/resources/orgs.ts
|
|
1125
|
+
var OrgMembers = class {
|
|
1126
|
+
constructor(http) {
|
|
1127
|
+
this.http = http;
|
|
1128
|
+
}
|
|
1129
|
+
http;
|
|
1130
|
+
/** `GET /v1/orgs/{id}/members` */
|
|
1131
|
+
async list(orgId, opts = {}) {
|
|
1132
|
+
const raw = await this.http.json(
|
|
1133
|
+
`/v1/orgs/${encodeURIComponent(orgId)}/members`,
|
|
1134
|
+
{ ext: opts.ext, signal: opts.signal }
|
|
1135
|
+
);
|
|
1136
|
+
return [...raw.members ?? []];
|
|
1137
|
+
}
|
|
1138
|
+
/** `POST /v1/orgs/{id}/members` — add or re-role (owner/admin). */
|
|
1139
|
+
add(orgId, body, opts = {}) {
|
|
1140
|
+
return this.http.json(
|
|
1141
|
+
`/v1/orgs/${encodeURIComponent(orgId)}/members`,
|
|
1142
|
+
{ body, ext: opts.ext, signal: opts.signal }
|
|
1143
|
+
);
|
|
1144
|
+
}
|
|
1145
|
+
/** `DELETE /v1/orgs/{id}/members/{userId}` */
|
|
1146
|
+
remove(orgId, userId, opts = {}) {
|
|
1147
|
+
return this.http.json(
|
|
1148
|
+
`/v1/orgs/${encodeURIComponent(orgId)}/members/${encodeURIComponent(userId)}`,
|
|
1149
|
+
{ method: "DELETE", ext: opts.ext, signal: opts.signal }
|
|
1150
|
+
);
|
|
1151
|
+
}
|
|
1152
|
+
};
|
|
1153
|
+
var Orgs = class {
|
|
1154
|
+
constructor(http) {
|
|
1155
|
+
this.http = http;
|
|
1156
|
+
this.members = new OrgMembers(http);
|
|
1157
|
+
}
|
|
1158
|
+
http;
|
|
1159
|
+
members;
|
|
1160
|
+
/** `POST /v1/orgs` */
|
|
1161
|
+
create(name, opts = {}) {
|
|
1162
|
+
return this.http.json("/v1/orgs", { body: { name }, ext: opts.ext, signal: opts.signal });
|
|
1163
|
+
}
|
|
1164
|
+
/** `GET /v1/orgs` */
|
|
1165
|
+
async list(opts = {}) {
|
|
1166
|
+
const raw = await this.http.json("/v1/orgs", {
|
|
1167
|
+
ext: opts.ext,
|
|
1168
|
+
signal: opts.signal
|
|
1169
|
+
});
|
|
1170
|
+
return [...raw.orgs ?? []];
|
|
1171
|
+
}
|
|
1172
|
+
};
|
|
1173
|
+
|
|
1174
|
+
// src/resources/projects.ts
|
|
1175
|
+
var Projects = class {
|
|
1176
|
+
constructor(http) {
|
|
1177
|
+
this.http = http;
|
|
1178
|
+
}
|
|
1179
|
+
http;
|
|
1180
|
+
/** `GET /v1/projects` — projects you own plus those of your orgs, newest first. */
|
|
1181
|
+
async list(opts = {}) {
|
|
1182
|
+
const raw = await this.http.json("/v1/projects", {
|
|
1183
|
+
ext: opts.ext,
|
|
1184
|
+
signal: opts.signal
|
|
1185
|
+
});
|
|
1186
|
+
return [...raw.projects ?? []];
|
|
1187
|
+
}
|
|
1188
|
+
/** `POST /v1/projects` — 409 `slug_taken` when the slug is already yours. */
|
|
1189
|
+
async create(body, opts = {}) {
|
|
1190
|
+
const raw = await this.http.json("/v1/projects", {
|
|
1191
|
+
body,
|
|
1192
|
+
ext: opts.ext,
|
|
1193
|
+
signal: opts.signal
|
|
1194
|
+
});
|
|
1195
|
+
return raw.project;
|
|
1196
|
+
}
|
|
1197
|
+
/** `GET /v1/projects/{id}` — the project plus every environment in it. */
|
|
1198
|
+
get(projectId, opts = {}) {
|
|
1199
|
+
return this.http.json(`/v1/projects/${encodeURIComponent(projectId)}`, {
|
|
1200
|
+
ext: opts.ext,
|
|
1201
|
+
signal: opts.signal
|
|
1202
|
+
});
|
|
1203
|
+
}
|
|
1204
|
+
/** `PATCH /v1/projects/{id}` — owner or org owner/admin; the slug cannot change. */
|
|
1205
|
+
async update(projectId, body, opts = {}) {
|
|
1206
|
+
const raw = await this.http.json(
|
|
1207
|
+
`/v1/projects/${encodeURIComponent(projectId)}`,
|
|
1208
|
+
{ method: "PATCH", body, ext: opts.ext, signal: opts.signal }
|
|
1209
|
+
);
|
|
1210
|
+
return raw.project;
|
|
1211
|
+
}
|
|
1212
|
+
/** `DELETE /v1/projects/{id}` — 409 `project_has_environments` until all are destroyed. */
|
|
1213
|
+
delete(projectId, opts = {}) {
|
|
1214
|
+
return this.http.json(`/v1/projects/${encodeURIComponent(projectId)}`, {
|
|
1215
|
+
method: "DELETE",
|
|
1216
|
+
ext: opts.ext,
|
|
1217
|
+
signal: opts.signal
|
|
1218
|
+
});
|
|
1219
|
+
}
|
|
1220
|
+
};
|
|
1221
|
+
|
|
1222
|
+
// src/resources/responses.ts
|
|
1223
|
+
var Responses = class {
|
|
1224
|
+
constructor(http) {
|
|
1225
|
+
this.http = http;
|
|
1226
|
+
}
|
|
1227
|
+
http;
|
|
1228
|
+
async create(body, opts = {}) {
|
|
1229
|
+
const res = await this.http.request("/v1/responses", {
|
|
1230
|
+
body: { ...body, model: body.model ?? "codai", stream: false },
|
|
1231
|
+
ext: opts.ext,
|
|
1232
|
+
signal: opts.signal
|
|
1233
|
+
});
|
|
1234
|
+
const raw = await res.json();
|
|
1235
|
+
return {
|
|
1236
|
+
outputText: typeof raw.output_text === "string" ? raw.output_text : "",
|
|
1237
|
+
raw,
|
|
1238
|
+
requestId: res.headers.get("x-codai-trace-id") ?? res.headers.get("x-request-id"),
|
|
1239
|
+
routedTo: res.headers.get("x-codai-routed-to"),
|
|
1240
|
+
headers: res.headers
|
|
1241
|
+
};
|
|
1242
|
+
}
|
|
1243
|
+
stream(body, opts = {}) {
|
|
1244
|
+
const requestPromise = this.http.request("/v1/responses", {
|
|
1245
|
+
body: { ...body, model: body.model ?? "codai", stream: true },
|
|
1246
|
+
ext: opts.ext,
|
|
1247
|
+
signal: opts.signal,
|
|
1248
|
+
timeoutMs: 0,
|
|
1249
|
+
noRetry: true
|
|
1250
|
+
});
|
|
1251
|
+
let resolveFinal;
|
|
1252
|
+
let rejectFinal;
|
|
1253
|
+
const final = new Promise((resolve, reject) => {
|
|
1254
|
+
resolveFinal = resolve;
|
|
1255
|
+
rejectFinal = reject;
|
|
1256
|
+
});
|
|
1257
|
+
final.catch(() => {
|
|
1258
|
+
});
|
|
1259
|
+
let outputText = "";
|
|
1260
|
+
let response = null;
|
|
1261
|
+
let res = null;
|
|
1262
|
+
async function* events() {
|
|
1263
|
+
try {
|
|
1264
|
+
res = await requestPromise;
|
|
1265
|
+
for await (const frame of readSse(res, opts.signal)) {
|
|
1266
|
+
const ev = frameJson(frame);
|
|
1267
|
+
if (!ev) continue;
|
|
1268
|
+
if (typeof ev.type !== "string") ev.type = frame.event;
|
|
1269
|
+
if (ev.type === "response.output_text.delta" && typeof ev.delta === "string") {
|
|
1270
|
+
outputText += ev.delta;
|
|
1271
|
+
} else if (ev.type === "response.completed" && ev.response) {
|
|
1272
|
+
response = ev.response;
|
|
1273
|
+
}
|
|
1274
|
+
yield ev;
|
|
1275
|
+
if (ev.type === "response.completed") break;
|
|
1276
|
+
}
|
|
1277
|
+
resolveFinal({
|
|
1278
|
+
outputText,
|
|
1279
|
+
response,
|
|
1280
|
+
requestId: res.headers.get("x-codai-trace-id") ?? res.headers.get("x-request-id") ?? null,
|
|
1281
|
+
routedTo: res.headers.get("x-codai-routed-to"),
|
|
1282
|
+
headers: res.headers
|
|
1283
|
+
});
|
|
1284
|
+
} catch (err) {
|
|
1285
|
+
rejectFinal(err);
|
|
1286
|
+
throw err;
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
async function* textOnly() {
|
|
1290
|
+
for await (const ev of events()) {
|
|
1291
|
+
if (ev.type === "response.output_text.delta" && typeof ev.delta === "string")
|
|
1292
|
+
yield ev.delta;
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
return {
|
|
1296
|
+
[Symbol.asyncIterator]: () => events(),
|
|
1297
|
+
text: () => ({ [Symbol.asyncIterator]: () => textOnly() }),
|
|
1298
|
+
final
|
|
1299
|
+
};
|
|
1300
|
+
}
|
|
1301
|
+
};
|
|
1302
|
+
|
|
1303
|
+
// src/resources/sessions.ts
|
|
1304
|
+
var enc = encodeURIComponent;
|
|
1305
|
+
var SessionLease = class {
|
|
1306
|
+
constructor(http) {
|
|
1307
|
+
this.http = http;
|
|
1308
|
+
}
|
|
1309
|
+
http;
|
|
1310
|
+
/** `POST /v1/sessions/{id}/lease` — CAS claim; `force: true` takes over a live lease. */
|
|
1311
|
+
acquire(sessionId, body = {}, opts = {}) {
|
|
1312
|
+
return this.http.json(`/v1/sessions/${enc(sessionId)}/lease`, {
|
|
1313
|
+
method: "POST",
|
|
1314
|
+
body,
|
|
1315
|
+
ext: opts.ext,
|
|
1316
|
+
signal: opts.signal
|
|
1317
|
+
});
|
|
1318
|
+
}
|
|
1319
|
+
/** `PUT /v1/sessions/{id}/lease` — heartbeat every 10 s (TTL 30 s). */
|
|
1320
|
+
renew(sessionId, opts = {}) {
|
|
1321
|
+
return this.http.json(`/v1/sessions/${enc(sessionId)}/lease`, {
|
|
1322
|
+
method: "PUT",
|
|
1323
|
+
ext: opts.ext,
|
|
1324
|
+
signal: opts.signal
|
|
1325
|
+
});
|
|
1326
|
+
}
|
|
1327
|
+
/** `DELETE /v1/sessions/{id}/lease` */
|
|
1328
|
+
release(sessionId, opts = {}) {
|
|
1329
|
+
return this.http.json(`/v1/sessions/${enc(sessionId)}/lease`, {
|
|
1330
|
+
method: "DELETE",
|
|
1331
|
+
ext: opts.ext,
|
|
1332
|
+
signal: opts.signal
|
|
1333
|
+
});
|
|
1334
|
+
}
|
|
1335
|
+
};
|
|
1336
|
+
var SessionEvents = class {
|
|
1337
|
+
constructor(http) {
|
|
1338
|
+
this.http = http;
|
|
1339
|
+
}
|
|
1340
|
+
http;
|
|
1341
|
+
/** `GET /v1/sessions/{id}/events?after=&limit=` */
|
|
1342
|
+
list(sessionId, query = {}, opts = {}) {
|
|
1343
|
+
return this.http.json(`/v1/sessions/${enc(sessionId)}/events`, {
|
|
1344
|
+
query: { after: query.after, limit: query.limit },
|
|
1345
|
+
ext: opts.ext,
|
|
1346
|
+
signal: opts.signal
|
|
1347
|
+
});
|
|
1348
|
+
}
|
|
1349
|
+
/** `POST /v1/sessions/{id}/events` — executor only (needs the live lease). */
|
|
1350
|
+
append(sessionId, body, opts = {}) {
|
|
1351
|
+
return this.http.json(`/v1/sessions/${enc(sessionId)}/events`, {
|
|
1352
|
+
body,
|
|
1353
|
+
ext: opts.ext,
|
|
1354
|
+
signal: opts.signal
|
|
1355
|
+
});
|
|
1356
|
+
}
|
|
1357
|
+
};
|
|
1358
|
+
var SessionControls = class {
|
|
1359
|
+
constructor(http) {
|
|
1360
|
+
this.http = http;
|
|
1361
|
+
}
|
|
1362
|
+
http;
|
|
1363
|
+
/** `GET /v1/sessions/{id}/controls?applied=&target=` — drain the control queue. */
|
|
1364
|
+
async list(sessionId, query = {}, opts = {}) {
|
|
1365
|
+
const raw = await this.http.json(
|
|
1366
|
+
`/v1/sessions/${enc(sessionId)}/controls`,
|
|
1367
|
+
{
|
|
1368
|
+
query: { applied: query.applied, target: query.target },
|
|
1369
|
+
ext: opts.ext,
|
|
1370
|
+
signal: opts.signal
|
|
1371
|
+
}
|
|
1372
|
+
);
|
|
1373
|
+
return [...raw.controls ?? []];
|
|
1374
|
+
}
|
|
1375
|
+
/** `POST /v1/sessions/{id}/control` — send a control to the executor (editor+). */
|
|
1376
|
+
submit(sessionId, body, opts = {}) {
|
|
1377
|
+
return this.http.json(`/v1/sessions/${enc(sessionId)}/control`, {
|
|
1378
|
+
body,
|
|
1379
|
+
ext: opts.ext,
|
|
1380
|
+
signal: opts.signal
|
|
1381
|
+
});
|
|
1382
|
+
}
|
|
1383
|
+
/** `POST /v1/sessions/{id}/control/{cid}/applied` — executor only. */
|
|
1384
|
+
markApplied(sessionId, controlId, opts = {}) {
|
|
1385
|
+
return this.http.json(
|
|
1386
|
+
`/v1/sessions/${enc(sessionId)}/control/${enc(controlId)}/applied`,
|
|
1387
|
+
{ method: "POST", ext: opts.ext, signal: opts.signal }
|
|
1388
|
+
);
|
|
1389
|
+
}
|
|
1390
|
+
};
|
|
1391
|
+
var SessionShares = class {
|
|
1392
|
+
constructor(http) {
|
|
1393
|
+
this.http = http;
|
|
1394
|
+
}
|
|
1395
|
+
http;
|
|
1396
|
+
/** `GET /v1/sessions/{id}/shares` — owner only. */
|
|
1397
|
+
async list(sessionId, opts = {}) {
|
|
1398
|
+
const raw = await this.http.json(
|
|
1399
|
+
`/v1/sessions/${enc(sessionId)}/shares`,
|
|
1400
|
+
{ ext: opts.ext, signal: opts.signal }
|
|
1401
|
+
);
|
|
1402
|
+
return [...raw.shares ?? []];
|
|
1403
|
+
}
|
|
1404
|
+
/** `POST /v1/sessions/{id}/shares` — `token` is returned once for link shares. */
|
|
1405
|
+
create(sessionId, body, opts = {}) {
|
|
1406
|
+
return this.http.json(`/v1/sessions/${enc(sessionId)}/shares`, {
|
|
1407
|
+
body,
|
|
1408
|
+
ext: opts.ext,
|
|
1409
|
+
signal: opts.signal
|
|
1410
|
+
});
|
|
1411
|
+
}
|
|
1412
|
+
/** `DELETE /v1/sessions/{id}/shares/{shareId}` */
|
|
1413
|
+
delete(sessionId, shareId, opts = {}) {
|
|
1414
|
+
return this.http.json(
|
|
1415
|
+
`/v1/sessions/${enc(sessionId)}/shares/${enc(shareId)}`,
|
|
1416
|
+
{ method: "DELETE", ext: opts.ext, signal: opts.signal }
|
|
1417
|
+
);
|
|
1418
|
+
}
|
|
1419
|
+
/** `GET /v1/sessions/shared-with-me` */
|
|
1420
|
+
async sharedWithMe(query = {}, opts = {}) {
|
|
1421
|
+
const raw = await this.http.json(
|
|
1422
|
+
"/v1/sessions/shared-with-me",
|
|
1423
|
+
{
|
|
1424
|
+
query: { archived: query.archived ? "1" : void 0 },
|
|
1425
|
+
ext: opts.ext,
|
|
1426
|
+
signal: opts.signal
|
|
1427
|
+
}
|
|
1428
|
+
);
|
|
1429
|
+
return [...raw.sessions ?? []];
|
|
1430
|
+
}
|
|
1431
|
+
};
|
|
1432
|
+
var Sessions = class {
|
|
1433
|
+
constructor(http) {
|
|
1434
|
+
this.http = http;
|
|
1435
|
+
this.events = new SessionEvents(http);
|
|
1436
|
+
this.controls = new SessionControls(http);
|
|
1437
|
+
this.lease = new SessionLease(http);
|
|
1438
|
+
this.shares = new SessionShares(http);
|
|
1439
|
+
}
|
|
1440
|
+
http;
|
|
1441
|
+
events;
|
|
1442
|
+
controls;
|
|
1443
|
+
lease;
|
|
1444
|
+
shares;
|
|
1445
|
+
/** `POST /v1/sessions` — idempotent on `session_key` (200 existing / 201 created). */
|
|
1446
|
+
async create(body = {}, opts = {}) {
|
|
1447
|
+
const res = await this.http.request("/v1/sessions", {
|
|
1448
|
+
body,
|
|
1449
|
+
ext: opts.ext,
|
|
1450
|
+
signal: opts.signal
|
|
1451
|
+
});
|
|
1452
|
+
const session = await res.json();
|
|
1453
|
+
return { ...session, created: res.status === 201 };
|
|
1454
|
+
}
|
|
1455
|
+
/** `GET /v1/sessions?limit=&archived=` — own sessions first, then shared. */
|
|
1456
|
+
async list(query = {}, opts = {}) {
|
|
1457
|
+
const raw = await this.http.json("/v1/sessions", {
|
|
1458
|
+
query: { limit: query.limit, archived: query.archived ? "1" : void 0, v: "2" },
|
|
1459
|
+
ext: opts.ext,
|
|
1460
|
+
signal: opts.signal
|
|
1461
|
+
});
|
|
1462
|
+
return [...raw.sessions ?? []];
|
|
1463
|
+
}
|
|
1464
|
+
/** `GET /v1/sessions/{id}` — detail with members, lease and presence. */
|
|
1465
|
+
get(sessionId, query = {}, opts = {}) {
|
|
1466
|
+
return this.http.json(`/v1/sessions/${enc(sessionId)}`, {
|
|
1467
|
+
query: { share: query.share, v: "2" },
|
|
1468
|
+
ext: opts.ext,
|
|
1469
|
+
signal: opts.signal
|
|
1470
|
+
});
|
|
1471
|
+
}
|
|
1472
|
+
/** `PATCH /v1/sessions/{id}` — rename / archive (owner). */
|
|
1473
|
+
update(sessionId, body, opts = {}) {
|
|
1474
|
+
return this.http.json(`/v1/sessions/${enc(sessionId)}`, {
|
|
1475
|
+
method: "PATCH",
|
|
1476
|
+
body,
|
|
1477
|
+
ext: opts.ext,
|
|
1478
|
+
signal: opts.signal
|
|
1479
|
+
});
|
|
1480
|
+
}
|
|
1481
|
+
/** `DELETE /v1/sessions/{id}` — irreversible (owner). */
|
|
1482
|
+
delete(sessionId, opts = {}) {
|
|
1483
|
+
return this.http.json(`/v1/sessions/${enc(sessionId)}`, {
|
|
1484
|
+
method: "DELETE",
|
|
1485
|
+
ext: opts.ext,
|
|
1486
|
+
signal: opts.signal
|
|
1487
|
+
});
|
|
1488
|
+
}
|
|
1489
|
+
/** `POST /v1/sessions/{id}/dispatch` — queue a `send` for one device and wake it over FCM. */
|
|
1490
|
+
dispatch(sessionId, body, opts = {}) {
|
|
1491
|
+
return this.http.json(`/v1/sessions/${enc(sessionId)}/dispatch`, {
|
|
1492
|
+
body,
|
|
1493
|
+
ext: opts.ext,
|
|
1494
|
+
signal: opts.signal
|
|
1495
|
+
});
|
|
1496
|
+
}
|
|
1497
|
+
/**
|
|
1498
|
+
* `GET /v1/sessions/{id}/stream?after=` — live SSE of `event` / `control` /
|
|
1499
|
+
* `lease` / `presence` frames. `: ping` heartbeats are skipped. The server
|
|
1500
|
+
* closes after 30 min; reconnect with `after` = highest `seq` seen.
|
|
1501
|
+
*/
|
|
1502
|
+
async *stream(sessionId, query = {}, opts = {}) {
|
|
1503
|
+
const frames = this.http.sse(`/v1/sessions/${enc(sessionId)}/stream`, {
|
|
1504
|
+
query: { after: query.after, share: query.share },
|
|
1505
|
+
ext: opts.ext,
|
|
1506
|
+
signal: opts.signal
|
|
1507
|
+
});
|
|
1508
|
+
for await (const frame of frames) {
|
|
1509
|
+
const data = frameJson(frame);
|
|
1510
|
+
if (!data) continue;
|
|
1511
|
+
yield { event: frame.event, data };
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
};
|
|
1515
|
+
|
|
1516
|
+
// src/resources/system-one.ts
|
|
1517
|
+
var SystemOne = class {
|
|
1518
|
+
constructor(http) {
|
|
1519
|
+
this.http = http;
|
|
1520
|
+
}
|
|
1521
|
+
http;
|
|
1522
|
+
/**
|
|
1523
|
+
* Ask one or more typed questions about `state`. Throws on 503 `s1_unavailable` like any other
|
|
1524
|
+
* error — keep a local fallback and catch it: the gateway never substitutes a generative model.
|
|
1525
|
+
*/
|
|
1526
|
+
async create(body, opts = {}) {
|
|
1527
|
+
const res = await this.http.request("/v1/systemone", {
|
|
1528
|
+
body,
|
|
1529
|
+
ext: opts.ext,
|
|
1530
|
+
signal: opts.signal
|
|
1531
|
+
});
|
|
1532
|
+
return await res.json();
|
|
1533
|
+
}
|
|
1534
|
+
};
|
|
1535
|
+
|
|
1536
|
+
// src/resources/tasks.ts
|
|
1537
|
+
var Tasks = class {
|
|
1538
|
+
constructor(http) {
|
|
1539
|
+
this.http = http;
|
|
1540
|
+
}
|
|
1541
|
+
http;
|
|
1542
|
+
/** `GET /v1/tasks` — newest first; page with `cursor` = previous `next_cursor`. */
|
|
1543
|
+
list(query = {}, opts = {}) {
|
|
1544
|
+
return this.http.json("/v1/tasks", {
|
|
1545
|
+
query: { outcome: query.outcome, limit: query.limit, cursor: query.cursor },
|
|
1546
|
+
ext: opts.ext,
|
|
1547
|
+
signal: opts.signal
|
|
1548
|
+
});
|
|
1549
|
+
}
|
|
1550
|
+
/** `GET /v1/tasks/pending` — tasks awaiting your confirmation (max 100). */
|
|
1551
|
+
async pending(opts = {}) {
|
|
1552
|
+
const raw = await this.http.json("/v1/tasks/pending", {
|
|
1553
|
+
ext: opts.ext,
|
|
1554
|
+
signal: opts.signal
|
|
1555
|
+
});
|
|
1556
|
+
return [...raw.tasks ?? []];
|
|
1557
|
+
}
|
|
1558
|
+
/** `GET /v1/tasks/stats?since=` */
|
|
1559
|
+
stats(since, opts = {}) {
|
|
1560
|
+
return this.http.json("/v1/tasks/stats", {
|
|
1561
|
+
query: { since: since instanceof Date ? since.toISOString() : since },
|
|
1562
|
+
ext: opts.ext,
|
|
1563
|
+
signal: opts.signal
|
|
1564
|
+
});
|
|
1565
|
+
}
|
|
1566
|
+
/** `GET /v1/tasks/{id}` */
|
|
1567
|
+
get(id, opts = {}) {
|
|
1568
|
+
return this.http.json(`/v1/tasks/${encodeURIComponent(id)}`, {
|
|
1569
|
+
ext: opts.ext,
|
|
1570
|
+
signal: opts.signal
|
|
1571
|
+
});
|
|
1572
|
+
}
|
|
1573
|
+
/** `POST /v1/tasks/{id}/confirm` — answer "Did codai solve it?". */
|
|
1574
|
+
confirm(id, body, opts = {}) {
|
|
1575
|
+
return this.http.json(`/v1/tasks/${encodeURIComponent(id)}/confirm`, {
|
|
1576
|
+
body,
|
|
1577
|
+
ext: opts.ext,
|
|
1578
|
+
signal: opts.signal
|
|
1579
|
+
});
|
|
1580
|
+
}
|
|
1581
|
+
};
|
|
1582
|
+
|
|
1583
|
+
// src/resources/triggers.ts
|
|
1584
|
+
var Triggers = class {
|
|
1585
|
+
constructor(http) {
|
|
1586
|
+
this.http = http;
|
|
1587
|
+
}
|
|
1588
|
+
http;
|
|
1589
|
+
/** `GET /v1/triggers` — never includes the signing secret. */
|
|
1590
|
+
list(opts = {}) {
|
|
1591
|
+
return this.http.json("/v1/triggers", { ext: opts.ext, signal: opts.signal });
|
|
1592
|
+
}
|
|
1593
|
+
/** `POST /v1/triggers` — the response carries the signing `secret` ONCE. */
|
|
1594
|
+
create(body, opts = {}) {
|
|
1595
|
+
return this.http.json("/v1/triggers", { method: "POST", body, ext: opts.ext, signal: opts.signal });
|
|
1596
|
+
}
|
|
1597
|
+
/** `PATCH /v1/triggers/{id}` — name / enabled / policy. */
|
|
1598
|
+
update(id, body, opts = {}) {
|
|
1599
|
+
return this.http.json(`/v1/triggers/${encodeURIComponent(id)}`, { method: "PATCH", body, ext: opts.ext, signal: opts.signal });
|
|
1600
|
+
}
|
|
1601
|
+
/** `DELETE /v1/triggers/{id}` — its hook URL stops working immediately. */
|
|
1602
|
+
delete(id, opts = {}) {
|
|
1603
|
+
return this.http.json(`/v1/triggers/${encodeURIComponent(id)}`, { method: "DELETE", ext: opts.ext, signal: opts.signal });
|
|
1604
|
+
}
|
|
1605
|
+
/** `POST /v1/triggers/{id}/rotate-secret` — new secret, shown once. */
|
|
1606
|
+
rotateSecret(id, opts = {}) {
|
|
1607
|
+
return this.http.json(`/v1/triggers/${encodeURIComponent(id)}/rotate-secret`, { method: "POST", body: {}, ext: opts.ext, signal: opts.signal });
|
|
1608
|
+
}
|
|
1609
|
+
/** `POST /v1/triggers/{id}/test` — manual fire through the full pipeline; returns the decision + reason. */
|
|
1610
|
+
/** `PUT /v1/triggers/{id}/secret` — store a secret issued by the source (e.g. a Sentry integration's client secret). */
|
|
1611
|
+
setSecret(id, secret, opts = {}) {
|
|
1612
|
+
return this.http.json(`/v1/triggers/${encodeURIComponent(id)}/secret`, { method: "PUT", body: { secret }, ext: opts.ext, signal: opts.signal, noRetry: true });
|
|
1613
|
+
}
|
|
1614
|
+
/** `POST /v1/triggers/{id}/test` — manual fire through the full pipeline; returns the decision + reason. */
|
|
1615
|
+
test(id, body = {}, opts = {}) {
|
|
1616
|
+
return this.http.json(`/v1/triggers/${encodeURIComponent(id)}/test`, { method: "POST", body, ext: opts.ext, signal: opts.signal });
|
|
1617
|
+
}
|
|
1618
|
+
/** `GET /v1/triggers/{id}/events?limit=` — recent deliveries with decisions. */
|
|
1619
|
+
events(id, limit, opts = {}) {
|
|
1620
|
+
return this.http.json(`/v1/triggers/${encodeURIComponent(id)}/events`, { query: { limit }, ext: opts.ext, signal: opts.signal });
|
|
1621
|
+
}
|
|
1622
|
+
/**
|
|
1623
|
+
* `POST /v1/hooks/{id}` — send an event to a `generic` / `form` trigger from your own code. Signs the body
|
|
1624
|
+
* (`X-Codai-Signature: t=<unix>,v1=HMAC_SHA256(secret, "<t>.<body>")`, WebCrypto — Node 20+, browsers, Deno,
|
|
1625
|
+
* Workers). `deliveryId` makes retries idempotent. The hook needs no API key (the signature is the auth).
|
|
1626
|
+
*/
|
|
1627
|
+
async fireHook(id, secret, event, opts = {}) {
|
|
1628
|
+
const raw = JSON.stringify(event);
|
|
1629
|
+
const t = Math.floor(Date.now() / 1e3);
|
|
1630
|
+
const key = await crypto.subtle.importKey("raw", new TextEncoder().encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
|
|
1631
|
+
const mac = new Uint8Array(await crypto.subtle.sign("HMAC", key, new TextEncoder().encode(`${t}.${raw}`)));
|
|
1632
|
+
const v1 = Array.from(mac, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
1633
|
+
return this.http.json(`/v1/hooks/${encodeURIComponent(id)}`, {
|
|
1634
|
+
method: "POST",
|
|
1635
|
+
rawBody: raw,
|
|
1636
|
+
ext: {
|
|
1637
|
+
...opts.ext,
|
|
1638
|
+
headers: {
|
|
1639
|
+
...opts.ext?.headers,
|
|
1640
|
+
"Content-Type": "application/json",
|
|
1641
|
+
"x-codai-signature": `t=${t},v1=${v1}`,
|
|
1642
|
+
...opts.deliveryId ? { "x-codai-delivery": opts.deliveryId } : {}
|
|
1643
|
+
}
|
|
1644
|
+
},
|
|
1645
|
+
signal: opts.signal
|
|
1646
|
+
});
|
|
1647
|
+
}
|
|
1648
|
+
};
|
|
1649
|
+
var Notifications = class {
|
|
1650
|
+
constructor(http) {
|
|
1651
|
+
this.http = http;
|
|
1652
|
+
}
|
|
1653
|
+
http;
|
|
1654
|
+
/** `GET /v1/notifications?unread=1&limit=` — newest first. */
|
|
1655
|
+
list(query = {}, opts = {}) {
|
|
1656
|
+
return this.http.json("/v1/notifications", {
|
|
1657
|
+
query: { unread: query.unread ? "1" : void 0, limit: query.limit },
|
|
1658
|
+
ext: opts.ext,
|
|
1659
|
+
signal: opts.signal
|
|
1660
|
+
});
|
|
1661
|
+
}
|
|
1662
|
+
/** `POST /v1/notifications/read` — `ids` omitted = every unread one. */
|
|
1663
|
+
markRead(ids, opts = {}) {
|
|
1664
|
+
return this.http.json("/v1/notifications/read", { method: "POST", body: ids ? { ids } : {}, ext: opts.ext, signal: opts.signal });
|
|
1665
|
+
}
|
|
1666
|
+
};
|
|
1667
|
+
|
|
1668
|
+
// src/resources/tokens.ts
|
|
1669
|
+
var Tokens = class {
|
|
1670
|
+
constructor(http) {
|
|
1671
|
+
this.http = http;
|
|
1672
|
+
}
|
|
1673
|
+
http;
|
|
1674
|
+
/**
|
|
1675
|
+
* Mint a short-lived token (60–3600 s, default 600) bound to one surface.
|
|
1676
|
+
* Only a real `codai_` key may mint.
|
|
1677
|
+
*/
|
|
1678
|
+
async create(scope, ttlSeconds, opts = {}) {
|
|
1679
|
+
const raw = await this.http.json("/v1/tokens", {
|
|
1680
|
+
body: { scope, ...ttlSeconds !== void 0 ? { ttl_seconds: ttlSeconds } : {} },
|
|
1681
|
+
ext: opts.ext,
|
|
1682
|
+
signal: opts.signal
|
|
1683
|
+
});
|
|
1684
|
+
return { token: raw.token, expiresAt: raw.expires_at, scope: raw.scope, raw };
|
|
1685
|
+
}
|
|
1686
|
+
};
|
|
1687
|
+
|
|
1688
|
+
// src/resources/tools.ts
|
|
1689
|
+
var Tools = class {
|
|
1690
|
+
constructor(http) {
|
|
1691
|
+
this.http = http;
|
|
1692
|
+
}
|
|
1693
|
+
http;
|
|
1694
|
+
/** `POST /v1/tools/search` — verified web search (never falls back to an unverified provider). */
|
|
1695
|
+
search(body, opts = {}) {
|
|
1696
|
+
return this.http.json("/v1/tools/search", {
|
|
1697
|
+
body,
|
|
1698
|
+
ext: opts.ext,
|
|
1699
|
+
signal: opts.signal
|
|
1700
|
+
});
|
|
1701
|
+
}
|
|
1702
|
+
/** `POST /v1/tools/fetch` — fetch + extract a public page (SSRF-guarded). */
|
|
1703
|
+
fetch(body, opts = {}) {
|
|
1704
|
+
return this.http.json("/v1/tools/fetch", {
|
|
1705
|
+
body,
|
|
1706
|
+
ext: opts.ext,
|
|
1707
|
+
signal: opts.signal
|
|
1708
|
+
});
|
|
1709
|
+
}
|
|
1710
|
+
};
|
|
1711
|
+
|
|
1712
|
+
// src/operations.ts
|
|
1713
|
+
var OPERATION_METHODS = {
|
|
1714
|
+
// Chat & Completions
|
|
1715
|
+
createChatCompletion: "chat.completions.create",
|
|
1716
|
+
createResponse: "responses.create",
|
|
1717
|
+
createMessage: "messages.create",
|
|
1718
|
+
// Embeddings & Audio
|
|
1719
|
+
createEmbeddings: "embeddings.create",
|
|
1720
|
+
createSystemOne: "systemOne.create",
|
|
1721
|
+
createTranscription: "audio.transcribe",
|
|
1722
|
+
createSpeech: "audio.speech",
|
|
1723
|
+
createEphemeralToken: "tokens.create",
|
|
1724
|
+
// Models
|
|
1725
|
+
listModels: "models.list",
|
|
1726
|
+
// Health
|
|
1727
|
+
getHealth: "health.get",
|
|
1728
|
+
getHealthReady: "health.ready",
|
|
1729
|
+
getStatus: "health.status",
|
|
1730
|
+
// Agents
|
|
1731
|
+
runAgentSync: "agents.run",
|
|
1732
|
+
createAgentRun: "agents.runs.create",
|
|
1733
|
+
getAgentRunStats: "agents.runs.stats",
|
|
1734
|
+
getAgentRun: "agents.runs.get",
|
|
1735
|
+
listAgentRunSteps: "agents.runs.steps",
|
|
1736
|
+
streamAgentRun: "agents.runs.stream",
|
|
1737
|
+
cancelAgentRun: "agents.runs.cancel",
|
|
1738
|
+
// Tools
|
|
1739
|
+
toolsSearch: "tools.search",
|
|
1740
|
+
toolsFetch: "tools.fetch",
|
|
1741
|
+
// Tasks
|
|
1742
|
+
listTasks: "tasks.list",
|
|
1743
|
+
listPendingTasks: "tasks.pending",
|
|
1744
|
+
getTaskStats: "tasks.stats",
|
|
1745
|
+
getTask: "tasks.get",
|
|
1746
|
+
confirmTask: "tasks.confirm",
|
|
1747
|
+
// Event triggers + inbox
|
|
1748
|
+
listTriggers: "triggers.list",
|
|
1749
|
+
createTrigger: "triggers.create",
|
|
1750
|
+
updateTrigger: "triggers.update",
|
|
1751
|
+
deleteTrigger: "triggers.delete",
|
|
1752
|
+
rotateTriggerSecret: "triggers.rotateSecret",
|
|
1753
|
+
setTriggerSecret: "triggers.setSecret",
|
|
1754
|
+
testTrigger: "triggers.test",
|
|
1755
|
+
listTriggerEvents: "triggers.events",
|
|
1756
|
+
receiveTriggerHook: "triggers.fireHook",
|
|
1757
|
+
listNotifications: "notifications.list",
|
|
1758
|
+
markNotificationsRead: "notifications.markRead",
|
|
1759
|
+
// Sessions
|
|
1760
|
+
createSession: "sessions.create",
|
|
1761
|
+
listSessions: "sessions.list",
|
|
1762
|
+
getSession: "sessions.get",
|
|
1763
|
+
updateSession: "sessions.update",
|
|
1764
|
+
deleteSession: "sessions.delete",
|
|
1765
|
+
listSessionEvents: "sessions.events.list",
|
|
1766
|
+
appendSessionEvents: "sessions.events.append",
|
|
1767
|
+
listSessionControls: "sessions.controls.list",
|
|
1768
|
+
submitSessionControl: "sessions.controls.submit",
|
|
1769
|
+
markSessionControlApplied: "sessions.controls.markApplied",
|
|
1770
|
+
dispatchToDevice: "sessions.dispatch",
|
|
1771
|
+
claimSessionLease: "sessions.lease.acquire",
|
|
1772
|
+
heartbeatSessionLease: "sessions.lease.renew",
|
|
1773
|
+
releaseSessionLease: "sessions.lease.release",
|
|
1774
|
+
streamSession: "sessions.stream",
|
|
1775
|
+
// Devices
|
|
1776
|
+
getMyDispatchInbox: "devices.dispatchInbox",
|
|
1777
|
+
listDevices: "devices.list",
|
|
1778
|
+
updateDevice: "devices.update",
|
|
1779
|
+
deleteDevice: "devices.delete",
|
|
1780
|
+
// Hosts
|
|
1781
|
+
listHosts: "hosts.list",
|
|
1782
|
+
streamHost: "hosts.stream",
|
|
1783
|
+
execOnHost: "hosts.exec",
|
|
1784
|
+
postHostExecResult: "hosts.postResult",
|
|
1785
|
+
// Orgs & Sharing
|
|
1786
|
+
listSessionsSharedWithMe: "sessions.shares.sharedWithMe",
|
|
1787
|
+
listSessionShares: "sessions.shares.list",
|
|
1788
|
+
createSessionShare: "sessions.shares.create",
|
|
1789
|
+
deleteSessionShare: "sessions.shares.delete",
|
|
1790
|
+
createOrg: "orgs.create",
|
|
1791
|
+
listOrgs: "orgs.list",
|
|
1792
|
+
listOrgMembers: "orgs.members.list",
|
|
1793
|
+
addOrgMember: "orgs.members.add",
|
|
1794
|
+
removeOrgMember: "orgs.members.remove",
|
|
1795
|
+
// Projects
|
|
1796
|
+
listProjects: "projects.list",
|
|
1797
|
+
createProject: "projects.create",
|
|
1798
|
+
getProject: "projects.get",
|
|
1799
|
+
updateProject: "projects.update",
|
|
1800
|
+
deleteProject: "projects.delete",
|
|
1801
|
+
// Environments
|
|
1802
|
+
listEnvironments: "environments.list",
|
|
1803
|
+
createEnvironment: "environments.create",
|
|
1804
|
+
enrollEnvironment: "environments.enroll",
|
|
1805
|
+
getEnvironment: "environments.get",
|
|
1806
|
+
updateEnvironment: "environments.update",
|
|
1807
|
+
startEnvironment: "environments.start",
|
|
1808
|
+
stopEnvironment: "environments.stop",
|
|
1809
|
+
archiveEnvironment: "environments.archive",
|
|
1810
|
+
destroyEnvironment: "environments.destroy",
|
|
1811
|
+
createEnvironmentEnrollToken: "environments.enrollToken",
|
|
1812
|
+
listEnvironmentMembers: "environments.members.list",
|
|
1813
|
+
addEnvironmentMember: "environments.members.add",
|
|
1814
|
+
setMyEnvironmentSshKey: "environments.members.setSshKey",
|
|
1815
|
+
setEnvironmentMemberRole: "environments.members.setRole",
|
|
1816
|
+
removeEnvironmentMember: "environments.members.remove",
|
|
1817
|
+
issueMyEnvironmentSshCert: "environments.members.sshCert",
|
|
1818
|
+
getEnvironmentSshCa: "environments.sshCa",
|
|
1819
|
+
listEnvironmentTasks: "environments.tasks.list",
|
|
1820
|
+
createEnvironmentTask: "environments.tasks.create",
|
|
1821
|
+
getEnvironmentTask: "environments.tasks.get",
|
|
1822
|
+
listEnvironmentPorts: "environments.ports.list",
|
|
1823
|
+
setEnvironmentPort: "environments.ports.set",
|
|
1824
|
+
removeEnvironmentPort: "environments.ports.remove",
|
|
1825
|
+
listEnvironmentSecrets: "environments.secrets.list",
|
|
1826
|
+
setEnvironmentSecret: "environments.secrets.set",
|
|
1827
|
+
removeEnvironmentSecret: "environments.secrets.remove",
|
|
1828
|
+
// Account
|
|
1829
|
+
getAccount: "account.get",
|
|
1830
|
+
patchAccount: "account.update",
|
|
1831
|
+
getReceipt: "receipt.get",
|
|
1832
|
+
submitFeedback: "feedback.submit",
|
|
1833
|
+
listPhoneModels: "phoneModels.list"
|
|
1834
|
+
};
|
|
1835
|
+
var STREAMING_OPERATIONS = /* @__PURE__ */ new Set([
|
|
1836
|
+
"streamAgentRun",
|
|
1837
|
+
"streamSession",
|
|
1838
|
+
"streamHost"
|
|
1839
|
+
]);
|
|
1840
|
+
|
|
1841
|
+
// src/index.ts
|
|
1842
|
+
var Codai = class {
|
|
1843
|
+
http;
|
|
1844
|
+
/** Resource group (`chat.completions.create/stream`) that is also callable with the 0.2.x `chat({ messages })` shape. */
|
|
1845
|
+
chat;
|
|
1846
|
+
messages;
|
|
1847
|
+
responses;
|
|
1848
|
+
embeddings;
|
|
1849
|
+
audio;
|
|
1850
|
+
/** Typed decisions from codai-s1 (`systemOne.create({ state, questions })`). */
|
|
1851
|
+
systemOne;
|
|
1852
|
+
tokens;
|
|
1853
|
+
models;
|
|
1854
|
+
health;
|
|
1855
|
+
agents;
|
|
1856
|
+
tools;
|
|
1857
|
+
tasks;
|
|
1858
|
+
/** Event triggers — webhooks that notify you or start agent sessions. */
|
|
1859
|
+
triggers;
|
|
1860
|
+
/** Inbox of trigger / cloud-task notifications. */
|
|
1861
|
+
notifications;
|
|
1862
|
+
sessions;
|
|
1863
|
+
devices;
|
|
1864
|
+
hosts;
|
|
1865
|
+
orgs;
|
|
1866
|
+
projects;
|
|
1867
|
+
environments;
|
|
1868
|
+
account;
|
|
1869
|
+
receipt;
|
|
1870
|
+
feedback;
|
|
1871
|
+
phoneModels;
|
|
1872
|
+
constructor(opts) {
|
|
1873
|
+
if (!opts.apiKey) throw new Error("Codai: apiKey is required");
|
|
1874
|
+
this.http = new HttpClient({
|
|
1875
|
+
apiKey: opts.apiKey,
|
|
1876
|
+
baseUrl: opts.baseUrl ?? "https://ai.codai.ro",
|
|
1877
|
+
timeoutMs: opts.timeoutMs ?? 12e4,
|
|
1878
|
+
maxRetries: opts.maxRetries ?? 2,
|
|
1879
|
+
fetch: opts.fetch,
|
|
1880
|
+
defaults: {
|
|
1881
|
+
...opts.defaults,
|
|
1882
|
+
...opts.sessionId ? { sessionId: opts.sessionId } : {},
|
|
1883
|
+
...opts.device ? { device: opts.device } : {},
|
|
1884
|
+
...opts.deviceName ? { deviceName: opts.deviceName } : {},
|
|
1885
|
+
...opts.devicePlatform ? { devicePlatform: opts.devicePlatform } : {},
|
|
1886
|
+
...opts.client ? { client: opts.client } : {}
|
|
1887
|
+
}
|
|
1888
|
+
});
|
|
1889
|
+
const http = this.http;
|
|
1890
|
+
this.messages = new Messages(http);
|
|
1891
|
+
this.responses = new Responses(http);
|
|
1892
|
+
this.audio = new Audio(http);
|
|
1893
|
+
this.systemOne = new SystemOne(http);
|
|
1894
|
+
this.tokens = new Tokens(http);
|
|
1895
|
+
this.health = new Health(http);
|
|
1896
|
+
this.agents = new Agents(http);
|
|
1897
|
+
this.tools = new Tools(http);
|
|
1898
|
+
this.tasks = new Tasks(http);
|
|
1899
|
+
this.triggers = new Triggers(http);
|
|
1900
|
+
this.notifications = new Notifications(http);
|
|
1901
|
+
this.sessions = new Sessions(http);
|
|
1902
|
+
this.devices = new Devices(http);
|
|
1903
|
+
this.hosts = new Hosts(http);
|
|
1904
|
+
this.orgs = new Orgs(http);
|
|
1905
|
+
this.projects = new Projects(http);
|
|
1906
|
+
this.environments = new Environments(http);
|
|
1907
|
+
this.account = new Account(http);
|
|
1908
|
+
this.receipt = new Receipts(http);
|
|
1909
|
+
this.phoneModels = new PhoneModels(http);
|
|
1910
|
+
this.chat = callable(
|
|
1911
|
+
new Chat(http),
|
|
1912
|
+
(o) => this.chat.completions.create(this.legacyBody(o), {
|
|
1913
|
+
ext: this.legacyExt(o),
|
|
1914
|
+
signal: o.signal
|
|
1915
|
+
})
|
|
1916
|
+
);
|
|
1917
|
+
this.embeddings = callable(new Embeddings(http), async (o) => {
|
|
1918
|
+
const r = await this.embeddings.create(
|
|
1919
|
+
{ input: o.input, model: o.model, dimensions: o.dimensions },
|
|
1920
|
+
{ signal: o.signal }
|
|
1921
|
+
);
|
|
1922
|
+
return { embeddings: r.embeddings, raw: r.raw };
|
|
1923
|
+
});
|
|
1924
|
+
this.models = callable(
|
|
1925
|
+
new Models(http),
|
|
1926
|
+
async () => (await this.models.list()).map((m) => ({ id: m.id }))
|
|
1927
|
+
);
|
|
1928
|
+
this.feedback = callable(
|
|
1929
|
+
new Feedback(http),
|
|
1930
|
+
async (requestId, rating, comment) => {
|
|
1931
|
+
await this.feedback.submit({
|
|
1932
|
+
event_id: requestId,
|
|
1933
|
+
rating,
|
|
1934
|
+
...comment ? { comment } : {}
|
|
1935
|
+
});
|
|
1936
|
+
}
|
|
1937
|
+
);
|
|
1938
|
+
}
|
|
1939
|
+
// ------------------------------------------------- 0.2.x compatibility aliases
|
|
1940
|
+
/** Alias of `chat.completions.stream()` with the 0.2.x option names. */
|
|
1941
|
+
chatStream(opts) {
|
|
1942
|
+
return this.chat.completions.stream(this.legacyBody(opts), {
|
|
1943
|
+
ext: this.legacyExt(opts),
|
|
1944
|
+
signal: opts.signal
|
|
1945
|
+
});
|
|
1946
|
+
}
|
|
1947
|
+
/** Alias of `tokens.create()`. */
|
|
1948
|
+
mintToken(scope, ttlSeconds) {
|
|
1949
|
+
return this.tokens.create(scope, ttlSeconds);
|
|
1950
|
+
}
|
|
1951
|
+
legacyBody(opts) {
|
|
1952
|
+
return {
|
|
1953
|
+
model: opts.model ?? "codai",
|
|
1954
|
+
messages: opts.messages,
|
|
1955
|
+
...opts.temperature !== void 0 ? { temperature: opts.temperature } : {},
|
|
1956
|
+
...opts.maxTokens !== void 0 ? { max_tokens: opts.maxTokens } : {},
|
|
1957
|
+
...opts.tools ? { tools: opts.tools } : {}
|
|
1958
|
+
};
|
|
1959
|
+
}
|
|
1960
|
+
legacyExt(opts) {
|
|
1961
|
+
return {
|
|
1962
|
+
...opts.ext,
|
|
1963
|
+
...opts.sessionId ? { sessionId: opts.sessionId } : {},
|
|
1964
|
+
...opts.agentMode ? { mode: "agent" } : {},
|
|
1965
|
+
...opts.compact ? { compact: opts.compact } : {},
|
|
1966
|
+
...opts.bestOf !== void 0 ? { bestOf: opts.bestOf } : {},
|
|
1967
|
+
...opts.agentId ? { agentId: opts.agentId } : {}
|
|
1968
|
+
};
|
|
1969
|
+
}
|
|
1970
|
+
};
|
|
1971
|
+
function callable(target, fn) {
|
|
1972
|
+
const f = ((...args) => fn(...args));
|
|
1973
|
+
for (const key of allKeys(target)) {
|
|
1974
|
+
const value = target[key];
|
|
1975
|
+
Object.defineProperty(f, key, {
|
|
1976
|
+
value: typeof value === "function" ? value.bind(target) : value,
|
|
1977
|
+
enumerable: true,
|
|
1978
|
+
writable: false
|
|
1979
|
+
});
|
|
1980
|
+
}
|
|
1981
|
+
return f;
|
|
1982
|
+
}
|
|
1983
|
+
function allKeys(obj) {
|
|
1984
|
+
const keys = /* @__PURE__ */ new Set();
|
|
1985
|
+
let cur = obj;
|
|
1986
|
+
while (cur && cur !== Object.prototype) {
|
|
1987
|
+
for (const k of Object.getOwnPropertyNames(cur)) if (k !== "constructor") keys.add(k);
|
|
1988
|
+
cur = Object.getPrototypeOf(cur);
|
|
1989
|
+
}
|
|
1990
|
+
return [...keys];
|
|
334
1991
|
}
|
|
335
1992
|
var index_default = Codai;
|
|
336
1993
|
|
|
337
|
-
export { Codai, CodaiError, index_default as default };
|
|
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 };
|
|
338
1995
|
//# sourceMappingURL=index.js.map
|
|
339
1996
|
//# sourceMappingURL=index.js.map
|