Skip to content

fix(codex-cli): pin the official CLI credential store to file on disk (#641) - #642

Merged
ndycode merged 4 commits into
mainfrom
fix/641-keychain-prompts
Jul 28, 2026
Merged

fix(codex-cli): pin the official CLI credential store to file on disk (#641)#642
ndycode merged 4 commits into
mainfrom
fix/641-keychain-prompts

Conversation

@ndycode

@ndycode ndycode commented Jul 28, 2026

Copy link
Copy Markdown
Owner

Summary

  • Fixes [bug] CodexBar continuously asks me to log into the login keychain despite "Always Allow" being used #641 — CodexBar (and any other front-end that execs the official Codex binary directly) kept raising macOS login-keychain prompts, and "Always Allow" never stuck.
  • Root cause is not this project's own storage: multi-auth state is plaintext JSON under ~/.codex/multi-auth and never touches the keychain. The prompts come from the official Codex CLI when cli_auth_credentials_store in ~/.codex/config.toml is left on "keychain".
  • The wrapper already forwards -c cli_auth_credentials_store="file", but that only covers processes it spawns. A third-party front-end reads config.toml instead — so the persisted value is what actually decides whether the keychain gets opened.
  • Persisting it used to be lazy: it happened only inside setCodexCliActiveSelection (switch / login / health check / repair). A user who never switched accounts through the manager could sit on the keychain store forever.

What Changed

Proactive enforcement (lib/codex-cli/writer.ts, lib/runtime/first-run.ts, scripts/codex.js)

  • ensureCodexCliFileAuthStore is now exported and defaults to the resolved config.toml path.
  • First-run setup reconciles the store as a new authStore step (marker payload bumped to v2). Deliberately not gated on isCodexCliSyncEnabled() — turning account mirroring off is not a request to re-enable keychain prompts.
  • Wrapper startup runs the same reconcile before forwarding, which also self-heals a config that something else flipped back (e.g. a fresh official codex login). It is idempotent, so no write amplification on the common path.

Top-level scoping (the part that made the old behavior actively wrong)

  • Both the read and the write now only consider assignments before the first [table] header. Previously a cli_auth_credentials_store under [profiles.work] was rewritten in place — mangling a deliberate scoped setting while leaving the top-level default on the keychain, so the prompts survived the "fix". doctor had the mirror bug and reported that profile-scoped value as healthy.

Doctor (lib/codex-manager/repair-commands.ts)

  • codex-auth-store gains a real --fix remediation instead of an unactionable warning, and re-reports as ok afterwards. Respects --dry-run; a failed rewrite degrades to a codex-auth-store-fix warning rather than aborting doctor.
  • Without --fix, the warning now names the command to run. On darwin, once pinned, it notes that credentials from an earlier official codex login may still sit in the login keychain, are no longer read, and can be cleared manually.

Deliberately not done: nothing reads or deletes keychain items. security delete-generic-password would itself raise the exact prompt this PR removes, and it would add a macOS-only subprocess surface where none exists today. Prevention via config is deterministic and cross-platform.

Toggles

Env var Effect
CODEX_MULTI_AUTH_FORCE_FILE_AUTH_STORE=0 Skips the per-invocation -c override and the wrapper-startup reconcile (kept coherent)
CODEX_MULTI_AUTH_ENFORCE_CLI_FILE_AUTH_STORE=0 Skips every config.toml rewrite (first-run, sync, doctor --fix) — previously undocumented, now in docs/configuration.md and docs/development/CONFIG_FIELDS.md

Validation

  • npm run lint
  • npm run typecheck
  • npm test — 334 files, 5247 passed / 6 skipped, 0 failed
  • npm test -- test/documentation.test.ts
  • npm run build

New coverage: top-level vs [profiles.*] scoping, keychain→file rewrite, already-file no-op, missing-file creation, enforcement opt-out, first-run enforcement + failure isolation, wrapper reconcile / opt-out / absent-dist / non-fatal write failure, and doctor fix / dry-run / already-ok / rewrite-failure paths.

Docs and Governance Checklist

  • README updated (if user-visible behavior changed) — not needed, no surface change
  • docs/getting-started.md updated — not needed
  • docs/features.md updated — not needed
  • relevant docs/reference/* pages updated — docs/reference/storage-paths.md, plus docs/configuration.md, docs/development/CONFIG_FIELDS.md, and a new docs/troubleshooting.md entry for the symptom
  • docs/upgrade.md updated — not needed, no migration
  • SECURITY.md and CONTRIBUTING.md reviewed for alignment

Risk and Rollback

  • Risk level: low-medium. The change writes to ~/.codex/config.toml on more paths than before, so it touches a user-owned file at wrapper startup. Mitigations: idempotent (no write when already "file"), atomic rename with the existing EPERM/EBUSY retry, non-fatal on failure (forwarding proceeds), and fully opt-out-able. Top-level scoping means an existing [profiles.*] setting is no longer clobbered — a strict improvement over main.
  • Windows: a locked or read-only config.toml is swallowed and forwarding continues; explicitly covered by a test.
  • Rollback: revert the commit. No state migration, no persisted format change beyond an additive authStore field in the first-run marker (existence-gated, never parsed for behavior).

Additional Notes

Users already hitting this can fix it immediately with codex-multi-auth doctor --fix once this ships; existing installs whose first-run marker is already claimed are covered by the wrapper-startup guard.

🤖 Generated with Claude Code

note: greptile review for oc-chatgpt-multi-auth. cite files like lib/foo.ts:123. confirm regression tests + windows concurrency/token redaction coverage.

Greptile Summary

this revision improves persisted codex cli file-store enforcement and its diagnostics.

  • scopes auth-store reads and rewrites to top-level toml assignments.
  • migrates first-run markers and retries ordinary auth-store enforcement failures.
  • reconciles the persisted store during wrapper startup and adds doctor remediation.
  • documents the opt-outs, storage behavior, macos keychain guidance, and windows filesystem handling.

Confidence Score: 3/5

this pr is not yet safe to merge because an incomplete first-run marker can permanently suppress auth-store retries, and concurrent config.toml replacements can still discard provider or auth-store updates.

the ordinary failed-migration path now remains pre-v2 and retries, but the initial claim itself is still version 2; if windows filesystem retries or another marker finalization failure are exhausted, the next invocation incorrectly treats that incomplete claim as finished. config.toml reconciliation also remains an unlocked read-modify-rename that can race app bind or unbind and lose either writer’s changes.

Files Needing Attention: lib/runtime/first-run.ts, lib/codex-cli/writer.ts, lib/runtime/app-bind.ts, scripts/codex.js

Important Files Changed

Filename Overview
lib/runtime/first-run.ts adds versioned auth-store migration and ordinary failure retries, but an incomplete version-2 claim can still suppress retries after finalization failure.
lib/codex-cli/writer.ts adds top-level toml reconciliation with windows rename retries, while cross-process config writes remain uncoordinated.
scripts/codex.js invokes best-effort persisted auth-store reconciliation before every forwarded codex launch.
lib/codex-manager/repair-commands.ts adds doctor diagnosis, dry-run planning, and best-effort remediation for the persisted auth-store mode.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[manager first run] --> B[enforce file auth store]
  C[wrapper startup] --> B
  D[doctor --fix] --> B
  B --> E[rewrite top-level config.toml assignment]
  E --> F[official codex uses file credentials]
Loading

Reviews (4): Last reviewed commit: "fix(codex-cli): preserve config.toml for..." | Re-trigger Greptile

Context used (3)

Repeated macOS login-keychain prompts (#641) come from the official Codex
CLI, not from this project's own storage, which is plaintext JSON and never
touches the keychain. The wrapper forwards `-c cli_auth_credentials_store="file"`
on every command it launches, but that only covers processes it spawns.
Third-party front-ends such as CodexBar exec the official binary directly and
read ~/.codex/config.toml instead, so a config left on "keychain" keeps
prompting no matter how many times the user clicks "Always Allow".

Persisting the value used to be lazy: it happened only inside
setCodexCliActiveSelection, i.e. on switch/login/health-check/repair. A user
who never switched accounts through the manager could stay on the keychain
store indefinitely.

Reconcile it proactively instead, at first-run setup and again as an
idempotent guard on wrapper startup, and give `doctor --fix` a real
remediation rather than a bare warning.

Also scope both the read and the write to top-level assignments. A
`cli_auth_credentials_store` under `[profiles.*]` was previously rewritten in
place, which mangled the scoped setting while leaving the top-level default
on the keychain -- so the prompts survived the "fix". Doctor had the mirror
bug and would report a profile-scoped value as healthy.

The keychain itself is never read or written: `security find-generic-password`
would raise the very prompt this change removes. Stale entries from an earlier
official `codex login` simply stop being read, and doctor says so on darwin.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

the cli auth store is now reconciled to top-level file storage during first-run setup, wrapper startup, and doctor --fix. opt-out variables distinguish per-invocation overrides from persisted config.toml rewrites, while profile settings remain unchanged.

Changes

cli auth-store enforcement

Layer / File(s) Summary
top-level config parsing and reconciliation
lib/codex-cli/writer.ts:259, test/codex-cli-writer.test.ts:559, test/helpers/cli-test-fixtures.ts:264
adds top-level parsing, preserves profile-scoped values, creates missing configuration, and supports persisted enforcement opt-out.
first-run auth-store step
lib/runtime/first-run.ts:401, test/first-run.test.ts:337, docs/reference/storage-paths.md:62
adds auth-store marker status, v1 migration replay, best-effort enforcement, and related tests and documentation.
wrapper startup reconciliation
scripts/codex.js:1086, scripts/codex.js:5076, test/codex-bin-wrapper.test.ts:3027
reconciles persisted configuration before forwarding, swallows writer failures, and covers missing-module and concurrent launches.
doctor remediation and diagnostics
lib/codex-manager/repair-commands.ts:1806, test/repair-commands.test.ts:1061, docs/troubleshooting.md:113
adds detection, --fix, dry-run handling, failure warnings, and macos keychain troubleshooting guidance.
configuration and storage documentation
docs/configuration.md:103, docs/development/CONFIG_FIELDS.md:290, docs/reference/storage-paths.md:93
documents the two opt-out scopes, marker migration behavior, and windows startup failure handling.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Frontend as third-party frontend
  participant Wrapper as codex-multi-auth wrapper
  participant Writer as codex cli writer
  participant Config as ~/.codex/config.toml
  participant Codex as official Codex cli
  Frontend->>Wrapper: invoke command
  Wrapper->>Writer: reconcile persisted auth store
  Writer->>Config: write top-level cli_auth_credentials_store = "file"
  Wrapper->>Codex: forward command with per-invocation file override
Loading

Possibly related PRs

Suggested labels: bug

Suggested reviewers: claude

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.18% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ⚠️ Warning the title has the right type and scope, but the summary exceeds the 72-character limit. shorten it to something like fix(codex-cli): pin cli credential store to file.
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed It addresses #641 with file-backed auth-store enforcement in lib/runtime/first-run.ts, scripts/codex.js, and lib/codex-manager/repair-commands.ts.
Out of Scope Changes check ✅ Passed The docs and tests in docs/* and test/* support the auth-store fix, and no unrelated changes stand out.
Description check ✅ Passed the description covers summary, changes, validation, docs, risk, and rollback with the required sections.
📋 Issue Planner

Built with CodeRabbit's Coding Plans for faster development and fewer bugs.

View plan used: #641

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/641-keychain-prompts
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch fix/641-keychain-prompts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
lib/codex-cli/writer.ts (1)

303-339: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

retry transient temporary-file writes on windows.

the new reconciliation path at lib/codex-cli/writer.ts:339 relies on atomicWriteText, but lib/codex-cli/writer.ts:216 performs the temporary writeFile once; only rename retries EPERM/EBUSY. retry transient write failures too, otherwise a locked/virus-scanned temp path leaves persistence unenforced.

based on learnings: “use atomic writes and retry EPERM/EBUSY failures for Windows configuration writes.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/codex-cli/writer.ts` around lines 303 - 339, Update atomicWriteText to
retry transient EPERM and EBUSY failures from the temporary writeFile operation,
using the same retry behavior already applied to rename. Ensure retries cover
Windows temporary-file locking or scanning failures before propagating the
error, while preserving the existing atomic write flow used by
ensureCodexCliFileAuthStore.

Sources: Coding guidelines, Learnings

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/codex-cli/writer.ts`:
- Around line 272-276: Update readTopLevelCodexCliAuthStoreMode to parse
cli_auth_credentials_store values enclosed in either TOML double or single
quotes, preserving trimming and null behavior. Add a regression test in the
codex CLI writer test suite covering a single-quoted literal value such as
'file'.

In `@lib/codex-manager/repair-commands.ts`:
- Around line 1806-1813: The auth-store remediation must be reported
independently of managed accounts. In
lib/codex-manager/repair-commands.ts:1806-1813, track the auth-store fix as
planned in dry-run and applied otherwise, and include that state in the fix
metadata so fix.changed is true even with no accounts; preserve the existing
supplemental auth-store action. In test/repair-commands.test.ts:1063-1133, add
no-account and dry-run assertions covering fix.changed and the auth-store
action.

In `@lib/runtime/first-run.ts`:
- Around line 29-30: Update the first-run marker handling around
existsSync(markerPath) to exclusively claim and migrate v1 markers to
FIRST_RUN_MARKER_VERSION 2 before returning setup complete. During this
migration, invoke only defaultEnforceAuthStore to persistently reconcile the
top-level cli_auth_credentials_store value to "file"; do not rerun app bind or
launcher setup. Add a regression test covering an existing v1 marker and the
resulting auth-store setting.

In `@test/codex-bin-wrapper.test.ts`:
- Around line 3079-3091: Extend the missing compiled auth-store coverage around
the existing test and runWrapper helper by launching multiple wrapper processes
concurrently with the compiled module absent. Await all results and assert every
invocation exits successfully and forwards the expected exec status arguments,
covering the lazy-load/missing-dist startup path.

---

Outside diff comments:
In `@lib/codex-cli/writer.ts`:
- Around line 303-339: Update atomicWriteText to retry transient EPERM and EBUSY
failures from the temporary writeFile operation, using the same retry behavior
already applied to rename. Ensure retries cover Windows temporary-file locking
or scanning failures before propagating the error, while preserving the existing
atomic write flow used by ensureCodexCliFileAuthStore.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: a06c1735-89d1-45cd-8037-8fb5d60cec41

📥 Commits

Reviewing files that changed from the base of the PR and between 4cdf7a4 and 7f36dcd.

📒 Files selected for processing (13)
  • docs/configuration.md
  • docs/development/CONFIG_FIELDS.md
  • docs/reference/storage-paths.md
  • docs/troubleshooting.md
  • lib/codex-cli/writer.ts
  • lib/codex-manager/repair-commands.ts
  • lib/runtime/first-run.ts
  • scripts/codex.js
  • test/codex-bin-wrapper.test.ts
  • test/codex-cli-writer.test.ts
  • test/first-run.test.ts
  • test/helpers/cli-test-fixtures.ts
  • test/repair-commands.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: Greptile Review
🧰 Additional context used
📓 Path-based instructions (27)
**/*.{ts,js}

📄 CodeRabbit inference engine (AGENTS.md)

Use ESM only ("type": "module"), Node >= 18.17

Files:

  • test/helpers/cli-test-fixtures.ts
  • test/codex-cli-writer.test.ts
  • scripts/codex.js
  • test/codex-bin-wrapper.test.ts
  • test/repair-commands.test.ts
  • lib/codex-manager/repair-commands.ts
  • lib/codex-cli/writer.ts
  • test/first-run.test.ts
  • lib/runtime/first-run.ts
**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Do not use as any, @ts-ignore, or @ts-expect-error in TypeScript files

Files:

  • test/helpers/cli-test-fixtures.ts
  • test/codex-cli-writer.test.ts
  • test/codex-bin-wrapper.test.ts
  • test/repair-commands.test.ts
  • lib/codex-manager/repair-commands.ts
  • lib/codex-cli/writer.ts
  • test/first-run.test.ts
  • lib/runtime/first-run.ts
{scripts/**/*.js,test/**/*.ts}

📄 CodeRabbit inference engine (AGENTS.md)

Do not use bare recursive delete logic in Windows-sensitive scripts/tests without retry handling

Files:

  • test/helpers/cli-test-fixtures.ts
  • test/codex-cli-writer.test.ts
  • scripts/codex.js
  • test/codex-bin-wrapper.test.ts
  • test/repair-commands.test.ts
  • test/first-run.test.ts
**/*.{js,ts,mjs,cjs}

📄 CodeRabbit inference engine (README.md)

**/*.{js,ts,mjs,cjs}: Keep npm installation scripts side-effect-free; postinstall may print a short notice but must not modify runtime state or perform setup, especially in CI or non-interactive installs.
Never run npm install or update commands automatically; only display a manual upgrade notice when appropriate.
Do not publish or take ownership of a global codex binary; preserve the official OpenAI installation as the owner of the codex command.
Keep runtime rotation and local bridge services loopback-only, and protect local bridge access with hashed client tokens.
Keep OAuth credentials and account state local; do not send them to external services as part of normal account management.
Treat Responses background mode as opt-in: requests with background: true must use stateful store=true, while default stateless routing uses store=false.
Use bounded outbound request budgets, avoid whole-pool replay when every account is rate-limited, and enter cooldown after repeated cross-account 5xx bursts.
Make experimental synchronization and backup flows non-destructive by default: preview before applying sync, preserve destination-only accounts, and fail safely on backup filename collisions.

Files:

  • test/helpers/cli-test-fixtures.ts
  • test/codex-cli-writer.test.ts
  • scripts/codex.js
  • test/codex-bin-wrapper.test.ts
  • test/repair-commands.test.ts
  • lib/codex-manager/repair-commands.ts
  • lib/codex-cli/writer.ts
  • test/first-run.test.ts
  • lib/runtime/first-run.ts
test/**

⚙️ CodeRabbit configuration file

tests must stay deterministic and use vitest. demand regression cases that reproduce concurrency bugs, token refresh races, and windows filesystem behavior. reject changes that mock real secrets or skip assertions.

Files:

  • test/helpers/cli-test-fixtures.ts
  • test/codex-cli-writer.test.ts
  • test/codex-bin-wrapper.test.ts
  • test/repair-commands.test.ts
  • test/first-run.test.ts
docs/**/*.md

📄 CodeRabbit inference engine (docs/STYLE_GUIDE.md)

docs/**/*.md: User-facing documentation should follow the page template: Title and one-line lead, Quick path commands, Core operational workflow, Troubleshooting or failure handling, and Related links
Use short sections and scan-friendly tables in documentation where they improve clarity
Prefer direct, actionable language in documentation
Use runnable command examples in documentation
Explain expected outcomes after critical commands in documentation
Keep terminology consistent with runtime names in documentation
Avoid speculative language when behavior is deterministic in documentation
Put the user problem in the first paragraph before implementation detail
Use descriptive page titles such as codex-multi-auth Features instead of generic titles on public docs
Do not repeat keyword lists in every section; search terms should appear only where they help a developer understand the page
Canonical command family is codex-multi-auth ...
Canonical runtime root is ~/.codex/multi-auth
Runtime rotation must be described as default-on unless the release policy changes
Legacy command/path references belong only in migration contexts in documentation
Compatibility aliases (codex multi auth, codex multi-auth, codex multiauth) belong only in command reference, troubleshooting, or migration contexts
Keep command flags aligned with runtime usage text in documentation
Avoid non-runnable command snippets in documentation
Avoid conflicting path guidance across documentation
Avoid legacy-first onboarding language in documentation

Organize repository documentation according to the defined layers: product entry, user operations, reference, and development.

docs/**/*.md: Do not describe codex-multi-auth as replacing @openai/codex or publishing the global codex binary; preserve the official CLI's ownership of codex.
Use codex-multi-auth for account management, and reserve codex-multi-auth-codex or mcodex for intentionally forwarding official Codex commands th...

Files:

  • docs/reference/storage-paths.md
  • docs/development/CONFIG_FIELDS.md
  • docs/troubleshooting.md
  • docs/configuration.md
docs/reference/**/*.md

📄 CodeRabbit inference engine (docs/STYLE_GUIDE.md)

New flags/settings/paths must be reflected in docs/reference/*

docs/reference/**/*.md: Keep command, API, error-contract, settings, and storage-path details in the canonical reference documentation.
Document compatibility aliases (codex multi auth, codex multi-auth, and codex multiauth) only in command-reference, troubleshooting, or migration sections.

Files:

  • docs/reference/storage-paths.md
docs/**/*

📄 CodeRabbit inference engine (docs/features.md)

docs/**/*: Keep all governance, account, usage, budget, routing, and runtime state local under ~/.codex/multi-auth; do not implement this as a hosted multi-user service.
Store only redacted usage ledger data; never store prompts or tokens in usage records.
Store only hashes and prefixes for local bridge client tokens; plain cma_local_* secrets must not be persisted or re-exposed.
Enforce account pause and drain policies at runtime on the rotation path.
Apply evaluateRuntimePolicy before runtime account selection, including pause/drain, budgets, routing profiles, and capability checks.
Rotate accounts before streaming response bytes when quota, authentication refresh, network, or server failures occur.
An invocation-level account force-pin is ephemeral, fails hard when unavailable, and must never modify the persisted switch pin.
Keep temporary provider configuration isolated from normal official Codex state by using a shadow CODEX_HOME for wrapper-launched sessions.
Use the canonical local data root ~/.codex/multi-auth and support Storage V3 migrations from older layouts.
Support project-scoped account pools and share account identity across linked worktrees of the same repository.
Persist quota cache data and selected-account synchronization so forecast/dashboard state survives runs and the active account can be written to official ~/.codex auth files.
Provide browser-first OAuth with device-auth and manual callback fallbacks, including --device-auth, --manual, --no-browser, and CODEX_AUTH_NO_BROWSER=1.
Provide an optional loopback-only local bridge for /health, /v1/models, and /v1/responses, with token lifecycle operations and deterministic integration snippets.
Ensure codex-multi-auth features prints the built-in numbered checklist through feature ID 54 and treat it as distinct from the complete product feature map.
Keep the runtime rotation proxy local-only and default-on, with status telemetry covering settings, app binding, waits...

Files:

  • docs/reference/storage-paths.md
  • docs/development/CONFIG_FIELDS.md
  • docs/troubleshooting.md
  • docs/configuration.md
docs/**/*.{md,mdx}

📄 CodeRabbit inference engine (docs/upgrade.md)

Document the canonical command names, runtime paths, configuration precedence, storage migration behavior, and upgrade procedures consistently across the referenced documentation.

Files:

  • docs/reference/storage-paths.md
  • docs/development/CONFIG_FIELDS.md
  • docs/troubleshooting.md
  • docs/configuration.md
docs/**

⚙️ CodeRabbit configuration file

keep README, SECURITY, and docs consistent with actual CLI flags and workflows. whenever behavior changes, require updated upgrade notes and mention new npm scripts.

Files:

  • docs/reference/storage-paths.md
  • docs/development/CONFIG_FIELDS.md
  • docs/troubleshooting.md
  • docs/configuration.md
docs/development/CONFIG_FIELDS.md

📄 CodeRabbit inference engine (docs/development/RUNBOOK_ADD_CONFIG_FIELD.md)

Update docs/development/CONFIG_FIELDS.md with field inventory details when adding new configuration fields

Maintain full field inventory in docs/development/CONFIG_FIELDS.md

Files:

  • docs/development/CONFIG_FIELDS.md
docs/development/**/*.md

📄 CodeRabbit inference engine (docs/DOCUMENTATION.md)

Keep internal architecture, configuration flow, repository ownership, testing, parity, metadata, and audit guidance in development documentation.

Files:

  • docs/development/CONFIG_FIELDS.md
docs/development/**/*

📄 CodeRabbit inference engine (docs/development/CONFIG_FLOW.md)

docs/development/**/*: Resolve the runtime root directory in this order: CODEX_MULTI_AUTH_DIR; explicit non-default CODEX_HOME/multi-auth; existing account-storage roots under CODEX_HOME or ~/.codex; canonical ~/.codex/multi-auth; and legacy paths only when storage signals exist.
Read dashboardDisplaySettings and pluginConfig from settings.json, while preserving legacy compatibility loading and migration.
Resolve pluginConfig values using this precedence: existing CODEX_MULTI_AUTH_CONFIG_PATH file, valid unified settings.json configuration, legacy compatibility configuration, then DEFAULT_PLUGIN_CONFIG; apply environment-variable overrides afterward.
Ignore a configured but nonexistent CODEX_MULTI_AUTH_CONFIG_PATH during loading, but create it on the first save while the variable remains set.
Resolve dashboard display values from persisted dashboardDisplaySettings, followed by normalization and fallback defaults.
Resolve account storage by selecting the root directory, using the global accounts file by default, using a project-namespaced path when project-scoped mode is active, and attempting applicable legacy project-file migration.
Normalize standalone codex-multi-auth bare subcommands to auth ... before dispatch; normalize wrapper aliases; run auth-manager commands locally; forward out-of-scope wrapper commands to the official Codex CLI.
For forwarded request-bearing commands, honor runtime rotation: resolve CODEX_MULTI_AUTH_RUNTIME_ROTATION_PROXY, then pluginConfig.codexRuntimeRotationProxy, which defaults to enabled.
When rotation is enabled for a requesting command, use a per-process-token loopback Responses proxy, a temporary shadow CODEX_HOME, and a rewritten config.toml; synchronize refreshed official Codex state on exit and remove the shadow home.
The runtime proxy must select or refresh managed accounts and rotate on rate-limit, authentication, network, or server failures before streaming begins.
The plugin host m...

Files:

  • docs/development/CONFIG_FIELDS.md
docs/development/**/*.{md,mdx}

📄 CodeRabbit inference engine (docs/development/TESTING.md)

When documentation changes, verify every command snippet is runnable, path references match runtime modules, cross-links are valid, and the feature matrix matches implemented features.

Files:

  • docs/development/CONFIG_FIELDS.md
docs/{index.md,getting-started.md,faq.md,architecture.md,features.md,configuration.md,troubleshooting.md,privacy.md,upgrade.md}

📄 CodeRabbit inference engine (docs/DOCUMENTATION.md)

Keep the listed public documentation pages as the canonical sources for operator onboarding, FAQ, architecture, features, configuration, troubleshooting, privacy, and upgrades.

Files:

  • docs/troubleshooting.md
  • docs/configuration.md
test/**/*.test.ts

📄 CodeRabbit inference engine (test/AGENTS.md)

test/**/*.test.ts: Write Vitest test suites with globals enabled (describe, it, expect)
Maintain 80%+ coverage threshold across statements, branches, functions, and lines
Use removeWithRetry() for Windows filesystem cleanup instead of bare fs.rm to handle EBUSY, EPERM, and ENOTEMPTY errors
Do not rely on dist/ in tests; use source files instead
Do not skip tests without justification
Relax lint rules for test files as configured in eslint.config.js

Files:

  • test/codex-cli-writer.test.ts
  • test/codex-bin-wrapper.test.ts
  • test/repair-commands.test.ts
  • test/first-run.test.ts
scripts/codex*.js

📄 CodeRabbit inference engine (AGENTS.md)

Do not bypass the official Codex CLI by reimplementing general Codex commands in the wrapper

Files:

  • scripts/codex.js
{scripts/*.js,lib/codex-manager/**/*.ts}

📄 CodeRabbit inference engine (AGENTS.md)

Canonical package name is codex-multi-auth; canonical command family is codex-multi-auth ...

Files:

  • scripts/codex.js
  • lib/codex-manager/repair-commands.ts
{lib,scripts}/**/*.{ts,js}

📄 CodeRabbit inference engine (AGENTS.md)

Windows filesystem safety: retry transient EBUSY/EPERM/ENOTEMPTY cleanup and write failures where tests cover Windows locks

Files:

  • scripts/codex.js
  • lib/codex-manager/repair-commands.ts
  • lib/codex-cli/writer.ts
  • lib/runtime/first-run.ts
test/**/codex-bin-wrapper.test.ts

📄 CodeRabbit inference engine (test/AGENTS.md)

Test bin wrapper lazy-load and missing dist handling with concurrent invocations in codex-bin-wrapper.test.ts

Files:

  • test/codex-bin-wrapper.test.ts
lib/**/*.ts

📄 CodeRabbit inference engine (lib/AGENTS.md)

lib/**/*.ts: Route all public exports through lib/index.ts or documented package subpaths.
Keep module dependencies acyclic and preserve the layering types/constants → storage → accounts → runtime → manager/CLI; lower layers must not import higher layers.
Preserve runtime rotation pass-through semantics except for intentionally changed auth or provider headers.
Deduplicate emails using normalizeEmailKey(), which trims and lowercases the email.
Use classes for state requiring multiple independent instances or dependency injection, including AccountManager, CircuitBreaker, SessionAffinityStore, and the CodexError hierarchy. Reserve module-level state for genuinely process-global concerns and provide a test reset helper for such state.
Never import from dist/ in source tests or library code.
Never suppress type errors.
Never patch official Codex application binaries for desktop routing.
Never use bare recursive cleanup in Windows-sensitive paths without retry handling.

Files:

  • lib/codex-manager/repair-commands.ts
  • lib/codex-cli/writer.ts
  • lib/runtime/first-run.ts
lib/**

⚙️ CodeRabbit configuration file

focus on auth rotation, windows filesystem IO, and concurrency. verify every change cites affected tests (vitest) and that new queues handle EBUSY/429 scenarios. check for logging that leaks tokens or emails.

Files:

  • lib/codex-manager/repair-commands.ts
  • lib/codex-cli/writer.ts
  • lib/runtime/first-run.ts
lib/{request,codex-cli}/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

ChatGPT-backed Codex request compatibility requires stateless defaults (store: false) unless explicit background-mode compatibility is enabled

Files:

  • lib/codex-cli/writer.ts
lib/runtime/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Do not expose account emails or tokens in runtime proxy client response headers or logs

Files:

  • lib/runtime/first-run.ts
{lib/runtime/**/*.ts,lib/policy/**/*.ts}

📄 CodeRabbit inference engine (AGENTS.md)

Runtime rotation is default-on through codexRuntimeRotationProxy; users can opt out with codex-multi-auth rotation disable or CODEX_MULTI_AUTH_RUNTIME_ROTATION_PROXY=0

Files:

  • lib/runtime/first-run.ts
lib/{storage,runtime}/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Local project-owned state defaults to ~/.codex/multi-auth; official Codex state remains under ~/.codex

Files:

  • lib/runtime/first-run.ts
lib/{runtime-rotation-proxy.ts,runtime/**/*.ts}

📄 CodeRabbit inference engine (lib/AGENTS.md)

Runtime rotation must fail open to normal official Codex forwarding when startup helpers are unavailable.

Files:

  • lib/runtime/first-run.ts
🧠 Learnings (3)
📓 Common learnings
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:06:03.936Z
Learning: Use `~/.codex/multi-auth/settings.json` as the canonical settings file, with top-level `version`, `dashboardDisplaySettings`, and `pluginConfig` fields.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:06:13.447Z
Learning: Use `~/.codex/multi-auth` as the default plugin-owned storage root, overridable with `CODEX_MULTI_AUTH_DIR`; when `CODEX_HOME` is set to a non-default directory, resolve storage strictly to `$CODEX_HOME/multi-auth` without scanning the default root.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:06:13.447Z
Learning: Keep official Codex CLI files (`~/.codex/accounts.json`, `~/.codex/auth.json`, and `~/.codex/config.toml`) separate from plugin-owned multi-auth files.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:06:13.447Z
Learning: Preserve the official CLI file-backed authentication layout; wrapper-forwarded non-auth commands must use `cli_auth_credentials_store="file"` unless explicitly configured otherwise.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:06:13.447Z
Learning: Never read or write the keychain or the `security` CLI.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:06:13.447Z
Learning: Persistently reconcile the top-level `cli_auth_credentials_store` value to `"file"` during first-run setup, wrapper startup, and `doctor --fix`, while leaving values inside `[profiles.*]` tables unchanged.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:06:13.447Z
Learning: Honor `CODEX_MULTI_AUTH_FORCE_FILE_AUTH_STORE=0` by disabling both wrapper-injected overrides and startup config reconciliation; honor `CODEX_MULTI_AUTH_ENFORCE_CLI_FILE_AUTH_STORE=0` by disabling config rewrites while retaining per-invocation overrides.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:06:13.447Z
Learning: First-run setup must use an exclusive-create marker so concurrent first invocations run setup at most once; setup failures must be debug-logged and must not block the user command.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:06:13.447Z
Learning: Use atomic writes and retry `EPERM`/`EBUSY` failures for Windows configuration writes; if reconciliation still fails because the file is locked or read-only, swallow the failure and continue forwarding.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:06:13.447Z
Learning: Use `settings.json.bak` only when `settings.json` exists but is unreadable; suppress flagged-account backup recovery while its reset-intent marker remains present.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:06:13.447Z
Learning: Exclude cache-like artifacts and `.reset-intent` markers from recovery candidates; report deterministic canonical and discovered manual backup lists through `getBackupMetadata()`.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:06:13.447Z
Learning: Named backup exports must be stored under the plugin-owned `backups` namespace, append `.json` when omitted, allow only letters, numbers, `_`, and `-`, reject path traversal and reserved `.rotate.`, `.tmp`, and `.wal` names, and avoid overwriting existing files unless an explicit force path is used.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:06:13.447Z
Learning: The local bridge must remain loopback-only, expose only `/health`, `/v1/models`, and `/v1/responses`, and persist token hashes rather than plaintext tokens; plaintext tokens may be shown only during token creation or rotation.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:06:13.447Z
Learning: Enforce account policy pause and drain entries at selection time through `evaluateRuntimePolicy`, excluding blocked accounts from hybrid rotation.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:06:13.447Z
Learning: Write app-bind provider configuration only after backing up the real `~/.codex/config.toml`; rotation disable and app unbind must restore the backup and remove the router startup entry.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:06:13.447Z
Learning: Validate storage and migration behavior with `npm run build` and the specified storage tests before shipping backup or restore changes.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:06:23.769Z
Learning: Use `codex-multi-auth` as the canonical account-manager command family; do not assume the package publishes a global `codex` binary. `codex-multi-auth-codex` is an optional forwarding wrapper.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:06:23.769Z
Learning: Only one Windows/WSL environment should run browser-based OAuth login at a time because the callback URI is fixed to `http://localhost:1455/auth/callback`; use device auth when coordination is impractical.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:06:23.769Z
Learning: Treat Windows and WSL account state as separate; accounts must be authenticated independently in each environment.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:06:23.769Z
Learning: Do not store project accounts in shared storage when repositories must remain isolated; use project-scoped storage according to the storage-path rules.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:06:23.769Z
Learning: Keep `minRotationIntervalMs` at least `60000` milliseconds by default to reduce rapid account rotation and OAuth anti-abuse invalidation.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:06:23.769Z
Learning: The runtime proxy must remain loopback-only and route Responses traffic only for forwarded, request-bearing official Codex sessions and supported app launches.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:06:23.769Z
Learning: Keep `pidOffsetEnabled` enabled for multi-process parallel workloads unless identical account scoring across processes is intentional.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:06:23.769Z
Learning: Configure bounded `retryAllAccountsMaxRetries` and `retryAllAccountsMaxWaitMs` when enabling `retryAllAccountsRateLimited`; avoid waits that can exceed the host client's response-header timeout.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:06:23.769Z
Learning: Distinguish the plugin's `fetchTimeoutMs` and `streamStallTimeoutMs` from the host client's provider-header timeout; change the host setting for host timeout errors.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:06:23.769Z
Learning: The project's own account state is stored as plain JSON under `~/.codex/multi-auth`; it must not read or delete macOS keychain items. Set the official CLI credential store to `file` unless keychain storage is deliberately retained.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:06:23.769Z
Learning: Soft reset operations must remove only the account pool and settings; they must preserve usage ledgers, budgets, account policies, routing profiles, bridge tokens, quota cache, and observability files.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:06:23.769Z
Learning: Complete uninstall must run `codex-multi-auth uninstall` before removing the npm package, because npm 7+ does not reliably run the required preuninstall cleanup.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:06:23.769Z
Learning: The uninstall cleanup must preserve the shared `bun.lock` when other Codex plugins remain installed and delete it only when this package is the sole plugin or `Codex.json` is absent.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:06:23.769Z
Learning: Use `--clear-accounts` only when permanently leaving the package because it irreversibly deletes stored credentials.
📚 Learning: 2026-06-04T06:14:18.093Z
Learnt from: ndycode
Repo: ndycode/codex-multi-auth PR: 510
File: test/scheduling-strategy-config.test.ts:1-1
Timestamp: 2026-06-04T06:14:18.093Z
Learning: In ndycode/codex-multi-auth, do not flag explicit imports from "vitest" (e.g., describe, it, expect, beforeEach/afterEach, etc.) in test files as issues—even if the Vitest config sets `globals: true`. The repo’s established convention is to keep these imports for consistency with neighboring tests; removing them would make files outliers.

Applied to files:

  • test/codex-cli-writer.test.ts
  • test/codex-bin-wrapper.test.ts
  • test/repair-commands.test.ts
  • test/first-run.test.ts
📚 Learning: 2026-06-04T06:14:24.975Z
Learnt from: ndycode
Repo: ndycode/codex-multi-auth PR: 510
File: test/runtime-rotation-proxy.test.ts:2478-2491
Timestamp: 2026-06-04T06:14:24.975Z
Learning: In ndycode/codex-multi-auth test files (e.g. `test/*.test.ts`), when creating V3 storage fixtures for accounts, it’s an intentional convention to use `as never` for deliberately minimal stored-account objects that only include `refreshToken`, `addedAt`, and `lastUsed`. Do not treat `as never` here as a type-safety problem: optional/other fields are expected to be populated by the runtime during execution, and the cast is used solely to keep the fixture minimal and consistent across existing tests.

Applied to files:

  • test/codex-cli-writer.test.ts
  • test/codex-bin-wrapper.test.ts
  • test/repair-commands.test.ts
  • test/first-run.test.ts
🪛 ast-grep (0.44.1)
test/codex-bin-wrapper.test.ts

[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { type SpawnSyncReturns, spawn, spawnSync } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { type SpawnSyncReturns, spawn, spawnSync } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

lib/codex-manager/repair-commands.ts

[warning] 1789-1789: Filesystem path is not a string literal; a request-/variable-derived path can enable path traversal. Validate and normalize the path before use.
Context: fs.readFile(codexConfigPath, "utf-8")
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(detect-non-literal-fs-filename-typescript)

lib/codex-cli/writer.ts

[warning] 308-308: Filesystem path is not a string literal; a request-/variable-derived path can enable path traversal. Validate and normalize the path before use.
Context: fs.readFile(configPath, "utf-8")
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(detect-non-literal-fs-filename-typescript)

Comment thread lib/codex-cli/writer.ts Outdated
Comment thread lib/codex-manager/repair-commands.ts Outdated
Comment thread lib/runtime/first-run.ts
Comment thread test/codex-bin-wrapper.test.ts
Four findings from review, all real:

- config.toml values could be TOML literal strings. `cli_auth_credentials_store
  = 'file'` read as unset, so doctor warned about a healthy config and the
  wrapper rewrote it to `"file"` on every startup. Both quote forms are now
  recognized, and an already-correct value is left alone whatever its quoting.

- doctor's fix metadata under-reported the remediation. `fix.changed` was
  computed only when managed accounts existed, so a machine with no accounts --
  exactly the state a user hitting keychain prompts tends to be in -- got
  `changed: false` alongside a non-empty `actions` array. `--fix --dry-run` also
  planned nothing, unlike every other doctor fix; it now emits a prepared action.

- the marker version bump was decorative. ensureFirstRunSetup short-circuits on
  marker existence, so already-installed users would never have run the new
  step. v1 markers are now migrated in place, replaying only the auth-store
  step; app bind and launcher install are deliberately not rerun so shortcuts
  the user removed stay removed. No exclusive claim is taken because both the
  config rewrite and the marker write are idempotent and atomic. An unreadable
  marker is treated as pre-v2 rather than re-triggering full setup.

- test/AGENTS.md requires the bin wrapper's lazy-load and missing-dist paths to
  be covered under concurrent invocations; the missing-dist test was
  single-process. Added concurrent coverage both with and without the compiled
  module, and switched the call recorder to per-pid files so the fixture itself
  cannot race.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ndycode

ndycode commented Jul 28, 2026

Copy link
Copy Markdown
Owner Author

Addressed all four review findings in 56a0c85. All were real; notes on each:

1. TOML literal strings (writer.ts) — confirmed. cli_auth_credentials_store = 'file' read as unset, so doctor warned about a healthy config and the wrapper-startup guard rewrote it to "file" on every launch. Both quote forms are now recognized, and an already-correct value is left alone regardless of quoting, so we no longer churn a valid config.

2. Fix metadata under-reporting (repair-commands.ts) — confirmed, and it mattered most for the exact persona this PR targets. fix.changed was only computed inside the accounts.length > 0 gate, so a machine with no accounts got changed: false next to a non-empty actions array. Auth-store remediation is now tracked independently of account storage, and --fix --dry-run emits a Prepared … (dry-run) action like every other doctor fix.

3. Marker migration (first-run.ts) — correct, and the most important of the four: the version bump was decorative, since ensureFirstRunSetup short-circuits on marker existence. v1 markers now migrate in place, replaying only the auth-store step — app bind and launcher install are deliberately not rerun so shortcuts a user removed stay removed.

On the "add an exclusively claimed migration" part, I went a different way deliberately: no claim is taken. Both operations are idempotent and atomic (ensureCodexCliFileAuthStore no-ops on a config already pinned; atomicWriteMarker renames into place), so concurrent migrations converge rather than race — a second lock would add a failure mode without removing one. There's a test asserting two concurrent migrations converge. An unreadable or truncated marker is treated as pre-v2 and migrated, rather than re-triggering full setup.

4. Concurrent missing-dist coverage (codex-bin-wrapper.test.ts) — matches the documented convention in test/AGENTS.md:142. Added concurrent coverage both with and without the compiled module present, and switched the fixture's call recorder from shared appendFileSync to per-pid files so the harness itself can't race under concurrency.

Verification: npm test 335 files / 5261 passed, 0 failed; lint, typecheck, typecheck:scripts, build all clean.

Comment thread lib/runtime/first-run.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
lib/codex-manager/repair-commands.ts (1)

1839-1844: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

--fix that silently no-ops leaves the user with a bare warning.

lib/codex-manager/repair-commands.ts:1842 gates the remediation hint on !options.fix, so when the user does run doctor --fix and ensureCodexCliFileAuthStore returns false — the documented CODEX_MULTI_AUTH_ENFORCE_CLI_FILE_AUTH_STORE=0 opt-out short-circuits at lib/codex-cli/writer.ts:314 — the check stays warn with nothing but mode=keychain. no error, no action, no reason. the #641 persona then has no idea why the fix they were told to run did nothing.

💡 proposed fix: explain the no-op
 		if (codexAuthStoreMode !== "file" && !options.fix) {
 			authStoreDetails.push("run `codex-multi-auth doctor --fix` to pin it to file");
 		}
+		if (codexAuthStoreMode !== "file" && options.fix && !authStoreFixChanged) {
+			authStoreDetails.push(
+				"--fix did not rewrite config.toml; check CODEX_MULTI_AUTH_ENFORCE_CLI_FILE_AUTH_STORE",
+			);
+		}

worth a companion case in test/repair-commands.test.ts alongside the existing EBUSY test.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/codex-manager/repair-commands.ts` around lines 1839 - 1844, Update the
repair flow around ensureCodexCliFileAuthStore and authStoreDetails so a --fix
attempt that returns false adds a warning detail explaining that remediation was
skipped, including the enforce-file-auth-store opt-out and the required action.
Preserve the existing non-fix remediation hint, and add a companion test in
repair-commands.test.ts covering the no-op case.
docs/reference/storage-paths.md (1)

80-83: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

the two opt-out bullets disagree about which sites they cover.

line 80 names three reconcile points (first-run, wrapper startup, doctor --fix), but line 83's parenthetical for CODEX_MULTI_AUTH_ENFORCE_CLI_FILE_AUTH_STORE lists "first-run, account switch/login sync, and doctor --fix" — it drops wrapper startup and adds a fourth site that line 80 never mentions. since lib/codex-cli/writer.ts:314 short-circuits every ensureCodexCliFileAuthStore call including the wrapper's, the prose "every config.toml rewrite" is correct but the list contradicts it, and a reader comparing lines 82 and 83 will conclude the wrapper reconcile is only controllable via CODEX_MULTI_AUTH_FORCE_FILE_AUTH_STORE.

📝 proposed wording
-- Set `CODEX_MULTI_AUTH_ENFORCE_CLI_FILE_AUTH_STORE=0` to opt out of every `config.toml` rewrite (first-run, account switch/login sync, and `doctor --fix`) while leaving the per-invocation `-c` override in place.
+- Set `CODEX_MULTI_AUTH_ENFORCE_CLI_FILE_AUTH_STORE=0` to opt out of every persisted `config.toml` rewrite (first-run, wrapper startup, account switch/login sync, and `doctor --fix`) while leaving the per-invocation `-c` override in place.

also worth aligning line 80's "three points" with the switch/login-sync site if that one really does rewrite the persisted value.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/reference/storage-paths.md` around lines 80 - 83, Align the two opt-out
bullets so they describe the same persisted config rewrite sites. Update the
“three points” list and the CODEX_MULTI_AUTH_ENFORCE_CLI_FILE_AUTH_STORE
description to include wrapper startup and account switch/login sync only if
both are actual ensureCodexCliFileAuthStore paths, while preserving the
distinction that FORCE controls wrapper startup reconciliation and ENFORCE
disables every config.toml rewrite.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/runtime/first-run.ts`:
- Around line 456-491: Update migrateFirstRunMarker in lib/runtime/first-run.ts
(lines 456-491) to preserve marker.version ?? 1 when authStore is "failed", and
use FIRST_RUN_MARKER_VERSION only for completed or skipped outcomes. Update
test/first-run.test.ts (lines 438-462) to expect the pre-v2 marker after
enforceAuthStore throws, then add a second ensureFirstRunSetup call verifying
the migration retries and converges to v2.

---

Outside diff comments:
In `@docs/reference/storage-paths.md`:
- Around line 80-83: Align the two opt-out bullets so they describe the same
persisted config rewrite sites. Update the “three points” list and the
CODEX_MULTI_AUTH_ENFORCE_CLI_FILE_AUTH_STORE description to include wrapper
startup and account switch/login sync only if both are actual
ensureCodexCliFileAuthStore paths, while preserving the distinction that FORCE
controls wrapper startup reconciliation and ENFORCE disables every config.toml
rewrite.

In `@lib/codex-manager/repair-commands.ts`:
- Around line 1839-1844: Update the repair flow around
ensureCodexCliFileAuthStore and authStoreDetails so a --fix attempt that returns
false adds a warning detail explaining that remediation was skipped, including
the enforce-file-auth-store opt-out and the required action. Preserve the
existing non-fix remediation hint, and add a companion test in
repair-commands.test.ts covering the no-op case.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 740702b2-649f-4c1f-b72e-38b9e5b9ada9

📥 Commits

Reviewing files that changed from the base of the PR and between 7f36dcd and 56a0c85.

📒 Files selected for processing (9)
  • docs/configuration.md
  • docs/reference/storage-paths.md
  • lib/codex-cli/writer.ts
  • lib/codex-manager/repair-commands.ts
  • lib/runtime/first-run.ts
  • test/codex-bin-wrapper.test.ts
  • test/codex-cli-writer.test.ts
  • test/first-run.test.ts
  • test/repair-commands.test.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (22)
test/**/*.test.ts

📄 CodeRabbit inference engine (test/AGENTS.md)

test/**/*.test.ts: Write Vitest test suites with globals enabled (describe, it, expect)
Maintain 80%+ coverage threshold across statements, branches, functions, and lines
Use removeWithRetry() for Windows filesystem cleanup instead of bare fs.rm to handle EBUSY, EPERM, and ENOTEMPTY errors
Do not rely on dist/ in tests; use source files instead
Do not skip tests without justification
Relax lint rules for test files as configured in eslint.config.js

Files:

  • test/codex-cli-writer.test.ts
  • test/first-run.test.ts
  • test/codex-bin-wrapper.test.ts
  • test/repair-commands.test.ts
**/*.{ts,js}

📄 CodeRabbit inference engine (AGENTS.md)

Use ESM only ("type": "module"), Node >= 18.17

Files:

  • test/codex-cli-writer.test.ts
  • lib/codex-cli/writer.ts
  • lib/codex-manager/repair-commands.ts
  • test/first-run.test.ts
  • test/codex-bin-wrapper.test.ts
  • test/repair-commands.test.ts
  • lib/runtime/first-run.ts
**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Do not use as any, @ts-ignore, or @ts-expect-error in TypeScript files

Files:

  • test/codex-cli-writer.test.ts
  • lib/codex-cli/writer.ts
  • lib/codex-manager/repair-commands.ts
  • test/first-run.test.ts
  • test/codex-bin-wrapper.test.ts
  • test/repair-commands.test.ts
  • lib/runtime/first-run.ts
{scripts/**/*.js,test/**/*.ts}

📄 CodeRabbit inference engine (AGENTS.md)

Do not use bare recursive delete logic in Windows-sensitive scripts/tests without retry handling

Files:

  • test/codex-cli-writer.test.ts
  • test/first-run.test.ts
  • test/codex-bin-wrapper.test.ts
  • test/repair-commands.test.ts
**/*.{js,ts,mjs,cjs}

📄 CodeRabbit inference engine (README.md)

**/*.{js,ts,mjs,cjs}: Keep npm installation scripts side-effect-free; postinstall may print a short notice but must not modify runtime state or perform setup, especially in CI or non-interactive installs.
Never run npm install or update commands automatically; only display a manual upgrade notice when appropriate.
Do not publish or take ownership of a global codex binary; preserve the official OpenAI installation as the owner of the codex command.
Keep runtime rotation and local bridge services loopback-only, and protect local bridge access with hashed client tokens.
Keep OAuth credentials and account state local; do not send them to external services as part of normal account management.
Treat Responses background mode as opt-in: requests with background: true must use stateful store=true, while default stateless routing uses store=false.
Use bounded outbound request budgets, avoid whole-pool replay when every account is rate-limited, and enter cooldown after repeated cross-account 5xx bursts.
Make experimental synchronization and backup flows non-destructive by default: preview before applying sync, preserve destination-only accounts, and fail safely on backup filename collisions.

Files:

  • test/codex-cli-writer.test.ts
  • lib/codex-cli/writer.ts
  • lib/codex-manager/repair-commands.ts
  • test/first-run.test.ts
  • test/codex-bin-wrapper.test.ts
  • test/repair-commands.test.ts
  • lib/runtime/first-run.ts
test/**

⚙️ CodeRabbit configuration file

tests must stay deterministic and use vitest. demand regression cases that reproduce concurrency bugs, token refresh races, and windows filesystem behavior. reject changes that mock real secrets or skip assertions.

Files:

  • test/codex-cli-writer.test.ts
  • test/first-run.test.ts
  • test/codex-bin-wrapper.test.ts
  • test/repair-commands.test.ts
docs/**/*.md

📄 CodeRabbit inference engine (docs/STYLE_GUIDE.md)

docs/**/*.md: User-facing documentation should follow the page template: Title and one-line lead, Quick path commands, Core operational workflow, Troubleshooting or failure handling, and Related links
Use short sections and scan-friendly tables in documentation where they improve clarity
Prefer direct, actionable language in documentation
Use runnable command examples in documentation
Explain expected outcomes after critical commands in documentation
Keep terminology consistent with runtime names in documentation
Avoid speculative language when behavior is deterministic in documentation
Put the user problem in the first paragraph before implementation detail
Use descriptive page titles such as codex-multi-auth Features instead of generic titles on public docs
Do not repeat keyword lists in every section; search terms should appear only where they help a developer understand the page
Canonical command family is codex-multi-auth ...
Canonical runtime root is ~/.codex/multi-auth
Runtime rotation must be described as default-on unless the release policy changes
Legacy command/path references belong only in migration contexts in documentation
Compatibility aliases (codex multi auth, codex multi-auth, codex multiauth) belong only in command reference, troubleshooting, or migration contexts
Keep command flags aligned with runtime usage text in documentation
Avoid non-runnable command snippets in documentation
Avoid conflicting path guidance across documentation
Avoid legacy-first onboarding language in documentation

Organize repository documentation according to the defined layers: product entry, user operations, reference, and development.

docs/**/*.md: Do not describe codex-multi-auth as replacing @openai/codex or publishing the global codex binary; preserve the official CLI's ownership of codex.
Use codex-multi-auth for account management, and reserve codex-multi-auth-codex or mcodex for intentionally forwarding official Codex commands th...

Files:

  • docs/reference/storage-paths.md
  • docs/configuration.md
docs/reference/**/*.md

📄 CodeRabbit inference engine (docs/STYLE_GUIDE.md)

New flags/settings/paths must be reflected in docs/reference/*

docs/reference/**/*.md: Keep command, API, error-contract, settings, and storage-path details in the canonical reference documentation.
Document compatibility aliases (codex multi auth, codex multi-auth, and codex multiauth) only in command-reference, troubleshooting, or migration sections.

Files:

  • docs/reference/storage-paths.md
docs/**/*

📄 CodeRabbit inference engine (docs/features.md)

docs/**/*: Keep all governance, account, usage, budget, routing, and runtime state local under ~/.codex/multi-auth; do not implement this as a hosted multi-user service.
Store only redacted usage ledger data; never store prompts or tokens in usage records.
Store only hashes and prefixes for local bridge client tokens; plain cma_local_* secrets must not be persisted or re-exposed.
Enforce account pause and drain policies at runtime on the rotation path.
Apply evaluateRuntimePolicy before runtime account selection, including pause/drain, budgets, routing profiles, and capability checks.
Rotate accounts before streaming response bytes when quota, authentication refresh, network, or server failures occur.
An invocation-level account force-pin is ephemeral, fails hard when unavailable, and must never modify the persisted switch pin.
Keep temporary provider configuration isolated from normal official Codex state by using a shadow CODEX_HOME for wrapper-launched sessions.
Use the canonical local data root ~/.codex/multi-auth and support Storage V3 migrations from older layouts.
Support project-scoped account pools and share account identity across linked worktrees of the same repository.
Persist quota cache data and selected-account synchronization so forecast/dashboard state survives runs and the active account can be written to official ~/.codex auth files.
Provide browser-first OAuth with device-auth and manual callback fallbacks, including --device-auth, --manual, --no-browser, and CODEX_AUTH_NO_BROWSER=1.
Provide an optional loopback-only local bridge for /health, /v1/models, and /v1/responses, with token lifecycle operations and deterministic integration snippets.
Ensure codex-multi-auth features prints the built-in numbered checklist through feature ID 54 and treat it as distinct from the complete product feature map.
Keep the runtime rotation proxy local-only and default-on, with status telemetry covering settings, app binding, waits...

Files:

  • docs/reference/storage-paths.md
  • docs/configuration.md
docs/**/*.{md,mdx}

📄 CodeRabbit inference engine (docs/upgrade.md)

Document the canonical command names, runtime paths, configuration precedence, storage migration behavior, and upgrade procedures consistently across the referenced documentation.

Files:

  • docs/reference/storage-paths.md
  • docs/configuration.md
docs/**

⚙️ CodeRabbit configuration file

keep README, SECURITY, and docs consistent with actual CLI flags and workflows. whenever behavior changes, require updated upgrade notes and mention new npm scripts.

Files:

  • docs/reference/storage-paths.md
  • docs/configuration.md
{lib,scripts}/**/*.{ts,js}

📄 CodeRabbit inference engine (AGENTS.md)

Windows filesystem safety: retry transient EBUSY/EPERM/ENOTEMPTY cleanup and write failures where tests cover Windows locks

Files:

  • lib/codex-cli/writer.ts
  • lib/codex-manager/repair-commands.ts
  • lib/runtime/first-run.ts
lib/{request,codex-cli}/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

ChatGPT-backed Codex request compatibility requires stateless defaults (store: false) unless explicit background-mode compatibility is enabled

Files:

  • lib/codex-cli/writer.ts
lib/**/*.ts

📄 CodeRabbit inference engine (lib/AGENTS.md)

lib/**/*.ts: Route all public exports through lib/index.ts or documented package subpaths.
Keep module dependencies acyclic and preserve the layering types/constants → storage → accounts → runtime → manager/CLI; lower layers must not import higher layers.
Preserve runtime rotation pass-through semantics except for intentionally changed auth or provider headers.
Deduplicate emails using normalizeEmailKey(), which trims and lowercases the email.
Use classes for state requiring multiple independent instances or dependency injection, including AccountManager, CircuitBreaker, SessionAffinityStore, and the CodexError hierarchy. Reserve module-level state for genuinely process-global concerns and provide a test reset helper for such state.
Never import from dist/ in source tests or library code.
Never suppress type errors.
Never patch official Codex application binaries for desktop routing.
Never use bare recursive cleanup in Windows-sensitive paths without retry handling.

Files:

  • lib/codex-cli/writer.ts
  • lib/codex-manager/repair-commands.ts
  • lib/runtime/first-run.ts
lib/**

⚙️ CodeRabbit configuration file

focus on auth rotation, windows filesystem IO, and concurrency. verify every change cites affected tests (vitest) and that new queues handle EBUSY/429 scenarios. check for logging that leaks tokens or emails.

Files:

  • lib/codex-cli/writer.ts
  • lib/codex-manager/repair-commands.ts
  • lib/runtime/first-run.ts
docs/{index.md,getting-started.md,faq.md,architecture.md,features.md,configuration.md,troubleshooting.md,privacy.md,upgrade.md}

📄 CodeRabbit inference engine (docs/DOCUMENTATION.md)

Keep the listed public documentation pages as the canonical sources for operator onboarding, FAQ, architecture, features, configuration, troubleshooting, privacy, and upgrades.

Files:

  • docs/configuration.md
{scripts/*.js,lib/codex-manager/**/*.ts}

📄 CodeRabbit inference engine (AGENTS.md)

Canonical package name is codex-multi-auth; canonical command family is codex-multi-auth ...

Files:

  • lib/codex-manager/repair-commands.ts
test/**/codex-bin-wrapper.test.ts

📄 CodeRabbit inference engine (test/AGENTS.md)

Test bin wrapper lazy-load and missing dist handling with concurrent invocations in codex-bin-wrapper.test.ts

Files:

  • test/codex-bin-wrapper.test.ts
lib/runtime/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Do not expose account emails or tokens in runtime proxy client response headers or logs

Files:

  • lib/runtime/first-run.ts
{lib/runtime/**/*.ts,lib/policy/**/*.ts}

📄 CodeRabbit inference engine (AGENTS.md)

Runtime rotation is default-on through codexRuntimeRotationProxy; users can opt out with codex-multi-auth rotation disable or CODEX_MULTI_AUTH_RUNTIME_ROTATION_PROXY=0

Files:

  • lib/runtime/first-run.ts
lib/{storage,runtime}/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Local project-owned state defaults to ~/.codex/multi-auth; official Codex state remains under ~/.codex

Files:

  • lib/runtime/first-run.ts
lib/{runtime-rotation-proxy.ts,runtime/**/*.ts}

📄 CodeRabbit inference engine (lib/AGENTS.md)

Runtime rotation must fail open to normal official Codex forwarding when startup helpers are unavailable.

Files:

  • lib/runtime/first-run.ts
🧠 Learnings (3)
📓 Common learnings
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:40:40.334Z
Learning: Resolve runtime configuration sources in this order: existing CODEX_MULTI_AUTH_CONFIG_PATH file, valid unified settings.json pluginConfig, legacy compatibility files, then DEFAULT_PLUGIN_CONFIG; apply environment-variable overrides after source selection.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:40:40.334Z
Learning: Treat a set-but-missing CODEX_MULTI_AUTH_CONFIG_PATH as ignored during loading, while still using it as the save target.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:40:40.334Z
Learning: When CODEX_HOME is non-default, resolve multi-auth strictly under $CODEX_HOME/multi-auth without scanning other roots; CODEX_MULTI_AUTH_DIR may re-home multi-auth-owned files.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:40:40.334Z
Learning: Treat deprecated selectors such as gpt-5-codex and gpt-5.1-codex* as compatibility aliases, retrying with the current documented Codex model only after an actual unsupported-model response; fall back to gpt-5.4 only after a real unsupported-model response.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:40:40.334Z
Learning: The runtime rotation proxy must preserve request bodies and streaming responses, replace outbound authorization with the selected managed account, remove hop-by-hop/private metadata headers and stale decoded content-encoding, and return a structured pool-exhaustion error when no account is available.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:40:40.334Z
Learning: Token revocation responses must be returned directly instead of rotating accounts; revoked accounts receive the configured token-invalidation cooldown, and rotation must honor minRotationIntervalMs unless disabled.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:40:40.334Z
Learning: In sequential scheduling, keep using the active account until exhaustion, reclaim recovered accounts when scanning wraps, and never override a manual switch pin; sequential mode intentionally ignores per-session affinity.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:40:40.334Z
Learning: For parallel agents, pidOffsetEnabled should provide deterministic per-process account bias; retry-all-accounts waits must remain bounded by configured retry and wait budgets; routingMutex only serializes selection within one process.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:40:40.334Z
Learning: Package install scripts must remain side-effect-free; postinstall may print only a short notice and must not perform setup or updates.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:40:40.334Z
Learning: First-run desktop self-healing must run only on durable global installs, use the first-run marker, and avoid consuming or replaying setup for npx/project-local installs except the documented migration behavior.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:40:40.334Z
Learning: The wrapper must never automatically run npm install or update commands; version checks may only print a manual upgrade notice on a TTY or with CODEX_MULTI_AUTH_DEBUG=1.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:40:40.334Z
Learning: Persistent app binding must back up and restore the real Codex config, use a localhost router and user startup entry, and never patch official app files.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:41:11.581Z
Learning: Use device authentication for remote, SSH, container, or headless environments, and use manual login only when device authentication is unavailable.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:41:11.581Z
Learning: Only one Windows/WSL installation should run browser OAuth login at a time because the callback port is fixed at `localhost:1455`.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:41:11.581Z
Learning: Treat account state as environment-specific: Windows and WSL installations maintain separate state directories and require independent sign-in.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:41:11.581Z
Learning: Re-login accounts after `missing field id_token`, `refresh_token_reused`, or `token_expired` authentication failures.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:41:11.581Z
Learning: Do not rapidly rotate accounts through the proxy; keep `minRotationIntervalMs` at least `60000` and re-login accounts whose tokens were invalidated.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:41:11.581Z
Learning: Keep runtime rotation disabled or enabled consistently with the stored setting and environment override, and use the forwarding wrapper for routed official Codex sessions.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:41:11.581Z
Learning: For high parallelism, add accounts, keep `pidOffsetEnabled` enabled, use bounded `retryAllAccountsMaxRetries` and `retryAllAccountsMaxWaitMs`, and understand that `routingMutex` coordinates only within one process.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:41:11.581Z
Learning: Keep plugin request and stream-stall timeouts bounded and distinguish them from the host client's provider-header timeout.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:41:11.581Z
Learning: Use project-scoped storage when repositories should not share accounts, and migrate worktree state by running `codex-multi-auth list` in the worktree.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:41:11.581Z
Learning: Soft reset must remove only the account pool and settings; it must preserve usage ledgers, budgets, policies, routing profiles, bridge tokens, quota cache, and observability files.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:41:11.581Z
Learning: Run `codex-multi-auth uninstall` before `npm uninstall -g codex-multi-auth` to remove residual plugin, launcher, cache, and app-bind artifacts; use `--clear-accounts` only for an intentional irreversible credential wipe.
Learnt from: CR
Repo: ndycode/codex-multi-auth

Timestamp: 2026-07-28T11:41:11.581Z
Learning: Bug reports should include the report and doctor JSON output, Codex and package versions, global npm package status, and the failing command's complete terminal output.
📚 Learning: 2026-06-04T06:14:18.093Z
Learnt from: ndycode
Repo: ndycode/codex-multi-auth PR: 510
File: test/scheduling-strategy-config.test.ts:1-1
Timestamp: 2026-06-04T06:14:18.093Z
Learning: In ndycode/codex-multi-auth, do not flag explicit imports from "vitest" (e.g., describe, it, expect, beforeEach/afterEach, etc.) in test files as issues—even if the Vitest config sets `globals: true`. The repo’s established convention is to keep these imports for consistency with neighboring tests; removing them would make files outliers.

Applied to files:

  • test/codex-cli-writer.test.ts
  • test/first-run.test.ts
  • test/codex-bin-wrapper.test.ts
  • test/repair-commands.test.ts
📚 Learning: 2026-06-04T06:14:24.975Z
Learnt from: ndycode
Repo: ndycode/codex-multi-auth PR: 510
File: test/runtime-rotation-proxy.test.ts:2478-2491
Timestamp: 2026-06-04T06:14:24.975Z
Learning: In ndycode/codex-multi-auth test files (e.g. `test/*.test.ts`), when creating V3 storage fixtures for accounts, it’s an intentional convention to use `as never` for deliberately minimal stored-account objects that only include `refreshToken`, `addedAt`, and `lastUsed`. Do not treat `as never` here as a type-safety problem: optional/other fields are expected to be populated by the runtime during execution, and the cast is used solely to keep the fixture minimal and consistent across existing tests.

Applied to files:

  • test/codex-cli-writer.test.ts
  • test/first-run.test.ts
  • test/codex-bin-wrapper.test.ts
  • test/repair-commands.test.ts
🪛 ast-grep (0.44.1)
test/codex-bin-wrapper.test.ts

[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { type SpawnSyncReturns, spawn, spawnSync } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { type SpawnSyncReturns, spawn, spawnSync } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { type SpawnSyncReturns, spawn, spawnSync } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { type SpawnSyncReturns, spawn, spawnSync } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { type SpawnSyncReturns, spawn, spawnSync } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { type SpawnSyncReturns, spawn, spawnSync } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { type SpawnSyncReturns, spawn, spawnSync } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

🔇 Additional comments (12)
lib/codex-cli/writer.ts (2)

269-287: LGTM!


311-355: LGTM!

test/codex-cli-writer.test.ts (1)

586-607: LGTM!

Also applies to: 661-667

lib/runtime/first-run.ts (2)

54-91: LGTM!


513-593: LGTM!

test/first-run.test.ts (1)

337-436: LGTM!

Also applies to: 464-490

test/codex-bin-wrapper.test.ts (2)

104-140: LGTM!


3033-3143: LGTM!

lib/codex-manager/repair-commands.ts (1)

2259-2274: LGTM!

test/repair-commands.test.ts (1)

1038-1059: LGTM!

Also applies to: 1098-1118, 1171-1180

docs/configuration.md (1)

181-184: LGTM!

docs/reference/storage-paths.md (1)

62-70: LGTM!

Also applies to: 93-93, 155-155

Comment thread lib/runtime/first-run.ts
…lure

A failed auth-store step was recorded with the current marker version, and
ensureFirstRunSetup short-circuits on version alone. One transient Windows
EPERM/EBUSY on a locked config.toml therefore left the CLI on keychain mode
permanently -- manufacturing the exact symptom this branch exists to remove.

markerVersionFor keeps the marker pre-v2 when the step fails, so the next
invocation replays it. The step is idempotent, so the retry costs one extra
marker read and self-heals as soon as the write succeeds.

Applied to initial setup as well, not just the migration path: a first-run
auth-store failure recorded the current version and never retried either. It
now records the older version, and the next run replays only that step --
never app bind or launcher install.

"skipped" still advances the version. It is the normal outcome for a config
already pinned to "file", so treating it as unfinished would keep the marker
below the current version forever on healthy installs. It also covers an
explicit CODEX_MULTI_AUTH_ENFORCE_CLI_FILE_AUTH_STORE=0 opt-out, which is a
user choice; if that opt-out is later removed, the wrapper-startup guard and
`doctor --fix` both still reconcile the config.

The previous test asserted the buggy behavior -- that a failed step still
recorded the current version -- which is why the suite stayed green. Replaced
with fail -> stays pre-v2 -> retries -> converges, for both the migration and
initial-setup paths.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment thread lib/runtime/first-run.ts
Three defects found while auditing this branch, two of them introduced or
amplified by it.

Line endings were not preserved. Splitting on /\r?\n/ and rejoining with "\n"
rewrote an entire CRLF config.toml as LF -- an edit nobody asked for, and one
this branch made far more likely by running the reconcile on every wrapper
startup rather than only on account switch. lib/runtime/config-toml.ts already
had the right pattern; this now follows it.

The top-level table scoping used /^\s*\[/, which also matches a continuation
line of a multi-line array such as `[1, 2],`. That cut the scan short, so a
real top-level assignment below it was missed and a second one appended.
Duplicate keys are invalid TOML and would stop the official CLI from starting
at all. Replaced with the complete-header shape used by readTomlTableName,
extended to tolerate a trailing comment.

`doctor --fix --dry-run` planned a rewrite that CODEX_MULTI_AUTH_ENFORCE_CLI_
FILE_AUTH_STORE=0 would have suppressed. shouldEnforceCodexCliFileAuthStore is
now exported so the plan and the apply agree.

Also reset the auth-store writer mocks per test: vi.clearAllMocks() keeps
implementations, so one case's mockReturnValue leaked into the next. That bled
a false expectation between the new doctor tests.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ndycode

ndycode commented Jul 28, 2026

Copy link
Copy Markdown
Owner Author

Two more rounds since the last update — one fixing a bug both reviewers caught, one from a self-audit of the whole branch.

285af39 — failed auth-store step no longer strands the user

Both CodeRabbit and Greptile independently flagged this and both were right. migrateFirstRunMarker wrote version: 2 even when the step failed, and ensureFirstRunSetup short-circuits on version alone — so one transient Windows EPERM/EBUSY on a locked config.toml would leave the CLI on keychain mode permanently. That is the exact #641 symptom, i.e. the fix could have manufactured the bug it fixes.

My own test had asserted the buggy behaviour (marker.version === FIRST_RUN_MARKER_VERSION after a failure), which is why the suite stayed green. Replaced with fail → stays pre-v2 → retries → converges.

Extended beyond what was reported: initial setup had the identical flaw. A first-run auth-store failure also recorded the current version and never retried. It now records the older version, and the next run replays only that step via the migration path — never app bind or launcher.

On Greptile's wider form (treat skipped as unfinished too): not taken, deliberately. skipped is the normal outcome for a config already pinned to "file", so that would keep healthy installs below the current version forever and make the field meaningless again. It also covers an explicit CODEX_MULTI_AUTH_ENFORCE_CLI_FILE_AUTH_STORE=0 opt-out — a user's choice, not a failure — and if that opt-out is later removed, the wrapper-startup guard and doctor --fix both still reconcile the config. CodeRabbit's narrower "only on failed" was the correct read.

58a5d0f — three defects found auditing the branch

  1. CRLF configs were silently converted to LF. split(/\r?\n/) + join("\n") rewrote the whole file. Pre-existing, but this branch made it fire on every wrapper startup instead of only on account switch, so a Windows user with a CRLF config.toml would get an unrequested whole-file rewrite. lib/runtime/config-toml.ts already had the right pattern; this now follows it.

  2. Duplicate-key corruption from a too-loose table-header regex — introduced by this branch. The top-level scoping used /^\s*\[/, which also matches a continuation line of a multi-line array ([1, 2],). That cuts the scan short, misses a real top-level assignment below it, and appends a second one. Duplicate keys are invalid TOML and would stop the official CLI from starting at all. Now uses the complete-header shape from readTomlTableName, extended to tolerate a trailing comment.

  3. --fix --dry-run promised a rewrite the opt-out would suppress. shouldEnforceCodexCliFileAuthStore is now exported so plan and apply agree.

Also reset the auth-store writer mocks per test — vi.clearAllMocks() keeps implementations, so one case's mockReturnValue leaked into the next (this file's own comment warns about exactly that).

Audited and found clean

Wrapper guard placement and lazy-import fallback; TOML_TABLE_HEADER_PATTERN has no /g flag, so no lastIndex statefulness; empty/missing-file handling; new checks are warn severity so doctor's exit code is unchanged; and config.toml is explicitly excluded from shadow-home sync-back (SHADOW_HOME_CONFIG_FILE), so the rotation path cannot clobber the pin. There is now exactly one auth-store config parser, shared by the writer and doctor.

One thing flagged rather than changed: the wrapper (resolveWindowsUserHomeDir) and the lib (getResolvedUserHomeDir) order HOME and HOMEDRIVE+HOMEPATH differently. They agree whenever USERPROFILE is set — effectively always — and CODEX_HOME short-circuits both. Pre-existing, and changing home resolution carries more risk than the theoretical divergence it would remove.

Verification: npm test 335 files / 5268 passed, 0 failed; lint, typecheck, typecheck:scripts, build all clean.

@ndycode
ndycode merged commit 11130ae into main Jul 28, 2026
2 checks passed
@ndycode ndycode mentioned this pull request Jul 28, 2026
6 tasks
ResponseIV pushed a commit to ResponseIV/codex-multi-auth that referenced this pull request Aug 5, 2026
Two fixes that change where the official Codex CLI keeps its state, plus a
diagnostic that can now repair the first instead of only reporting it.

Minor rather than patch: behaviour changes, it is not purely corrective. The
wrapper now writes cli_auth_credentials_store into ~/.codex/config.toml at
first run and on wrapper startup, where before it only did so on switch or
login. 2.7.1 was explicitly "no new features and no configuration changes";
this one does change configuration behaviour, and documents a previously
undocumented opt-out.

Closes ndycode#641. Landed as ndycode#642, ndycode#639, and ndycode#643.
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.

[bug] CodexBar continuously asks me to log into the login keychain despite "Always Allow" being used

1 participant