Skip to content

Commit 7116a87

Browse files
Cmochanceclaude
andauthored
feat(login): surface and recover from missing codex CLI (#27)
* feat(login): surface and recover from missing codex CLI Login on Windows used to hide a missing codex CLI behind mojibake: the fallback `cmd /C codex login` printed cmd.exe's GBK "command not found" message, and `String::from_utf8_lossy` mangled it into U+FFFD soup. The toast was unreadable and the only escape was reinstalling codex. This change replaces the cmd fallback with a typed `REAL_CODEX_NOT_FOUND` error and gives the user a recovery path: - New "Codex CLI 路径" dialog auto-opens on REAL_CODEX_NOT_FOUND. It shows the currently resolved path + source, common install locations as click-to-fill chips, and Save / Clear / Cancel actions. - A user-supplied path is persisted as `user_codex_path` in install_state.json and takes priority over auto-discovery, with a silent fallback when the override file disappears so the user is never permanently wedged. - Settings page now has a "Codex CLI 路径" row showing the resolved path so users can adjust it any time, not only after a failure. - Login button doubles as a cancel button while a login is in flight: clicking sends SIGTERM (Unix) / taskkill (Windows) to the codex process so a closed OAuth tab no longer leaves a permanent spinner. Bump to 1.5.6. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(login): preserve stderr on failure; tighten cancel toast race Three follow-ups from PR review: - `Command::output()` previously captured codex login's stdio for the LOGIN_FAILED toast; the spawn+wait_with_output split silently dropped it. Pipe stdout/stderr explicitly so the message surface stays the same when codex login itself errors out. - Roll back `cancelledLoginProfile` when the backend reports no login was actually cancelled, so a click that races a natural failure doesn't mask the real toast. - Document the PID-reuse caveat in `login_cancel.rs` — the slot clears after wait_with_output, so a cancel in that microsecond window could theoretically target a recycled PID. Accepted for v1.5.6; long-term fix is a Child-handle slot. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(win): patch InstallState constructors and exhaustive match for user_codex_path CI on Linux (which compiles the Windows runtime path) caught three spots I missed locally because the macOS toolchain doesn't cross-check the win module. Same shape as the mac install.rs fix already in this PR: - Two `InstallState { ... }` constructors in win install.rs needed the new `user_codex_path` field (preserve the previous value). - `run_codex_auth_refresh` had a `match real_codex_source` that wasn't exhaustive after `RealCodexPathSource::UserOverride` was added. - Two test fixtures had to set `user_codex_path: None` explicitly. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * chore(git): ignore .claude runtime artifacts Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(win): patch test InstallState constructors for user_codex_path Two more InstallState literals in win/runtime/process.rs tests that the mac toolchain didn't surface (Linux CI compiles the win cfg path). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 24d79a0 commit 7116a87

26 files changed

Lines changed: 1206 additions & 78 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,5 @@ local_doc/
2323
agent/
2424
codex_history/
2525
.codex/
26+
.claude/
2627
docs/

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "codex_switch",
33
"private": true,
4-
"version": "1.5.4",
4+
"version": "1.5.6",
55
"type": "module",
66
"scripts": {
77
"dev": "vite",

src-tauri/Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src-tauri/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "codex_switch"
3-
version = "1.5.4"
3+
version = "1.5.6"
44
description = "Native Tauri control panel for Codex account switching"
55
authors = ["Cmochance"]
66
edition = "2021"

src-tauri/mac/front/index.html

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,15 @@ <h2 class="section-title" data-i18n-key="runtimeTitle">Runtime Console</h2>
195195
<strong data-i18n-key="settingsUpdateUrl">Update URL</strong>
196196
<input id="settings-update-url-input" class="settings-input" type="url" value="https://api.github.com/repos/Cmochance/Codex_Account_Switch/releases/latest" />
197197
</label>
198+
<div class="settings-row settings-row--stacked">
199+
<strong id="settings-codex-cli-label">Codex CLI path</strong>
200+
<div class="settings-row-content">
201+
<p id="settings-codex-cli-value" class="settings-value">--</p>
202+
<div class="settings-action-row">
203+
<button id="settings-codex-cli-button" class="settings-action-button" type="button">Change</button>
204+
</div>
205+
</div>
206+
</div>
198207
<div class="settings-row settings-row--stacked">
199208
<strong data-i18n-key="settingsConfigBackup">Configuration backup</strong>
200209
<div class="settings-row-content">
@@ -368,6 +377,43 @@ <h3 id="base-url-dialog-title">Custom Base Url</h3>
368377
</form>
369378
</dialog>
370379

380+
<dialog id="codex-cli-dialog" class="dialog">
381+
<form id="codex-cli-form" class="dialog-form" method="dialog">
382+
<div class="dialog-copy">
383+
<h3 id="codex-cli-dialog-title">Codex CLI path</h3>
384+
<p id="codex-cli-dialog-copy">Codex Switch couldn't find the codex CLI on this machine, or the cached path is wrong. Pick the real codex binary so login can spawn it directly.</p>
385+
<p class="codex-cli-current">
386+
<strong id="codex-cli-current-label">Current path</strong>
387+
<span id="codex-cli-current-value" class="codex-cli-current-value">--</span>
388+
<span id="codex-cli-current-source" class="codex-cli-current-source" hidden></span>
389+
</p>
390+
</div>
391+
392+
<label class="field">
393+
<span id="codex-cli-input-label">codex executable path</span>
394+
<input
395+
id="codex-cli-input"
396+
name="codex_cli_path"
397+
type="text"
398+
autocomplete="off"
399+
spellcheck="false"
400+
placeholder="/full/path/to/codex"
401+
/>
402+
</label>
403+
404+
<p id="codex-cli-suggestions-heading" class="codex-cli-suggestions-heading">Common locations</p>
405+
<div id="codex-cli-suggestions" class="codex-cli-suggestions"></div>
406+
407+
<p id="codex-cli-dialog-error" class="dialog-error" hidden></p>
408+
409+
<div class="dialog-actions">
410+
<button id="cancel-codex-cli-button" class="ghost-button" type="button">Cancel</button>
411+
<button id="clear-codex-cli-button" class="ghost-button" type="button" hidden>Clear override</button>
412+
<button id="submit-codex-cli-button" class="primary-button" type="submit">Save</button>
413+
</div>
414+
</form>
415+
</dialog>
416+
371417
<dialog id="update-dialog" class="dialog">
372418
<div class="dialog-form">
373419
<div class="dialog-copy">

src-tauri/mac/runtime/install.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ pub fn refresh_install_state(codex_home: &Path) -> AppResult<()> {
219219
let state = InstallState {
220220
real_codex_path,
221221
path_added_by_installer: previous_state.path_added_by_installer,
222+
user_codex_path: previous_state.user_codex_path,
222223
};
223224
save_install_state(Some(codex_home), &state);
224225
Ok(())
@@ -267,9 +268,11 @@ pub fn install_from(
267268
copy_runtime_cli(source_cli_path, &runtime_cli_path)?;
268269
write_codex_shim(&codex_home)?;
269270

271+
let previous_state = load_install_state(Some(&codex_home));
270272
let state = InstallState {
271273
real_codex_path: Some(real_codex_path.to_string_lossy().into_owned()),
272274
path_added_by_installer: false,
275+
user_codex_path: previous_state.user_codex_path,
273276
};
274277
save_install_state(Some(&codex_home), &state);
275278

src-tauri/mac/runtime/mod.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@ pub use crate::shared::{paths, profiles, profiles_index};
1313

1414
pub mod actions {
1515
pub use super::profile_actions::{
16-
add_profile, clear_profile_account, delete_profile, login_current_profile, login_profile,
17-
open_codex_app, open_contact, open_profile_folder, open_releases, open_url,
18-
open_xiaohongshu, rename_profile, update_profile_base_url,
16+
add_profile, clear_codex_cli_path, clear_profile_account, delete_profile,
17+
get_codex_cli_status, login_current_profile, login_profile, open_codex_app, open_contact,
18+
open_profile_folder, open_releases, open_url, open_xiaohongshu, rename_profile,
19+
set_codex_cli_path, update_profile_base_url,
1920
};
2021
pub use super::refresh_runtime::refresh_profile;
2122
}

0 commit comments

Comments
 (0)