@askalf/dario 5.5.86 → 5.5.87
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 +130 -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,49 @@ 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);
|
|
380
437
|
const target = `${CODEX_BACKEND_BASE_URL.replace(/\/$/, '')}/responses`;
|
|
381
438
|
const abort = new AbortController();
|
|
382
439
|
const timeout = setTimeout(() => abort.abort(), upstreamTimeoutMs);
|
|
@@ -394,14 +451,33 @@ export async function forwardToCodex(req, res, body, creds, corsOrigin, security
|
|
|
394
451
|
if (verbose)
|
|
395
452
|
console.error(`[dario] codex backend ${upstream.status}: ${detail.slice(0, 300)}`);
|
|
396
453
|
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
|
-
}));
|
|
454
|
+
res.end(errBody('Upstream Codex backend error', { status: upstream.status, account: creds.alias }));
|
|
402
455
|
return;
|
|
403
456
|
}
|
|
404
|
-
|
|
457
|
+
// OpenAI shape: one stateful line-in/line-out translator (unchanged).
|
|
458
|
+
// Anthropic shape: parse the typed Responses event stream, then map events
|
|
459
|
+
// to Anthropic SSE, keeping the terminal `response` for the collapse.
|
|
460
|
+
const translator = isAnthropic ? null : createResponsesTranslator(model);
|
|
461
|
+
const sseParser = isAnthropic ? createResponsesSSEParser() : null;
|
|
462
|
+
const antTranslator = isAnthropic ? responsesStreamToAnthropicSSE({ requestModel: model }) : null;
|
|
463
|
+
let terminalResponse = null;
|
|
464
|
+
let anthropicFailed = false;
|
|
465
|
+
const emitAnthropic = (events) => {
|
|
466
|
+
for (const ev of events) {
|
|
467
|
+
const t = ev.type ?? '';
|
|
468
|
+
if (isTerminalResponsesEvent(t)) {
|
|
469
|
+
const r = ev.response;
|
|
470
|
+
if (r)
|
|
471
|
+
terminalResponse = r;
|
|
472
|
+
if (t === 'response.failed')
|
|
473
|
+
anthropicFailed = true;
|
|
474
|
+
}
|
|
475
|
+
for (const out of antTranslator.push(ev)) {
|
|
476
|
+
if (clientWantsStream)
|
|
477
|
+
res.write(formatResponsesAnthropicSSE(out));
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
};
|
|
405
481
|
if (clientWantsStream) {
|
|
406
482
|
res.writeHead(200, {
|
|
407
483
|
'Content-Type': 'text/event-stream',
|
|
@@ -420,7 +496,13 @@ export async function forwardToCodex(req, res, body, creds, corsOrigin, security
|
|
|
420
496
|
const { done, value } = await reader.read();
|
|
421
497
|
if (done)
|
|
422
498
|
break;
|
|
423
|
-
|
|
499
|
+
const decoded = decoder.decode(value, { stream: true });
|
|
500
|
+
if (isAnthropic) {
|
|
501
|
+
// The parser owns its own partial-frame buffering.
|
|
502
|
+
emitAnthropic(sseParser.push(decoded));
|
|
503
|
+
continue;
|
|
504
|
+
}
|
|
505
|
+
buffered += decoded;
|
|
424
506
|
// SSE frames are newline-delimited; keep the trailing partial line.
|
|
425
507
|
const lines = buffered.split('\n');
|
|
426
508
|
buffered = lines.pop() ?? '';
|
|
@@ -434,13 +516,46 @@ export async function forwardToCodex(req, res, body, creds, corsOrigin, security
|
|
|
434
516
|
finally {
|
|
435
517
|
reader.releaseLock();
|
|
436
518
|
}
|
|
437
|
-
if (
|
|
519
|
+
if (isAnthropic) {
|
|
520
|
+
emitAnthropic(sseParser.flush());
|
|
521
|
+
}
|
|
522
|
+
else if (buffered.length > 0) {
|
|
438
523
|
const out = translator.chunk(buffered);
|
|
439
524
|
if (out && clientWantsStream)
|
|
440
525
|
res.write(out);
|
|
441
526
|
}
|
|
442
527
|
}
|
|
443
|
-
|
|
528
|
+
// A stream that ended in failure must not collapse into a 200 with empty
|
|
529
|
+
// content: to a non-streaming client that is indistinguishable from a model
|
|
530
|
+
// that chose to say nothing. Streaming clients already saw the terminal
|
|
531
|
+
// event, so only the collapse needs this.
|
|
532
|
+
const upstreamFailed = isAnthropic
|
|
533
|
+
? (anthropicFailed || isFailedResponse(terminalResponse))
|
|
534
|
+
: translator.didFail();
|
|
535
|
+
if (!clientWantsStream && upstreamFailed) {
|
|
536
|
+
const detail = failedResponseMessage(terminalResponse);
|
|
537
|
+
if (verbose)
|
|
538
|
+
console.error(`[dario] codex backend (${creds.alias}) response failed: ${detail}`);
|
|
539
|
+
res.writeHead(502, { 'Content-Type': 'application/json', ...securityHeaders });
|
|
540
|
+
res.end(errBody(`Codex backend response failed: ${detail}`, { account: creds.alias }));
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
if (isAnthropic) {
|
|
544
|
+
if (clientWantsStream) {
|
|
545
|
+
for (const out of antTranslator.end())
|
|
546
|
+
res.write(formatResponsesAnthropicSSE(out));
|
|
547
|
+
res.end();
|
|
548
|
+
}
|
|
549
|
+
else {
|
|
550
|
+
res.writeHead(200, {
|
|
551
|
+
'Content-Type': 'application/json',
|
|
552
|
+
'Access-Control-Allow-Origin': corsOrigin,
|
|
553
|
+
...securityHeaders,
|
|
554
|
+
});
|
|
555
|
+
res.end(JSON.stringify(responsesToAnthropicResponse(terminalResponse ?? {}, model)));
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
else if (clientWantsStream) {
|
|
444
559
|
res.end();
|
|
445
560
|
}
|
|
446
561
|
else {
|
|
@@ -460,7 +575,7 @@ export async function forwardToCodex(req, res, body, creds, corsOrigin, security
|
|
|
460
575
|
console.error(`[dario] codex backend (${creds.alias}) error: ${detail}`);
|
|
461
576
|
if (!res.headersSent) {
|
|
462
577
|
res.writeHead(502, { 'Content-Type': 'application/json', ...securityHeaders });
|
|
463
|
-
res.end(
|
|
578
|
+
res.end(errBody('Upstream Codex backend error', { account: creds.alias }));
|
|
464
579
|
}
|
|
465
580
|
else {
|
|
466
581
|
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.87",
|
|
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": {
|