Skip to content

Commit ca7b104

Browse files
authored
fix(codex): warn about stale Codex app-servers after a catalog write (#576)
A long-lived Codex app-server keeps serving its in-memory model list, so ocx sync could write a correct catalog and Codex would still show the old models (#476). Restarting the proxy or re-running sync did not help; only killing the app-server did, which is why rebooting appeared to fix it. Detect those processes and say so, gated on the catalogWritten/cacheSynced signal from the first half of this change so a no-op sync stays quiet. ocx sync --restart-codex and ocx sync-cache --restart-codex opt into a SIGTERM, never SIGKILL. Process matching is the risky part and is deliberately narrow: UID-scoped on Unix, exact cmdline match rather than a broad *codex* sweep, and it understands quoted paths and value-taking globals so it cannot mistake a neighbouring process for an app-server.
1 parent c48a6ee commit ca7b104

15 files changed

Lines changed: 1161 additions & 17 deletions

File tree

docs-site/src/content/docs/guides/codex-integration.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,11 @@ If a model is missing from Codex, or the catalog order/visibility looks wrong, c
245245
independently of other providers.
246246
5. **Cache and `ocx sync`** — live catalogs are cached for about five minutes (`modelCacheTtlMs`,
247247
default `300000`). Run `ocx sync` to force a fresh fetch and rewrite the catalog immediately.
248+
6. **Running Codex `app-server`** — rewriting the on-disk catalog is not enough while a long-lived
249+
Codex `app-server` (Desktop / CLI background host) keeps the previous list in memory. `ocx sync`
250+
and `ocx sync-cache` warn when those processes are detected. Restart them with
251+
`ocx sync --restart-codex` (or stop the matching `app-server` processes yourself), then let Codex
252+
recreate them so the new list appears.
248253

249254
:::caution[Other local writers]
250255
Catalog writes (`opencodex-catalog.json`, `config.toml`) are atomic **inside** opencodex, which only

docs-site/src/content/docs/reference/cli.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,14 +142,21 @@ opencodex local config only if all restore steps succeeded. `remove` is an alias
142142

143143
## Models & Codex
144144

145-
### `ocx sync`
145+
### `ocx sync [--restart-codex]`
146146

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

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

152-
Invalidate Codex's local model picker cache so it is rebuilt from the active opencodex catalog.
156+
### `ocx sync-cache [--restart-codex]`
157+
158+
Invalidate Codex's local model picker cache so it is rebuilt from the active opencodex catalog. The
159+
same stale-`app-server` warning and optional `--restart-codex` behavior as `ocx sync` apply.
153160

154161
### `ocx v2 [subcommand]`
155162

gui/src/i18n/de.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ export const de: Record<TKey, string> = {
202202
"dash.syncModels": "Modelle synchronisieren",
203203
"dash.syncing": "Synchronisiere…",
204204
"dash.syncOk": "Synchronisierung abgeschlossen. {count} Modell(e) angehängt.",
205-
"dash.syncStaleHint": "Falls die Codex-App weiterhin eine alte Liste zeigt, starte ihren langlebigen App-Server-Prozess neu.",
205+
"dash.syncStaleHint": "Falls Codex weiterhin eine alte Liste zeigt, starte den langlebigen App-Server neu ({cmd}).",
206206
"dash.syncFailed": "Synchronisierung fehlgeschlagen: {error}",
207207
"dash.projectConfigTitle": "Projekt-Codex-Konfig umgeht OpenCodex",
208208
"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.",

gui/src/i18n/en.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ export const en = {
212212
"dash.syncModels": "Sync models",
213213
"dash.syncing": "Syncing…",
214214
"dash.syncOk": "Sync complete. {count} model(s) appended.",
215-
"dash.syncStaleHint": "If Codex App still shows an older list, restart its long-lived app-server process.",
215+
"dash.syncStaleHint": "If Codex still shows an older list, restart its long-lived app-server ({cmd}).",
216216
"dash.syncFailed": "Sync failed: {error}",
217217
"dash.projectConfigTitle": "Project Codex config bypasses OpenCodex",
218218
"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.",

gui/src/i18n/ja.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ export const ja: Record<TKey, string> = {
211211
"dash.syncModels": "モデルを同期",
212212
"dash.syncing": "同期中…",
213213
"dash.syncOk": "同期完了。{count} 個のモデルを追加しました。",
214-
"dash.syncStaleHint": "Codex App がまだ古いリストを表示する場合、長時間稼働の app-server プロセスを再起動してください。",
214+
"dash.syncStaleHint": "Codex がまだ古いリストを表示する場合、長時間稼働の app-server を再起動してください({cmd})。",
215215
"dash.syncFailed": "同期失敗: {error}",
216216
"dash.projectConfigTitle": "プロジェクトの Codex 設定が OpenCodex をバイパスします",
217217
"dash.projectConfigHint": "これらのリポジローカル設定は OpenCodex プロキシを上書きします(例: OpenCode Go に直接ルーティング)。~/.codex/config.toml のルーティングがそのプロジェクトで適用されるように削除してください。",

gui/src/i18n/ko.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ export const ko: Record<TKey, string> = {
206206
"dash.syncModels": "모델 동기화",
207207
"dash.syncing": "동기화 중…",
208208
"dash.syncOk": "동기화 완료. {count}개 모델이 추가됐습니다.",
209-
"dash.syncStaleHint": "Codex App에 여전히 예전 목록이 보이면 오래 실행 중인 app-server 프로세스를 재시작하세요.",
209+
"dash.syncStaleHint": "Codex에 여전히 예전 목록이 보이면 오래 실행 중인 app-server를 재시작하세요 ({cmd}).",
210210
"dash.syncFailed": "동기화 실패: {error}",
211211
"dash.projectConfigTitle": "프로젝트 Codex 설정이 OpenCodex를 우회합니다",
212212
"dash.projectConfigHint": "저장소 로컬 설정이 OpenCodex 프록시를 덮어씁니다(예: OpenCode Go로 직접 라우팅). 해당 프로젝트에서 ~/.codex/config.toml 프록시를 쓰려면 제거하세요.",

gui/src/i18n/ru.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ export const ru: Record<TKey, string> = {
211211
"dash.syncModels": "Синхронизировать модели",
212212
"dash.syncing": "Синхронизация…",
213213
"dash.syncOk": "Синхронизация завершена. Добавлено моделей: {count}.",
214-
"dash.syncStaleHint": "Если Codex App всё ещё показывает старый список, перезапустите его долгоживущий процесс app-server.",
214+
"dash.syncStaleHint": "Если Codex всё ещё показывает старый список, перезапустите долгоживущий app-server ({cmd}).",
215215
"dash.syncFailed": "Ошибка синхронизации: {error}",
216216
"dash.projectConfigTitle": "Конфигурация Codex в проекте обходит OpenCodex",
217217
"dash.projectConfigHint": "Эти локальные настройки репозитория переопределяют прокси OpenCodex (например, направляют запросы напрямую в OpenCode Go). Удалите их, чтобы в этом проекте действовала маршрутизация из ~/.codex/config.toml.",

gui/src/i18n/zh.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ export const zh: Record<TKey, string> = {
206206
"dash.syncModels": "同步模型",
207207
"dash.syncing": "同步中…",
208208
"dash.syncOk": "同步完成。已追加 {count} 个模型。",
209-
"dash.syncStaleHint": "如果 Codex App 仍显示旧列表,请重启长期运行的 app-server 进程。",
209+
"dash.syncStaleHint": "如果 Codex 仍显示旧列表,请重启长期运行的 app-server({cmd})。",
210210
"dash.syncFailed": "同步失败:{error}",
211211
"dash.projectConfigTitle": "项目 Codex 配置绕过了 OpenCodex",
212212
"dash.projectConfigHint": "这些仓库级设置会覆盖 OpenCodex 代理(例如直接走 OpenCode Go)。请移除它们,以便该项目使用 ~/.codex/config.toml 的代理路由。",

gui/src/pages/dashboard-overview-sections.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { IconAlert, IconExternal, IconInfo, IconRefresh } from "../icons";
2+
import { Trans } from "../i18n/provider";
23
import { Select } from "../ui";
34
import { EFFORT_CAP_LEVELS, requireJson, sidecarBackendForModel, updateJobLabel } from "./dashboard-shared";
45
import type { useDashboardData } from "./use-dashboard-data";
@@ -195,7 +196,7 @@ export function DashboardMaintenancePanel({ d }: { d: Dash }) {
195196
{t("dash.syncOk", { count: syncResult.added })}
196197
{syncResult.warning ? ` ${syncResult.warning}` : ""}
197198
{syncResult.nativeSubagentDefaultsWarning ? ` ${syncResult.nativeSubagentDefaultsWarning}` : ""}
198-
{syncResult.staleAppServerHint ? ` ${t("dash.syncStaleHint")}` : ""}
199+
{syncResult.staleAppServerHint ? <>{" "}<Trans k="dash.syncStaleHint" cmd="ocx sync --restart-codex" /></> : null}
199200
</span>
200201
</div>
201202
)}

src/cli/help.ts

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,22 @@ const helpEntries: Record<string, HelpEntry> = {
6060
],
6161
},
6262
ensure: { usage: "ocx ensure", summary: "Ensure the proxy is running and Codex config/cache are current." },
63-
sync: { usage: "ocx sync", summary: "Fetch provider models and inject them into Codex config." },
64-
"sync-cache": { usage: "ocx sync-cache", summary: "Refresh Codex's model cache from the active catalog." },
63+
sync: {
64+
usage: "ocx sync [--restart-codex]",
65+
summary: "Fetch provider models and inject them into Codex config.",
66+
details: [
67+
"After writing the catalog, warns if long-lived Codex app-server processes are still running.",
68+
"--restart-codex sends SIGTERM only to matching app-server / code-mode-host processes (may interrupt active turns).",
69+
],
70+
},
71+
"sync-cache": {
72+
usage: "ocx sync-cache [--restart-codex]",
73+
summary: "Refresh Codex's model cache from the active catalog.",
74+
details: [
75+
"Warns when Codex app-server processes still hold an in-memory model list.",
76+
"--restart-codex sends SIGTERM only to matching app-server / code-mode-host processes (may interrupt active turns).",
77+
],
78+
},
6579
status: { usage: "ocx status", summary: "Check proxy server status." },
6680
doctor: { usage: "ocx doctor", summary: "Diagnose environment/network issues (paths, WSL /mnt, proxy env, ChatGPT reachability)." },
6781
debug: {
@@ -246,8 +260,9 @@ Usage:
246260
ocx codex-shim <sub> Auto-start proxy when \`codex\` launches (install|status|uninstall|remove)
247261
ocx tray <sub> Windows status tray (install|start|stop|status|uninstall)
248262
ocx ensure Ensure the proxy is running and Codex config/cache are current
249-
ocx sync Fetch models from providers and inject into Codex config
250-
ocx sync-cache Refresh Codex's model cache from the active catalog
263+
ocx sync [--restart-codex] Fetch models from providers and inject into Codex config
264+
ocx sync-cache [--restart-codex]
265+
Refresh Codex's model cache from the active catalog
251266
ocx status Check proxy server status
252267
ocx doctor Diagnose environment/network issues (WSL, proxy, ChatGPT reachability)
253268
ocx debug <scope> provider/usage/injection/claude on|off|status|reset

0 commit comments

Comments
 (0)