Skip to content
Open
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ Codex CLI-first multi-account OAuth manager for the official `@openai/codex` CLI
### Option A: Standard install

```bash
npm i -g @openai/codex
npm i -g codex-multi-auth
```

Expand All @@ -74,16 +75,18 @@ codex auth status

### Step-by-step

1. Install global package:
1. Install global packages:
- `npm i -g @openai/codex`
- `npm i -g codex-multi-auth`
2. Run first login flow with `codex auth login`
3. Validate state with `codex auth status` and `codex auth check`
3. Validate state with `codex auth list` and `codex auth check`
4. Confirm routing with `codex auth forecast --live`

### Verification

```bash
codex auth status
codex auth list
codex auth check
```

Expand All @@ -95,7 +98,7 @@ codex auth check

```bash
codex auth login
codex auth status
codex auth list
codex auth check
codex auth forecast --live
```
Expand Down
2 changes: 2 additions & 0 deletions docs/reference/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Compatibility aliases are supported:
| `codex auth switch <index>` | Set active account by index |
| `codex auth check` | Run quick account health check |
| `codex auth features` | Print implemented feature summary |
| `codex auth restore-backup` | Open the backup restore picker directly |

---

Expand Down Expand Up @@ -111,6 +112,7 @@ codex auth report --live --json
Repair and recovery:

