Skip to content
Closed
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
5 changes: 5 additions & 0 deletions docs-site/src/content/docs/guides/codex-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,11 @@ If a model is missing from Codex, or the catalog order/visibility looks wrong, c
independently of other providers.
5. **Cache and `ocx sync`** — live catalogs are cached for about five minutes (`modelCacheTtlMs`,
default `300000`). Run `ocx sync` to force a fresh fetch and rewrite the catalog immediately.
6. **Running Codex `app-server`** — rewriting the on-disk catalog is not enough while a long-lived
Codex `app-server` (Desktop / CLI background host) keeps the previous list in memory. `ocx sync`
and `ocx sync-cache` warn when those processes are detected. Restart them with
`ocx sync --restart-codex` (or stop the matching `app-server` processes yourself), then let Codex
recreate them so the new list appears.

:::caution[Other local writers]
Catalog writes (`opencodex-catalog.json`, `config.toml`) are atomic **inside** opencodex, which only
Expand Down
13 changes: 10 additions & 3 deletions docs-site/src/content/docs/reference/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,14 +142,21 @@ opencodex local config only if all restore steps succeeded. `remove` is an alias

## Models & Codex

### `ocx sync`
### `ocx sync [--restart-codex]`

Fetch the live model list from every configured provider and re-inject the merged catalog into Codex.
Run it after adding a provider or to refresh available models.

### `ocx sync-cache`
If long-lived Codex `app-server` processes are still running, `ocx sync` warns that they may keep
serving the previous in-memory model list even though `opencodex-catalog.json` / `models_cache.json`
were updated. Pass `--restart-codex` to send `SIGTERM` only to matching `codex … app-server` and
`codex-code-mode-host` processes owned by the current user (active turns may be interrupted). Broad
`pkill -f codex` matching is intentionally avoided.
Comment on lines +150 to +154

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Make all stale-process guidance reflect the write-status gate.

Stale warnings and restarts occur only after a successful catalog or cache write. No-op syncs do not terminate matching processes.

  • docs-site/src/content/docs/reference/cli.md#L150-L154: state that --restart-codex acts only after a successful write.
  • docs-site/src/content/docs/guides/codex-integration.md#L194-L198: add the same condition to the warning and recovery steps.
  • gui/src/pages/dashboard-overview-sections.tsx#L225-L225: ensure ocx sync --restart-codex performs a write after the API sync, or show a command that reliably rewrites the cache.
📍 Affects 3 files
  • docs-site/src/content/docs/reference/cli.md#L150-L154 (this comment)
  • docs-site/src/content/docs/guides/codex-integration.md#L194-L198
  • gui/src/pages/dashboard-overview-sections.tsx#L225-L225
🤖 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 `@docs-site/src/content/docs/reference/cli.md` around lines 150 - 154, Update
docs-site/src/content/docs/reference/cli.md lines 150-154 to state that
--restart-codex acts only after a successful catalog or cache write. Apply the
same write-status condition to the stale-process warning and recovery steps in
docs-site/src/content/docs/guides/codex-integration.md lines 194-198. In
gui/src/pages/dashboard-overview-sections.tsx line 225, ensure the ocx sync
--restart-codex command performs a write after API sync, or replace it with a
command that reliably rewrites the cache.

Source: Path instructions


Invalidate Codex's local model picker cache so it is rebuilt from the active opencodex catalog.
### `ocx sync-cache [--restart-codex]`

Invalidate Codex's local model picker cache so it is rebuilt from the active opencodex catalog. The
same stale-`app-server` warning and optional `--restart-codex` behavior as `ocx sync` apply.
Comment on lines +145 to +159

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n -C 8 'SIGTERM|process\.platform|win32|app-server|code-mode-host' \
  src/codex/app-server-processes.ts src/cli/index.ts

Repository: lidge-jun/opencodex

Length of output: 25366


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- process discovery implementation ---'
sed -n '232,370p' src/codex/app-server-processes.ts
printf '%s\n' '--- platform-aware matching and restart flow ---'
sed -n '367,515p' src/codex/app-server-processes.ts
printf '%s\n' '--- related tests and documentation references ---'
rg -n -C 4 'listWindowsSnapshots|restartCodexAppServers|afterCatalogWriteHandleAppServers|restart-codex|sync-cache' \
  test tests src docs-site 2>/dev/null || true
