From 412b18d904d6e156ea4e51f57cc33652dbb9019f Mon Sep 17 00:00:00 2001 From: thisisjun786 <259586770+thisisjun786@users.noreply.github.com> Date: Wed, 9 Sep 2026 23:34:54 +0900 Subject: [PATCH 1/2] fix(catalog): include OpenCodex account-specific models --- .../components/subagent-config/dist/catalog.js | 5 +++-- .../subagent-config/dist/live-catalog.js | 8 ++++++++ .../components/subagent-config/src/catalog.ts | 5 +++-- .../subagent-config/src/live-catalog.ts | 8 ++++++++ .../subagent-config/test/live-catalog.test.ts | 17 +++++++++++++++++ 5 files changed, 39 insertions(+), 4 deletions(-) diff --git a/plugins/codexclaw/components/subagent-config/dist/catalog.js b/plugins/codexclaw/components/subagent-config/dist/catalog.js index bb81a9bf..b8f6795e 100644 --- a/plugins/codexclaw/components/subagent-config/dist/catalog.js +++ b/plugins/codexclaw/components/subagent-config/dist/catalog.js @@ -85,7 +85,7 @@ export function reasoningEfforts(raw ) { }))]; } -export function readNativeCatalog(env = process.env) { +export function readNativeCatalog(env = process.env, accountSelectorsOnly = false) { const path = nativeCatalogPath(env); if (!path || !existsSync(path)) return null; try { @@ -96,10 +96,11 @@ export function readNativeCatalog(env = process.env) return list.flatMap(raw => { const id = entryKey(raw); const row = raw && typeof raw === "object" ? raw : {}; + if (accountSelectorsOnly && (row.opencodex_catalog_kind !== "account-selector-v1" || !id || id.split("/").length !== 2 || id.split("/").some(part => !part))) return []; if (!id?.trim() || seen.has(id) || row.disabled === true || row.visibility === "hide") return []; seen.add(id); const source = isRoutedSlug(id) ? "ocx" : "native"; - return [{ id, source, label: id, reasoningEfforts: reasoningEfforts(row.reasoningEfforts ?? row.supported_reasoning_levels) }]; + return [{ id, source, label: typeof row.display_name === "string" && row.display_name.trim() ? row.display_name : id, reasoningEfforts: reasoningEfforts(row.reasoningEfforts ?? row.supported_reasoning_levels) }]; }); } catch { return null; } } diff --git a/plugins/codexclaw/components/subagent-config/dist/live-catalog.js b/plugins/codexclaw/components/subagent-config/dist/live-catalog.js index a69a08d5..c6ba618f 100644 --- a/plugins/codexclaw/components/subagent-config/dist/live-catalog.js +++ b/plugins/codexclaw/components/subagent-config/dist/live-catalog.js @@ -103,6 +103,14 @@ export async function readCatalog(options = {}) if (native === null) throw new Error("native catalog unavailable"); entries = native; } + if (source === "ocx") { + const seen = new Set(entries.map(entry => entry.id)); + for (const entry of readNativeCatalog(env, true) ?? []) { + if (seen.has(entry.id)) continue; + seen.add(entry.id); + entries.push(entry); + } + } const catalog = { state: source === "ocx" ? "ocx-active" : "native-catalog", entries, status: "fresh", source, fetchedAt: new Date(now()).toISOString() }; if (!persist(path, key, catalog, home)) catalog.message = "Model list loaded; its cache could not be saved."; diff --git a/plugins/codexclaw/components/subagent-config/src/catalog.ts b/plugins/codexclaw/components/subagent-config/src/catalog.ts index 647a1dbd..26f3a18f 100644 --- a/plugins/codexclaw/components/subagent-config/src/catalog.ts +++ b/plugins/codexclaw/components/subagent-config/src/catalog.ts @@ -85,7 +85,7 @@ export function reasoningEfforts(raw: unknown): string[] | null { }))]; } -export function readNativeCatalog(env: NodeJS.ProcessEnv = process.env): CatalogEntry[] | null { +export function readNativeCatalog(env: NodeJS.ProcessEnv = process.env, accountSelectorsOnly = false): CatalogEntry[] | null { const path = nativeCatalogPath(env); if (!path || !existsSync(path)) return null; try { @@ -96,10 +96,11 @@ export function readNativeCatalog(env: NodeJS.ProcessEnv = process.env): Catalog return list.flatMap(raw => { const id = entryKey(raw); const row = raw && typeof raw === "object" ? raw as Record : {}; + if (accountSelectorsOnly && (row.opencodex_catalog_kind !== "account-selector-v1" || !id || id.split("/").length !== 2 || id.split("/").some(part => !part))) return []; if (!id?.trim() || seen.has(id) || row.disabled === true || row.visibility === "hide") return []; seen.add(id); const source: ModelSource = isRoutedSlug(id) ? "ocx" : "native"; - return [{ id, source, label: id, reasoningEfforts: reasoningEfforts(row.reasoningEfforts ?? row.supported_reasoning_levels) }]; + return [{ id, source, label: typeof row.display_name === "string" && row.display_name.trim() ? row.display_name : id, reasoningEfforts: reasoningEfforts(row.reasoningEfforts ?? row.supported_reasoning_levels) }]; }); } catch { return null; } } diff --git a/plugins/codexclaw/components/subagent-config/src/live-catalog.ts b/plugins/codexclaw/components/subagent-config/src/live-catalog.ts index ae62c9a7..a71234d3 100644 --- a/plugins/codexclaw/components/subagent-config/src/live-catalog.ts +++ b/plugins/codexclaw/components/subagent-config/src/live-catalog.ts @@ -103,6 +103,14 @@ export async function readCatalog(options: CatalogOptions = {}): Promise entry.id)); + for (const entry of readNativeCatalog(env, true) ?? []) { + if (seen.has(entry.id)) continue; + seen.add(entry.id); + entries.push(entry); + } + } const catalog: LiveCatalog = { state: source === "ocx" ? "ocx-active" : "native-catalog", entries, status: "fresh", source, fetchedAt: new Date(now()).toISOString() }; if (!persist(path, key, catalog, home)) catalog.message = "Model list loaded; its cache could not be saved."; diff --git a/plugins/codexclaw/components/subagent-config/test/live-catalog.test.ts b/plugins/codexclaw/components/subagent-config/test/live-catalog.test.ts index cc3b0fe3..bf59f0df 100644 --- a/plugins/codexclaw/components/subagent-config/test/live-catalog.test.ts +++ b/plugins/codexclaw/components/subagent-config/test/live-catalog.test.ts @@ -83,3 +83,20 @@ test('real subprocess timeout and oversized stdout are bounded', {skip:process.p const started=Date.now();await assert.rejects(runOcxModels(childEnv)); assert.ok(Date.now()-started<18_000,'timeout failed to stop owned subprocess'); }); + + +test('account selectors merge with live models and disappear after catalog refresh', async t => { + const {env}=fixture(t); + const path=join(env.CODEX_HOME,'models_cache.json'); + const account={slug:'main/gpt-daybreak-blue-latest',display_name:'main / Daybreak Blue',opencodex_catalog_kind:'account-selector-v1',supported_reasoning_levels:[{effort:'high'}]}; + writeFileSync(path,JSON.stringify({models:[account,{...account,slug:'second/gpt-daybreak-blue-latest'},{slug:'stale/general'},{...account,slug:'hidden/daybreak',visibility:'hide'}]})); + const options={env,forceRefresh:true,runOcx:async()=>JSON.stringify([{namespaced:'gpt-daybreak-blue-latest',native:true}])}; + const result=await readCatalog(options); + assert.deepEqual(result.entries.map(e=>e.id),['gpt-daybreak-blue-latest','main/gpt-daybreak-blue-latest','second/gpt-daybreak-blue-latest']); + assert.equal(result.entries[1].label,'main / Daybreak Blue'); + assert.deepEqual(result.entries[1].reasoningEfforts,['high']); + const duplicate=await readCatalog({...options,runOcx:async()=>JSON.stringify([{namespaced:account.slug,native:true}])}); + assert.equal(duplicate.entries.filter(e=>e.id===account.slug).length,1); + writeFileSync(path,JSON.stringify({models:[]})); + assert.deepEqual((await readCatalog(options)).entries.map(e=>e.id),['gpt-daybreak-blue-latest']); +}); From 901a46380d4ebda4329e5444c9f49407eedf3fa2 Mon Sep 17 00:00:00 2001 From: thisisjun786 <259586770+thisisjun786@users.noreply.github.com> Date: Thu, 10 Sep 2026 00:01:13 +0900 Subject: [PATCH 2/2] docs: align test badges with measured catalog suite --- README.ko.md | 2 +- README.md | 2 +- README.zh.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.ko.md b/README.ko.md index 25a8ba02..06b1cbaf 100644 --- a/README.ko.md +++ b/README.ko.md @@ -13,7 +13,7 @@

CI - 2,793 tests passing + 2,794 tests passing 28 skills 24 hooks Documentation diff --git a/README.md b/README.md index e191b096..e7837eb2 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@

CI - 2,793 tests passing + 2,794 tests passing 28 skills 24 hooks Documentation diff --git a/README.zh.md b/README.zh.md index 6a9d63f5..590f651c 100644 --- a/README.zh.md +++ b/README.zh.md @@ -13,7 +13,7 @@

CI - 2,793 tests passing + 2,794 tests passing 28 skills 24 hooks Documentation