@askalf/dario 5.5.86 → 5.5.88
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 +1 -1
- package/dist/anthropic-responses-translate.d.ts +522 -0
- package/dist/anthropic-responses-translate.js +839 -0
- package/dist/codex-backend.d.ts +34 -2
- package/dist/codex-backend.js +141 -15
- package/dist/provider-adapter.d.ts +11 -4
- package/dist/provider-adapter.js +11 -6
- package/dist/proxy.js +2 -2
- package/package.json +1 -1
package/dist/codex-backend.d.ts
CHANGED
|
@@ -76,6 +76,22 @@ export declare function extractChatGPTAccountId(idToken: string | undefined): st
|
|
|
76
76
|
* the user would expect to find in their history
|
|
77
77
|
*/
|
|
78
78
|
export declare function chatCompletionsToResponses(body: Record<string, unknown>): Record<string, unknown>;
|
|
79
|
+
/** Which wire shape the CLIENT spoke; selects both translation ends. */
|
|
80
|
+
export type CodexRequestShape = 'openai' | 'anthropic';
|
|
81
|
+
/**
|
|
82
|
+
* The Responses stream has THREE terminal events, not two. Missing
|
|
83
|
+
* `response.failed` is not inert in either direction: on the chat path the
|
|
84
|
+
* stream would end with no finish frame and no `[DONE]` (an SDK parser waits
|
|
85
|
+
* forever or throws), and on the Anthropic path the non-streaming collapse
|
|
86
|
+
* would find no terminal response and hand back a well-formed EMPTY success —
|
|
87
|
+
* a failure that reads as a normal, empty turn. Found by review on dario#1141;
|
|
88
|
+
* the chat-path half was the same bug, pre-existing.
|
|
89
|
+
*/
|
|
90
|
+
export declare function isTerminalResponsesEvent(type: string): boolean;
|
|
91
|
+
/** True when a terminal Responses payload describes a FAILURE rather than a turn. */
|
|
92
|
+
export declare function isFailedResponse(resp: unknown): boolean;
|
|
93
|
+
/** The upstream message on a failed Responses payload, for the error body. */
|
|
94
|
+
export declare function failedResponseMessage(resp: unknown): string;
|
|
79
95
|
/**
|
|
80
96
|
* Stateful per-request translator: Responses SSE in, chat/completions out.
|
|
81
97
|
*
|
|
@@ -93,15 +109,31 @@ export declare function chatCompletionsToResponses(body: Record<string, unknown>
|
|
|
93
109
|
export declare function createResponsesTranslator(model: string): {
|
|
94
110
|
/** Feed one raw SSE line. Returns the line to forward, or null. */
|
|
95
111
|
chunk(line: string): string | null;
|
|
112
|
+
/** True when the upstream stream terminated as a FAILURE. */
|
|
113
|
+
didFail(): boolean;
|
|
96
114
|
/** Everything seen so far, as one non-streaming chat.completion body. */
|
|
97
115
|
complete(): Record<string, unknown>;
|
|
98
116
|
};
|
|
99
117
|
export declare function buildCodexHeaders(creds: CodexAccountCredentials): Record<string, string>;
|
|
100
118
|
/**
|
|
101
|
-
* Serve a
|
|
119
|
+
* Serve a request from a stored Codex account, in either client wire shape.
|
|
120
|
+
*
|
|
121
|
+
* `shape` picks the two translation ends; everything between — headers, the
|
|
122
|
+
* timeout, the upstream POST, the read loop, the error paths — is shared,
|
|
123
|
+
* because only the translation differs between an OpenAI-shape and an
|
|
124
|
+
* Anthropic-shape client:
|
|
125
|
+
*
|
|
126
|
+
* openai chat/completions ─┐ ┌─ chat.completion(.chunk)
|
|
127
|
+
* ├→ Responses (codex) ┤
|
|
128
|
+
* anthropic messages ─────────┘ └─ Anthropic SSE / Message
|
|
129
|
+
*
|
|
130
|
+
* dario always asks the backend for a stream and collapses it here when the
|
|
131
|
+
* client didn't want one, so `stream: true` is forced on the way out for both
|
|
132
|
+
* shapes. For the Anthropic shape the non-streaming body is rebuilt from the
|
|
133
|
+
* `response` object carried by the terminal `response.completed` event.
|
|
102
134
|
*
|
|
103
135
|
* `fetchImpl` is injectable so the translation and header construction are
|
|
104
136
|
* testable without network (test/codex-backend.mjs), matching the pattern
|
|
105
137
|
* test/codex-oauth.mjs already uses.
|
|
106
138
|
*/
|
|
107
|
-
export declare function forwardToCodex(req: IncomingMessage, res: ServerResponse, body: Buffer, creds: CodexAccountCredentials, corsOrigin: string, securityHeaders: Record<string, string>, upstreamTimeoutMs: number, verbose: boolean, fetchImpl?: typeof fetch): Promise<void>;
|
|
139
|
+
export declare function forwardToCodex(req: IncomingMessage, res: ServerResponse, body: Buffer, creds: CodexAccountCredentials, corsOrigin: string, securityHeaders: Record<string, string>, upstreamTimeoutMs: number, verbose: boolean, shape?: CodexRequestShape, fetchImpl?: typeof fetch): Promise<void>;
|
package/dist/codex-backend.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { anthropicToResponsesRequest, createResponsesSSEParser, formatResponsesAnthropicSSE, responsesStreamToAnthropicSSE, responsesToAnthropicResponse, } from './anthropic-responses-translate.js';
|
|
1
2
|
export const CODEX_BACKEND_BASE_URL = process.env.DARIO_CODEX_BASE_URL || 'https://chatgpt.com/backend-api/codex';
|
|
2
3
|
/** Originator string the codex CLI identifies itself with. */
|
|
3
4
|
const CODEX_ORIGINATOR = 'codex_cli_rs';
|
|
@@ -207,6 +208,32 @@ export function chatCompletionsToResponses(body) {
|
|
|
207
208
|
out.reasoning = { effort: body.reasoning_effort };
|
|
208
209
|
return out;
|
|
209
210
|
}
|
|
211
|
+
/**
|
|
212
|
+
* The Responses stream has THREE terminal events, not two. Missing
|
|
213
|
+
* `response.failed` is not inert in either direction: on the chat path the
|
|
214
|
+
* stream would end with no finish frame and no `[DONE]` (an SDK parser waits
|
|
215
|
+
* forever or throws), and on the Anthropic path the non-streaming collapse
|
|
216
|
+
* would find no terminal response and hand back a well-formed EMPTY success —
|
|
217
|
+
* a failure that reads as a normal, empty turn. Found by review on dario#1141;
|
|
218
|
+
* the chat-path half was the same bug, pre-existing.
|
|
219
|
+
*/
|
|
220
|
+
export function isTerminalResponsesEvent(type) {
|
|
221
|
+
return type === 'response.completed' || type === 'response.incomplete' || type === 'response.failed';
|
|
222
|
+
}
|
|
223
|
+
/** True when a terminal Responses payload describes a FAILURE rather than a turn. */
|
|
224
|
+
export function isFailedResponse(resp) {
|
|
225
|
+
if (!resp || typeof resp !== 'object')
|
|
226
|
+
return false;
|
|
227
|
+
const r = resp;
|
|
228
|
+
return r.status === 'failed' || (r.error !== null && r.error !== undefined);
|
|
229
|
+
}
|
|
230
|
+
/** The upstream message on a failed Responses payload, for the error body. */
|
|
231
|
+
export function failedResponseMessage(resp) {
|
|
232
|
+
const e = resp?.error;
|
|
233
|
+
const msg = e && typeof e.message === 'string' ? e.message : '';
|
|
234
|
+
const code = e && typeof e.code === 'string' ? e.code : '';
|
|
235
|
+
return msg || code || 'the Codex backend ended the response as failed';
|
|
236
|
+
}
|
|
210
237
|
/**
|
|
211
238
|
* Stateful per-request translator: Responses SSE in, chat/completions out.
|
|
212
239
|
*
|
|
@@ -229,6 +256,7 @@ export function createResponsesTranslator(model) {
|
|
|
229
256
|
const toolCalls = new Map();
|
|
230
257
|
let nextToolIndex = 0;
|
|
231
258
|
let roleSent = false;
|
|
259
|
+
let failed = false;
|
|
232
260
|
const frame = (delta, finish) => `data: ${JSON.stringify({
|
|
233
261
|
id,
|
|
234
262
|
object: 'chat.completion.chunk',
|
|
@@ -307,7 +335,9 @@ export function createResponsesTranslator(model) {
|
|
|
307
335
|
acc.args += d;
|
|
308
336
|
return opened(frame({ tool_calls: [{ index: acc.index, function: { arguments: d } }] }, null));
|
|
309
337
|
}
|
|
310
|
-
if (type
|
|
338
|
+
if (isTerminalResponsesEvent(type)) {
|
|
339
|
+
if (type === 'response.failed' || isFailedResponse(e.response))
|
|
340
|
+
failed = true;
|
|
311
341
|
const r = e.response;
|
|
312
342
|
const u = r?.usage;
|
|
313
343
|
if (u) {
|
|
@@ -321,6 +351,10 @@ export function createResponsesTranslator(model) {
|
|
|
321
351
|
}
|
|
322
352
|
return null;
|
|
323
353
|
},
|
|
354
|
+
/** True when the upstream stream terminated as a FAILURE. */
|
|
355
|
+
didFail() {
|
|
356
|
+
return failed;
|
|
357
|
+
},
|
|
324
358
|
/** Everything seen so far, as one non-streaming chat.completion body. */
|
|
325
359
|
complete() {
|
|
326
360
|
const calls = [...toolCalls.values()].sort((a, b) => a.index - b.index);
|
|
@@ -357,26 +391,60 @@ export function buildCodexHeaders(creds) {
|
|
|
357
391
|
return headers;
|
|
358
392
|
}
|
|
359
393
|
/**
|
|
360
|
-
* Serve a
|
|
394
|
+
* Serve a request from a stored Codex account, in either client wire shape.
|
|
395
|
+
*
|
|
396
|
+
* `shape` picks the two translation ends; everything between — headers, the
|
|
397
|
+
* timeout, the upstream POST, the read loop, the error paths — is shared,
|
|
398
|
+
* because only the translation differs between an OpenAI-shape and an
|
|
399
|
+
* Anthropic-shape client:
|
|
400
|
+
*
|
|
401
|
+
* openai chat/completions ─┐ ┌─ chat.completion(.chunk)
|
|
402
|
+
* ├→ Responses (codex) ┤
|
|
403
|
+
* anthropic messages ─────────┘ └─ Anthropic SSE / Message
|
|
404
|
+
*
|
|
405
|
+
* dario always asks the backend for a stream and collapses it here when the
|
|
406
|
+
* client didn't want one, so `stream: true` is forced on the way out for both
|
|
407
|
+
* shapes. For the Anthropic shape the non-streaming body is rebuilt from the
|
|
408
|
+
* `response` object carried by the terminal `response.completed` event.
|
|
361
409
|
*
|
|
362
410
|
* `fetchImpl` is injectable so the translation and header construction are
|
|
363
411
|
* testable without network (test/codex-backend.mjs), matching the pattern
|
|
364
412
|
* test/codex-oauth.mjs already uses.
|
|
365
413
|
*/
|
|
366
|
-
export async function forwardToCodex(req, res, body, creds, corsOrigin, securityHeaders, upstreamTimeoutMs, verbose, fetchImpl = fetch) {
|
|
414
|
+
export async function forwardToCodex(req, res, body, creds, corsOrigin, securityHeaders, upstreamTimeoutMs, verbose, shape = 'openai', fetchImpl = fetch) {
|
|
367
415
|
void req;
|
|
416
|
+
const isAnthropic = shape === 'anthropic';
|
|
417
|
+
// An Anthropic-shape error body is {type,error{type,message}}; an OpenAI one
|
|
418
|
+
// is {error}. A client SDK reads its own shape, so errors follow the request.
|
|
419
|
+
const errBody = (message, extra = {}) => JSON.stringify(isAnthropic
|
|
420
|
+
? { type: 'error', error: { type: 'api_error', message }, ...extra }
|
|
421
|
+
: { error: message, ...extra });
|
|
368
422
|
let parsed;
|
|
369
423
|
try {
|
|
370
424
|
parsed = JSON.parse(body.toString());
|
|
371
425
|
}
|
|
372
426
|
catch {
|
|
373
427
|
res.writeHead(400, { 'Content-Type': 'application/json', ...securityHeaders });
|
|
374
|
-
res.end(
|
|
428
|
+
res.end(errBody(`Codex backend requires a JSON ${isAnthropic ? 'messages' : 'chat/completions'} body`));
|
|
375
429
|
return;
|
|
376
430
|
}
|
|
377
431
|
const clientWantsStream = parsed.stream === true;
|
|
378
432
|
const model = String(parsed.model ?? '');
|
|
379
|
-
|
|
433
|
+
// stream is forced: the backend is always streamed and collapsed here.
|
|
434
|
+
const upstreamBody = isAnthropic
|
|
435
|
+
? { ...anthropicToResponsesRequest(parsed, model), stream: true }
|
|
436
|
+
: chatCompletionsToResponses(parsed);
|
|
437
|
+
// The ChatGPT Codex backend REJECTS an output cap outright:
|
|
438
|
+
// 400 {"detail":"Unsupported parameter: max_output_tokens"}
|
|
439
|
+
// Both builders set it from the client's max_tokens / max_completion_tokens,
|
|
440
|
+
// so BOTH shapes 400 whenever a client asks for one. It stayed hidden because
|
|
441
|
+
// every smoke test so far happened to omit max_tokens; it then showed up as a
|
|
442
|
+
// 100% failure on the Anthropic path, where the Messages API REQUIRES
|
|
443
|
+
// max_tokens and so always produced it. Stripped here rather than in either
|
|
444
|
+
// translator because it is a property of THIS backend, not of either wire
|
|
445
|
+
// format — the same builders are correct against an API-key Responses
|
|
446
|
+
// endpoint, which does support the parameter.
|
|
447
|
+
delete upstreamBody.max_output_tokens;
|
|
380
448
|
const target = `${CODEX_BACKEND_BASE_URL.replace(/\/$/, '')}/responses`;
|
|
381
449
|
const abort = new AbortController();
|
|
382
450
|
const timeout = setTimeout(() => abort.abort(), upstreamTimeoutMs);
|
|
@@ -394,14 +462,33 @@ export async function forwardToCodex(req, res, body, creds, corsOrigin, security
|
|
|
394
462
|
if (verbose)
|
|
395
463
|
console.error(`[dario] codex backend ${upstream.status}: ${detail.slice(0, 300)}`);
|
|
396
464
|
res.writeHead(upstream.status, { 'Content-Type': 'application/json', ...securityHeaders });
|
|
397
|
-
res.end(
|
|
398
|
-
error: 'Upstream Codex backend error',
|
|
399
|
-
status: upstream.status,
|
|
400
|
-
account: creds.alias,
|
|
401
|
-
}));
|
|
465
|
+
res.end(errBody('Upstream Codex backend error', { status: upstream.status, account: creds.alias }));
|
|
402
466
|
return;
|
|
403
467
|
}
|
|
404
|
-
|
|
468
|
+
// OpenAI shape: one stateful line-in/line-out translator (unchanged).
|
|
469
|
+
// Anthropic shape: parse the typed Responses event stream, then map events
|
|
470
|
+
// to Anthropic SSE, keeping the terminal `response` for the collapse.
|
|
471
|
+
const translator = isAnthropic ? null : createResponsesTranslator(model);
|
|
472
|
+
const sseParser = isAnthropic ? createResponsesSSEParser() : null;
|
|
473
|
+
const antTranslator = isAnthropic ? responsesStreamToAnthropicSSE({ requestModel: model }) : null;
|
|
474
|
+
let terminalResponse = null;
|
|
475
|
+
let anthropicFailed = false;
|
|
476
|
+
const emitAnthropic = (events) => {
|
|
477
|
+
for (const ev of events) {
|
|
478
|
+
const t = ev.type ?? '';
|
|
479
|
+
if (isTerminalResponsesEvent(t)) {
|
|
480
|
+
const r = ev.response;
|
|
481
|
+
if (r)
|
|
482
|
+
terminalResponse = r;
|
|
483
|
+
if (t === 'response.failed')
|
|
484
|
+
anthropicFailed = true;
|
|
485
|
+
}
|
|
486
|
+
for (const out of antTranslator.push(ev)) {
|
|
487
|
+
if (clientWantsStream)
|
|
488
|
+
res.write(formatResponsesAnthropicSSE(out));
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
};
|
|
405
492
|
if (clientWantsStream) {
|
|
406
493
|
res.writeHead(200, {
|
|
407
494
|
'Content-Type': 'text/event-stream',
|
|
@@ -420,7 +507,13 @@ export async function forwardToCodex(req, res, body, creds, corsOrigin, security
|
|
|
420
507
|
const { done, value } = await reader.read();
|
|
421
508
|
if (done)
|
|
422
509
|
break;
|
|
423
|
-
|
|
510
|
+
const decoded = decoder.decode(value, { stream: true });
|
|
511
|
+
if (isAnthropic) {
|
|
512
|
+
// The parser owns its own partial-frame buffering.
|
|
513
|
+
emitAnthropic(sseParser.push(decoded));
|
|
514
|
+
continue;
|
|
515
|
+
}
|
|
516
|
+
buffered += decoded;
|
|
424
517
|
// SSE frames are newline-delimited; keep the trailing partial line.
|
|
425
518
|
const lines = buffered.split('\n');
|
|
426
519
|
buffered = lines.pop() ?? '';
|
|
@@ -434,13 +527,46 @@ export async function forwardToCodex(req, res, body, creds, corsOrigin, security
|
|
|
434
527
|
finally {
|
|
435
528
|
reader.releaseLock();
|
|
436
529
|
}
|
|
437
|
-
if (
|
|
530
|
+
if (isAnthropic) {
|
|
531
|
+
emitAnthropic(sseParser.flush());
|
|
532
|
+
}
|
|
533
|
+
else if (buffered.length > 0) {
|
|
438
534
|
const out = translator.chunk(buffered);
|
|
439
535
|
if (out && clientWantsStream)
|
|
440
536
|
res.write(out);
|
|
441
537
|
}
|
|
442
538
|
}
|
|
443
|
-
|
|
539
|
+
// A stream that ended in failure must not collapse into a 200 with empty
|
|
540
|
+
// content: to a non-streaming client that is indistinguishable from a model
|
|
541
|
+
// that chose to say nothing. Streaming clients already saw the terminal
|
|
542
|
+
// event, so only the collapse needs this.
|
|
543
|
+
const upstreamFailed = isAnthropic
|
|
544
|
+
? (anthropicFailed || isFailedResponse(terminalResponse))
|
|
545
|
+
: translator.didFail();
|
|
546
|
+
if (!clientWantsStream && upstreamFailed) {
|
|
547
|
+
const detail = failedResponseMessage(terminalResponse);
|
|
548
|
+
if (verbose)
|
|
549
|
+
console.error(`[dario] codex backend (${creds.alias}) response failed: ${detail}`);
|
|
550
|
+
res.writeHead(502, { 'Content-Type': 'application/json', ...securityHeaders });
|
|
551
|
+
res.end(errBody(`Codex backend response failed: ${detail}`, { account: creds.alias }));
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
if (isAnthropic) {
|
|
555
|
+
if (clientWantsStream) {
|
|
556
|
+
for (const out of antTranslator.end())
|
|
557
|
+
res.write(formatResponsesAnthropicSSE(out));
|
|
558
|
+
res.end();
|
|
559
|
+
}
|
|
560
|
+
else {
|
|
561
|
+
res.writeHead(200, {
|
|
562
|
+
'Content-Type': 'application/json',
|
|
563
|
+
'Access-Control-Allow-Origin': corsOrigin,
|
|
564
|
+
...securityHeaders,
|
|
565
|
+
});
|
|
566
|
+
res.end(JSON.stringify(responsesToAnthropicResponse(terminalResponse ?? {}, model)));
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
else if (clientWantsStream) {
|
|
444
570
|
res.end();
|
|
445
571
|
}
|
|
446
572
|
else {
|
|
@@ -460,7 +586,7 @@ export async function forwardToCodex(req, res, body, creds, corsOrigin, security
|
|
|
460
586
|
console.error(`[dario] codex backend (${creds.alias}) error: ${detail}`);
|
|
461
587
|
if (!res.headersSent) {
|
|
462
588
|
res.writeHead(502, { 'Content-Type': 'application/json', ...securityHeaders });
|
|
463
|
-
res.end(
|
|
589
|
+
res.end(errBody('Upstream Codex backend error', { account: creds.alias }));
|
|
464
590
|
}
|
|
465
591
|
else {
|
|
466
592
|
try {
|
|
@@ -64,10 +64,17 @@ export interface ProviderAdapter {
|
|
|
64
64
|
* subscription even when an API-key backend is also configured; anything not
|
|
65
65
|
* listed — `gpt-4o` and friends — is untouched and still lands on that backend.
|
|
66
66
|
*
|
|
67
|
-
*
|
|
68
|
-
* chat/completions⇄Responses
|
|
69
|
-
*
|
|
70
|
-
*
|
|
67
|
+
* Serves BOTH request shapes. The ChatGPT backend speaks Responses, and dario
|
|
68
|
+
* now owns a translation for each side of it: chat/completions⇄Responses in
|
|
69
|
+
* codex-backend.ts, and Messages⇄Responses in anthropic-responses-translate.ts
|
|
70
|
+
* (dario#1141). So an Anthropic-shape /v1/messages request naming a listed slug
|
|
71
|
+
* is served from the subscription too — which is what lets a Claude-shaped
|
|
72
|
+
* harness (Claude Code, or any Anthropic-SDK client) run on a ChatGPT plan.
|
|
73
|
+
*
|
|
74
|
+
* There is deliberately no path guard: the claim is driven by the MODEL, and a
|
|
75
|
+
* Claude model is never in `codexModels`, so Claude traffic on either path is
|
|
76
|
+
* untouched. The `openai` adapter below keeps its OpenAI-path guard — the
|
|
77
|
+
* API-key backend has no Messages translation.
|
|
71
78
|
*/
|
|
72
79
|
export declare const codexAdapter: ProviderAdapter;
|
|
73
80
|
/**
|
package/dist/provider-adapter.js
CHANGED
|
@@ -31,10 +31,17 @@ import { isCodexModel } from './codex-backend.js';
|
|
|
31
31
|
* subscription even when an API-key backend is also configured; anything not
|
|
32
32
|
* listed — `gpt-4o` and friends — is untouched and still lands on that backend.
|
|
33
33
|
*
|
|
34
|
-
*
|
|
35
|
-
* chat/completions⇄Responses
|
|
36
|
-
*
|
|
37
|
-
*
|
|
34
|
+
* Serves BOTH request shapes. The ChatGPT backend speaks Responses, and dario
|
|
35
|
+
* now owns a translation for each side of it: chat/completions⇄Responses in
|
|
36
|
+
* codex-backend.ts, and Messages⇄Responses in anthropic-responses-translate.ts
|
|
37
|
+
* (dario#1141). So an Anthropic-shape /v1/messages request naming a listed slug
|
|
38
|
+
* is served from the subscription too — which is what lets a Claude-shaped
|
|
39
|
+
* harness (Claude Code, or any Anthropic-SDK client) run on a ChatGPT plan.
|
|
40
|
+
*
|
|
41
|
+
* There is deliberately no path guard: the claim is driven by the MODEL, and a
|
|
42
|
+
* Claude model is never in `codexModels`, so Claude traffic on either path is
|
|
43
|
+
* untouched. The `openai` adapter below keeps its OpenAI-path guard — the
|
|
44
|
+
* API-key backend has no Messages translation.
|
|
38
45
|
*/
|
|
39
46
|
export const codexAdapter = {
|
|
40
47
|
id: 'codex',
|
|
@@ -42,8 +49,6 @@ export const codexAdapter = {
|
|
|
42
49
|
claimsPrimary(ctx) {
|
|
43
50
|
if (!ctx.hasCodexAccount)
|
|
44
51
|
return false;
|
|
45
|
-
if (!ctx.isOpenAIPath)
|
|
46
|
-
return false;
|
|
47
52
|
if (ctx.forcedProvider === 'claude' || ctx.forcedProvider === 'openai')
|
|
48
53
|
return false;
|
|
49
54
|
return ctx.forcedProvider === 'codex' || isCodexModel(ctx.model, ctx.codexModels);
|
package/dist/proxy.js
CHANGED
|
@@ -2498,7 +2498,7 @@ export async function startProxy(opts = {}) {
|
|
|
2498
2498
|
// an account added while the proxy runs routes on the very next
|
|
2499
2499
|
// request; the absent answer is cached ~30s, so an idle proxy with
|
|
2500
2500
|
// no codex account is not stat-ing the filesystem per request.
|
|
2501
|
-
if (
|
|
2501
|
+
if (await hasAnyCodexAccount()) {
|
|
2502
2502
|
const stored = await selectCodexAccount();
|
|
2503
2503
|
if (stored) {
|
|
2504
2504
|
codexCreds = await getFreshCodexAccount(stored);
|
|
@@ -2520,7 +2520,7 @@ export async function startProxy(opts = {}) {
|
|
|
2520
2520
|
console.log(`[dario] #${requestCount} ${req.method} ${urlPath} (model: ${rawModel}) → codex account ${codexCreds.alias}`);
|
|
2521
2521
|
}
|
|
2522
2522
|
requestCount++;
|
|
2523
|
-
await forwardToCodex(req, res, body, codexCreds, corsOrigin, SECURITY_HEADERS, upstreamTimeoutMs, verbose);
|
|
2523
|
+
await forwardToCodex(req, res, body, codexCreds, corsOrigin, SECURITY_HEADERS, upstreamTimeoutMs, verbose, isOpenAI ? 'openai' : 'anthropic');
|
|
2524
2524
|
return;
|
|
2525
2525
|
}
|
|
2526
2526
|
if (rawModel && openaiBackend && decision.provider === 'openai') {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@askalf/dario",
|
|
3
|
-
"version": "5.5.
|
|
3
|
+
"version": "5.5.88",
|
|
4
4
|
"description": "Use your Claude Pro/Max subscription in any tool — Cursor, Cline, Aider, the Agent SDK, your scripts — at subscription pricing, not per-token API bills. One local Anthropic + OpenAI-compatible endpoint.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|