Repository navigation
feat(switch): restart the Codex app-server daemon after switching - #163
codejunkienick wants to merge 2 commits into
Conversation
Codex CLI sessions attach to a shared app-server daemon that keeps the previous account in memory, so a switch silently has no effect until the daemon restarts. When the daemon control socket resolves, print a hint pointing at `codex app-server daemon restart`. Refs Loongphy#162 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Instead of only hinting, restart a running daemon through its managed binary (`codex app-server daemon restart`) so open Codex sessions pick up the new account; they reconnect on their own. `codex-auth config daemon --restart off` opts out and falls back to the hint. A failed restart never fails the switch. `--json` restarts silently; `--live` is untouched. The activate/save/print sequence shared by the picker, query, and previous switch paths moves into one finishSwitch helper. Refs Loongphy#162 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@codejunkienick, thanks for identifying and integrating the daemon restart here. @mdbaniani's workaround in #162—running The differences from this PR are:
Real Codex 0.160.0 testing passed: unchanged-file recovery, no restart without the option, and A→B→A account verification through #164 is an alternative policy and lifecycle implementation for maintainer review; these improvements could also be incorporated here. |
* fix: coordinate Codex daemon lifecycle with bounded helpers Probe a responding daemon through the supported version command and restart only on an explicit request. Bound output collection and process exit, including helpers that close their pipes before exiting; terminate and reap only our child on timeout. Use the selected CODEX_HOME and prefer its managed executable. Keep absent and indeterminate states distinct, accepting the supported lifecycle detection/restart race. Motivated by codejunkienick’s PR #163 (#163); implements its daemon coordination idea with an explicit contract and independent code. No persistent configuration or registry schema changes. * feat(switch): add explicit daemon restart and stale-auth recovery Add --restart-daemon to one-shot query, picker, previous-account and JSON paths at their shared activation boundary. Persist account and registry state before daemon coordination; explicit requests include unchanged-file recovery and retry. Preserve the existing JSON stdout document and put lifecycle diagnostics and quoted home-specific recovery commands on stderr. Requested restart failures return the existing operational exit code 1. Reject the option with --live and retain other activation behavior. Cover selected-home propagation, persistence ordering, unchanged selection, previous and multi-match routes, failed selection, absent and indeterminate daemons, JSON failures and retry. Native suite: 446 passed, 5 skipped; required isolated list smoke and Windows/macOS cross-builds passed. Related: #162 and codejunkienick’s #163. This opts into disruptive restarts without adding persistent config or changing the registry schema. * fix(daemon): resolve relative PATH entries safely Use the cwd-relative canonicalization API for PATH candidates, which can be relative. The absolute-only API asserts on a valid relative PATH entry when managed Codex layouts are unavailable. Add an isolated CLI regression that removes the managed executable, finds the lifecycle helper through a relative PATH directory, and verifies restart and selected-home propagation. Linux suite: 447 passed, 5 skipped. * fix(daemon): skip non-executable Unix candidates Check executable access for Unix managed and PATH candidates so a non-executable regular file cannot mask a later usable Codex binary. Retain the regular native executable check on Windows. Extend the relative-PATH regression to ./fallback and empty PATH entries, and add an isolated CLI test with a non-executable decoy before the valid helper. No new resolver abstraction. Linux suite: 448 passed, 5 skipped; required isolated list smoke passed. * fix: prefer non-disruptive auth reload before daemon restart Two-tier --restart-daemon coordination, per the maintainer review: - ensure() probes the daemon, then attempts 'app-server daemon reload-auth' (codex daemons) with a 10s budget before falling back to the full restart (upstream daemons without reload support) - reload outcomes: .reloaded (credentials applied in place, no restart) and .deferred (daemon busy with a running turn — deferred, exit 0, rerun later); reload unavailability or failure falls through to the existing restart path - diagnostics for both new outcomes; docs updated - fake lifecycle script gains reload-auth variants; client + integration expectations updated for the version → reload-auth → restart sequence [skip ci] --------- Co-authored-by: S. Ahmed <198355839+DRanger666@users.noreply.github.com> Co-authored-by: Loongphy <Loongphy@outlook.com>
|
Closing as duplicate of #164, which implements the same daemon-restart fix and has been merged (plus a two-tier follow-up: non-disruptive auth reload on codex daemons, restart fallback for upstream daemons). Thanks for identifying the shared-daemon root cause — the fix is covered there. |
Fixes #162
Summary
Codex CLI sessions attach to a shared app-server daemon that loads
auth.jsononly at startup, socodex-auth switchlooks like it does nothing. This PR makes the switch take effect: when the daemon is running,switchrestarts it.<codex_home>/app-server-control/app-server-control.sockresolves. A stale symlink left by a stopped daemon doesn't count.<codex_home>/packages/app-server-daemon/current/bin/codex app-server daemon restart, which is the daemon's own managed binary, so there's no PATH lookup or version drift.codex-auth config daemon --restart off, stored as top-levelcodex_daemon_restartinregistry.json. Default is on, and the setting is kept acrossimport --purge. When it's off,switchprints a hint instead.--jsonrestarts silently and its output is unchanged.--liveis unchanged.<query>and-paths shared the same activate → save → print sequence. It's now onefinishSwitchhelper, so the restart lives in a single place.Why a restart and not a reload
The app-server protocol has no method to re-read
auth.json. The only way to inject tokens (account/login/startwithchatgptAuthTokens) is marked "FOR OPENAI INTERNAL USE ONLY". A restart is cheap, though. Against a real 0.158 daemon, an attachedcodexTUI reconnected on its own ("Reconnected. No input was resent…"), kept its unsent draft, and/statusreflected the newauth.json. The one cost is that a turn running at that moment is interrupted. That's whyconfig daemon --restart offexists, and it's noted indocs/commands/switch.md.The repo only prompts for destructive deletes (
remove). A restart deletes nothing and sessions recover on their own, so this restarts without a prompt, similar to the heavier kill approach in #149.Verification
zig build test: 442 passed, 5 skipped. New tests: parser tests forconfig daemon, plus integration tests with a fake daemon for restart on, restart off, restart failed, and config persistence.zig build run -- list, andzig fmt --check src tests.CODEX_HOME:--restart off: PID unchanged, hint printed.--json: PID changed, JSON output intact.(Tested locally with
-Dtarget=x86_64-linux-musl, because Zig 0.16's linker rejects the.sframesection in the glibc 2.44crt1.oon my Arch machine (GCC 16, binutils 2.47). This is unrelated to the change.)🤖 Generated with Claude Code