Skip to content
Open
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
2 changes: 1 addition & 1 deletion README.ko.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

<p align="center">
<a href="https://github.com/lidge-jun/codexclaw/actions/workflows/ci.yml"><img src="https://github.com/lidge-jun/codexclaw/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
<img src="https://img.shields.io/badge/tests-2%2C793_passing-brightgreen" alt="2,793 tests passing">
<img src="https://img.shields.io/badge/tests-2%2C794_passing-brightgreen" alt="2,794 tests passing">
<img src="https://img.shields.io/badge/skills-29-blue" alt="29 skills">
<img src="https://img.shields.io/badge/hooks-24-blue" alt="24 hooks">
<a href="https://lidge-jun.github.io/codexclaw/"><img src="https://img.shields.io/badge/docs-codexclaw-black" alt="Documentation"></a>
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

<p align="center">
<a href="https://github.com/lidge-jun/codexclaw/actions/workflows/ci.yml"><img src="https://github.com/lidge-jun/codexclaw/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
<img src="https://img.shields.io/badge/tests-2%2C793_passing-brightgreen" alt="2,793 tests passing">
<img src="https://img.shields.io/badge/tests-2%2C794_passing-brightgreen" alt="2,794 tests passing">
<img src="https://img.shields.io/badge/skills-29-blue" alt="29 skills">
<img src="https://img.shields.io/badge/hooks-24-blue" alt="24 hooks">
<a href="https://lidge-jun.github.io/codexclaw/"><img src="https://img.shields.io/badge/docs-codexclaw-black" alt="Documentation"></a>
Expand Down
2 changes: 1 addition & 1 deletion README.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

<p align="center">
<a href="https://github.com/lidge-jun/codexclaw/actions/workflows/ci.yml"><img src="https://github.com/lidge-jun/codexclaw/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
<img src="https://img.shields.io/badge/tests-2%2C793_passing-brightgreen" alt="2,793 tests passing">
<img src="https://img.shields.io/badge/tests-2%2C794_passing-brightgreen" alt="2,794 tests passing">
<img src="https://img.shields.io/badge/skills-29-blue" alt="29 skills">
<img src="https://img.shields.io/badge/hooks-24-blue" alt="24 hooks">
<a href="https://lidge-jun.github.io/codexclaw/"><img src="https://img.shields.io/badge/docs-codexclaw-black" alt="Documentation"></a>
Expand Down
5 changes: 3 additions & 2 deletions plugins/codexclaw/components/subagent-config/dist/catalog.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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; }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.";
Expand Down
5 changes: 3 additions & 2 deletions plugins/codexclaw/components/subagent-config/src/catalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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<string, unknown> : {};
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; }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,14 @@ export async function readCatalog(options: CatalogOptions = {}): Promise<LiveCat
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: 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.";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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']);
});
Loading