printf '%s\n' '--- locale CLI documentation files ---'
find docs-site/src/content/docs -type f -path '*/cli.md' -print 2>/dev/null | sort

Repository: lidge-jun/opencodex

Length of output: 49661


🏁 Script executed:

#!/bin/bash
set -euo pipefail

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

paths = [
    Path("docs-site/src/content/docs/reference/cli.md"),
    Path("docs-site/src/content/docs/ja/reference/cli.md"),
    Path("docs-site/src/content/docs/ko/reference/cli.md"),
    Path("docs-site/src/content/docs/ru/reference/cli.md"),
    Path("docs-site/src/content/docs/zh-cn/reference/cli.md"),
]
for path in paths:
    lines = path.read_text(encoding="utf-8").splitlines()
    starts = [i for i, line in enumerate(lines) if line.startswith("### `ocx sync")]
    print(f"--- {path} ---")
    for start in starts:
        end = next((i for i in range(start + 1, len(lines))
                    if lines[i].startswith("### ")), len(lines))
        block = lines[start:end]
        print(f"lines {start + 1}-{end}:")
        print("\n".join(block))
        print("has_restart_flag=", any("--restart-codex" in line for line in block))
        print("has_stale_warning=", any("app-server" in line or "app-server" in line.lower() for line in block))
PY

printf '%s\n' '--- platform dispatch and signal call ---'
rg -n -C 3 'defaultListSnapshots|platform === "win32"|process\.kill\(pid, signal\)|powershell\.exe|GetOwner' \
  src/codex/app-server-processes.ts

Repository: lidge-jun/opencodex

Length of output: 5886


🌐 Web query:

Node.js process.kill SIGTERM Windows behavior official documentation

💡 Result:

On Windows, POSIX signals are not supported, meaning there is no direct equivalent to the termination behavior triggered by signals on non-Windows platforms [1][2]. However, Node.js provides emulation for specific signals when using process.kill or subprocess.kill [1][2]. When you use process.kill(pid, 'SIGTERM') or subprocess.kill('SIGTERM') on Windows: 1. Forced Termination: The signal is treated as an instruction for unconditional and abrupt termination [1][2]. The process is killed immediately, similar to how it would be handled by 'SIGKILL' [2]. 2. Signal Reporting: Despite the forced nature of the termination, Node.js emulates the behavior for child processes, meaning the subprocess will correctly report that it was terminated by 'SIGTERM' [1][2]. 3. No Graceful Shutdown: Because Windows does not support POSIX signals, a Node.js process on Windows cannot listen for or handle 'SIGTERM' to perform a graceful shutdown [1][3]. While you can register a listener for 'SIGTERM' in your code, it will not be triggered when the process is killed in this manner [1][4]. In summary, 'SIGTERM' on Windows acts as a forced kill rather than a request for graceful exit [1][2]. This behavior applies specifically to 'SIGINT', 'SIGTERM', 'SIGQUIT', and 'SIGKILL', which are the signals supported by the Node.js emulation layer on Windows [2][5].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path

paths = [
    Path("docs-site/src/content/docs/reference/cli.md"),
    Path("docs-site/src/content/docs/ja/reference/cli.md"),
    Path("docs-site/src/content/docs/ko/reference/cli.md"),
    Path("docs-site/src/content/docs/ru/reference/cli.md"),
    Path("docs-site/src/content/docs/zh-cn/reference/cli.md"),
]
for path in paths:
    lines = path.read_text(encoding="utf-8").splitlines()
    starts = [i for i, line in enumerate(lines) if line.startswith("### `ocx sync")]
    print(f"--- {path} ---")
    for start in starts:
        end = next(
            (i for i in range(start + 1, len(lines)) if lines[i].startswith("### ")),
            len(lines),
        )
        block = lines[start:end]
        print(f"lines {start + 1}-{end}:")
        print("\n".join(block))
        print("has_restart_flag=", any("--restart-codex" in line for line in block))
        print("has_stale_warning=", any("app-server" in line.lower() for line in block))
PY

printf '%s\n' '--- platform dispatch and signal call ---'
rg -n -C 3 'defaultListSnapshots|platform === "win32"|process\.kill\(pid, signal\)|powershell\.exe|GetOwner' \
  src/codex/app-server-processes.ts

