Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion docs-site/src/content/docs/guides/codex-app-models.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@ opencodex adds separate `<selector>/<native-openai-model>` rows for the mapped a
the bare native rows from the Codex picker. Selector labels are user-chosen public names with no
built-in account-role meaning. Selecting a qualified row uses only its mapped account, does not
change the active Pool account, and fails closed instead of switching accounts when the target is
unavailable. See [Exact Codex account selectors](/reference/configuration/routing/#exact-codex-account-selectors).
unavailable. If Codex's account-scoped catalog contains a visible, API-supported OpenAI-family id
that is not yet in opencodex's static set, the exact id is preserved as a selector-qualified row
for eligible main-account selectors; it is not copied to an unrelated account and is not added to
the bare or API-key model list. The row is matched on the field shape a real catalog row has,
which filters malformed entries — it does not prove the id came from an upstream response, since
the cache is a user-owned file. See [Exact Codex account selectors](/reference/configuration/routing/#exact-codex-account-selectors).

When the `codexAccountNamespaces` map is empty, account-qualified picker rows are off. If
`codexAccountPickerEnabled` is omitted with a non-empty map, they are treated as enabled for
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,13 @@ more than one provider, so use explicit namespaces when a bare model could be am
`codexAccountNamespaces` maps a public selector such as `side` to one stored Codex account. A
request for `side/gpt-5.6-sol` uses only that account, even when the canonical `openai` provider is
in Direct mode, and sends the bare `gpt-5.6-sol` model id upstream. Only bare native OpenAI-family
ids are valid after the selector.
ids are valid after the selector. Account-scoped ids observed in Codex's current model catalog may
also be preserved exactly when they are not yet part of opencodex's static set; the observation must
carry the field shape of a real catalog row, stays qualified to its matching account selector, and
is never promoted into the global bare model list. That shape check filters malformed and minimal
rows — it is not a trust control, because the models cache is a user-owned file and a complete
hand-written row is indistinguishable from an upstream observation. Nothing new becomes routable:
a bare `gpt-*` id under an account selector is accepted by the router regardless of the catalog.

Exact selection bypasses Pool assignment strategy and ordinary thread affinity. If the mapped
account is missing, paused, cooling down, unusable, or requires reauthentication, the request fails
Expand Down
2 changes: 1 addition & 1 deletion src/codex/catalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Public surface preserved exactly; importers keep using "src/codex/catalog".
export { isMediaGenerationModelId, shouldExposeRoutedModel, readCodexCatalogPath, readCatalog, normalizeRoutedCatalogEntry, catalogModelSlug, filterSupportedNativeSlugs, catalogModelSupportsReasoningSummaries } from "./catalog/parsing";
export type { CatalogModel, MultiAgentMode } from "./catalog/parsing";
export { CODEX_NATIVE_ALIAS_CATALOG_KIND, NATIVE_OPENAI_MODELS, configuredNativeAliasSlugs, desktopAllowlistSuppressedNativeSlugs, isNativeAliasCatalogEntry, nativeOpenAiContextWindow, disabledNativeSlugs, visibleNativeSlugs, desktopVisibleNativeSlugs, nativeModelRows, applyNativeVisibility, upstreamNativeEntry, nativeOpenAiSlugs, listCatalogNativeSlugs, nativeInputModalities, nativeReasoningEfforts, nativeDefaultReasoningEffort, shouldIncludeAccountBoundNativeOpenAi, shouldIncludeNativeOpenAi } from "./catalog/metadata";
export { accountBoundNativeOpenAiSlugs, accountBoundNativeOpenAiSlugsBySelector, CODEX_NATIVE_ALIAS_CATALOG_KIND, NATIVE_OPENAI_MODELS, configuredNativeAliasSlugs, desktopAllowlistSuppressedNativeSlugs, isNativeAliasCatalogEntry, nativeOpenAiContextWindow, disabledNativeSlugs, visibleNativeSlugs, desktopVisibleNativeSlugs, nativeModelRows, applyNativeVisibility, observedAccountBoundNativeEntries, observedAccountBoundNativeOpenAiSlugs, upstreamNativeEntry, nativeOpenAiSlugs, listCatalogNativeSlugs, nativeInputModalities, nativeReasoningEfforts, nativeDefaultReasoningEffort, shouldIncludeAccountBoundNativeOpenAi, shouldIncludeNativeOpenAi } from "./catalog/metadata";
export { isSpawnableCodexCandidate, codexExecInvocation, loadBundledCodexCatalog, materializeBundledCodexCatalog, loadCatalogTemplate } from "./catalog/bundled";
export { nativeEffortClamp, shouldApplyNativeEffortClamp, catalogModelEfforts, codexSupportedReasoningEfforts, clampedDefaultEffort, clampEntryToCodexSupportedEfforts, clampCatalogModelsToCodexSupport } from "./catalog/effort";
export { applyProviderConfigHints, isDatedVariantId, filterCatalogVisibleModels, gatherRoutedModels, clearGatherRoutedModelsInflight, augmentRoutedModelsWithRegistryOpenAiApiRows, augmentRoutedModelsWithMetadata, resolveComboCatalogMember, configuredComboTargetModelsByProvider } from "./catalog/provider-fetch";
Expand Down
16 changes: 16 additions & 0 deletions src/codex/catalog/bundled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,22 @@ export function readCurrentCatalogOrCache(): RawCatalog | null {
return readCatalog(path) ?? readCatalog(activeCodexModelsCachePath());
}

/**
* Read the user-owned Codex catalog surfaces without substituting the bundled catalog.
*
* The bundled catalog is intentionally the authority for static native metadata on the default
* path. Account-qualified discovery needs the opposite view: an exact model id that Codex has
* observed in the user's catalog/cache may be account-scoped even when this release does not know
* it statically yet.
*/
export function readCurrentCodexCatalog(): RawCatalog | null {
return readCatalog(readCodexCatalogPath());
}

export function readCurrentCodexModelsCache(): RawCatalog | null {
return readCatalog(activeCodexModelsCachePath());
}

export function loadCatalogTemplate(): RawEntry | null {
const catalogPath = readCodexCatalogPath();
const bundled = loadBundledCodexCatalog();
Expand Down
185 changes: 180 additions & 5 deletions src/codex/catalog/metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { copyFileSync, existsSync, mkdirSync, readFileSync, realpathSync } from
import { delimiter, dirname, join, resolve } from "node:path";
import { atomicWriteFile, expandUserPath, getConfigDir, websocketsEnabled } from "../../config";
import { CODEX_CONFIG_PATH, CODEX_MODELS_CACHE_PATH, DEFAULT_CATALOG_PATH, readRootTomlString, resolveCodexConfigPath } from "../paths";
import { codexAccountNamespaceEntries, isMainCodexAccountTarget } from "../account-namespaces";
import { clearModelCache, DEFAULT_MODEL_CACHE_TTL_MS, getFreshCached, getStaleCached, isModelsFetchCoolingDown, markModelsFetchFailure, setCached } from "../model-cache";
import { buildModelsRequest, resolveModelsAuthToken } from "../../oauth";
import type { OcxConfig, OcxProviderConfig } from "../../types";
Expand Down Expand Up @@ -33,8 +34,8 @@ import upstreamModelsSnapshot from "../data/upstream-models.json";


import type { RawEntry } from "./parsing";
import { readCurrentCatalogOrCache, unique } from "./bundled";
import { trustedAccountBoundNativeCatalogSlug } from "./account-models";
import { readCurrentCatalogOrCache, readCurrentCodexCatalog, readCurrentCodexModelsCache, unique } from "./bundled";
import { trustedAccountBoundNativeCatalogSlug, visibleCodexAccountSelectors } from "./account-models";
import { CODEX_NATIVE_ALIAS_CATALOG_KIND } from "./kinds";
import { NATIVE_OPENAI_MODELS, SUPPORTED_NATIVE_OPENAI_SLUGS } from "./native-models";
export { CODEX_NATIVE_ALIAS_CATALOG_KIND } from "./kinds";
Expand Down Expand Up @@ -194,10 +195,35 @@ export function shouldIncludeNativeOpenAi(config: Pick<OcxConfig, "providers">):
return !hasEnabledProvider || shouldIncludeAccountBoundNativeOpenAi(config);
}

type AccountSelectorConfig = Pick<
OcxConfig,
"codexAccounts" | "codexAccountNamespaces" | "codexAccountPickerEnabled"
>;

function mainAccountSelectors(config: AccountSelectorConfig): string[] {
const targets = new Map(codexAccountNamespaceEntries(config));
return visibleCodexAccountSelectors(config).filter(selector =>
isMainCodexAccountTarget(targets.get(selector) ?? ""));
}

/** Native slugs exposed to Claude Desktop show/export/apply (opt-out via claudeCode.desktopNativeModels). */
export function desktopVisibleNativeSlugs(config: Pick<OcxConfig, "claudeCode" | "disabledModels" | "combos">): string[] {
export function desktopVisibleNativeSlugs(
config: Pick<OcxConfig, "claudeCode" | "disabledModels" | "combos" | "providers"
| "codexAccounts" | "codexAccountNamespaces" | "codexAccountPickerEnabled">,
): string[] {
if (config.claudeCode?.desktopNativeModels === false) return [];
return visibleNativeSlugs(config);
const visible = visibleNativeSlugs(config);
if (!shouldIncludeAccountBoundNativeOpenAi(config)) return visible;
const qualified = [...accountBoundNativeOpenAiSlugsBySelector(config).entries()].flatMap(([selector, slugs]) =>
slugs
.filter(slug => !SUPPORTED_NATIVE_OPENAI_SLUGS.has(slug))
.map(slug => `${selector}/${slug}`),
);
const disabled = new Set(config.disabledModels ?? []);
return unique([
...visible,
...qualified.filter(slug => !disabled.has(slug) && !disabled.has(slug.slice(slug.indexOf("/") + 1))),
]);
}

export function nativeModelRows(config: Pick<OcxConfig, "disabledModels" | "combos" | "providerContextCaps">): Array<{ slug: string; disabled: boolean; contextWindow?: number }> {
Expand All @@ -214,6 +240,7 @@ export function applyNativeVisibility(
entries: RawEntry[],
disabledModels: ReadonlySet<string>,
hideBareNative = false,
observedNativeSlugs: ReadonlySet<string> = new Set(),
): RawEntry[] {
for (const entry of entries) {
if (isNativeAliasCatalogEntry(entry)) continue;
Expand All @@ -222,7 +249,7 @@ export function applyNativeVisibility(
const nativeSlug = accountBoundSlug ?? slug;
if (!nativeSlug
|| (!accountBoundSlug && slug.includes("/"))
|| !SUPPORTED_NATIVE_OPENAI_SLUGS.has(nativeSlug)) continue;
|| (!SUPPORTED_NATIVE_OPENAI_SLUGS.has(nativeSlug) && !observedNativeSlugs.has(nativeSlug))) continue;
const disabled = disabledModels.has(nativeSlug)
|| (accountBoundSlug !== undefined && disabledModels.has(slug));
entry.visibility = disabled || (!accountBoundSlug && hideBareNative)
Expand Down Expand Up @@ -259,6 +286,154 @@ export function nativeOpenAiSlugs(): string[] {
return live.length > 0 ? unique([...live, ...DOCUMENTED_NATIVE_OPENAI_ADDITIONS]) : NATIVE_OPENAI_MODELS;
}

const ACCOUNT_BOUND_OPENAI_NATIVE_PREFIX = /^(?:gpt-|o1-|o3-|o4-)/;
const ACCOUNT_BOUND_OBSERVED_NATIVE_MARKER = "opencodex_account_observed_native";
const ACCOUNT_BOUND_OBSERVED_SELECTORS_MARKER = "opencodex_account_observed_selectors";

function isAccountBoundOpenAiNativeSlug(slug: string): boolean {
return !slug.includes("/") && ACCOUNT_BOUND_OPENAI_NATIVE_PREFIX.test(slug);
}

/**
* Shape/plausibility filter for a candidate account-native row. **This is not a trust control.**
*
* It checks that a row carries the field shape a real Codex catalog row has, which rejects
* malformed and minimal hand-written rows. It cannot distinguish a genuine upstream observation
* from a complete row typed by hand into `$CODEX_HOME/models_cache.json`: there is no signature,
* source identity, or server attestation to check. A full-shape forged row is accepted, and
* `observedFullShapeRowIsAccepted` in tests/native-model-toggle.test.ts pins that so nobody
* later mistakes this predicate for a security boundary.
*
* That is acceptable here because the file is user-owned and written by Codex itself: anyone
* able to rewrite it can already edit `config.json` or run `ocx` directly, and `router.ts`
* accepts any bare `gpt-*` id under an account namespace regardless of this catalog. What the
* filter buys is that garbage rows do not get advertised through discovery — not that an
* advertised row is proven genuine.
*/
function hasNativeCatalogRowShape(entry: RawEntry): boolean {
const levels = entry.supported_reasoning_levels;
const messages = entry.model_messages;
return typeof entry.base_instructions === "string"
&& entry.base_instructions.length > 0
&& (typeof entry.comp_hash === "string" || entry.comp_hash === null)
&& entry.shell_type === "shell_command"
&& Array.isArray(levels)
&& levels.length > 0
&& levels.every(level => typeof level === "object" && level !== null
&& typeof (level as { effort?: unknown }).effort === "string")
&& typeof messages === "object"
&& messages !== null
&& !Array.isArray(messages);
}

function observedAccountBoundNativeSlug(entry: RawEntry): string | undefined {
const accountBound = trustedAccountBoundNativeCatalogSlug(entry);
const slug = accountBound ?? (typeof entry.slug === "string" ? entry.slug : "");
if (!isAccountBoundOpenAiNativeSlug(slug)
|| entry.supported_in_api !== true
|| !hasNativeCatalogRowShape(entry)
|| (entry.visibility !== "list" && entry[ACCOUNT_BOUND_OBSERVED_NATIVE_MARKER] !== true)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep disabled observed models eligible for re-enabling

When a user disables an unknown account-scoped model, convergence writes its generated selector-qualified row with visibility: "hide" but without opencodex_account_observed_native. This condition then rejects that row on the next catalog read, so accountBoundNativeOpenAiSlugsBySelector() forgets the model and /api/model-visibility no longer includes it in supportedNative; an attempt to re-enable the same model therefore returns 400 until Codex happens to observe it upstream again. Treat trusted generated account-bound rows as observations regardless of their current visibility, while still applying the current selector filter, and add a disable/re-enable regression test.

AGENTS.md reference: src/AGENTS.md:L24-L26

Useful? React with 👍 / 👎.

return undefined;
}
return slug;
}

/**
* Return exact, previously observed account-native rows that are not in the static release set.
* The result is used only to carry a hidden observation across startup cache invalidation.
*/
export function observedAccountBoundNativeEntries(
observedEntries: readonly RawEntry[],
): RawEntry[] {
const seen = new Set<string>();
return observedEntries.flatMap(entry => {
const slug = observedAccountBoundNativeSlug(entry);
// Only carry bare upstream observations across cache replacement. Account-qualified rows are
// already a projection of the current selector map and must not preserve private/stale labels.
if (!slug
|| typeof entry.slug !== "string"
|| entry.slug.includes("/")
|| SUPPORTED_NATIVE_OPENAI_SLUGS.has(slug)
|| seen.has(slug)) return [];
seen.add(slug);
return [structuredClone(entry)];
});
}

/**
* Native ids observed in the user's Codex catalog/cache for account-qualified discovery.
*
* Unknown ids are deliberately returned only to callers that build selector-qualified rows. The
* static bare set remains the source of truth for global/API-key discovery, while this preserves
* exact account-scoped ids such as `gpt-daybreak-blue-latest` until the static set catches up.
*/
export function accountBoundNativeOpenAiSlugs(
observedEntries: readonly RawEntry[] = [
...(readCurrentCodexModelsCache()?.models ?? []),
// Existing generated rows are also safe to reuse after a process starts without a cache
// invalidation pass; bare user-authored catalog rows are intentionally not trusted here.
...(readCurrentCodexCatalog()?.models ?? []).filter(entry =>
trustedAccountBoundNativeCatalogSlug(entry) !== undefined),
],
): string[] {
const observed = observedEntries.flatMap(entry => {
const slug = observedAccountBoundNativeSlug(entry);
return slug === undefined ? [] : [slug];
});
return unique([...NATIVE_OPENAI_MODELS, ...observed]);
}

/**
* Resolve account-native ids per public selector. Bare observations come from Codex's main
* catalog/cache, so they are eligible only for selectors that target the main account. A
* generated qualified row carries its own selector and never gets copied to an unrelated pool
* account. An explicit observation marker is public selector metadata only; private account ids
* never enter the catalog or cache.
*/
export function accountBoundNativeOpenAiSlugsBySelector(
config: AccountSelectorConfig,
observedEntries: readonly RawEntry[] = [
...(readCurrentCodexModelsCache()?.models ?? []),
...(readCurrentCodexCatalog()?.models ?? []).filter(entry =>
trustedAccountBoundNativeCatalogSlug(entry) !== undefined),
],
Comment on lines +370 to +399

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 Performance & Scalability | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Find callers that rely on the disk-reading default parameters.
set -euo pipefail

echo "== accountBoundNativeOpenAiSlugsBySelector call sites =="
rg -nP --type=ts -C4 '\baccountBoundNativeOpenAiSlugsBySelector\s*\('

echo "== accountBoundNativeOpenAiSlugs call sites =="
rg -nP --type=ts -C4 '\baccountBoundNativeOpenAiSlugs\s*\('

echo "== observedAccountBoundNativeOpenAiSlugs call sites =="
rg -nP --type=ts -C4 '\bobservedAccountBoundNativeOpenAiSlugs\s*\('

echo "== desktopVisibleNativeSlugs call sites =="
rg -nP --type=ts -C4 '\bdesktopVisibleNativeSlugs\s*\('

echo "== confirm the /v1/models handler encloses the src/server/index.ts calls =="
rg -nP --type=ts -C25 "url\.pathname === \"/v1/models\"" src/server/index.ts

Repository: lidge-jun/opencodex

Length of output: 214


🏁 Script executed:

set -euo pipefail

echo "== matching files =="
fd -t f -e ts . | rg '(^|/)(metadata|parsing|sync|convergence|index|model-rows|model-routes)\.ts$'

echo "== symbol definitions and call sites =="
rg -n -F -e 'accountBoundNativeOpenAiSlugsBySelector' \
  -e 'accountBoundNativeOpenAiSlugs' \
  -e 'observedAccountBoundNativeOpenAiSlugs' \
  -e 'desktopVisibleNativeSlugs' \
  -e 'readCurrentCodexModelsCache' \
  -e 'readCurrentCodexCatalog' \
  --glob '*.ts' .

echo "== metadata structure =="
metadata="$(fd -t f -a 'metadata.ts' . | head -n1)"
test -n "$metadata"
wc -l "$metadata"
sed -n '330,455p' "$metadata"

echo "== server model-list context =="
index="$(fd -t f -a 'index.ts' . | rg '(^|/)src/server/index\.ts$' | head -n1)"
test -n "$index"
rg -n -C35 'v1/models|accountBoundNativeOpenAiSlugsBySelector|desktopVisibleNativeSlugs|observedAccountBoundNativeOpenAiSlugs' "$index"

echo "== management callers =="
for name in model-rows.ts model-routes.ts; do
  file="$(fd -t f -a "$name" . | head -n1)"
  if test -n "$file"; then
    echo "--- $file"
    rg -n -C12 'accountBoundNativeOpenAiSlugs|accountBoundNativeOpenAiSlugsBySelector|observedAccountBoundNativeOpenAiSlugs' "$file" || true
  fi
done

Repository: lidge-jun/opencodex

Length of output: 28406


🏁 Script executed:

set -euo pipefail

echo "== metadata implementation =="
sed -n '190,240p' src/codex/catalog/metadata.ts
sed -n '350,445p' src/codex/catalog/metadata.ts

echo "== bundled readers and parsing =="
sed -n '500,555p' src/codex/catalog/bundled.ts
rg -n -C12 'function readCatalog|export .*readCatalog|readCatalog\(' src/codex/catalog/parsing.ts src/codex/catalog/bundled.ts

echo "== complete /v1/models response branches =="
sed -n '869,1015p' src/server/index.ts

echo "== all relevant direct callers with function context =="
for file in src/codex/catalog/metadata.ts src/server/index.ts src/server/management/model-rows.ts src/server/management/model-routes.ts src/cli/claude-desktop.ts src/server/management/native-integration-routes.ts src/server/management/agent-settings-routes.ts src/server/management/shared.ts; do
  echo "--- $file"
  rg -n -C8 'accountBoundNativeOpenAiSlugsBySelector|accountBoundNativeOpenAiSlugs\(|observedAccountBoundNativeOpenAiSlugs|desktopVisibleNativeSlugs\(' "$file" || true
done

echo "== catalog/cache file size and reader caching =="
rg -n -C8 'CODEX.*CATALOG|MODELS.*CACHE|catalogPath|modelsCache|readFileSync|existsSync|JSON.parse' src/codex/catalog/bundled.ts src/codex/catalog/parsing.ts
find . -type f \( -name 'model_catalog.json' -o -name '*models*cache*.json' \) -printf '%s %p\n' 2>/dev/null | head -20

Repository: lidge-jun/opencodex

Length of output: 50375


🏁 Script executed:

set -euo pipefail

echo "== account-bound inclusion condition =="
rg -n -C12 'function shouldIncludeAccountBoundNativeOpenAi|export function shouldIncludeAccountBoundNativeOpenAi|shouldIncludeAccountBoundNativeOpenAi' src/codex/catalog/metadata.ts

echo "== concise line-numbered target regions =="
nl -ba src/codex/catalog/metadata.ts | sed -n '208,228p;366,438p'
nl -ba src/server/index.ts | sed -n '869,947p'
nl -ba src/server/management/model-rows.ts | sed -n '45,68p'
nl -ba src/server/management/model-routes.ts | sed -n '232,245p'

echo "== read path resolution =="
rg -n -C10 'function readCodexCatalogPath|export function readCodexCatalogPath|function activeCodexModelsCachePath|export function activeCodexModelsCachePath' src/codex

echo "== deterministic static verifier for default-reader call paths =="
python3 - <<'PY'
from pathlib import Path
import re

metadata = Path("src/codex/catalog/metadata.ts").read_text()
for name in ("accountBoundNativeOpenAiSlugs", "accountBoundNativeOpenAiSlugsBySelector"):
    m = re.search(rf"export function {name}\s*\\([^)]*\\)\\s*(?::[^{{]+)?\\{{", metadata, re.S)
    assert m, name
    body = metadata[m.start():]
    body = body[:body.find("\nexport function ", 1)] if "\nexport function " in body[1:] else body
    print(name, "default disk-reader expressions:", body.count("readCurrentCodexModelsCache()"), body.count("readCurrentCodexCatalog()"))

index = Path("src/server/index.ts").read_text()
for pattern in (
    r"accountBoundNativeOpenAiSlugsBySelector\\(config\\)",
    r"desktopVisibleNativeSlugs\\(config\\)",
):
    print(pattern, "matches:", len(re.findall(pattern, index)))

print("desktop call is inside Anthropic branch:",
      index.index("desktopVisibleNativeSlugs(config)") > index.index("if (wantsAnthropicList")
      and index.index("desktopVisibleNativeSlugs(config)") < index.index("if (url.searchParams.has(\"client_version\""))
)
PY

Repository: lidge-jun/opencodex

Length of output: 3512


🏁 Script executed:

set -euo pipefail

echo "== concise line-numbered target regions =="
sed -n '208,228p' src/codex/catalog/metadata.ts | awk '{printf "%d %s\n", NR+207, $0}'
sed -n '366,438p' src/codex/catalog/metadata.ts | awk '{printf "%d %s\n", NR+365, $0}'
sed -n '869,947p' src/server/index.ts | awk '{printf "%d %s\n", NR+868, $0}'
sed -n '45,68p' src/server/management/model-rows.ts | awk '{printf "%d %s\n", NR+44, $0}'
sed -n '232,245p' src/server/management/model-routes.ts | awk '{printf "%d %s\n", NR+231, $0}'

echo "== read path resolution =="
rg -n -C10 'function readCodexCatalogPath|export function readCodexCatalogPath|function activeCodexModelsCachePath|export function activeCodexModelsCachePath' src/codex || true

echo "== deterministic static verifier for default-reader call paths =="
python3 - <<'PY'
from pathlib import Path
import re

metadata = Path("src/codex/catalog/metadata.ts").read_text()
for name in ("accountBoundNativeOpenAiSlugs", "accountBoundNativeOpenAiSlugsBySelector"):
    m = re.search(rf"export function {name}\s*\([^)]*\)\s*(?::[^{]+)?\{{", metadata, re.S)
    assert m, name
    tail = metadata[m.start():]
    next_export = tail.find("\nexport function ", 1)
    body = tail if next_export < 0 else tail[:next_export]
    print(name, "readCurrentCodexModelsCache calls:", body.count("readCurrentCodexModelsCache()"),
          "readCurrentCodexCatalog calls:", body.count("readCurrentCodexCatalog()"))

index = Path("src/server/index.ts").read_text()
for expression in (
    "accountBoundNativeOpenAiSlugsBySelector(config)",
    "desktopVisibleNativeSlugs(config)",
):
    print(expression, "matches:", index.count(expression))

start = index.index("if (wantsAnthropicList")
end = index.index('if (url.searchParams.has("client_version")', start)
print("desktop call is inside Anthropic branch:", "desktopVisibleNativeSlugs(config)" in index[start:end])
PY

Repository: lidge-jun/opencodex

Length of output: 16779


🏁 Script executed:

set -euo pipefail

python3 - <<'PY'
from pathlib import Path
import re

metadata = Path("src/codex/catalog/metadata.ts").read_text()
for name in ("accountBoundNativeOpenAiSlugs", "accountBoundNativeOpenAiSlugsBySelector"):
    pattern = r"export function " + name + r"\s*\([^)]*\)\s*(?::[^{]+)?\{"
    m = re.search(pattern, metadata, re.S)
    assert m, name
    tail = metadata[m.start():]
    next_export = tail.find("\nexport function ", 1)
    body = tail if next_export < 0 else tail[:next_export]
    print(name, "readCurrentCodexModelsCache calls:", body.count("readCurrentCodexModelsCache()"),
          "readCurrentCodexCatalog calls:", body.count("readCurrentCodexCatalog()"))

index = Path("src/server/index.ts").read_text()
print("accountBoundNativeOpenAiSlugsBySelector(config) matches:",
      index.count("accountBoundNativeOpenAiSlugsBySelector(config)"))
print("desktopVisibleNativeSlugs(config) matches:",
      index.count("desktopVisibleNativeSlugs(config)"))

start = index.index("if (wantsAnthropicList")
end = index.index('if (url.searchParams.has("client_version")', start)
print("desktop call is inside Anthropic branch:",
      "desktopVisibleNativeSlugs(config)" in index[start:end])
PY

Repository: lidge-jun/opencodex

Length of output: 276


Reuse one observed-entry snapshot per discovery request. accountBoundNativeOpenAiSlugsBySelector defaults to synchronous catalog and cache reads at src/codex/catalog/metadata.ts:393-400. The /v1/models handler invokes this default at src/server/index.ts:904. Anthropic discovery invokes desktopVisibleNativeSlugs(config) twice at lines 925 and 942, and that function invokes the same default at metadata.ts:217. When both files exist, one Anthropic discovery request can therefore perform six JSON parses and block Bun's event loop. Read both sources once after fetchAllModels, pass the snapshot through accountBoundNativeOpenAiSlugsBySelector and desktopVisibleNativeSlugs, and reuse the computed desktop list. Apply the same snapshot pattern to src/server/management/model-rows.ts:57 and src/server/management/model-routes.ts:238.

🧰 Tools
🪛 ast-grep (0.45.1)

[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFileSync } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/codex/catalog/metadata.ts` around lines 370 - 399, Reuse a single
catalog/cache observed-entry snapshot per discovery request instead of
triggering repeated synchronous reads. After fetchAllModels, read both sources
once and pass the snapshot into accountBoundNativeOpenAiSlugsBySelector and
desktopVisibleNativeSlugs, caching the computed desktop list for both Anthropic
checks; apply the same snapshot propagation in model-rows and model-routes.

Source: Path instructions

): ReadonlyMap<string, readonly string[]> {
const selectors = visibleCodexAccountSelectors(config);
const mainSelectors = new Set(mainAccountSelectors(config));
const result = new Map<string, Set<string>>(
selectors.map(selector => [selector, new Set(NATIVE_OPENAI_MODELS)]),
);
for (const entry of observedEntries) {
const slug = observedAccountBoundNativeSlug(entry);
if (slug === undefined || SUPPORTED_NATIVE_OPENAI_SLUGS.has(slug)) continue;
const generated = trustedAccountBoundNativeCatalogSlug(entry);
const generatedSelector = generated === undefined || typeof entry.slug !== "string"
? undefined
: entry.slug.slice(0, entry.slug.indexOf("/"));
const markedSelectors = Array.isArray(entry[ACCOUNT_BOUND_OBSERVED_SELECTORS_MARKER])
? entry[ACCOUNT_BOUND_OBSERVED_SELECTORS_MARKER].filter((value): value is string => typeof value === "string")
: [];
const eligible = generatedSelector !== undefined
? (mainSelectors.has(generatedSelector) ? [generatedSelector] : [])
: markedSelectors.length > 0
? markedSelectors.filter(selector => mainSelectors.has(selector))
: [...mainSelectors];
Comment on lines +416 to +420

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Rebind observations when a main-account selector changes

When a user renames a selector that maps to @main, previously preserved unknown models disappear: after the first convergence the only evidence is either a generated row carrying the old selector or a hidden observation whose marker lists the old selector, and both branches here reject the model because the old name is no longer in mainSelectors. Subsequent catalog and /v1/models generation therefore omit the model permanently even though the new selector targets the same account. Associate observations with the account target rather than the transient public label, or make every current main-account selector eligible for main-account observations.

Useful? React with 👍 / 👎.

for (const selector of eligible) {
const rows = result.get(selector);
if (rows) rows.add(slug);
}
}
return new Map([...result.entries()].map(([selector, slugs]) => [selector, [...slugs]]));
}

/** Unknown native ids observed from Codex, excluding the static release set. */
export function observedAccountBoundNativeOpenAiSlugs(
observedEntries?: readonly RawEntry[],
): string[] {
const all = accountBoundNativeOpenAiSlugs(observedEntries);
return all.filter(slug => !SUPPORTED_NATIVE_OPENAI_SLUGS.has(slug));
}

function catalogNativeSlugs(): string[] {
const cat = readCurrentCatalogOrCache();
const models = cat?.models ?? [];
Expand Down
Loading
Loading