@jacobbd/relay-ai 0.4.0 → 0.4.3
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 +15 -9
- package/dist/{chunk-RTZ3X3KZ.js → chunk-VSXPAZX4.js} +243 -28
- package/dist/chunk-VSXPAZX4.js.map +1 -0
- package/dist/cli.js +8 -4
- package/dist/cli.js.map +1 -1
- package/dist/ui/public/index.html +1 -1
- package/dist/{ui-command-PJG4Y2QY.js → ui-command-GEO7ACPW.js} +7 -3
- package/dist/ui-command-GEO7ACPW.js.map +1 -0
- package/package.json +3 -1
- package/dist/chunk-RTZ3X3KZ.js.map +0 -1
- package/dist/ui-command-PJG4Y2QY.js.map +0 -1
package/README.md
CHANGED
|
@@ -9,17 +9,17 @@
|
|
|
9
9
|
|
|
10
10
|
[](https://www.npmjs.com/package/@jacobbd/relay-ai)
|
|
11
11
|
[](LICENSE)
|
|
12
|
-
[](https://buymeacoffee.com/jacobbd)
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
> ☕ **If you find relay-ai useful, consider [buying me a coffee](https://buymeacoffee.com/jacobbd).**
|
|
14
|
+
> It's free and built in my spare time — but testing every provider runs up a real AI bill. A coffee helps me cover it and keep shipping. Thank you! 🙏
|
|
15
|
+
>
|
|
16
|
+
> <a href="https://buymeacoffee.com/jacobbd"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" height="42"></a>
|
|
17
|
+
|
|
18
|
+
📺 **Watch the Demos**
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
| **Claude Code / Cowork / Desktop** | **Codex CLI & Desktop App** | **Gemini CLI** |
|
|
21
|
+
|:---:|:---:|:---:|
|
|
22
|
+
| [](https://youtu.be/IvsUPHLhX0o) | [](https://youtu.be/42oiOB8IAu4) | [](https://www.youtube.com/watch?v=g7JKvqOHJl4) |
|
|
23
23
|
|
|
24
24
|
**relay-ai** is an interactive CLI — and now a **visual launcher** — that connects AI coding tools to any provider and runs local API gateways on your machine. It supports **Claude Code**, **Claude Desktop (Cowork + Code)**, the **OpenAI Codex CLI**, the **ChatGPT desktop app in Codex mode (macOS + Windows)**, **Google Gemini CLI**, and experimental **Antigravity CLI / IDE** support.
|
|
25
25
|
|
|
@@ -570,6 +570,12 @@ The deprecated `OPENCODE_STARTER_HOME` env var still works as a fallback for `RE
|
|
|
570
570
|
|
|
571
571
|
Private beta right now. Issues and PRs welcome on GitHub.
|
|
572
572
|
|
|
573
|
+
## Support
|
|
574
|
+
|
|
575
|
+
If relay-ai saves you time or money, you can help cover the AI bills that go into building and testing it against every provider. Any support is hugely appreciated. 🙏
|
|
576
|
+
|
|
577
|
+
<a href="https://buymeacoffee.com/jacobbd"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" height="42"></a>
|
|
578
|
+
|
|
573
579
|
## Disclaimer
|
|
574
580
|
|
|
575
581
|
This project and its creator have **no affiliation** with OpenCode, Anthropic, Claude, Google, GitHub, OpenAI, xAI, or any other vendor named or integrated here. Trademarks belong to their respective owners.
|
|
@@ -11,7 +11,7 @@ import { join } from "path";
|
|
|
11
11
|
// package.json
|
|
12
12
|
var package_default = {
|
|
13
13
|
name: "@jacobbd/relay-ai",
|
|
14
|
-
version: "0.4.
|
|
14
|
+
version: "0.4.3",
|
|
15
15
|
publishConfig: {
|
|
16
16
|
access: "public"
|
|
17
17
|
},
|
|
@@ -81,10 +81,12 @@ var package_default = {
|
|
|
81
81
|
picocolors: "^1.1.1",
|
|
82
82
|
"smol-toml": "^1.6.1",
|
|
83
83
|
"venice-ai-sdk-provider": "^2.0.2",
|
|
84
|
+
ws: "^8.21.0",
|
|
84
85
|
zod: "^3.25.76"
|
|
85
86
|
},
|
|
86
87
|
devDependencies: {
|
|
87
88
|
"@types/node": "^22.0.0",
|
|
89
|
+
"@types/ws": "^8.18.1",
|
|
88
90
|
"@vitest/coverage-v8": "^2.1.9",
|
|
89
91
|
tsup: "^8.0.0",
|
|
90
92
|
typescript: "^5.5.0",
|
|
@@ -112,6 +114,9 @@ var BACKENDS = {
|
|
|
112
114
|
baseUrl: "https://opencode.ai/zen/go"
|
|
113
115
|
}
|
|
114
116
|
};
|
|
117
|
+
var CODEX_RESPONSES_LITE_WS_URL = "wss://chatgpt.com/backend-api/codex/responses";
|
|
118
|
+
var CODEX_RESPONSES_LITE_VERSION = "0.144.1";
|
|
119
|
+
var CODEX_RESPONSES_WEBSOCKETS_BETA = "responses_websockets=2026-02-06";
|
|
115
120
|
var CONFLICTING_ENV_VARS = [
|
|
116
121
|
"CLAUDE_CODE_USE_VERTEX",
|
|
117
122
|
"ANTHROPIC_VERTEX_PROJECT_ID",
|
|
@@ -312,6 +317,161 @@ async function runOpenAiDeviceCodeFlow(onDeviceCode, opts) {
|
|
|
312
317
|
return pollOpenAiDeviceCodeToken(deviceData, opts);
|
|
313
318
|
}
|
|
314
319
|
|
|
320
|
+
// src/oauth/responses-websocket.ts
|
|
321
|
+
var RESPONSES_LITE_HEADER = "x-openai-internal-codex-responses-lite";
|
|
322
|
+
var TERMINAL_EVENT_TYPES = /* @__PURE__ */ new Set(["response.completed", "response.failed", "response.incomplete"]);
|
|
323
|
+
function toHeaderRecord(headers) {
|
|
324
|
+
const out = {};
|
|
325
|
+
if (!headers) return out;
|
|
326
|
+
if (headers instanceof Headers) {
|
|
327
|
+
headers.forEach((value, key) => {
|
|
328
|
+
out[key] = value;
|
|
329
|
+
});
|
|
330
|
+
} else if (Array.isArray(headers)) {
|
|
331
|
+
for (const [key, value] of headers) out[key] = value;
|
|
332
|
+
} else {
|
|
333
|
+
for (const [key, value] of Object.entries(headers)) out[key] = String(value);
|
|
334
|
+
}
|
|
335
|
+
return out;
|
|
336
|
+
}
|
|
337
|
+
function hasResponsesLiteHeader(headers) {
|
|
338
|
+
return Object.entries(headers).some(
|
|
339
|
+
([k, v]) => k.toLowerCase() === RESPONSES_LITE_HEADER && v.toLowerCase() === "true"
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
function bodyToString(body) {
|
|
343
|
+
if (body == null) return "";
|
|
344
|
+
if (typeof body === "string") return body;
|
|
345
|
+
if (body instanceof Uint8Array) return Buffer.from(body).toString("utf8");
|
|
346
|
+
if (body instanceof ArrayBuffer) return Buffer.from(new Uint8Array(body)).toString("utf8");
|
|
347
|
+
return String(body);
|
|
348
|
+
}
|
|
349
|
+
function applyResponsesLiteShape(payload) {
|
|
350
|
+
const reasoning = payload.reasoning && typeof payload.reasoning === "object" ? { ...payload.reasoning } : {};
|
|
351
|
+
reasoning.context = "all_turns";
|
|
352
|
+
return {
|
|
353
|
+
...payload,
|
|
354
|
+
reasoning,
|
|
355
|
+
parallel_tool_calls: false,
|
|
356
|
+
store: false
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
function createResponsesWebSocketFetch(wsUrl, log7) {
|
|
360
|
+
const debug = (msg) => {
|
|
361
|
+
try {
|
|
362
|
+
log7?.(`ws: ${msg}`);
|
|
363
|
+
} catch {
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
return async (_input, init) => {
|
|
367
|
+
const { WebSocket } = await import("ws");
|
|
368
|
+
const headers = toHeaderRecord(init?.headers);
|
|
369
|
+
headers["OpenAI-Beta"] = CODEX_RESPONSES_WEBSOCKETS_BETA;
|
|
370
|
+
debug(`connecting ${wsUrl} headers=[${Object.keys(headers).join(", ")}]`);
|
|
371
|
+
let payload = {};
|
|
372
|
+
try {
|
|
373
|
+
payload = JSON.parse(bodyToString(init?.body));
|
|
374
|
+
} catch {
|
|
375
|
+
payload = {};
|
|
376
|
+
}
|
|
377
|
+
if (hasResponsesLiteHeader(headers)) {
|
|
378
|
+
payload = applyResponsesLiteShape(payload);
|
|
379
|
+
}
|
|
380
|
+
const outgoing = JSON.stringify({ type: "response.create", ...payload });
|
|
381
|
+
const encoder = new TextEncoder();
|
|
382
|
+
let socket;
|
|
383
|
+
let frameCount = 0;
|
|
384
|
+
const stream = new ReadableStream({
|
|
385
|
+
start(controller) {
|
|
386
|
+
let closed = false;
|
|
387
|
+
const close = () => {
|
|
388
|
+
if (closed) return;
|
|
389
|
+
closed = true;
|
|
390
|
+
try {
|
|
391
|
+
controller.close();
|
|
392
|
+
} catch {
|
|
393
|
+
}
|
|
394
|
+
try {
|
|
395
|
+
socket.close();
|
|
396
|
+
} catch {
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
const fail = (message) => {
|
|
400
|
+
if (closed) return;
|
|
401
|
+
debug(`fail: ${message}`);
|
|
402
|
+
try {
|
|
403
|
+
controller.enqueue(encoder.encode(
|
|
404
|
+
`data: ${JSON.stringify({ type: "error", error: { message } })}
|
|
405
|
+
|
|
406
|
+
`
|
|
407
|
+
));
|
|
408
|
+
} catch {
|
|
409
|
+
}
|
|
410
|
+
close();
|
|
411
|
+
};
|
|
412
|
+
socket = new WebSocket(wsUrl, { headers });
|
|
413
|
+
socket.on("open", () => {
|
|
414
|
+
debug(`open \u2014 sending ${outgoing.length}B payload`);
|
|
415
|
+
socket.send(outgoing);
|
|
416
|
+
});
|
|
417
|
+
socket.on("unexpected-response", (_req, res) => {
|
|
418
|
+
debug(`unexpected-response status=${res.statusCode}`);
|
|
419
|
+
});
|
|
420
|
+
socket.on("message", (data) => {
|
|
421
|
+
const text4 = Array.isArray(data) ? Buffer.concat(data).toString("utf8") : data.toString("utf8");
|
|
422
|
+
frameCount += 1;
|
|
423
|
+
if (frameCount <= 3) debug(`frame#${frameCount}: ${text4.slice(0, 200)}`);
|
|
424
|
+
let event;
|
|
425
|
+
try {
|
|
426
|
+
event = JSON.parse(text4);
|
|
427
|
+
} catch {
|
|
428
|
+
controller.enqueue(encoder.encode(`data: ${text4.replace(/\r?\n/g, " ")}
|
|
429
|
+
|
|
430
|
+
`));
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify(event)}
|
|
434
|
+
|
|
435
|
+
`));
|
|
436
|
+
const type = event.type;
|
|
437
|
+
if (typeof type === "string" && TERMINAL_EVENT_TYPES.has(type)) {
|
|
438
|
+
debug(`terminal event: ${type} (after ${frameCount} frames)`);
|
|
439
|
+
close();
|
|
440
|
+
}
|
|
441
|
+
});
|
|
442
|
+
socket.on("error", (err) => fail(err.message));
|
|
443
|
+
socket.on("close", (code, reason) => {
|
|
444
|
+
debug(`close code=${code} frames=${frameCount}${reason?.length ? ` reason=${reason.toString("utf8").slice(0, 200)}` : ""}`);
|
|
445
|
+
if (closed) return;
|
|
446
|
+
if (code === 1e3 || code === 1005) {
|
|
447
|
+
close();
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
fail(`WebSocket closed (${code})${reason?.length ? `: ${reason.toString("utf8")}` : ""}`);
|
|
451
|
+
});
|
|
452
|
+
const signal = init?.signal;
|
|
453
|
+
if (signal) {
|
|
454
|
+
if (signal.aborted) {
|
|
455
|
+
close();
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
signal.addEventListener("abort", close, { once: true });
|
|
459
|
+
}
|
|
460
|
+
},
|
|
461
|
+
cancel() {
|
|
462
|
+
try {
|
|
463
|
+
socket?.close();
|
|
464
|
+
} catch {
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
});
|
|
468
|
+
return new Response(stream, {
|
|
469
|
+
status: 200,
|
|
470
|
+
headers: { "content-type": "text/event-stream; charset=utf-8" }
|
|
471
|
+
});
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
|
|
315
475
|
// src/oauth/claude-identity.ts
|
|
316
476
|
import { createHash, randomUUID } from "crypto";
|
|
317
477
|
var CLAUDE_CODE_CLI_VERSION = "2.1.195";
|
|
@@ -504,14 +664,21 @@ async function createLanguageModel(spec) {
|
|
|
504
664
|
if (npm === "@ai-sdk/openai") {
|
|
505
665
|
const { createOpenAI } = await import("@ai-sdk/openai");
|
|
506
666
|
const accountId = spec.authType === "oauth" ? spec.oauthAccountId ?? extractOpenAiAccountId({ access_token: apiKey }) : void 0;
|
|
507
|
-
const
|
|
667
|
+
const oauthOptions = spec.authType === "oauth" ? {
|
|
508
668
|
apiKey,
|
|
509
669
|
baseURL: "https://chatgpt.com/backend-api/codex",
|
|
510
670
|
headers: {
|
|
511
671
|
...accountId ? { "ChatGPT-Account-Id": accountId } : {},
|
|
512
|
-
originator: "relay-ai"
|
|
513
|
-
|
|
514
|
-
|
|
672
|
+
originator: "relay-ai",
|
|
673
|
+
// Responses-Lite models (backend prefer_websockets/use_responses_lite,
|
|
674
|
+
// e.g. gpt-5.6-luna) require these on the request.
|
|
675
|
+
...spec.useResponsesLite ? { version: CODEX_RESPONSES_LITE_VERSION, "x-openai-internal-codex-responses-lite": "true" } : {}
|
|
676
|
+
},
|
|
677
|
+
// Models the backend flags with prefer_websockets are only served over
|
|
678
|
+
// the WebSocket Responses transport, not HTTP.
|
|
679
|
+
...spec.preferWebSockets ? { fetch: createResponsesWebSocketFetch(CODEX_RESPONSES_LITE_WS_URL, spec.onDebug) } : {}
|
|
680
|
+
} : { apiKey };
|
|
681
|
+
const openai = createOpenAI(oauthOptions);
|
|
515
682
|
return shouldUseOpenAiResponsesEndpoint(modelId) ? openai.responses(modelId) : openai.chat(modelId);
|
|
516
683
|
}
|
|
517
684
|
if (npm === "@ai-sdk/xai") {
|
|
@@ -5490,7 +5657,10 @@ function startProxyCatalog(routes, defaultAliasId, debug = false) {
|
|
|
5490
5657
|
authType: route.authType,
|
|
5491
5658
|
oauthAccountId: route.oauthAccountId,
|
|
5492
5659
|
providerData: route.providerData,
|
|
5493
|
-
headers: route.headers
|
|
5660
|
+
headers: route.headers,
|
|
5661
|
+
useResponsesLite: route.useResponsesLite,
|
|
5662
|
+
preferWebSockets: route.preferWebSockets,
|
|
5663
|
+
onDebug: (msg) => plog(() => msg)
|
|
5494
5664
|
});
|
|
5495
5665
|
if (clientWantsStream) {
|
|
5496
5666
|
res.writeHead(200, {
|
|
@@ -5620,7 +5790,9 @@ function startProxy(completionsUrl, modelId, debug = false, contextWindow, sdk,
|
|
|
5620
5790
|
providerData: sdk?.providerData,
|
|
5621
5791
|
supportedParameters: sdk?.supportedParameters,
|
|
5622
5792
|
reasoning: sdk?.reasoning,
|
|
5623
|
-
interleavedReasoningField: sdk?.interleavedReasoningField
|
|
5793
|
+
interleavedReasoningField: sdk?.interleavedReasoningField,
|
|
5794
|
+
useResponsesLite: sdk?.useResponsesLite,
|
|
5795
|
+
preferWebSockets: sdk?.preferWebSockets
|
|
5624
5796
|
}], clientModelId, debug);
|
|
5625
5797
|
}
|
|
5626
5798
|
|
|
@@ -6480,7 +6652,9 @@ function cachedModelToLocal(cached, provider) {
|
|
|
6480
6652
|
contextWindow: cached.contextWindow ?? resolveContextWindow(id),
|
|
6481
6653
|
supportedParameters: cached.supportedParameters,
|
|
6482
6654
|
reasoning: cached.reasoning ?? modelsDev?.reasoning,
|
|
6483
|
-
interleavedReasoningField: cached.interleavedReasoningField ?? modelsDev?.interleaved?.field
|
|
6655
|
+
interleavedReasoningField: cached.interleavedReasoningField ?? modelsDev?.interleaved?.field,
|
|
6656
|
+
useResponsesLite: cached.useResponsesLite,
|
|
6657
|
+
preferWebSockets: cached.preferWebSockets
|
|
6484
6658
|
};
|
|
6485
6659
|
}
|
|
6486
6660
|
function providerAllowsAnonymousFreeModels(provider) {
|
|
@@ -6642,6 +6816,8 @@ function localProvidersToServerModels(localProviders) {
|
|
|
6642
6816
|
supportedParameters: model.supportedParameters,
|
|
6643
6817
|
reasoning: model.reasoning,
|
|
6644
6818
|
interleavedReasoningField: model.interleavedReasoningField,
|
|
6819
|
+
useResponsesLite: model.useResponsesLite,
|
|
6820
|
+
preferWebSockets: model.preferWebSockets,
|
|
6645
6821
|
headers: provider.headers,
|
|
6646
6822
|
providerData: provider.providerData
|
|
6647
6823
|
}))
|
|
@@ -7111,10 +7287,12 @@ async function handleAnthropicMessages(req, res, options, modelCache, plog) {
|
|
|
7111
7287
|
sendJson(res, 200, anthropicResponse);
|
|
7112
7288
|
}
|
|
7113
7289
|
} catch (err) {
|
|
7114
|
-
const message =
|
|
7290
|
+
const message = formatUpstreamError(err);
|
|
7115
7291
|
plog(`sdk error npm=${model.npm} upstream=${upstreamModelId(model)}: ${message}`);
|
|
7116
|
-
if (!res.headersSent)
|
|
7117
|
-
|
|
7292
|
+
if (!res.headersSent) {
|
|
7293
|
+
const status = upstreamHttpStatus(err, message);
|
|
7294
|
+
sendJson(res, status === 500 ? 502 : status, { error: { message } });
|
|
7295
|
+
} else res.end();
|
|
7118
7296
|
}
|
|
7119
7297
|
return;
|
|
7120
7298
|
}
|
|
@@ -7164,10 +7342,12 @@ async function handleOpenAIChatCompletions(req, res, options, modelCache, plog)
|
|
|
7164
7342
|
sendJson(res, 200, response);
|
|
7165
7343
|
}
|
|
7166
7344
|
} catch (err) {
|
|
7167
|
-
const message =
|
|
7345
|
+
const message = formatUpstreamError(err);
|
|
7168
7346
|
plog(`sdk error npm=${model.npm} upstream=${upstreamModelId(model)}: ${message}`);
|
|
7169
|
-
if (!res.headersSent)
|
|
7170
|
-
|
|
7347
|
+
if (!res.headersSent) {
|
|
7348
|
+
const status = upstreamHttpStatus(err, message);
|
|
7349
|
+
sendJson(res, status === 500 ? 502 : status, { error: { message } });
|
|
7350
|
+
} else res.end();
|
|
7171
7351
|
}
|
|
7172
7352
|
}
|
|
7173
7353
|
function lookupModel(res, catalog, modelId) {
|
|
@@ -7209,7 +7389,9 @@ async function getOrInitLanguageModel(modelCache, model, npm, baseURL, apiKey, v
|
|
|
7209
7389
|
authType: model.authType,
|
|
7210
7390
|
oauthAccountId: model.oauthAccountId,
|
|
7211
7391
|
vertex,
|
|
7212
|
-
headers: model.headers
|
|
7392
|
+
headers: model.headers,
|
|
7393
|
+
useResponsesLite: model.useResponsesLite,
|
|
7394
|
+
preferWebSockets: model.preferWebSockets
|
|
7213
7395
|
});
|
|
7214
7396
|
modelCache.set(cacheKey, languageModel);
|
|
7215
7397
|
}
|
|
@@ -8211,7 +8393,9 @@ function parseModelList(body, npm) {
|
|
|
8211
8393
|
freeStatus,
|
|
8212
8394
|
modelFormat: format,
|
|
8213
8395
|
npm,
|
|
8214
|
-
supportedParameters: Array.isArray(row.supported_parameters) ? row.supported_parameters : void 0
|
|
8396
|
+
supportedParameters: Array.isArray(row.supported_parameters) ? row.supported_parameters : void 0,
|
|
8397
|
+
useResponsesLite: typeof row.use_responses_lite === "boolean" ? row.use_responses_lite : void 0,
|
|
8398
|
+
preferWebSockets: typeof row.prefer_websockets === "boolean" ? row.prefer_websockets : void 0
|
|
8215
8399
|
});
|
|
8216
8400
|
}
|
|
8217
8401
|
return models;
|
|
@@ -8727,7 +8911,7 @@ var OPENAI_OAUTH_MODEL_SEEDS = [
|
|
|
8727
8911
|
// GPT-5.6 family (Sol / Terra / Luna)
|
|
8728
8912
|
{ id: "gpt-5.6-sol", name: "GPT-5.6 Sol", reasoning: true },
|
|
8729
8913
|
{ id: "gpt-5.6-terra", name: "GPT-5.6 Terra", reasoning: true },
|
|
8730
|
-
{ id: "gpt-5.6-luna", name: "GPT-5.6 Luna", reasoning: true },
|
|
8914
|
+
{ id: "gpt-5.6-luna", name: "GPT-5.6 Luna", reasoning: true, useResponsesLite: true, preferWebSockets: true },
|
|
8731
8915
|
// GPT-5.5 family (Pro)
|
|
8732
8916
|
{ id: "gpt-5.5", name: "GPT-5.5", reasoning: true },
|
|
8733
8917
|
// GPT-5.4 family
|
|
@@ -8754,7 +8938,9 @@ function buildOpenAiOAuthModels() {
|
|
|
8754
8938
|
contextWindow: resolveContextWindow(seed.id),
|
|
8755
8939
|
modelFormat: "openai",
|
|
8756
8940
|
npm: "@ai-sdk/openai",
|
|
8757
|
-
reasoning: seed.reasoning
|
|
8941
|
+
reasoning: seed.reasoning,
|
|
8942
|
+
useResponsesLite: seed.useResponsesLite,
|
|
8943
|
+
preferWebSockets: seed.preferWebSockets
|
|
8758
8944
|
};
|
|
8759
8945
|
});
|
|
8760
8946
|
}
|
|
@@ -8882,30 +9068,57 @@ async function refreshOAuthProvider(provider, accessToken) {
|
|
|
8882
9068
|
if (tpl === "antigravity") return refreshAntigravityOAuthModels(accessToken);
|
|
8883
9069
|
throw new Error(`refreshOAuthProvider: unsupported template "${tpl}"`);
|
|
8884
9070
|
}
|
|
9071
|
+
function readCapabilityFlags(m) {
|
|
9072
|
+
const bool = (v) => typeof v === "boolean" ? v : void 0;
|
|
9073
|
+
return {
|
|
9074
|
+
useResponsesLite: bool(m["use_responses_lite"]),
|
|
9075
|
+
preferWebSockets: bool(m["prefer_websockets"])
|
|
9076
|
+
};
|
|
9077
|
+
}
|
|
8885
9078
|
function parseOpenAiModelEntries(body) {
|
|
8886
9079
|
if (!body || typeof body !== "object") return [];
|
|
8887
9080
|
const b = body;
|
|
8888
9081
|
if (Array.isArray(b.models)) {
|
|
8889
|
-
return b.models.map((m) => ({
|
|
9082
|
+
return b.models.map((m) => ({
|
|
9083
|
+
id: m.slug ?? "",
|
|
9084
|
+
name: m.title ?? m.name ?? m.slug ?? "",
|
|
9085
|
+
context_window: m.context_window,
|
|
9086
|
+
...readCapabilityFlags(m)
|
|
9087
|
+
})).filter((m) => m.id.length > 0);
|
|
8890
9088
|
}
|
|
8891
9089
|
if (Array.isArray(b.data)) {
|
|
8892
|
-
return b.data.map((m) => ({
|
|
9090
|
+
return b.data.map((m) => ({
|
|
9091
|
+
id: m.id ?? "",
|
|
9092
|
+
name: m.name ?? m.id ?? "",
|
|
9093
|
+
context_window: m.context_window,
|
|
9094
|
+
...readCapabilityFlags(m)
|
|
9095
|
+
})).filter((m) => m.id.length > 0);
|
|
8893
9096
|
}
|
|
8894
9097
|
return [];
|
|
8895
9098
|
}
|
|
8896
|
-
function buildDynamicOAuthModel(
|
|
8897
|
-
|
|
9099
|
+
function buildDynamicOAuthModel(entry, seedById) {
|
|
9100
|
+
const seed = seedById.get(entry.id);
|
|
9101
|
+
if (seed) {
|
|
9102
|
+
return {
|
|
9103
|
+
...seed,
|
|
9104
|
+
useResponsesLite: entry.useResponsesLite ?? seed.useResponsesLite,
|
|
9105
|
+
preferWebSockets: entry.preferWebSockets ?? seed.preferWebSockets
|
|
9106
|
+
};
|
|
9107
|
+
}
|
|
9108
|
+
const { id } = entry;
|
|
8898
9109
|
const prefix = id.split("-")[0] ?? id;
|
|
8899
9110
|
return {
|
|
8900
9111
|
id,
|
|
8901
|
-
name,
|
|
9112
|
+
name: entry.name,
|
|
8902
9113
|
upstreamModelId: id,
|
|
8903
9114
|
family: prefix,
|
|
8904
9115
|
brand: deriveBrand(prefix),
|
|
8905
|
-
contextWindow:
|
|
9116
|
+
contextWindow: entry.context_window ?? resolveContextWindow(id),
|
|
8906
9117
|
modelFormat: "openai",
|
|
8907
9118
|
npm: "@ai-sdk/openai",
|
|
8908
|
-
reasoning: modelPrefersResponsesApi(id)
|
|
9119
|
+
reasoning: modelPrefersResponsesApi(id),
|
|
9120
|
+
useResponsesLite: entry.useResponsesLite,
|
|
9121
|
+
preferWebSockets: entry.preferWebSockets
|
|
8909
9122
|
};
|
|
8910
9123
|
}
|
|
8911
9124
|
async function fetchJsonWithAuth(url, accessToken, timeoutMs) {
|
|
@@ -8932,7 +9145,7 @@ async function fetchJsonWithAuth(url, accessToken, timeoutMs) {
|
|
|
8932
9145
|
async function refreshOpenAiOAuthModels(accessToken) {
|
|
8933
9146
|
const TIMEOUT_MS = 1e4;
|
|
8934
9147
|
const seedById = new Map(buildOpenAiOAuthModels().map((m) => [m.id, m]));
|
|
8935
|
-
const toModels = (entries) => entries.map((
|
|
9148
|
+
const toModels = (entries) => entries.map((entry) => buildDynamicOAuthModel(entry, seedById));
|
|
8936
9149
|
const claudeVersion = getInstalledClaudeVersion();
|
|
8937
9150
|
const codexResult = await fetchJsonWithAuth(
|
|
8938
9151
|
`https://chatgpt.com/backend-api/codex/models?client_version=${claudeVersion}`,
|
|
@@ -9268,7 +9481,9 @@ function modelToCached(model) {
|
|
|
9268
9481
|
apiUrl: model.apiBaseUrl,
|
|
9269
9482
|
supportedParameters: model.supportedParameters,
|
|
9270
9483
|
reasoning: model.reasoning,
|
|
9271
|
-
interleavedReasoningField: model.interleavedReasoningField
|
|
9484
|
+
interleavedReasoningField: model.interleavedReasoningField,
|
|
9485
|
+
useResponsesLite: model.useResponsesLite,
|
|
9486
|
+
preferWebSockets: model.preferWebSockets
|
|
9272
9487
|
};
|
|
9273
9488
|
}
|
|
9274
9489
|
function localProviderToRegistry(provider, opts) {
|
|
@@ -10193,4 +10408,4 @@ export {
|
|
|
10193
10408
|
quitClaudeAppGracefully,
|
|
10194
10409
|
launchOrRestartClaudeApp
|
|
10195
10410
|
};
|
|
10196
|
-
//# sourceMappingURL=chunk-
|
|
10411
|
+
//# sourceMappingURL=chunk-VSXPAZX4.js.map
|