@bitkyc08/opencodex 2.7.9 → 2.7.10

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.
@@ -16,7 +16,7 @@
16
16
  } catch (e) {}
17
17
  })();
18
18
  </script>
19
- <script type="module" crossorigin src="/assets/index-BAAFKwsh.js"></script>
19
+ <script type="module" crossorigin src="/assets/index-BU5WLK6k.js"></script>
20
20
  <link rel="stylesheet" crossorigin href="/assets/index-Cq8maiJf.css">
21
21
  </head>
22
22
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bitkyc08/opencodex",
3
- "version": "2.7.9",
3
+ "version": "2.7.10",
4
4
  "description": "Universal provider proxy for OpenAI Codex — use any LLM with Codex CLI/App/SDK",
5
5
  "type": "module",
6
6
  "main": "./bin/package-main.mjs",
@@ -10,6 +10,7 @@
10
10
  */
11
11
  import { saveConfig } from "../config";
12
12
  import type { OcxConfig, OcxProviderConfig } from "../types";
13
+ import { resolveProviderTransport } from "./xai-transport";
13
14
 
14
15
  // ---- cooldown state (in-memory, same as codex/routing.ts) ----
15
16
 
@@ -125,6 +126,31 @@ export function rotateKeyOn429(
125
126
  return null;
126
127
  }
127
128
 
129
+ interface RotateProviderTransportOptions {
130
+ retryAfter?: string | null;
131
+ now?: number;
132
+ attemptedKey?: string;
133
+ promptCacheKey?: string;
134
+ }
135
+
136
+ /** Rotate a failed key and re-apply provider-specific transport metadata to the replacement. */
137
+ export function rotateProviderTransportOn429(
138
+ config: OcxConfig,
139
+ providerName: string,
140
+ options: RotateProviderTransportOptions = {},
141
+ ): OcxProviderConfig | null {
142
+ const rotated = rotateKeyOn429(
143
+ config,
144
+ providerName,
145
+ options.retryAfter,
146
+ options.now,
147
+ options.attemptedKey,
148
+ );
149
+ return rotated
150
+ ? resolveProviderTransport(providerName, rotated, options.promptCacheKey)
151
+ : null;
152
+ }
153
+
128
154
  /** Clear cooldown state for a provider (e.g. after manual key management). */
129
155
  export function clearKeyCooldowns(providerName?: string): void {
130
156
  if (!providerName) {
@@ -18,6 +18,8 @@ export interface ProviderRegistryEntry {
18
18
  adapter: string;
19
19
  baseUrl: string;
20
20
  authKind: ProviderAuthKind;
21
+ /** OAuth preset may explicitly honor a persisted API-key billing mode. */
22
+ allowKeyAuthOverride?: boolean;
21
23
  allowPrivateNetworkByDefault?: boolean;
22
24
  keyOptional?: boolean;
23
25
  allowBaseUrlOverride?: boolean;
@@ -219,6 +221,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
219
221
  adapter: "openai-chat",
220
222
  baseUrl: "https://api.x.ai/v1",
221
223
  authKind: "oauth",
224
+ allowKeyAuthOverride: true,
222
225
  featured: true,
223
226
  oauthId: "xai",
224
227
  jawcodeBundle: "xai",
package/src/router.ts CHANGED
@@ -84,8 +84,13 @@ function routedProviderConfig(providerName: string, provider: OcxProviderConfig)
84
84
  assertProviderDestinationAllowed(providerName, provider);
85
85
  return { ...provider, apiKey: resolveEnvValue(provider.apiKey) };
86
86
  }
87
- const canonicalAuthMode = registryEntry.authKind === "forward" || registryEntry.authKind === "oauth"
88
- ? registryEntry.authKind
87
+ const explicitKeyOverride = registryEntry.authKind === "oauth"
88
+ && registryEntry.allowKeyAuthOverride === true
89
+ && provider.authMode === "key";
90
+ const canonicalAuthMode = explicitKeyOverride
91
+ ? "key"
92
+ : registryEntry.authKind === "forward" || registryEntry.authKind === "oauth"
93
+ ? registryEntry.authKind
89
94
  : provider.authMode === "forward" ? undefined : provider.authMode;
90
95
  const reasoningEffortMap = mergeRecord(registryEntry.reasoningEffortMap, provider.reasoningEffortMap);
91
96
  const modelReasoningEffortMap = mergeNestedRecord(registryEntry.modelReasoningEffortMap, provider.modelReasoningEffortMap);
@@ -40,7 +40,7 @@ import { fetchWithResetRetry } from "../lib/upstream-retry";
40
40
  import { isUsageDebugEnabled } from "../usage/debug";
41
41
  import { readJsonRequestBody, DecompressedBodyTooLargeError, UnsupportedContentEncodingError } from "./request-decompress";
42
42
  import { resolveAdapter, resolveWireProtocolOverride } from "./adapter-resolve";
43
- import { hasKeyPoolFailover, rotateKeyOn429 } from "../providers/key-failover";
43
+ import { hasKeyPoolFailover, rotateProviderTransportOn429 } from "../providers/key-failover";
44
44
  import { resolveProviderTransport } from "../providers/xai-transport";
45
45
  import type { WsData } from "./ws-bridge";
46
46
  import { registerTurn, trackStreamLifetime, unregisterTurn } from "./lifecycle";
@@ -886,11 +886,14 @@ export async function handleResponses(
886
886
  routedModelStallTimeoutMs: wsPlan.routedModelStallTimeoutMs,
887
887
  stallTimeoutSec: wsPlan.stallTimeoutSec,
888
888
  on429: retryAfter => {
889
- const rotated = rotateKeyOn429(config, route.providerName, retryAfter, Date.now(), route.provider.apiKey);
889
+ const rotated = rotateProviderTransportOn429(config, route.providerName, {
890
+ retryAfter,
891
+ now: Date.now(),
892
+ attemptedKey: route.provider.apiKey,
893
+ promptCacheKey: parsed.options.promptCacheKey,
894
+ });
890
895
  if (!rotated) return null;
891
- // Re-resolve the auth-mode transport so the conv-id / subscription headers derived at
892
- // line ~616 survive the key rotation (rotated providers come from raw config).
893
- route.provider = resolveProviderTransport(route.providerName, rotated, parsed.options.promptCacheKey);
896
+ route.provider = rotated;
894
897
  return resolveAdapter(
895
898
  resolveWireProtocolOverride(route.providerName, route.modelId, route.provider),
896
899
  config.cacheRetention,
@@ -941,14 +944,17 @@ export async function handleResponses(
941
944
  // request once per remaining key. OAuth/forward providers and single-key pools return null
942
945
  // immediately, so this stays a no-op for them (src/providers/key-failover.ts).
943
946
  while (upstreamResponse.status === 429 && hasKeyPoolFailover(route.provider)) {
944
- const rotated = rotateKeyOn429(config, route.providerName, upstreamResponse.headers.get("retry-after"), Date.now(), route.provider.apiKey);
947
+ const rotated = rotateProviderTransportOn429(config, route.providerName, {
948
+ retryAfter: upstreamResponse.headers.get("retry-after"),
949
+ now: Date.now(),
950
+ attemptedKey: route.provider.apiKey,
951
+ promptCacheKey: parsed.options.promptCacheKey,
952
+ });
945
953
  if (!rotated) break;
946
954
  // Release the failed response's socket before retrying; unread bodies otherwise linger
947
955
  // until runtime cleanup (one per rotated key under a rate-limit storm).
948
956
  try { void upstreamResponse.body?.cancel().catch(() => {}); } catch { /* already consumed/closed */ }
949
- // Same transport re-resolution as the streaming on429 path: keep conv-id + subscription
950
- // headers on the retried request instead of silently reverting to the raw config provider.
951
- route.provider = resolveProviderTransport(route.providerName, rotated, parsed.options.promptCacheKey);
957
+ route.provider = rotated;
952
958
  const retryAdapter = resolveAdapter(
953
959
  resolveWireProtocolOverride(route.providerName, route.modelId, route.provider),
954
960
  config.cacheRetention,