Skip to content

fix(win): adapt desktop-host discovery to the Codex → ChatGPT app merge - #54

Merged
Cmochance merged 2 commits into
mainfrom
fix/win-chatgpt-app-discovery
Jul 11, 2026
Merged

fix(win): adapt desktop-host discovery to the Codex → ChatGPT app merge#54
Cmochance merged 2 commits into
mainfrom
fix/win-chatgpt-app-discovery

Conversation

@Cmochance

Copy link
Copy Markdown
Owner

Summary

The Windows counterpart of the macOS adaptation shipped in v1.6.0 (#51). OpenAI folded the Codex desktop app into the ChatGPT desktop app on Windows too on 2026-07-09 (confirmed via the OpenAI help center, changelog, winget manifests and third-party tooling). The merged host runs as ChatGPT.exe while the MSIX package family stays OpenAI.Codex_2p2nqsd0c76g0, so the old Codex.exe-only tasklist/taskkill checks miss the running UI: switching skips quitting the host (leaving its in-memory app-server on the old account — the cross-contamination vector) and never relaunches it.

Anchored facts (no live Windows box — sourced from public evidence)

  • Merged host executable ChatGPT.exe; non-Store install under %LOCALAPPDATA%\Programs\ChatGPT\ (EchoBird chatgptdesktop/paths.json, file.net)
  • MSIX package family OpenAI.Codex_2p2nqsd0c76g0 unchanged across the in-place update (winget OpenAI.Codex 0.143.0, ReleaseDate 2026-07-08; OpenCodex asar scanner; "update as usual" in the OpenAI help center)
  • ChatGPT Classic is a separate package OpenAI.ChatGPT-Desktop_2p2nqsd0c76g0 whose executable is also ChatGPT.exe (KAPE ChatGPT.tkape)
  • Bundled CLI at <InstallLocation>\app\resources\codex.exe (codex-windows-fast-patch-skill), the Windows analog of macOS Contents/Resources/codex

Changes

  • is-running / quit enumerate all host names via one Win32_Process CIM query and classify each PID by its executable install identity: the OpenAI.Codex* package family (incl. beta) or a non-Store install embedding resources\codex.exe → ours; ChatGPT Classic → positively excluded, never killed; unattributable → counts as running (switch aborts) but never signalled. Same ours/other/unknown semantics as the macOS side.
  • quit signals by PID (taskkill /PID then /F) instead of /IM <name>, so a co-running ChatGPT Classic is untouched.
  • launch prefers the detected Store package (unchanged shell:AppsFolder id), then a qualified non-Store ChatGPT.exe/Codex.exe.
  • CLI discovery + Settings auto-detect gain the desktop-bundled CLI tiers (MSIX <InstallLocation>\app\resources\codex.exe and non-Store Programs\{ChatGPT,Codex}\resources\codex.exe).
  • Resilience (from the pre-push review): PowerShell-backed caches retry on transient failure instead of pinning "absent"; a broken tasklist falls through to classification rather than silently reporting "not running"; both probe failures log once.

Verification

  • cargo test --lib: 137 passed / 0 failed (4 new win-only tests: MSIX/Classic/beta classification, bundled-CLI detection, CIM-line parsing, process-name set). The win module compiles only on the Linux cargo test --lib CI job — that is the real compile gate here.
  • cargo fmt --all --check, cargo machete clean.
  • Not yet verified on a live Windows install — flagged in CHANGELOG. The three load-bearing facts (exe name, package family survival, bundled-CLI path) are sourced but unconfirmed on hardware; a tasklist / Get-AppxPackage OpenAI.* / package-dir check on a merged install would close that.

🤖 Generated with Claude Code

The merged Windows host runs as ChatGPT.exe (MSIX package family
unchanged: OpenAI.Codex_2p2nqsd0c76g0; non-Store installs under
%LOCALAPPDATA%\Programs\ChatGPT), so the Codex.exe-only tasklist /
taskkill checks missed the running UI: switch skipped quitting the
host, leaving its in-memory app-server on the old account, and never
relaunched it.

- is-running / quit enumerate both host names via one Win32_Process
  CIM query and classify each PID by executable install identity:
  OpenAI.Codex* package family (incl. beta) or a non-Store install
  embedding resources\codex.exe => ours; ChatGPT Classic
  (OpenAI.ChatGPT-Desktop_*, same exe name) => positively excluded;
  unattributable => counts as running, never signalled (mirrors the
  macOS ours/other/unknown semantics)
- quit signals by PID (graceful taskkill /PID, then /F) instead of
  /IM <name>, with the differentiated APP_EXIT_FAILED message
- launch prefers the detected Store package (shell:AppsFolder id is
  unchanged post-merge), then a qualified non-Store executable
- CLI discovery + Settings auto-detect gain the desktop-bundled CLI
  tiers: <MSIX InstallLocation>\app\resources\codex.exe and
  Programs\{ChatGPT,Codex}\resources\codex.exe

Facts anchored from public sources (winget manifests, KAPE targets,
third-party Codex tooling paths); pending verification on a live
Windows install — noted in CHANGELOG.
…beta coverage

Findings from the pre-push review pass (1 BLOCKER, 4 MEDIUM, 1 LOW):

- BLOCKER: reopen_codex_app_if_needed's match was non-exhaustive after
  AppLaunchTarget gained Executable — E0004, the whole win module (and
  the Linux CI job that compiles it) failed to build. Added the
  Executable arm so non-Store installs relaunch too.
- both PowerShell-backed OnceLock caches (Store shell target, MSIX
  InstallLocation) no longer pin a transient probe failure as
  'absent': they cache only an authoritative answer and retry
  otherwise, so a login-storm spawn failure can't wedge the bundled
  CLI candidate for the whole session.
- tasklist is no longer the sole short-circuit: a broken tasklist now
  returns Unavailable (not 'not running'), and is_codex_app_running
  falls through to PowerShell classification instead of silently
  skipping the host quit (the switch-time contamination direction).
  Both probe failures log once via OnceLock sentinels.
- Get-AppxPackage widened to 'OpenAI.Codex*' and the beta standalone
  process name 'Codex (Beta).exe' added to enumeration, so the beta
  channel is actually covered rather than only named in comments.
- quit's could-not-attribute message now hints at PowerShell being
  blocked; the wait loop uses classification directly and documents
  the ~7-12s worst-case latency.
@Cmochance
Cmochance merged commit b36718a into main Jul 11, 2026
3 checks passed
@Cmochance
Cmochance deleted the fix/win-chatgpt-app-discovery branch July 11, 2026 23:36

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1cdaf6f06b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// name probe and stay conservative — a bare name match counts
// as running so the switch aborts instead of proceeding over a
// possibly-live host (quit refuses to signal it).
return matches!(host_name_probe(), NameProbe::Present);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Treat an unavailable fallback as still running

When PowerShell classification returns no rows because the probe failed, this fallback only treats a second tasklist result of Present as running. If tasklist is also unavailable (NameProbe::Unavailable), the function returns false even though that state is explicitly unknown, so is_codex_app_running can proceed with a switch over a live host in the exact degraded environment this path is meant to handle. The conservative branch should include Unavailable as running/abort-worthy.

Useful? React with 👍 / 👎.

return Vec::new();
}
};
parse_pid_classification_lines(&String::from_utf8_lossy(&output.stdout))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Emit UTF-8 from PowerShell before parsing paths

