@bitkyc08/opencodex 2.6.5 → 2.6.6

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,8 +16,8 @@
16
16
  } catch (e) {}
17
17
  })();
18
18
  </script>
19
- <script type="module" crossorigin src="/assets/index-DcMEJXTd.js"></script>
20
- <link rel="stylesheet" crossorigin href="/assets/index-BwvDb198.css">
19
+ <script type="module" crossorigin src="/assets/index-DiIi6aZT.js"></script>
20
+ <link rel="stylesheet" crossorigin href="/assets/index-CSI5RHdZ.css">
21
21
  </head>
22
22
  <body>
23
23
  <div id="root"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bitkyc08/opencodex",
3
- "version": "2.6.5",
3
+ "version": "2.6.6",
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",
@@ -704,10 +704,11 @@ async function fetchProviderModels(name: string, prov: OcxProviderConfig, ttlMs:
704
704
  */
705
705
  export async function gatherRoutedModels(config: OcxConfig): Promise<CatalogModel[]> {
706
706
  const ttlMs = config.modelCacheTtlMs ?? DEFAULT_MODEL_CACHE_TTL_MS;
707
+ const activeProviders = Object.entries(config.providers).filter(([, prov]) => prov.disabled !== true);
707
708
  const lists = await Promise.all(
708
- Object.entries(config.providers).map(([name, prov]) => fetchProviderModels(name, prov, ttlMs, providerContextCap(config, name))),
709
+ activeProviders.map(([name, prov]) => fetchProviderModels(name, prov, ttlMs, providerContextCap(config, name))),
709
710
  );
710
- const all = augmentRoutedModelsWithJawcodeMetadata(lists.flat(), Object.keys(config.providers), config.providers, config)
711
+ const all = augmentRoutedModelsWithJawcodeMetadata(lists.flat(), activeProviders.map(([name]) => name), config.providers, config)
711
712
  // Drop image/video generation models (e.g. Grok image/video) — they are not usable by Codex and
712
713
  // must not surface in the dashboard, /v1/models, or the routed catalog. Single choke point.
713
714
  .filter(m => !isMediaGenerationModelId(m.id));
package/src/router.ts CHANGED
@@ -112,6 +112,10 @@ function routedProviderConfig(providerName: string, provider: OcxProviderConfig)
112
112
  };
113
113
  }
114
114
 
115
+ function activeProviderEntries(config: OcxConfig): [string, OcxProviderConfig][] {
116
+ return Object.entries(config.providers).filter(([, provider]) => provider.disabled !== true);
117
+ }
118
+
115
119
  export function routeModel(config: OcxConfig, modelId: string): RouteResult {
116
120
  // 0. Explicit "<provider>/<model>" namespace (e.g. "opencode-go/deepseek-v4-pro").
117
121
  // Only triggers when the prefix matches a CONFIGURED provider, so genuine
@@ -122,6 +126,7 @@ export function routeModel(config: OcxConfig, modelId: string): RouteResult {
122
126
  const provName = modelId.slice(0, slash);
123
127
  if (hasOwnProvider(config.providers, provName)) {
124
128
  const prov = config.providers[provName];
129
+ if (prov.disabled === true) throw new Error(`Provider is disabled: ${provName}`);
125
130
  return {
126
131
  providerName: provName,
127
132
  provider: routedProviderConfig(provName, prov),
@@ -130,7 +135,7 @@ export function routeModel(config: OcxConfig, modelId: string): RouteResult {
130
135
  }
131
136
  }
132
137
 
133
- for (const [provName, prov] of Object.entries(config.providers)) {
138
+ for (const [provName, prov] of activeProviderEntries(config)) {
134
139
  if (prov.defaultModel === modelId) {
135
140
  return {
136
141
  providerName: provName,
@@ -140,7 +145,7 @@ export function routeModel(config: OcxConfig, modelId: string): RouteResult {
140
145
  }
141
146
  }
142
147
 
143
- for (const [provName, prov] of Object.entries(config.providers)) {
148
+ for (const [provName, prov] of activeProviderEntries(config)) {
144
149
  if (prov.models && Array.isArray(prov.models) && (prov.models as string[]).includes(modelId)) {
145
150
  return {
146
151
  providerName: provName,
@@ -152,7 +157,7 @@ export function routeModel(config: OcxConfig, modelId: string): RouteResult {
152
157
 
153
158
  for (const [patternKey, prefixes] of Object.entries(MODEL_PROVIDER_PATTERNS)) {
154
159
  if (prefixes.some(prefix => modelId.startsWith(prefix))) {
155
- const matchingProvider = Object.entries(config.providers).find(
160
+ const matchingProvider = activeProviderEntries(config).find(
156
161
  ([name]) => name === patternKey || name.startsWith(patternKey)
157
162
  );
158
163
  if (matchingProvider) {
@@ -168,6 +173,7 @@ export function routeModel(config: OcxConfig, modelId: string): RouteResult {
168
173
 
169
174
  if (hasOwnProvider(config.providers, config.defaultProvider)) {
170
175
  const defaultProv = config.providers[config.defaultProvider];
176
+ if (defaultProv.disabled === true) throw new Error(`Default provider is disabled: ${config.defaultProvider}`);
171
177
  return {
172
178
  providerName: config.defaultProvider,
173
179
  provider: routedProviderConfig(config.defaultProvider, defaultProv),
package/src/server.ts CHANGED
@@ -1469,7 +1469,7 @@ export function corsHeaders(req?: Request, config?: OcxConfig): Record<string, s
1469
1469
  const allowOrigin = origin && req && config && isAllowedRequestOrigin(req, config) ? origin : _corsOrigin;
1470
1470
  return {
1471
1471
  "Access-Control-Allow-Origin": allowOrigin,
1472
- "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
1472
+ "Access-Control-Allow-Methods": "GET, POST, PUT, PATCH, DELETE, OPTIONS",
1473
1473
  "Access-Control-Allow-Headers": "Content-Type, Authorization, X-OpenCodex-API-Key",
1474
1474
  "Vary": "Origin",
1475
1475
  };
@@ -1582,6 +1582,7 @@ export function safeConfigDTO(config: OcxConfig): unknown {
1582
1582
  };
1583
1583
  for (const key of [
1584
1584
  "defaultModel",
1585
+ "disabled",
1585
1586
  "authMode",
1586
1587
  "liveModels",
1587
1588
  "models",
@@ -1722,6 +1723,7 @@ async function handleManagementAPI(req: Request, url: URL, config: OcxConfig): P
1722
1723
  return jsonResponse(Object.entries(config.providers).map(([name, p]) => ({
1723
1724
  name, adapter: p.adapter, baseUrl: publicProviderBaseUrl(p.baseUrl), defaultModel: p.defaultModel,
1724
1725
  hasApiKey: !!p.apiKey,
1726
+ disabled: p.disabled === true,
1725
1727
  })));
1726
1728
  }
1727
1729
 
@@ -1754,6 +1756,22 @@ async function handleManagementAPI(req: Request, url: URL, config: OcxConfig): P
1754
1756
  return jsonResponse({ success: true, name });
1755
1757
  }
1756
1758
 
1759
+ if (url.pathname === "/api/providers" && req.method === "PATCH") {
1760
+ const name = url.searchParams.get("name")?.trim();
1761
+ if (!name || !isValidProviderName(name) || !hasOwnProvider(config.providers, name)) return jsonResponse({ error: "unknown provider" }, 404);
1762
+ let body: { disabled?: unknown };
1763
+ try { body = await req.json(); } catch { return jsonResponse({ error: "invalid JSON body" }, 400); }
1764
+ if (typeof body.disabled !== "boolean") return jsonResponse({ error: "disabled boolean is required" }, 400);
1765
+ if (body.disabled && name === config.defaultProvider) {
1766
+ return jsonResponse({ error: "cannot disable the default provider; set another default first" }, 400);
1767
+ }
1768
+ const { saveConfig: save } = await import("./config");
1769
+ config.providers[name] = { ...config.providers[name], disabled: body.disabled };
1770
+ save(config);
1771
+ await refreshCodexCatalogBestEffort();
1772
+ return jsonResponse({ success: true, name, disabled: body.disabled });
1773
+ }
1774
+
1757
1775
  if (url.pathname === "/api/providers" && req.method === "DELETE") {
1758
1776
  const name = url.searchParams.get("name")?.trim();
1759
1777
  if (!name || !isValidProviderName(name) || !hasOwnProvider(config.providers, name)) return jsonResponse({ error: "unknown provider" }, 404);
package/src/types.ts CHANGED
@@ -267,6 +267,8 @@ export interface OcxWebSearchSidecarConfig {
267
267
  export interface OcxProviderConfig {
268
268
  adapter: string;
269
269
  baseUrl: string;
270
+ /** Keep provider settings on disk but exclude it from routing and model/catalog listings. */
271
+ disabled?: boolean;
270
272
  apiKey?: string;
271
273
  defaultModel?: string;
272
274
  models?: string[];
@@ -36,6 +36,7 @@ function clamp(s: string, max: number): string {
36
36
  /** First configured forward (ChatGPT passthrough) provider — the path with native image input. */
37
37
  function findForwardProvider(config: OcxConfig): OcxProviderConfig | undefined {
38
38
  for (const prov of Object.values(config.providers)) {
39
+ if (prov.disabled === true) continue;
39
40
  if (prov.authMode === "forward") return prov;
40
41
  }
41
42
  return undefined;
@@ -16,6 +16,7 @@ const DEFAULT_TIMEOUT_MS = 30_000;
16
16
  /** First configured forward (ChatGPT passthrough) provider — the only path with server-side web_search. */
17
17
  export function findForwardProvider(config: OcxConfig): OcxProviderConfig | undefined {
18
18
  for (const prov of Object.values(config.providers)) {
19
+ if (prov.disabled === true) continue;
19
20
  if (prov.authMode === "forward") return prov;
20
21
  }
21
22
  return undefined;