Repository: lidge-jun/opencodex

Length of output: 5886


Update the translated CLI pages.

src/codex/app-server-processes.ts:304-370 supports current-user process discovery on Windows through PowerShell, so no Unix-only manual fallback is required. However, docs-site/src/content/docs/{ja,ko,ru,zh-cn}/reference/cli.md still omits --restart-codex and the stale app-server warning. Add the documented restart behavior to each locale page.

🤖 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 `@docs-site/src/content/docs/reference/cli.md` around lines 145 - 159, Update
the translated CLI pages under the locale reference documentation for ja, ko,
ru, and zh-cn to match the English `ocx sync` and `ocx sync-cache` entries:
document `--restart-codex`, the stale long-lived `app-server` warning, and that
only matching current-user Codex processes are terminated, including Windows
support. Preserve each page’s existing language and structure.

Source: Path instructions


### `ocx v2 [subcommand]`

Expand Down
2 changes: 1 addition & 1 deletion gui/src/i18n/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ export const de: Record<TKey, string> = {
"dash.syncModels": "Modelle synchronisieren",
"dash.syncing": "Synchronisiere…",
"dash.syncOk": "Synchronisierung abgeschlossen. {count} Modell(e) angehängt.",
"dash.syncStaleHint": "Falls die Codex-App weiterhin eine alte Liste zeigt, starte ihren langlebigen App-Server-Prozess neu.",
"dash.syncStaleHint": "Falls Codex weiterhin eine alte Liste zeigt, starte den langlebigen App-Server neu ({cmd}).",
"dash.syncFailed": "Synchronisierung fehlgeschlagen: {error}",
"dash.projectConfigTitle": "Projekt-Codex-Konfig umgeht OpenCodex",
"dash.projectConfigHint": "Diese repo-lokalen Einstellungen überschreiben den OpenCodex-Proxy (z. B. direkt zu OpenCode Go routen). Entferne sie, damit die Routing aus ~/.codex/config.toml in diesem Projekt greift.",
Expand Down
2 changes: 1 addition & 1 deletion gui/src/i18n/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ export const en = {
"dash.syncModels": "Sync models",
"dash.syncing": "Syncing…",
"dash.syncOk": "Sync complete. {count} model(s) appended.",
"dash.syncStaleHint": "If Codex App still shows an older list, restart its long-lived app-server process.",
"dash.syncStaleHint": "If Codex still shows an older list, restart its long-lived app-server ({cmd}).",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Remove the conflicting “no restart is needed” guidance.

This new message says that a long-lived app-server can retain an old model list. However, dash.models.subtitle at Line 343 still says that “no restart is needed”; the German equivalent at Line 329 says the same.

Update the model-page copy to state that cache invalidation normally avoids a restart, but a stale app-server requires the restart command. Apply the same correction to the other locale files.

As per path instructions, translated locale pages must not contradict the English source.

🤖 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 `@gui/src/i18n/en.ts` at line 202, Update the model-page subtitle translations,
including dash.models.subtitle in English and German and the corresponding keys
in all locale files, to state that cache invalidation normally avoids a restart
but a stale long-lived app-server requires the provided restart command. Ensure
every translated locale matches the corrected English guidance and does not
claim that no restart is ever needed.

Source: Path instructions

"dash.syncFailed": "Sync failed: {error}",
"dash.projectConfigTitle": "Project Codex config bypasses OpenCodex",
"dash.projectConfigHint": "These repo-local settings override the OpenCodex proxy (e.g. route to OpenCode Go directly). Remove them so ~/.codex/config.toml routing applies in that project.",
Expand Down
2 changes: 1 addition & 1 deletion gui/src/i18n/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ export const ja: Record<TKey, string> = {
"dash.syncModels": "モデルを同期",
"dash.syncing": "同期中…",
"dash.syncOk": "同期完了。{count} 個のモデルを追加しました。",
"dash.syncStaleHint": "Codex App がまだ古いリストを表示する場合、長時間稼働の app-server プロセスを再起動してください。",
"dash.syncStaleHint": "Codex がまだ古いリストを表示する場合、長時間稼働の app-server を再起動してください({cmd})。",
"dash.syncFailed": "同期失敗: {error}",
"dash.projectConfigTitle": "プロジェクトの Codex 設定が OpenCodex をバイパスします",
"dash.projectConfigHint": "これらのリポジローカル設定は OpenCodex プロキシを上書きします(例: OpenCode Go に直接ルーティング)。~/.codex/config.toml のルーティングがそのプロジェクトで適用されるように削除してください。",
Expand Down
2 changes: 1 addition & 1 deletion gui/src/i18n/ko.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ export const ko: Record<TKey, string> = {
"dash.syncModels": "모델 동기화",
"dash.syncing": "동기화 중…",
"dash.syncOk": "동기화 완료. {count}개 모델이 추가됐습니다.",
"dash.syncStaleHint": "Codex App에 여전히 예전 목록이 보이면 오래 실행 중인 app-server 프로세스를 재시작하세요.",
"dash.syncStaleHint": "Codex에 여전히 예전 목록이 보이면 오래 실행 중인 app-server를 재시작하세요 ({cmd}).",
"dash.syncFailed": "동기화 실패: {error}",
"dash.projectConfigTitle": "프로젝트 Codex 설정이 OpenCodex를 우회합니다",
"dash.projectConfigHint": "저장소 로컬 설정이 OpenCodex 프록시를 덮어씁니다(예: OpenCode Go로 직접 라우팅). 해당 프로젝트에서 ~/.codex/config.toml 프록시를 쓰려면 제거하세요.",
Expand Down
2 changes: 1 addition & 1 deletion gui/src/i18n/ru.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ export const ru: Record<TKey, string> = {
"dash.syncModels": "Синхронизировать модели",
"dash.syncing": "Синхронизация…",
"dash.syncOk": "Синхронизация завершена. Добавлено моделей: {count}.",
"dash.syncStaleHint": "Если Codex App всё ещё показывает старый список, перезапустите его долгоживущий процесс app-server.",
"dash.syncStaleHint": "Если Codex всё ещё показывает старый список, перезапустите долгоживущий app-server ({cmd}).",
"dash.syncFailed": "Ошибка синхронизации: {error}",
"dash.projectConfigTitle": "Конфигурация Codex в проекте обходит OpenCodex",
"dash.projectConfigHint": "Эти локальные настройки репозитория переопределяют прокси OpenCodex (например, направляют запросы напрямую в OpenCode Go). Удалите их, чтобы в этом проекте действовала маршрутизация из ~/.codex/config.toml.",
Expand Down
2 changes: 1 addition & 1 deletion gui/src/i18n/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ export const zh: Record<TKey, string> = {
"dash.syncModels": "同步模型",
"dash.syncing": "同步中…",
"dash.syncOk": "同步完成。已追加 {count} 个模型。",
"dash.syncStaleHint": "如果 Codex App 仍显示旧列表,请重启长期运行的 app-server 进程。",
"dash.syncStaleHint": "如果 Codex 仍显示旧列表,请重启长期运行的 app-server({cmd})。",
"dash.syncFailed": "同步失败:{error}",
"dash.projectConfigTitle": "项目 Codex 配置绕过了 OpenCodex",
"dash.projectConfigHint": "这些仓库级设置会覆盖 OpenCodex 代理(例如直接走 OpenCode Go)。请移除它们,以便该项目使用 ~/.codex/config.toml 的代理路由。",
Expand Down
3 changes: 2 additions & 1 deletion gui/src/pages/dashboard-overview-sections.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { IconAlert, IconExternal, IconInfo, IconRefresh } from "../icons";
import { Trans } from "../i18n/provider";
import { Select } from "../ui";
import { EFFORT_CAP_LEVELS, requireJson, sidecarBackendForModel, updateJobLabel } from "./dashboard-shared";
import type { useDashboardData } from "./use-dashboard-data";
Expand Down Expand Up @@ -221,7 +222,7 @@ export function DashboardMaintenancePanel({ d }: { d: Dash }) {
<span>
{t("dash.syncOk", { count: syncResult.added })}
{syncResult.warning ? ` ${syncResult.warning}` : ""}
{syncResult.staleAppServerHint ? ` ${t("dash.syncStaleHint")}` : ""}
{syncResult.staleAppServerHint ? <>{" "}<Trans k="dash.syncStaleHint" cmd="ocx sync --restart-codex" /></> : null}
</span>
</div>
)}
Expand Down
23 changes: 19 additions & 4 deletions src/cli/help.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,22 @@ const helpEntries: Record<string, HelpEntry> = {
],
},
ensure: { usage: "ocx ensure", summary: "Ensure the proxy is running and Codex config/cache are current." },
sync: { usage: "ocx sync", summary: "Fetch provider models and inject them into Codex config." },
"sync-cache": { usage: "ocx sync-cache", summary: "Refresh Codex's model cache from the active catalog." },
sync: {
usage: "ocx sync [--restart-codex]",
summary: "Fetch provider models and inject them into Codex config.",
details: [
"After writing the catalog, warns if long-lived Codex app-server processes are still running.",
"--restart-codex sends SIGTERM only to matching app-server / code-mode-host processes (may interrupt active turns).",
],
},
"sync-cache": {
usage: "ocx sync-cache [--restart-codex]",
summary: "Refresh Codex's model cache from the active catalog.",
details: [
"Warns when Codex app-server processes still hold an in-memory model list.",
"--restart-codex sends SIGTERM only to matching app-server / code-mode-host processes (may interrupt active turns).",
],
},
status: { usage: "ocx status", summary: "Check proxy server status." },
doctor: { usage: "ocx doctor", summary: "Diagnose environment/network issues (paths, WSL /mnt, proxy env, ChatGPT reachability)." },
debug: {
Expand Down Expand Up @@ -186,8 +200,9 @@ Usage:
ocx codex-shim <sub> Auto-start proxy when \`codex\` launches (install|status|uninstall|remove)
ocx tray <sub> Windows status tray (install|start|stop|status|uninstall)
ocx ensure Ensure the proxy is running and Codex config/cache are current
ocx sync Fetch models from providers and inject into Codex config
ocx sync-cache Refresh Codex's model cache from the active catalog
ocx sync [--restart-codex] Fetch models from providers and inject into Codex config
ocx sync-cache [--restart-codex]
Refresh Codex's model cache from the active catalog
ocx status Check proxy server status
ocx doctor Diagnose environment/network issues (WSL, proxy, ChatGPT reachability)
ocx debug [provider|usage ...]
Expand Down
43 changes: 16 additions & 27 deletions src/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,21 +94,6 @@ async function waitForProxy(timeoutMs = 8_000): Promise<LiveProxy | null> {
return null;
}

/**
* A Grok fence sync that throws is best-effort by design — it must never block startup.
* Reporting nothing, however, is what lets a STALE fence survive: `~/.grok/config.toml`
* keeps naming whatever port the last successful sync wrote, and once that listener is
* gone every grok turn retries against a refused connection while our own log stays
* silent (2026-07-27 field report: 8 entries pinned to a dead 127.0.0.1:4179).
* So say what failed and name the single command that repairs it.
*/
function grokSyncFailureMessage(err: unknown): string {
const detail = err instanceof Error ? err.message : String(err);
return `Grok Build config sync failed: ${detail}. `
+ "~/.grok/config.toml may still point at a previous proxy port — "
+ "run 'ocx ensure' (or apply from the dashboard's Grok page) to repoint it.";
}

/** Argv for detached `start`, optionally hard-pinning the listen port. */
function startArgv(port?: number): string[] {
const args = [process.argv[1], "start"];
Expand Down Expand Up @@ -314,14 +299,7 @@ async function handleStart(options: { block?: boolean } = {}) {
const r = await syncGrokConfig(port, config, config.hostname ? { hostname: config.hostname } : {});
if (r.changed) console.log(" + Grok Build config updated (~/.grok/config.toml)");
else if (!r.ok) console.error(`⚠️ ${r.message}`);
} catch (err) {
// Best-effort: grok integration must never block startup. But swallowing the error
// silently is how a stale fence survives unnoticed — ~/.grok/config.toml keeps
// pointing at whatever port the LAST successful sync wrote, and if that listener is
// gone every grok turn retries against a refused connection with nothing in our log
// to explain it. Name the failure and the one command that repairs it.
console.error(`⚠️ ${grokSyncFailureMessage(err)}`);
}
} catch { /* best-effort — grok integration must never block startup */ }
if (options.block ?? true) {
setInterval(() => {}, 60_000);
await new Promise<void>(() => {});
Expand Down Expand Up @@ -349,7 +327,7 @@ async function handleEnsure() {
const g = await syncGrokConfig(live.port, config, live.hostname ? { hostname: live.hostname } : {});
if (g.changed) console.log(" + Grok Build config updated (~/.grok/config.toml)");
else if (!g.ok) console.error(`⚠️ ${g.message}`);
} catch (err) { console.error(`⚠️ ${grokSyncFailureMessage(err)}`); }
} catch { /* best-effort */ }
console.log(`✅ Proxy running on port ${live.port}`);
return;
}
Expand All @@ -376,7 +354,7 @@ async function handleEnsure() {
const g = await syncGrokConfig(port, config, config.hostname ? { hostname: config.hostname } : {});
if (g.changed) console.log(" + Grok Build config updated (~/.grok/config.toml)");
else if (!g.ok) console.error(`⚠️ ${g.message}`);
} catch (err) { console.error(`⚠️ ${grokSyncFailureMessage(err)}`); }
} catch { /* best-effort */ }
// Always sync the LIVE port: after a fallback-port start, config.port still names the
// busy preferred port — syncing that would point Codex at a dead listener.
await syncModelsToCodex(port).catch(e => {
Expand Down Expand Up @@ -766,7 +744,13 @@ switch (command) {
break;
}
case "sync": {
await syncModelsToCodex((await findLiveProxy())?.port);
const restartCodex = args.slice(1).includes("--restart-codex");
const syncResult = await syncModelsToCodex((await findLiveProxy())?.port);
// Only warn/restart when a catalog or models_cache write actually happened.
if (syncResult.catalogWritten || syncResult.cacheSynced) {
const { afterCatalogWriteHandleAppServers } = await import("../codex/app-server-processes");
afterCatalogWriteHandleAppServers({ restart: restartCodex, log: console });
}
break;
}
case "v2": {
Expand All @@ -775,8 +759,13 @@ switch (command) {
break;
}
case "sync-cache": {
const restartCodex = args.slice(1).includes("--restart-codex");
const { invalidateCodexModelsCache } = await import("../codex/catalog");
invalidateCodexModelsCache();
// Only warn/restart when models_cache was actually rewritten from a readable catalog.
if (invalidateCodexModelsCache()) {
const { afterCatalogWriteHandleAppServers } = await import("../codex/app-server-processes");
afterCatalogWriteHandleAppServers({ restart: restartCodex, log: console });
}
Comment on lines +762 to +768

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Print the full sync-cache CLI case to check user-facing output and exit codes.
set -euo pipefail

rg -n -A16 'case "sync-cache":' src/cli/index.ts

Repository: lidge-jun/opencodex

Length of output: 1058


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- CLI dispatch and sync-cache context ---'
sed -n '700,790p' src/cli/index.ts
printf '%s\n' '--- sync implementation ---'
sed -n '500,570p' src/codex/catalog/sync.ts
printf '%s\n' '--- command completion/error handling references ---'
rg -n -C4 'sync-cache|process\.exit|exitCode|invalidateCodexModelsCache|afterCatalogWriteHandleAppServers' src/cli src/codex

Repository: lidge-jun/opencodex

Length of output: 42692


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- CLI file outline and entrypoint ---'
ast-grep outline src/cli/index.ts
printf '%s\n' '--- CLI tail and top-level invocation ---'
tail -n 140 src/cli/index.ts
printf '%s\n' '--- all sync-cache references ---'
rg -n -C6 'sync-cache|invalidateCodexModelsCache' .

Repository: lidge-jun/opencodex

Length of output: 46857


Set a non-zero exit code when invalidateCodexModelsCache() returns false.

src/cli/index.ts:761-770 has no success output, but the sync-cache case still exits with status 0 when the catalog is missing, unreadable, or the cache write fails. Print an error and set process.exitCode = 1 before break.

🤖 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/cli/index.ts` around lines 762 - 768, Update the sync-cache flow around
invalidateCodexModelsCache so its false result logs an error and sets
process.exitCode = 1 before break; retain the existing restart handling when it
returns true.

break;
}
case "gui": {
Expand Down
Loading
Loading