This decodes the CIM output as UTF-8, but Windows PowerShell commonly writes redirected text using the active OEM code page. For non-Store installs under a non-ASCII %LOCALAPPDATA% path, the executable path is mojibaked before host_dir_embeds_codex_cli checks for resources\codex.exe; the PID becomes Unknown, which still blocks is_codex_app_running but is never signalled, so every switch aborts while that app is running. Set PowerShell's output encoding to UTF-8 (or decode the console code page) before parsing paths.

Useful? React with 👍 / 👎.

// Desktop-host bundled CLI as the last tier — mirrors the macOS
// app-bundle fallback so a machine whose only codex is the one
// inside the (merged) desktop app still resolves.
for candidate in desktop_host_bundled_cli_candidates() {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Probe bundled CLI candidates before saving them

When no normal CLI is found, this new fallback accepts the desktop-bundled resources\codex.exe solely because the file exists and then resolve_real_codex_cli_with_source persists it without the --version runnable probe used by Settings redetect. On MSIX installs where that exact packaged path exists but cannot be launched directly from outside the package (for example, upstream reports show WindowsApps\OpenAI.Codex...\app\resources\codex.exe failing with Access denied: openai/codex#31086), login and app-server refreshes will fail with a launch error instead of continuing discovery or reporting no usable CLI. Gate this fallback with the same runnable check before returning it.

Useful? React with 👍 / 👎.

// macOS `Contents/Resources/codex` qualifier). Classic has no such
// file, but its absence alone cannot rule a host out (probe
// failures), so it stays Unknown rather than Other.
if host_dir_embeds_codex_cli(Path::new(executable_path)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Recognize non-Store app-server children

For non-Store installs the desktop host's bundled child runs from %LOCALAPPDATA%\Programs\ChatGPT\resources\codex.exe, and the CIM filter above matches that process via Name='Codex.exe'. Classifying that child here calls host_dir_embeds_codex_cli on the child executable, which looks for resources\resources\codex.exe and returns false; the child becomes Unknown, which keeps is_codex_host_running_via_classification true but is never signalled, so a switch can time out after ChatGPT.exe exits whenever the app-server child lingers.

Useful? React with 👍 / 👎.

Cmochance added a commit that referenced this pull request Jul 11, 2026
- bump version 1.6.0 -> 1.6.1 (package.json / Cargo.toml / locks)
- CHANGELOG: promote Unreleased to 1.6.1 - 2026-07-12
- README (zh/en): refresh version:set example
- release-notes/v1.6.1.md: bilingual notes (Windows ChatGPT merge adaptation, #54)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant