@bitkyc08/opencodex 2.7.40 → 2.7.41
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 +63 -12
- package/assets/claude-code-models.gif +0 -0
- package/gui/dist/assets/index-B2J4t3te.css +1 -0
- package/gui/dist/assets/index-BmvM6wRb.js +65 -0
- package/gui/dist/index.html +2 -2
- package/package.json +3 -2
- package/src/adapters/google.ts +48 -9
- package/src/adapters/kiro-events.ts +15 -3
- package/src/adapters/kiro.ts +292 -28
- package/src/adapters/openai-chat.ts +63 -15
- package/src/adapters/openai-responses.ts +18 -0
- package/src/bridge.ts +76 -21
- package/src/chat/outbound.ts +66 -34
- package/src/claude/auth-detect.ts +229 -0
- package/src/claude/auth-mode-migration.ts +32 -0
- package/src/claude/auth-mode.ts +62 -0
- package/src/claude/desktop-3p-guard.ts +35 -0
- package/src/claude/desktop-3p.ts +121 -21
- package/src/claude/desktop-health.ts +26 -0
- package/src/claude/desktop-profile.ts +263 -0
- package/src/claude/inbound-debug.ts +4 -0
- package/src/claude/model-info.ts +9 -3
- package/src/cli/account-extended.ts +34 -0
- package/src/cli/account.ts +3 -1
- package/src/cli/claude-desktop.ts +152 -0
- package/src/cli/claude.ts +37 -3
- package/src/cli/doctor.ts +187 -6
- package/src/cli/help.ts +13 -1
- package/src/cli/index.ts +151 -57
- package/src/cli/status-oauth.ts +68 -0
- package/src/cli/status.ts +4 -0
- package/src/codex/account-lifecycle.ts +31 -0
- package/src/codex/auth-api.ts +133 -26
- package/src/codex/auth-collision.ts +55 -11
- package/src/codex/auth-context.ts +50 -6
- package/src/codex/catalog/provider-fetch.ts +6 -2
- package/src/codex/home.ts +61 -1
- package/src/codex/inject.ts +28 -66
- package/src/codex/injected-marker.ts +72 -0
- package/src/codex/journal.ts +39 -3
- package/src/codex/main-account-cache.ts +25 -0
- package/src/codex/model-cache.ts +20 -1
- package/src/codex/paths.ts +5 -0
- package/src/codex/routing.ts +138 -11
- package/src/codex/subagent-model-fallback.ts +455 -0
- package/src/codex/sync.ts +17 -0
- package/src/combos/failover.ts +10 -2
- package/src/combos/index.ts +1 -0
- package/src/combos/types.ts +9 -0
- package/src/config.ts +117 -0
- package/src/grok/inject.ts +339 -0
- package/src/grok/status.ts +88 -0
- package/src/grok/sync.ts +66 -0
- package/src/lib/destination-policy.ts +13 -0
- package/src/lib/errors.ts +59 -0
- package/src/lib/privacy.ts +9 -0
- package/src/lib/process-control.ts +57 -4
- package/src/oauth/health.ts +375 -0
- package/src/oauth/index.ts +69 -22
- package/src/oauth/kiro.ts +5 -3
- package/src/oauth/log.ts +48 -0
- package/src/oauth/store.ts +55 -6
- package/src/providers/alibaba-region-backup.ts +75 -0
- package/src/providers/alibaba-region-migration.ts +143 -0
- package/src/providers/alibaba-region-startup.ts +36 -0
- package/src/providers/api-keys.ts +5 -5
- package/src/providers/derive.ts +22 -1
- package/src/providers/free-directory.ts +181 -0
- package/src/providers/key-failover.ts +2 -2
- package/src/providers/kiro-models.ts +3 -2
- package/src/providers/openai-tiers.ts +1 -1
- package/src/providers/provider-id-rewrite.ts +150 -0
- package/src/providers/registry.ts +49 -2
- package/src/responses/parser.ts +49 -20
- package/src/responses/state.ts +156 -2
- package/src/router.ts +73 -5
- package/src/server/chat-completions.ts +78 -22
- package/src/server/claude-messages.ts +8 -0
- package/src/server/images.ts +2 -1
- package/src/server/index.ts +50 -30
- package/src/server/live.ts +2 -1
- package/src/server/management/agent-settings-routes.ts +337 -18
- package/src/server/management/api-access.ts +141 -0
- package/src/server/management/combo-routes.ts +3 -3
- package/src/server/management/config-routes.ts +4 -4
- package/src/server/management/logs-usage-routes.ts +64 -4
- package/src/server/management/model-routes.ts +114 -8
- package/src/server/management/oauth-account-routes.ts +31 -5
- package/src/server/management/provider-routes.ts +42 -11
- package/src/server/management/shared.ts +81 -2
- package/src/server/management/system-routes.ts +6 -1
- package/src/server/management-api.ts +24 -7
- package/src/server/port-reclaim.ts +261 -0
- package/src/server/request-log.ts +11 -5
- package/src/server/responses/collaboration.ts +11 -6
- package/src/server/responses/compact.ts +2 -1
- package/src/server/responses/core.ts +358 -155
- package/src/server/responses/passthrough-error.ts +53 -0
- package/src/server/search.ts +2 -1
- package/src/server/system-env.ts +23 -6
- package/src/server/windows-tcp-drop.ts +174 -0
- package/src/service.ts +43 -3
- package/src/types.ts +74 -6
- package/src/update/index.ts +30 -19
- package/src/update/job.ts +30 -15
- package/src/usage/log.ts +158 -2
- package/src/usage/summary.ts +8 -4
- package/src/web-search/loop.ts +4 -1
- package/gui/dist/assets/index-CMip1DzF.css +0 -1
- package/gui/dist/assets/index-cydcmbzC.js +0 -52
package/src/server/index.ts
CHANGED
|
@@ -12,6 +12,7 @@ import type { Server, ServerWebSocket } from "bun";
|
|
|
12
12
|
import {
|
|
13
13
|
DEFAULT_SUBAGENT_MODELS,
|
|
14
14
|
applyProxyEnv,
|
|
15
|
+
armClaudeCodeBaseline,
|
|
15
16
|
loadConfig,
|
|
16
17
|
saveConfig,
|
|
17
18
|
websocketsEnabled,
|
|
@@ -20,10 +21,12 @@ import { reconcileOAuthProviders } from "../oauth";
|
|
|
20
21
|
import { invalidateCodexModelsCache } from "../codex/catalog";
|
|
21
22
|
import { startMemoryWatchdog } from "./memory-watchdog";
|
|
22
23
|
import { runOpenAiTierStartupMigration } from "../providers/openai-tier-startup";
|
|
24
|
+
import { runAlibabaRegionStartupMigration } from "../providers/alibaba-region-startup";
|
|
23
25
|
import { isCanonicalOpenAiForwardProvider } from "../providers/openai-tiers";
|
|
24
26
|
import { providerCodexAccountMode } from "../providers/registry";
|
|
25
27
|
import {
|
|
26
28
|
CodexAccountCooldownError,
|
|
29
|
+
cooldownErrorMessage,
|
|
27
30
|
} from "../codex/auth-context";
|
|
28
31
|
export {
|
|
29
32
|
clearThreadAccountMap,
|
|
@@ -121,6 +124,7 @@ import { handleClaudeCountTokens, handleClaudeMessages } from "./claude-messages
|
|
|
121
124
|
import { handleChatCompletions } from "./chat-completions";
|
|
122
125
|
import { anthropicErrorResponse } from "../claude/outbound";
|
|
123
126
|
import { buildDesktop3pRegistry } from "../claude/desktop-3p";
|
|
127
|
+
import { runClaudeAuthModeMigration } from "../claude/auth-mode-migration";
|
|
124
128
|
import { handleImages } from "./images";
|
|
125
129
|
import { handleLive, logLiveSidebandFrame, parseLiveSidebandTarget, resolveLiveSidebandUpgrade } from "./live";
|
|
126
130
|
import { handleSearch } from "./search";
|
|
@@ -235,7 +239,7 @@ function attachLiveSidebandUpstream(ws: ServerWebSocket<WsData>): void {
|
|
|
235
239
|
// export function relaySseWithHeartbeat
|
|
236
240
|
|
|
237
241
|
export function startServer(port?: number) {
|
|
238
|
-
const config = runOpenAiTierStartupMigration(loadConfig());
|
|
242
|
+
const config = runAlibabaRegionStartupMigration(runOpenAiTierStartupMigration(loadConfig()));
|
|
239
243
|
applyProxyEnv(config);
|
|
240
244
|
assertServerAuthConfig(config);
|
|
241
245
|
// Refresh OAuth provider presets (models/noReasoningModels) from the registry so a proxy update
|
|
@@ -247,6 +251,11 @@ export function startServer(port?: number) {
|
|
|
247
251
|
config.subagentModels = [...DEFAULT_SUBAGENT_MODELS];
|
|
248
252
|
saveConfig(config);
|
|
249
253
|
}
|
|
254
|
+
// authMode migration (devlog 260726_claude_auth_auto/015): before "auto" existed,
|
|
255
|
+
// choosing Subscription DELETED the key, so a pre-upgrade block with no authMode is
|
|
256
|
+
// indistinguishable from "never chose". Pin those to subscription once so an upgrade
|
|
257
|
+
// never silently moves a deliberate subscriber onto proxy.
|
|
258
|
+
if (runClaudeAuthModeMigration(config)) saveConfig(config);
|
|
250
259
|
// Sidecar model migration (KST 2026-07-10 06:00 = UTC 2026-07-09 21:00): auto-migrate the old
|
|
251
260
|
// gpt-5.4-mini default to gpt-5.6-luna for both search and vision sidecars. Only touches configs
|
|
252
261
|
// still on the old default — explicit user choices are preserved.
|
|
@@ -266,6 +275,13 @@ export function startServer(port?: number) {
|
|
|
266
275
|
}
|
|
267
276
|
}
|
|
268
277
|
invalidateCodexModelsCache();
|
|
278
|
+
// Arm the `claudeCode` hand-edit guard (devlog 260726_claude_auth_auto/040 H1) BEFORE
|
|
279
|
+
// the server can serve a request, and AFTER the startup migrations above — those run
|
|
280
|
+
// against a config nobody else holds and are the documented exception to the save
|
|
281
|
+
// boundary, so the baseline should reflect what they wrote. Arming is eager on
|
|
282
|
+
// purpose: a lazy "arm on first save" loses exactly the hand edit made before that
|
|
283
|
+
// first save, which is the case the guard exists for.
|
|
284
|
+
armClaudeCodeBaseline(config);
|
|
269
285
|
// usage.jsonl already persists every request; rehydrate the in-memory Logs ring so
|
|
270
286
|
// /api/logs (and the GUI) survive `ocx stop` / `ocx start` process restarts.
|
|
271
287
|
hydrateRequestLogsFromDisk();
|
|
@@ -282,6 +298,18 @@ export function startServer(port?: number) {
|
|
|
282
298
|
// (0.0.0.0/::) and specific hosts are left untouched so intentional exposure is preserved.
|
|
283
299
|
const bindHost = /^localhost$/i.test(config.hostname ?? "") ? "127.0.0.1" : (config.hostname ?? "127.0.0.1");
|
|
284
300
|
|
|
301
|
+
// Codex treats empty / non-JSON 503 bodies as "Unknown error" (#452). Keep Retry-After and
|
|
302
|
+
// the server_is_overloaded code so clients can back off, but always return a JSON envelope.
|
|
303
|
+
function drainingResponse(req: Request): Response {
|
|
304
|
+
const response = formatErrorResponse(503, "server_error", "Service shutting down");
|
|
305
|
+
const headers = new Headers(response.headers);
|
|
306
|
+
for (const [name, value] of Object.entries(corsHeaders(req, config))) {
|
|
307
|
+
headers.set(name, value);
|
|
308
|
+
}
|
|
309
|
+
headers.set("Retry-After", "5");
|
|
310
|
+
return new Response(response.body, { status: 503, headers });
|
|
311
|
+
}
|
|
312
|
+
|
|
285
313
|
const server: Server<WsData> = Bun.serve<WsData>({
|
|
286
314
|
port: listenPort,
|
|
287
315
|
hostname: bindHost,
|
|
@@ -301,7 +329,7 @@ export function startServer(port?: number) {
|
|
|
301
329
|
// handshake-time only, so capture inbound headers and thread them into the pipeline.
|
|
302
330
|
if (url.pathname === "/v1/responses" && req.headers.get("upgrade")?.toLowerCase() === "websocket") {
|
|
303
331
|
if (isDraining()) {
|
|
304
|
-
return
|
|
332
|
+
return drainingResponse(req);
|
|
305
333
|
}
|
|
306
334
|
const apiAuthError = requireResponsesApiAuth(req, config);
|
|
307
335
|
if (apiAuthError) return withCors(apiAuthError, req, config);
|
|
@@ -362,8 +390,15 @@ export function startServer(port?: number) {
|
|
|
362
390
|
|| url.searchParams.get("flavor") === "anthropic";
|
|
363
391
|
if (wantsAnthropicList && !url.searchParams.has("client_version")) {
|
|
364
392
|
if (config.claudeCode?.enabled === false) return jsonResponse({ data: [] }, 200, req, config);
|
|
393
|
+
// Build Desktop 3P registry so inbound alias resolution works for subsequent requests.
|
|
394
|
+
buildDesktop3pRegistry(
|
|
395
|
+
[...visibleNativeSlugs(config)],
|
|
396
|
+
goOrdered.map(m => ({ provider: m.provider, id: m.id, contextWindow: m.contextWindow })),
|
|
397
|
+
config.claudeCode?.desktopProfile,
|
|
398
|
+
);
|
|
365
399
|
const { buildAnthropicModelInfos } = await import("../claude/model-info");
|
|
366
400
|
const { resolveAutoContext } = await import("../claude/context-windows");
|
|
401
|
+
const { activeDesktop3pAlias } = await import("../claude/desktop-3p");
|
|
367
402
|
// Per-surface id family (devlog 050): explicit ?ids= wins; otherwise the
|
|
368
403
|
// Claude Code CLI discovery UA (`claude-code/<version>`, binary n_()) gets
|
|
369
404
|
// readable claude-ocx ids and every other client (Desktop 3P) keeps the
|
|
@@ -374,12 +409,7 @@ export function startServer(port?: number) {
|
|
|
374
409
|
: idsParam === "desktop"
|
|
375
410
|
? "desktop3p" as const
|
|
376
411
|
: (/^claude-code\//i.test(req.headers.get("user-agent") ?? "") ? "readable" as const : "desktop3p" as const);
|
|
377
|
-
const data = buildAnthropicModelInfos([...visibleNativeSlugs(config)], goOrdered, resolveAutoContext(config.claudeCode), idStyle);
|
|
378
|
-
// Build Desktop 3P registry so inbound alias resolution works for subsequent requests.
|
|
379
|
-
buildDesktop3pRegistry(
|
|
380
|
-
[...visibleNativeSlugs(config)],
|
|
381
|
-
goOrdered.map(m => ({ provider: m.provider, id: m.id, contextWindow: m.contextWindow })),
|
|
382
|
-
);
|
|
412
|
+
const data = buildAnthropicModelInfos([...visibleNativeSlugs(config)], goOrdered, resolveAutoContext(config.claudeCode), idStyle, activeDesktop3pAlias);
|
|
383
413
|
return jsonResponse({ data }, 200, req, config);
|
|
384
414
|
}
|
|
385
415
|
if (url.searchParams.has("client_version")) {
|
|
@@ -406,7 +436,7 @@ export function startServer(port?: number) {
|
|
|
406
436
|
// before the /v1/* 404 guard below.
|
|
407
437
|
if (url.pathname === "/v1/responses/compact" && req.method === "POST") {
|
|
408
438
|
if (isDraining()) {
|
|
409
|
-
return
|
|
439
|
+
return drainingResponse(req);
|
|
410
440
|
}
|
|
411
441
|
const apiAuthError = requireResponsesApiAuth(req, config);
|
|
412
442
|
if (apiAuthError) return withCors(apiAuthError, req, config);
|
|
@@ -438,10 +468,7 @@ export function startServer(port?: number) {
|
|
|
438
468
|
) {
|
|
439
469
|
disableResponsesRequestTimeout(req, requestServer);
|
|
440
470
|
if (isDraining()) {
|
|
441
|
-
return
|
|
442
|
-
status: 503,
|
|
443
|
-
headers: { ...corsHeaders(req, config), "Retry-After": "5" },
|
|
444
|
-
});
|
|
471
|
+
return drainingResponse(req);
|
|
445
472
|
}
|
|
446
473
|
const apiAuthError = requireApiAuth(req, config, "data-plane");
|
|
447
474
|
if (apiAuthError) return withCors(apiAuthError, req, config);
|
|
@@ -460,10 +487,7 @@ export function startServer(port?: number) {
|
|
|
460
487
|
if (url.pathname === "/v1/alpha/search" && req.method === "POST") {
|
|
461
488
|
disableResponsesRequestTimeout(req, requestServer);
|
|
462
489
|
if (isDraining()) {
|
|
463
|
-
return
|
|
464
|
-
status: 503,
|
|
465
|
-
headers: { ...corsHeaders(req, config), "Retry-After": "5" },
|
|
466
|
-
});
|
|
490
|
+
return drainingResponse(req);
|
|
467
491
|
}
|
|
468
492
|
const apiAuthError = requireApiAuth(req, config, "data-plane");
|
|
469
493
|
if (apiAuthError) return withCors(apiAuthError, req, config);
|
|
@@ -487,7 +511,7 @@ export function startServer(port?: number) {
|
|
|
487
511
|
if (url.pathname === "/v1/responses" && req.method === "POST") {
|
|
488
512
|
disableResponsesRequestTimeout(req, requestServer);
|
|
489
513
|
if (isDraining()) {
|
|
490
|
-
return
|
|
514
|
+
return drainingResponse(req);
|
|
491
515
|
}
|
|
492
516
|
const apiAuthError = requireResponsesApiAuth(req, config);
|
|
493
517
|
if (apiAuthError) return withCors(apiAuthError, req, config);
|
|
@@ -526,7 +550,7 @@ export function startServer(port?: number) {
|
|
|
526
550
|
// Claude Code posts `/v1/messages?beta=true` — pathname match ignores the query (003 G9).
|
|
527
551
|
if (url.pathname === "/v1/messages/count_tokens" && req.method === "POST") {
|
|
528
552
|
if (isDraining()) {
|
|
529
|
-
return
|
|
553
|
+
return drainingResponse(req);
|
|
530
554
|
}
|
|
531
555
|
if (!hasValidApiAuth(req, config)) {
|
|
532
556
|
return withCors(anthropicErrorResponse(401, "opencodex API key required", "authentication_error"), req, config);
|
|
@@ -541,7 +565,7 @@ export function startServer(port?: number) {
|
|
|
541
565
|
if (url.pathname === "/v1/messages" && req.method === "POST") {
|
|
542
566
|
disableResponsesRequestTimeout(req, requestServer);
|
|
543
567
|
if (isDraining()) {
|
|
544
|
-
return
|
|
568
|
+
return drainingResponse(req);
|
|
545
569
|
}
|
|
546
570
|
if (!hasValidApiAuth(req, config)) {
|
|
547
571
|
return withCors(anthropicErrorResponse(401, "opencodex API key required", "authentication_error"), req, config);
|
|
@@ -564,7 +588,7 @@ export function startServer(port?: number) {
|
|
|
564
588
|
if (url.pathname === "/v1/chat/completions" && req.method === "POST") {
|
|
565
589
|
disableResponsesRequestTimeout(req, requestServer);
|
|
566
590
|
if (isDraining()) {
|
|
567
|
-
return
|
|
591
|
+
return drainingResponse(req);
|
|
568
592
|
}
|
|
569
593
|
const apiAuthError = requireResponsesApiAuth(req, config);
|
|
570
594
|
if (apiAuthError) return withCors(apiAuthError, req, config);
|
|
@@ -587,10 +611,7 @@ export function startServer(port?: number) {
|
|
|
587
611
|
) {
|
|
588
612
|
disableResponsesRequestTimeout(req, requestServer);
|
|
589
613
|
if (isDraining()) {
|
|
590
|
-
return
|
|
591
|
-
status: 503,
|
|
592
|
-
headers: { ...corsHeaders(req, config), "Retry-After": "5" },
|
|
593
|
-
});
|
|
614
|
+
return drainingResponse(req);
|
|
594
615
|
}
|
|
595
616
|
const apiAuthError = requireApiAuth(req, config, "data-plane");
|
|
596
617
|
if (apiAuthError) return withCors(apiAuthError, req, config);
|
|
@@ -618,10 +639,7 @@ export function startServer(port?: number) {
|
|
|
618
639
|
: null;
|
|
619
640
|
if (liveSidebandTarget) {
|
|
620
641
|
if (isDraining()) {
|
|
621
|
-
return
|
|
622
|
-
status: 503,
|
|
623
|
-
headers: { ...corsHeaders(req, config), "Retry-After": "5" },
|
|
624
|
-
});
|
|
642
|
+
return drainingResponse(req);
|
|
625
643
|
}
|
|
626
644
|
const apiAuthError = requireApiAuth(req, config, "data-plane");
|
|
627
645
|
if (apiAuthError) return withCors(apiAuthError, req, config);
|
|
@@ -796,9 +814,11 @@ export function startServer(port?: number) {
|
|
|
796
814
|
try {
|
|
797
815
|
if (err instanceof CodexAccountCooldownError) {
|
|
798
816
|
finalizeLog(429);
|
|
817
|
+
// Codex Desktop rides this WS transport, so it must carry the same
|
|
818
|
+
// actionable text as HTTP; a frame has no headers, hence message-only.
|
|
799
819
|
sendJsonFrame(ws, buildWsErrorFrame(429, {
|
|
800
820
|
type: "rate_limit_error",
|
|
801
|
-
message:
|
|
821
|
+
message: cooldownErrorMessage(err),
|
|
802
822
|
}));
|
|
803
823
|
return;
|
|
804
824
|
}
|
package/src/server/live.ts
CHANGED
|
@@ -24,6 +24,7 @@ import { appendFileSync } from "node:fs";
|
|
|
24
24
|
import { formatErrorResponse } from "../bridge";
|
|
25
25
|
import {
|
|
26
26
|
CodexAccountCooldownError,
|
|
27
|
+
cooldownErrorResponse,
|
|
27
28
|
CodexAuthContextError,
|
|
28
29
|
CodexPoolAuthenticationError,
|
|
29
30
|
CodexThreadAffinityExpiredError,
|
|
@@ -392,7 +393,7 @@ export async function resolveLiveRelay(
|
|
|
392
393
|
}
|
|
393
394
|
} catch (err) {
|
|
394
395
|
if (err instanceof CodexAccountCooldownError) {
|
|
395
|
-
forwardAuthError =
|
|
396
|
+
forwardAuthError = cooldownErrorResponse(err);
|
|
396
397
|
} else if (err instanceof CodexThreadAffinityExpiredError) {
|
|
397
398
|
forwardAuthError = formatErrorResponse(
|
|
398
399
|
409,
|