```bash
codex auth restore-backup
codex auth fix --dry-run
codex auth fix --live --model gpt-5-codex
codex auth doctor --fix
Expand Down
6 changes: 6 additions & 0 deletions docs/reference/public-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ Compatibility policy for Tier B:
- Existing exported symbols must not be removed in this release line.
- Deprecated usage may be documented, but hard removals require a major version transition plan.

Current additive compatibility note:

- `importAccounts()` now returns `{ imported, total, skipped, changed }` at runtime.
- The exported `ImportAccountsResult` type keeps `changed` optional so older callers modeling the legacy shape remain source-compatible.
- New callers should read `changed` to distinguish duplicate-only no-ops from metadata-refresh writes.

### Tier C: Internal APIs

Internal APIs are any non-exported internals and implementation details not covered by Tier A or Tier B.
Expand Down
14 changes: 14 additions & 0 deletions docs/reference/storage-paths.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Override root:
| --- | --- |
| Unified settings | `~/.codex/multi-auth/settings.json` |
| Accounts | `~/.codex/multi-auth/openai-codex-accounts.json` |
| Named backups | `~/.codex/multi-auth/backups/<name>.json` |
| Accounts backup | `~/.codex/multi-auth/openai-codex-accounts.json.bak` |
| Accounts WAL | `~/.codex/multi-auth/openai-codex-accounts.json.wal` |
| Flagged accounts | `~/.codex/multi-auth/openai-codex-flagged-accounts.json` |
Expand Down Expand Up @@ -56,6 +57,7 @@ Backup metadata:
When project-scoped behavior is enabled:

- `~/.codex/multi-auth/projects/<project-key>/openai-codex-accounts.json`
- `~/.codex/multi-auth/projects/<project-key>/backups/<name>.json`

`<project-key>` is derived as:

Expand Down Expand Up @@ -100,6 +102,17 @@ Rules:
- `.rotate.`, `.tmp`, and `.wal` names are rejected
- existing files are not overwritten unless a lower-level force path is used explicitly

Restore workflow:

1. Run `codex auth login`.
2. Open the `Recovery` section.
3. Choose `Restore From Backup`.
4. Pick a backup and confirm the merge summary before import.

Direct entrypoint:

- Run `codex auth restore-backup` to open the same picker without entering the full login dashboard first.

---

## oc-chatgpt Target Paths
Expand All @@ -115,6 +128,7 @@ Experimental sync targets the companion `oc-chatgpt-multi-auth` storage layout:
## Verification Commands

```bash
codex auth login
codex auth status
codex auth list
```
Expand Down
44 changes: 36 additions & 8 deletions docs/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,24 +89,52 @@ codex auth doctor --json

---

## Soft Reset
## Reset Options

PowerShell:
- Delete a single saved account: `codex auth login` → pick account → **Delete Account**
- Delete saved accounts: `codex auth login` → Danger Zone → **Delete Saved Accounts**
- Reset local state: `codex auth login` → Danger Zone → **Reset Local State**

Exact effects:

| Action | Saved accounts | Flagged/problem accounts | Settings | Codex CLI sync state | Quota cache |
| --- | --- | --- | --- | --- | --- |
| Delete Account | Delete the selected saved account | Delete the matching flagged/problem entry for that refresh token | Keep | Keep | Keep |
| Delete Saved Accounts | Delete all saved accounts | Keep | Keep | Keep | Keep |
| Reset Local State | Delete all saved accounts | Delete all flagged/problem accounts | Keep | Keep | Clear |

To perform the same actions manually:

Delete saved accounts only:

```powershell
Remove-Item "$HOME\.codex\multi-auth\openai-codex-accounts.json" -Force -ErrorAction SilentlyContinue
Remove-Item "$HOME\.codex\multi-auth\openai-codex-flagged-accounts.json" -Force -ErrorAction SilentlyContinue
Remove-Item "$HOME\.codex\multi-auth\settings.json" -Force -ErrorAction SilentlyContinue
codex auth login
Remove-Item "$HOME\.codex\multi-auth\openai-codex-accounts.json.wal" -Force -ErrorAction SilentlyContinue
Remove-Item "$HOME\.codex\multi-auth\openai-codex-accounts.json.bak*" -Force -ErrorAction SilentlyContinue
```

```bash
rm -f ~/.codex/multi-auth/openai-codex-accounts.json
rm -f ~/.codex/multi-auth/openai-codex-accounts.json.wal
rm -f ~/.codex/multi-auth/openai-codex-accounts.json.bak*
```

Bash:
Reset local state (also clears flagged/problem accounts and quota cache; preserves settings and Codex CLI sync state):

```powershell
Remove-Item "$HOME\.codex\multi-auth\openai-codex-accounts.json" -Force -ErrorAction SilentlyContinue
Remove-Item "$HOME\.codex\multi-auth\openai-codex-accounts.json.wal" -Force -ErrorAction SilentlyContinue
Remove-Item "$HOME\.codex\multi-auth\openai-codex-accounts.json.bak*" -Force -ErrorAction SilentlyContinue
Remove-Item "$HOME\.codex\multi-auth\openai-codex-flagged-accounts.json" -Force -ErrorAction SilentlyContinue
Remove-Item "$HOME\.codex\multi-auth\quota-cache.json" -Force -ErrorAction SilentlyContinue
```

```bash
rm -f ~/.codex/multi-auth/openai-codex-accounts.json
rm -f ~/.codex/multi-auth/openai-codex-accounts.json.wal
rm -f ~/.codex/multi-auth/openai-codex-accounts.json.bak*
rm -f ~/.codex/multi-auth/openai-codex-flagged-accounts.json
rm -f ~/.codex/multi-auth/settings.json
codex auth login
rm -f ~/.codex/multi-auth/quota-cache.json
```

---
Expand Down
56 changes: 39 additions & 17 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@ import {
} from "./lib/logger.js";
import { checkAndNotify } from "./lib/auto-update-checker.js";
import { handleContextOverflow } from "./lib/context-overflow.js";
import {
DESTRUCTIVE_ACTION_COPY,
deleteAccountAtIndex,
deleteSavedAccounts,
resetLocalState,
} from "./lib/destructive-actions.js";
import {
AccountManager,
getAccountIdCandidates,
Expand All @@ -122,13 +128,11 @@ import {
loadAccounts,
saveAccounts,
withAccountStorageTransaction,
clearAccounts,
setStoragePath,
exportAccounts,
importAccounts,
loadFlaggedAccounts,
saveFlaggedAccounts,
clearFlaggedAccounts,
findMatchingAccountIndex,
StorageError,
formatStorageErrorHint,
Expand Down Expand Up @@ -3101,19 +3105,18 @@ while (attempted.size < Math.max(1, accountCount)) {

if (menuResult.mode === "manage") {
if (typeof menuResult.deleteAccountIndex === "number") {
const target = workingStorage.accounts[menuResult.deleteAccountIndex];
if (target) {
workingStorage.accounts.splice(menuResult.deleteAccountIndex, 1);
clampActiveIndices(workingStorage);
await saveAccounts(workingStorage);
await saveFlaggedAccounts({
version: 1,
accounts: flaggedStorage.accounts.filter(
(flagged) => flagged.refreshToken !== target.refreshToken,
),
});
const deleted = await deleteAccountAtIndex({
storage: workingStorage,
index: menuResult.deleteAccountIndex,
});
if (deleted) {
invalidateAccountManagerCache();
console.log(`\nDeleted ${target.email ?? `Account ${menuResult.deleteAccountIndex + 1}`}.\n`);
const label = `Account ${menuResult.deleteAccountIndex + 1}`;
const flaggedNote =
deleted.removedFlaggedCount > 0
? ` Removed ${deleted.removedFlaggedCount} matching problem account${deleted.removedFlaggedCount === 1 ? "" : "s"}.`
: "";
console.log(`\nDeleted ${label}.${flaggedNote}\n`);
}
continue;
}
Expand Down Expand Up @@ -3143,16 +3146,35 @@ while (attempted.size < Math.max(1, accountCount)) {
if (menuResult.mode === "fresh") {
startFresh = true;
if (menuResult.deleteAll) {
await clearAccounts();
await clearFlaggedAccounts();
const result = await deleteSavedAccounts();
invalidateAccountManagerCache();
console.log(
"\nCleared saved accounts from active storage. Recovery snapshots remain available. Starting fresh.\n",
`\n${
result.accountsCleared
? DESTRUCTIVE_ACTION_COPY.deleteSavedAccounts.completed
: "Delete saved accounts completed with warnings. Some saved account artifacts could not be removed; see logs."
}\n`,
);
}
break;
}

if (menuResult.mode === "reset") {
startFresh = true;
const result = await resetLocalState();
invalidateAccountManagerCache();
console.log(
`\n${
result.accountsCleared &&
result.flaggedCleared &&
result.quotaCacheCleared
? DESTRUCTIVE_ACTION_COPY.resetLocalState.completed
: "Reset local state completed with warnings. Some local artifacts could not be removed; see logs."
}\n`,
);
break;
}

startFresh = false;
break;
}
Expand Down
Loading