Skip to content

Commit 82e5bef

Browse files
committed
fix: require confirmation before ChatGPT direct uninstall
1 parent f38e55f commit 82e5bef

7 files changed

Lines changed: 219 additions & 35 deletions

File tree

.codex/UNINSTALL.md

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,27 +29,37 @@ $uninstallJson = $null
2929
$uninstallResult = $null
3030
$status = $null
3131
$deferred = $false
32+
$confirmationRequired = $false
3233
3334
if (Test-Path $repoRoot) {
3435
$env:PYTHONPATH = Join-Path $repoRoot 'src'
3536
$statusJson = python -m codex_fast_proxy status
3637
$status = $statusJson | ConvertFrom-Json
3738
if ($status.config_matches -eq $true) {
3839
$uninstallJson = python -m codex_fast_proxy uninstall --defer-stop
40+
$uninstallResult = $uninstallJson | ConvertFrom-Json
3941
$uninstallJson
40-
Write-Host 'restart_required_before_cleanup=true'
41-
$deferred = $true
42+
if ($uninstallResult.status -eq 'confirmation_required') {
43+
Write-Host 'uninstall_confirmation_required=true'
44+
$confirmationRequired = $true
45+
} else {
46+
Write-Host 'restart_required_before_cleanup=true'
47+
$deferred = $true
48+
}
4249
} else {
4350
$uninstallJson = python -m codex_fast_proxy uninstall
4451
$uninstallResult = $uninstallJson | ConvertFrom-Json
45-
if ($uninstallResult.config_restore -eq 'skipped_config_changed') {
46-
$uninstallJson
52+
$uninstallJson
53+
if ($uninstallResult.status -eq 'confirmation_required') {
54+
Write-Host 'uninstall_confirmation_required=true'
55+
$confirmationRequired = $true
56+
} elseif ($uninstallResult.config_restore -eq 'skipped_config_changed') {
4757
throw 'Codex config changed after proxy install, and the selected provider no longer points to the recorded proxy. The proxy was not stopped and files were not removed. Review ~/.codex/config.toml or rerun uninstall with --force if you want to restore the recorded backup.'
4858
}
4959
}
5060
}
5161
52-
if (-not $deferred) {
62+
if ((-not $deferred) -and (-not $confirmationRequired)) {
5363
python -m pip uninstall -y codex-fast-proxy
5464
5565
if (Test-Path $skillNamespace) {
@@ -59,21 +69,23 @@ if (-not $deferred) {
5969
if (Test-Path $repoRoot) {
6070
Remove-Item -LiteralPath $repoRoot -Recurse -Force
6171
}
62-
63-
if ($uninstallJson) {
64-
$uninstallJson
65-
}
6672
}
6773
```
6874

6975
Report the uninstall JSON result when available.
7076

71-
If the uninstall JSON includes `direct_upstream_auth_warning`, report it before telling the user to
72-
restart Codex. This means Codex config has been restored to the direct third-party upstream, but the
73-
current Codex auth state still looks like ChatGPT account login. Direct upstream mode no longer has
74-
the proxy auth override, so model requests may send ChatGPT auth to the third-party provider and
75-
fail with 401. Tell the user to switch Codex App back to API-key/third-party provider auth before
76-
restarting, or keep the proxy enabled if they want ChatGPT-login UI with a third-party provider.
77+
If the block printed `uninstall_confirmation_required=true`, no uninstall changes were applied.
78+
Report `direct_upstream_auth_warning`, then ask the user whether they want to keep the proxy enabled
79+
or explicitly continue uninstalling. Continue only after the user clearly accepts the ChatGPT-login
80+
direct-upstream 401 risk; then rerun the manager with `--confirm-chatgpt-direct-uninstall`.
81+
82+
If the uninstall JSON has `status="uninstalled"` and includes `direct_upstream_auth_warning`, report
83+
it before telling the user to restart Codex. This means Codex config has been restored to the direct
84+
third-party upstream, but the current Codex auth state still looks like ChatGPT account login. Direct
85+
upstream mode no longer has the proxy auth override, so model requests may send ChatGPT auth to the
86+
third-party provider and fail with 401. Tell the user to switch Codex App back to
87+
API-key/third-party provider auth before restarting, or keep the proxy enabled if they want
88+
ChatGPT-login UI with a third-party provider.
7789

7890
When cleanup completed without `restart_required_before_cleanup=true`, explicitly tell the user:
7991

@@ -87,7 +99,14 @@ If the block printed `restart_required_before_cleanup=true`, explicitly tell the
8799
Codex config has been restored to direct upstream, and the proxy was left running temporarily to avoid interrupting the current process. Restart Codex App and return to this conversation, or open a new CLI process, then run uninstall again to finish cleanup.
88100
```
89101

90-
If `direct_upstream_auth_warning` is present, add this before the restart instruction:
102+
If the block printed `uninstall_confirmation_required=true`, explicitly tell the user:
103+
104+
```text
105+
No uninstall changes were applied because ChatGPT login appears active and direct upstream mode may return 401. Keep the proxy enabled for ChatGPT-login UI with a third-party provider, switch Codex App back to API-key/third-party provider auth before uninstalling, or explicitly confirm that you want to continue uninstalling anyway.
106+
```
107+
108+
If `status="uninstalled"` and `direct_upstream_auth_warning` is present, add this before the restart
109+
instruction:
91110

92111
```text
93112
Warning: ChatGPT login appears to be active. After uninstall restores direct upstream, requests no longer pass through the proxy upstream auth override. If Codex keeps using ChatGPT auth, the third-party provider may receive a ChatGPT token and return 401. Switch back to API-key/third-party provider auth before restarting, or keep the proxy enabled for ChatGPT-login UI with a third-party provider.

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,9 +135,10 @@ It does not show prompts, request bodies, response content, API keys, cookies, o
135135
and whether `service_tier` was injected.
136136
- Uninstall is two-phase when needed: restore config first, keep the proxy alive for the current
137137
session, then clean up after Codex restarts.
138-
- If uninstall restores direct upstream while ChatGPT login is still active, switch back to
139-
API-key/third-party auth first or keep the proxy enabled; direct third-party providers may reject
140-
ChatGPT auth with 401.
138+
- If ChatGPT login is active and uninstall would restore direct upstream, uninstall stops before
139+
changing config and asks for explicit confirmation. Keep the proxy enabled, switch back to
140+
API-key/third-party auth before uninstalling, or explicitly accept that direct third-party
141+
providers may reject ChatGPT auth with 401.
141142

142143
## Agent Skill And Discovery
143144

docs/advanced-usage.md

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -211,14 +211,26 @@ Fetch and follow instructions from https://raw.githubusercontent.com/gaoguobin/c
211211

212212
Two-phase uninstall:
213213

214-
1. First run restores Codex config to direct upstream and removes the startup hook.
215-
2. It may leave the proxy process alive so the current proxy-backed session can finish.
216-
3. Restart Codex App or open a new CLI process.
217-
4. Run uninstall again to stop the remaining proxy and remove files.
218-
219-
If uninstall reports `direct_upstream_auth_warning`, Codex config has been restored to direct
220-
upstream while ChatGPT auth still appears active. Switch back to API-key/third-party auth before
221-
restarting, or keep the proxy enabled if you want ChatGPT-login UI with a third-party provider.
214+
1. If ChatGPT login is active and direct upstream may 401, the first run stops with
215+
`status="confirmation_required"` before changing config, hooks, proxy process, or files.
216+
2. After explicit confirmation, or when no ChatGPT direct-upstream risk is detected, the first run
217+
restores Codex config to direct upstream and removes the startup hook.
218+
3. It may leave the proxy process alive so the current proxy-backed session can finish.
219+
4. Restart Codex App or open a new CLI process.
220+
5. Run uninstall again to stop the remaining proxy and remove files.
221+
222+
If uninstall reports `status="confirmation_required"`, no uninstall changes were applied. Keep the
223+
proxy enabled, switch Codex App back to API-key/third-party auth before uninstalling, or explicitly
224+
continue with:
225+
226+
```powershell
227+
python -m codex_fast_proxy uninstall --defer-stop --confirm-chatgpt-direct-uninstall
228+
```
229+
230+
If a confirmed uninstall reports `direct_upstream_auth_warning`, Codex config has been restored to
231+
direct upstream while ChatGPT auth still appears active. Switch back to API-key/third-party auth
232+
before restarting, or keep the proxy enabled if you want ChatGPT-login UI with a third-party
233+
provider.
222234

223235
## Terminal Recovery
224236

docs/interaction-decisions.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -682,11 +682,15 @@ may send ChatGPT auth directly to the third-party provider and receive 401 becau
682682

683683
Requirements:
684684

685-
- When uninstall restores direct upstream and detects ChatGPT auth, include a structured
686-
`direct_upstream_auth_warning` in the JSON output.
685+
- Before uninstall restores direct upstream, detect active ChatGPT auth. If the operation would newly
686+
remove the proxy auth override from the model path, return `status="confirmation_required"` and a
687+
structured `direct_upstream_auth_warning` without changing config, hooks, proxy process, or files.
688+
- Continue only after explicit confirmation, for example
689+
`--confirm-chatgpt-direct-uninstall`.
687690
- The warning must not include secrets; it may include the upstream URL and upstream auth env name.
688-
- Before telling the user to restart, tell them to switch Codex App back to API-key/third-party
689-
provider auth, or keep the proxy enabled if they want ChatGPT-login UI with a third-party provider.
691+
- Before telling the user to restart after a confirmed direct-upstream restore, tell them to switch
692+
Codex App back to API-key/third-party provider auth, or keep the proxy enabled if they want
693+
ChatGPT-login UI with a third-party provider.
690694
- Do not restore old `auth.json` automatically. A ChatGPT login state is user-owned auth state and
691695
requires explicit recovery instructions if the user wants to change it.
692696

skills/codex-fast-proxy/SKILL.md

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,12 @@ python -m codex_fast_proxy uninstall
113113
edited directly by the user or agent.
114114
- Running Codex processes do not hot-switch provider config. After enable, restart Codex App and resume the same conversation if desired, or open a new CLI process.
115115
- If the current process is already using the proxy, stopping the proxy can interrupt the conversation. Disable with `uninstall --defer-stop`, tell the user to restart Codex App or open a new CLI process, then run uninstall again to finish cleanup.
116-
- If uninstall output includes `direct_upstream_auth_warning`, report it before any restart
116+
- If uninstall output has `status="confirmation_required"`, no uninstall changes were applied.
117+
Report `direct_upstream_auth_warning` first. Ask whether the user wants to keep the proxy enabled,
118+
switch Codex App back to API-key/third-party provider auth before uninstalling, or explicitly
119+
continue despite the ChatGPT-login direct-upstream 401 risk. Only after explicit confirmation,
120+
rerun with `--confirm-chatgpt-direct-uninstall`.
121+
- If confirmed uninstall output includes `direct_upstream_auth_warning`, report it before any restart
117122
instruction. Restored direct upstream mode no longer has the proxy auth override; if Codex App
118123
remains signed in with ChatGPT, a third-party provider may receive ChatGPT auth and return 401.
119124
Tell the user to switch back to API-key/third-party provider auth before restarting, or keep the
@@ -164,8 +169,18 @@ python -m codex_fast_proxy uninstall
164169
- After a successful `install --start`, explicitly tell the user that Fast proxy is enabled, but the current Codex process will not hot-switch; they should restart Codex App and return to the conversation, or open a new CLI process.
165170
- After a successful `install --start`, always append this optional ChatGPT-login UI note even if
166171
the status summary is already long: the user may keep API-key mode for third-party API plus global Fast. If they want richer Codex App UI such as plugin marketplace, GitHub/Apps/connectors, manual Fast controls, status hints, and voice input, they should run `prepare-chatgpt-login` before switching Codex App to ChatGPT login; switching directly may cause 401.
167-
- After `uninstall --defer-stop`, explicitly tell the user that Codex config has been restored to direct upstream, and the proxy was left running temporarily to avoid interrupting the current process. They should restart Codex App and return to the conversation, or open a new CLI process, then run uninstall again to finish cleanup.
168-
- If `direct_upstream_auth_warning` is present after uninstall, first warn that ChatGPT login appears active. After direct upstream restore, requests no longer pass through the proxy upstream auth override. Keeping ChatGPT login may send ChatGPT auth to the third-party provider and return 401. The user should switch back to API-key/third-party provider auth before restarting, or keep the proxy enabled for ChatGPT-login UI with a third-party provider.
172+
- After `uninstall --defer-stop` returns `status="confirmation_required"`, explicitly tell the user
173+
no uninstall changes were applied because ChatGPT login appears active and direct upstream may
174+
return 401. Do not tell the user to restart yet.
175+
- After `uninstall --defer-stop` returns `status="uninstalled"`, explicitly tell the user that Codex
176+
config has been restored to direct upstream, and the proxy was left running temporarily to avoid
177+
interrupting the current process. They should restart Codex App and return to the conversation, or
178+
open a new CLI process, then run uninstall again to finish cleanup.
179+
- If `direct_upstream_auth_warning` is present after a confirmed uninstall, first warn that ChatGPT
180+
login appears active. After direct upstream restore, requests no longer pass through the proxy
181+
upstream auth override. Keeping ChatGPT login may send ChatGPT auth to the third-party provider
182+
and return 401. The user should switch back to API-key/third-party provider auth before
183+
restarting, or keep the proxy enabled for ChatGPT-login UI with a third-party provider.
169184

170185
Use `--provider <name>` only when the user names a provider or when `doctor` reports that no active provider can be selected.
171186

@@ -235,4 +250,7 @@ For upstream URL changes after enable, prefer `set-upstream --upstream-base <url
235250
latest saved benchmark summary and never starts benchmark runs.
236251
- `uninstall` restores the full backup when the current config still matches the installed state.
237252
- If the config changed but the selected provider still points to the local proxy, `uninstall` restores only that provider's `base_url` to `upstream_base` and preserves other config changes.
253+
- If ChatGPT login is active and uninstall would newly restore direct upstream, `uninstall` returns
254+
`status="confirmation_required"` before changing config, hooks, proxy process, or files unless
255+
`--confirm-chatgpt-direct-uninstall` is explicit.
238256
- If `uninstall` reports `config_restore="skipped_config_changed"`, do not delete the package or repo; the selected provider no longer points to the recorded proxy, so ask the user before using `--force`.

src/codex_fast_proxy/manager.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,13 @@ def direct_upstream_auth_warning(
152152
) -> dict[str, Any] | None:
153153
if restore_status not in DIRECT_UPSTREAM_RESTORE_STATUSES:
154154
return None
155+
return direct_upstream_auth_risk(paths, settings)
156+
157+
158+
def direct_upstream_auth_risk(
159+
paths: ProxyPaths,
160+
settings: ProxySettings | None,
161+
) -> dict[str, Any] | None:
155162
login = detect_login_mode(paths.codex_home)
156163
if not login.chatgpt_auth:
157164
return None
@@ -176,6 +183,32 @@ def direct_upstream_auth_warning(
176183
}
177184

178185

186+
def uninstall_needs_chatgpt_direct_confirmation(
187+
paths: ProxyPaths,
188+
manifest: dict[str, Any] | None,
189+
settings: ProxySettings | None,
190+
force: bool,
191+
) -> dict[str, Any] | None:
192+
warning = direct_upstream_auth_risk(paths, settings)
193+
if not warning or not manifest or not settings:
194+
return None
195+
196+
config_path = Path(manifest["config_path"])
197+
current_hash = sha256_file(config_path)
198+
if current_hash == manifest.get("config_hash_before"):
199+
return None
200+
if current_hash == manifest.get("config_hash_after"):
201+
return warning
202+
203+
config = load_toml_config(config_path)
204+
config_base_url = provider_base_url(config, settings.provider)
205+
if config_base_url == settings.base_url:
206+
return warning
207+
if force and config_base_url != settings.upstream_base:
208+
return warning
209+
return None
210+
211+
179212
def runtime_status(paths: ProxyPaths, health: dict[str, Any] | None) -> dict[str, Any]:
180213
proxy_runtime = health.get("runtime") if isinstance(health, dict) else None
181214
manager_runtime = {**runtime_details(), "manager_module_file": str(Path(__file__).resolve())}
@@ -2502,6 +2535,34 @@ def command_uninstall(args: argparse.Namespace) -> int:
25022535
paths = paths_for(args.codex_home)
25032536
manifest = read_json(paths.manifest_path)
25042537
manifest_settings = settings_from_dict(manifest["settings"]) if manifest else None
2538+
confirmation = uninstall_needs_chatgpt_direct_confirmation(
2539+
paths,
2540+
manifest,
2541+
manifest_settings,
2542+
getattr(args, "force", False),
2543+
)
2544+
if confirmation and not getattr(args, "confirm_chatgpt_direct_uninstall", False):
2545+
print(json_line({
2546+
"status": "confirmation_required",
2547+
"code": "chatgpt_auth_direct_upstream_uninstall_requires_confirmation",
2548+
"message": (
2549+
"ChatGPT login appears to be active. Uninstall would restore Codex config to the direct "
2550+
"third-party upstream before the proxy auth override is in the path, so future model "
2551+
"requests may fail with 401."
2552+
),
2553+
"direct_upstream_auth_warning": confirmation,
2554+
"config_changed": False,
2555+
"config_restore": "not_attempted",
2556+
"startup_hook": {"status": "unchanged"},
2557+
"stop_result": {"status": "not_attempted"},
2558+
"files": "kept",
2559+
"next_user_action": (
2560+
"Keep the proxy enabled for ChatGPT-login UI with a third-party provider, switch Codex App "
2561+
"back to API-key/third-party provider auth before uninstalling, or explicitly confirm "
2562+
"uninstall with --confirm-chatgpt-direct-uninstall."
2563+
),
2564+
}))
2565+
return 4
25052566
stop_result: dict[str, Any] = {"status": "not_attempted", "reason": "config_not_restored"}
25062567
restore_status = "no_manifest"
25072568
backup_path = None
@@ -2686,6 +2747,7 @@ def build_parser() -> argparse.ArgumentParser:
26862747
uninstall.add_argument("--force", action="store_true")
26872748
uninstall.add_argument("--keep-state", action="store_true")
26882749
uninstall.add_argument("--defer-stop", action="store_true")
2750+
uninstall.add_argument("--confirm-chatgpt-direct-uninstall", action="store_true")
26892751

26902752
return parser
26912753

0 commit comments

Comments
 (0)