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
7 changes: 3 additions & 4 deletions docs-site/src/content/docs/fr/guides/codex-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -385,10 +385,9 @@ d'actualisation `chatgpt` vaut `proactive` et si `tokenGuardian.codexWarmupEnabl

## Restauration de Codex natif

opencodex ne vous enferme jamais dans sa configuration. **`ocx stop` est l'unique commande qui restaure
entièrement Codex natif** : elle arrête le proxy et le service d'arrière-plan s'il est installé, puis supprime
toutes les lignes injectées et toutes les entrées routées du catalogue. La commande `codex` fonctionne alors
exactement comme si opencodex n'avait jamais été installé :
`ocx stop` arrête le proxy et le service d'arrière-plan installé, puis tente de restaurer Codex natif. OpenCodex retire les éléments de routage dont il peut vérifier la propriété et signale une restauration incomplète si les fichiers de configuration ne peuvent pas être récupérés en toute sécurité.

Si la configuration ou le profil actuel diffère de l'original sauvegardé et que le journal ne contient pas le hash de l'état injecté de ce fichier, la récupération automatique conserve les deux fichiers et le journal sans les modifier. Un fichier déjà identique à son original n'est pas réécrit. La réinjection d'une configuration routée refuse aussi cet état incertain ; une configuration native peut créer un nouvel instantané. Voir les [règles de récupération](/guides/codex-integration/#recovery-without-injection-hashes).

```bash
ocx stop # stop the proxy + service, restore native Codex
Expand Down
24 changes: 21 additions & 3 deletions docs-site/src/content/docs/guides/codex-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -605,9 +605,9 @@ off by default; it runs only when Token Guardian is enabled, the `chatgpt` refre

## Restoring native Codex

opencodex never traps you. **`ocx stop` is the single command that fully reverts to native Codex** — it
stops the proxy, stops the background service if one is installed, and strips every injected line and
routed catalog entry so plain `codex` works exactly as if opencodex was never there:
`ocx stop` stops the proxy and any installed background service, then attempts to restore native Codex. OpenCodex removes verified routing artifacts and reports an incomplete restore when it cannot safely recover configuration files.

Recovery may require manual review when the journal cannot verify the current files; see [recovery without injection hashes](#recovery-without-injection-hashes).

```bash
ocx stop # stop the proxy + service, restore native Codex
Expand All @@ -619,6 +619,24 @@ When opencodex runs as a managed [background service](/reference/cli/#ocx-servic
`OCX_SERVICE=1` so a service-driven restart does **not** thrash the Codex config — only an explicit
`ocx stop` / `ocx service stop` restores native Codex.

### Recovery without injection hashes

The journal saves the original `config.toml` and `opencodex.config.toml` plus hashes of the state
OpenCodex injected. A legacy journal or an interruption before those hashes were recorded cannot
prove that later file contents belong to OpenCodex. If either file differs from its saved original
and lacks its own injected-state hash, automatic journal recovery and native restore report failure
without changing either file or the journal. The saved original remains available for comparison;
review it alongside the current files before choosing a manual recovery action.

Files already equal to their saved originals are accepted without rewriting them. A missing file
is distinct from an empty file. Verified injected hashes still allow normal snapshot restoration,
and later edits in hash-backed configurations retain the existing owned-field cleanup behavior.

Sync and `ocx restore back` also reject an existing routed configuration whose hashless journal
does not match the pre-injection baseline. This prevents a new injection hash from being attached
to an older original. A genuinely native configuration can be saved as a fresh baseline before
injection. Explicit external-provider opt-out behavior is unchanged.


### Sub-agent fallback and V2 compatibility

Expand Down
4 changes: 3 additions & 1 deletion docs-site/src/content/docs/ja/guides/codex-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,9 @@ ChatGPT アカウントが Codex アカウント プールに追加されると

## ネイティブ Codexの復元

opencodex は決してあなたを罠にはめることはありません。 **`ocx stop` は、ネイティブ Codex に完全に戻す単一のコマンドです**。プロキシを停止し、バックグラウンド サービスがインストールされている場合はそれを停止し、挿入されたすべての行とルーティングされたカタログ エントリを削除するため、プレーンな `codex` は、opencodex が存在しなかったかのように正確に動作します。
`ocx stop` はプロキシとインストール済みのバックグラウンドサービスを停止し、ネイティブ Codex の復元を試みます。OpenCodex は所有を確認できるルーティング設定を削除し、設定ファイルを安全に復元できない場合は未完了として報告します。

現在の設定またはプロファイルが保存された元の内容と異なり、そのファイルの注入後の状態のハッシュがジャーナルにない場合、自動復元は両方のファイルとジャーナルを変更せずに残します。元の内容と同じファイルは再書き込みしません。ルーティング済み設定への再注入も、この未確認の状態では拒否されます。ネイティブ設定では新しいスナップショットを作成できます。[復元規則](/guides/codex-integration/#recovery-without-injection-hashes)を参照してください。

```bash
ocx stop # stop the proxy + service, restore native Codex
Expand Down
4 changes: 3 additions & 1 deletion docs-site/src/content/docs/ko/guides/codex-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,9 @@ ChatGPT 계정을 Codex account pool에 추가하면, opencodex는 이를 저장

## 네이티브 Codex 복원

opencodex는 절대 사용자를 가두지 않습니다. **`ocx stop`은 네이티브 Codex로 완전히 되돌리는 단일 명령입니다**. proxy를 중지하고, 설치된 background service가 있으면 그것도 중지한 뒤, 주입된 모든 라인과 라우팅된 catalog 항목을 제거해서 plain `codex`가 opencodex가 처음부터 없었던 것처럼 정확히 동작하게 합니다:
`ocx stop`은 proxy와 설치된 background service를 중지한 뒤 네이티브 Codex 복원을 시도합니다. OpenCodex 소유로 확인된 라우팅 항목을 제거하며, 설정 파일을 안전하게 복구할 수 없으면 미완료로 보고합니다.

현재 config 또는 profile이 저장된 원본과 다르고 해당 파일의 주입 상태 해시가 저널에 없으면, 자동 snapshot 복원은 두 파일과 저널을 변경하지 않고 검토용으로 남깁니다. 이미 원본과 같은 파일은 다시 쓰지 않습니다. 기존 라우팅 설정의 재주입도 이 불확실한 원본을 사용하지 않으며, 네이티브 설정에서는 새 snapshot을 만들 수 있습니다. [자세한 복구 규칙](/guides/codex-integration/#recovery-without-injection-hashes)을 참고하세요.

```bash
ocx stop # stop the proxy + service, restore native Codex
Expand Down
4 changes: 4 additions & 0 deletions docs-site/src/content/docs/ko/reference/cli/lifecycle.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ stop/start 대체 동작 없이 안전하게 실패합니다. 소유권을 확
프록시를 중지하지 않고 기본 Codex를 **복원**합니다. 주입된 설정 줄과 라우팅된 카탈로그 항목을
제거하므로 일반 `codex`가 다시 네이티브로 동작합니다. `eject`는 `restore`의 별칭입니다.

저장된 저널에 해당 파일의 주입 상태 해시가 없으면, 변경된 설정 파일을 덮어쓰는 대신 복원 실패를
보고합니다. 현재 파일과 저널은 검토용으로 보존됩니다.
[해시 없는 저널의 복구 규칙](/guides/codex-integration/#recovery-without-injection-hashes)을 참고하세요.

둘 중 어느 표기든 `back`을 붙이면 이미 실행 중인 프록시를 가리키도록 일반 `codex`를 다시
연결하되, 프록시 수명 주기는 바꾸지 않습니다.

Expand Down
4 changes: 4 additions & 0 deletions docs-site/src/content/docs/reference/cli/lifecycle.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ Idempotently ensure a background proxy is running, then sync its live model cata
Restore native Codex **without** stopping the proxy — strips the injected config lines and routed
catalog entries so plain `codex` works natively again. `eject` is an alias of `restore`.

Restoration reports failure instead of replacing changed configuration files when a saved journal
lacks the corresponding injection hashes. The current files and journal remain available for
review; see [recovery without injection hashes](/guides/codex-integration/#recovery-without-injection-hashes).

Pass `back` to either spelling to re-point plain `codex` at an already-running proxy without changing
the proxy lifecycle:

Expand Down
7 changes: 3 additions & 4 deletions docs-site/src/content/docs/ru/guides/codex-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -379,10 +379,9 @@ Responses item'ов (`input: [{ type: "message", ... }]`), ждёт `response.co

## Восстановление нативного Codex

opencodex не запирает вас внутри себя. **`ocx stop` — это единственная команда, которая полностью
возвращает нативный Codex**: она останавливает прокси, останавливает фоновую службу, если она
установлена, и убирает все внедрённые строки и маршрутизируемые записи каталога, так что обычный
`codex` снова работает так, будто opencodex никогда не существовал:
`ocx stop` останавливает прокси и установленную фоновую службу, затем пытается восстановить нативный Codex. OpenCodex удаляет настройки маршрутизации, принадлежность которых может подтвердить, и сообщает о неполном восстановлении, если файлы конфигурации нельзя безопасно восстановить.

Если текущая конфигурация или профиль отличаются от сохранённого оригинала, а журнал не содержит хеша внедрённого состояния этого файла, автоматическое восстановление сохраняет оба файла и журнал без изменений. Файл, уже совпадающий с оригиналом, не перезаписывается. Повторное внедрение в настроенную для прокси конфигурацию также отклоняет такое неподтверждённое состояние; нативная конфигурация может создать новый снимок. См. [правила восстановления](/guides/codex-integration/#recovery-without-injection-hashes).

```bash
ocx stop # stop the proxy + service, restore native Codex
Expand Down
7 changes: 3 additions & 4 deletions docs-site/src/content/docs/tr/guides/codex-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -437,10 +437,9 @@ olduğunda ve `tokenGuardian.codexWarmupEnabled` true olduğunda çalışır.

## Yerel Codex'i geri yükleme

opencodex sizi asla tuzağa düşürmez. **`ocx stop`, yerel Codex'e tamamen geri
dönen tek komuttur** — proxy'yi durdurur, kuruluysa arka plan servisini durdurur
ve enjekte edilen her satırı ve yönlendirilen katalog girdisini kaldırır,
böylece düz `codex` sanki opencodex hiç var olmamış gibi tam olarak çalışır:
`ocx stop`, proxy'yi ve kurulu arka plan servisini durdurur, ardından yerel Codex'i geri yüklemeyi dener. OpenCodex yalnızca sahipliğini doğrulayabildiği yönlendirme öğelerini kaldırır; yapılandırma dosyaları güvenle geri yüklenemiyorsa işlemin tamamlanmadığını bildirir.

Mevcut yapılandırma veya profil kayıtlı özgün içerikten farklıysa ve günlükte o dosyanın enjekte edilmiş durumunun karması yoksa otomatik kurtarma iki dosyayı ve günlüğü değiştirmeden korur. Özgün içerikle zaten aynı olan dosya yeniden yazılmaz. Yönlendirilmiş bir yapılandırmaya yeniden enjeksiyon da bu belirsiz durumu reddeder; yerel yapılandırma yeni bir anlık görüntü oluşturabilir. [Kurtarma kurallarına](/guides/codex-integration/#recovery-without-injection-hashes) bakın.

```bash
ocx stop # proxy'yi + servisi durdurun, yerel Codex'i geri yükleyin
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -321,9 +321,9 @@ fallback 行为,参见 [Sub-agent Surface](/guides/sub-agent-surface/)。

## 恢复原生 Codex

opencodex 绝不会把你困住。**`ocx stop` 是完全恢复原生 Codex 的单一命令** —— 它会停止 proxy、
停止后台服务(如已安装),并剥除所有注入的行和路由的目录条目,使普通的 `codex` 完全像 opencodex
从未存在过一样工作:
`ocx stop` 会停止 proxy 和已安装的后台服务,然后尝试恢复原生 Codex。OpenCodex 只移除能够确认归属的路由配置;如果无法安全恢复配置文件,会报告恢复未完成。

如果当前 config 或 profile 与保存的原始内容不同,且日志缺少该文件注入状态的哈希值,自动快照恢复会保留两个文件和日志,不作修改。已经与原始内容相同的文件不会重写。对已路由配置的再次注入也会拒绝使用这种未确认的基线;原生配置可以建立新的快照。详见[恢复规则](/guides/codex-integration/#recovery-without-injection-hashes)。

```bash
ocx stop # stop the proxy + service, restore native Codex
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -328,9 +328,9 @@ body。背景重新驗證是獨立功能,預設關閉;只有啟用 Token Gua

## 恢復原生 Codex

opencodex 絕不會把你困住。**`ocx stop` 是完整恢復原生 Codex 的單一命令**。它會停止 proxy、停止
背景服務(若已安裝),並移除所有注入行與路由目錄條目,讓普通的 `codex` 就像從未安裝 opencodex 一樣
運作:
`ocx stop` 會停止 proxy 與已安裝的背景服務,然後嘗試恢復原生 Codex。OpenCodex 只移除能確認歸屬的路由設定;若無法安全恢復設定檔,會回報恢復未完成。

若目前的 config 或 profile 與儲存的原始內容不同,且日誌缺少該檔案注入狀態的雜湊值,自動快照恢復會保留兩個檔案及日誌,不做修改。已與原始內容相同的檔案不會重新寫入。對已路由設定再次注入時,也會拒絕使用這種未確認的基準;原生設定可以建立新的快照。詳見[恢復規則](/guides/codex-integration/#recovery-without-injection-hashes)。

```bash
ocx stop # 停止 proxy + service,恢復原生 Codex
Expand Down
40 changes: 29 additions & 11 deletions src/codex/inject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
resolveEffectiveUserIdentity,
} from "./user-identity";
import {
hasUnverifiedJournalBaseline,
markJournalInjectedState,
journaledInjectedOpenaiBaseUrl,
journaledInjectedRealtimeWsBaseUrl,
Expand Down Expand Up @@ -1149,6 +1150,24 @@ export async function injectCodexConfig(
};
}

const journalBaselineIsNative = (): boolean => {
// Value evidence survives an app rewrite that removes the ownership comments.
const journaledBaseUrl = journaledInjectedOpenaiBaseUrl({ readOnly: true });
const journaledRealtimeWsBaseUrl = journaledInjectedRealtimeWsBaseUrl({ readOnly: true });
const looksInjectedByValue =
(journaledBaseUrl !== null && rootTomlString(rawContent, "openai_base_url") === journaledBaseUrl)
|| (journaledRealtimeWsBaseUrl !== null
&& rootTomlString(rawContent, REALTIME_WS_BASE_URL_KEY) === journaledRealtimeWsBaseUrl);
return !hasInjectedCodexRouting(rawContent) && !looksInjectedByValue;
};
const readCurrentProfile = (): string | null => existsSync(CODEX_PROFILE_PATH)
? readFileSync(CODEX_PROFILE_PATH, "utf-8")
: null;
const unverifiedJournalMessage = "Codex configuration was not written: the journal has no verified baseline for the current config/profile. Current files and the journal were preserved.";
if (!journalBaselineIsNative() && hasUnverifiedJournalBaseline(baselineContent, readCurrentProfile())) {
return { success: false, message: unverifiedJournalMessage };
}

if (options.validateOnly) {
return {
success: true,
Expand All @@ -1157,21 +1176,14 @@ export async function injectCodexConfig(
}

const applyNativeArtifacts = (): void => {
// #1798 again: a Codex app rewrite keeps values and drops the ownership comments, so
// marker evidence alone would classify our own routed config as the user's native
// baseline and replace the real original snapshot. Value evidence from the journal
// (the URLs the last injection recorded writing) blocks that misclassification.
const journaledBaseUrl = journaledInjectedOpenaiBaseUrl();
const journaledRealtimeWsBaseUrl = journaledInjectedRealtimeWsBaseUrl();
const looksInjectedByValue =
(journaledBaseUrl !== null && rootTomlString(rawContent, "openai_base_url") === journaledBaseUrl)
|| (journaledRealtimeWsBaseUrl !== null
&& rootTomlString(rawContent, REALTIME_WS_BASE_URL_KEY) === journaledRealtimeWsBaseUrl);
writeJournal({
currentStateIsNative: !hasInjectedCodexRouting(rawContent) && !looksInjectedByValue,
currentStateIsNative: journalBaselineIsNative(),
configContent: baselineContent,
owner: options.journalOwner,
});
// A native snapshot may have been refreshed above. An older hashless routed snapshot
// must not gain the new injection's hash and later overwrite preserved user edits.
if (hasUnverifiedJournalBaseline(baselineContent, readCurrentProfile())) throw new Error(unverifiedJournalMessage);
atomicWriteFile(CODEX_CONFIG_PATH, content);
atomicWriteFile(CODEX_PROFILE_PATH, profileContent);
markJournalInjectedState(content, profileContent, {
Expand Down Expand Up @@ -1733,6 +1745,12 @@ export function skippedRestoreEnvelope(success: boolean, message: string): Codex
function restoreCodexConfigInline(): CodexRestoreConfigResult {
try {
const journal = restoreJournalState();
if (journal.unverified) {
return {
state: "failed", changed: false, action: "failed",
message: "Codex journal recovery was not verified; current configuration files and the journal were preserved.",
};
}
const restored = journal.configRestored
? { success: true, message: "Codex config restored from opencodex journal." }
: removeCodexConfig({ preserveProfile: journal.profileRestored || journal.profileChanged });
Expand Down
Loading
Loading