Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,37 @@ Remove-Item "$env:LOCALAPPDATA\codex-auth\bin\codex-auth-auto.exe" -Force -Error
| Command | Description |
|---------|-------------|
| `codex-auth list [--live] [--api\|--skip-api]` | List all accounts. `--live` keeps refreshing the terminal view; `--api` forces remote refresh, while `--skip-api` forbids remote API use for this command. |
| `codex-auth login [--device-auth]` | Run `codex login` (optionally with `--device-auth`), then add the current account |
| `codex-auth login [--device-auth] [--group <name>]` | Run `codex login` (optionally with `--device-auth`), then add the current account to the default account set or a named group |
| `codex-auth switch [--live] [--auto] [--api\|--skip-api]` | Switch the active account interactively. Without `--live` it exits after one switch; with `--live` it stays open and keeps refreshing. `--auto` requires `--live` and auto-switches away from the current account when the live view shows it as exhausted or returns a non-200 usage API status. |
| `codex-auth switch <query>` | Switch the active account directly by row number, alias, or fuzzy match using stored local data only. |
| `codex-auth remove [--live] [--api\|--skip-api]` | Interactive remove. `--live` keeps the picker open after each deletion; `--api` forces remote refresh and `--skip-api` forbids remote API use for this command. |
| `codex-auth remove <query> [<query>...]` | Remove one or more accounts by row number, alias, email, account name, or `account_key` match using stored local data. |
| `codex-auth remove --all` | Remove all stored accounts. |
| `codex-auth status` | Show auto-switch, service, and usage status |

### Account Groups

Groups keep separate Codex account sets with their own `CODEX_HOME`. The `default` group is the normal Codex home. Named groups can be used to keep workspaces such as `work`, `trading`, or `personal` separate.

See [docs/account-groups.md](./docs/account-groups.md) for the full command reference.

| Command | Description |
|---------|-------------|
| `codex-auth group list` | List configured groups and account counts |
| `codex-auth group create <name> [<account>...]` | Create a group and optionally copy existing accounts into it |
| `codex-auth group <name> list [--live] [--api\|--skip-api]` | List accounts in one group |
| `codex-auth group <name> login [--device-auth]` | Login and add the account directly to one group |
| `codex-auth group <name> add <account> [<account>...]` | Copy accounts from another group into this group |
| `codex-auth group <name> copy [<account>...]` | Copy accounts into this group; without selectors, choose interactively |
| `codex-auth group <name> move [<account>...]` | Move accounts into this group; without selectors, choose interactively |
| `codex-auth group <name> switch [--live] [--auto] [--api\|--skip-api]` | Switch the active account inside one group |
| `codex-auth group <name> auto enable\|disable` | Enable or disable background auto-switching for one group |
| `codex-auth group <name> config api enable\|disable` | Enable or disable usage and account APIs for one group |
| `codex-auth group <name> status` | Show auto-switch and usage status for one group |
| `codex-auth group <name> launch [resume [session]] [-- <codext-arg>...]` | Launch `codext` with this group's `CODEX_HOME` |
| `codex-auth project set-group <name>` | Remember a group for the current project directory |
| `codex-auth launch [resume [session]] [-- <codext-arg>...]` | Launch `codext` with the remembered project group, or `default` |

### Import

| Command | Description |
Expand Down Expand Up @@ -226,8 +249,11 @@ Add the currently logged-in Codex account:
```shell
codex-auth login
codex-auth login --device-auth
codex-auth login --group work --device-auth
```

`--group <name>` creates the group when needed and stores the logged-in account in that group's Codex home.

### Import

#### Single File
Expand Down
169 changes: 169 additions & 0 deletions docs/account-groups.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
# Account Groups

Account groups provide separate Codex homes for separate account pools.

The managed layout is:

- `default` uses `~/.codex`, which is the normal Codex home.
- every other managed group uses a folder under `~/codex-auth/groups/`.
- `~/codex-auth/groups.json` stores mappings when a group name points at a folder with a different name.
- `~/codex-auth/projects.json` remembers the preferred group for project directories.
- archived group folders move under `~/codex-auth/archive/`.

For example, a `work` group normally uses:

```sh
~/codex-auth/groups/work
```

## Managed Group Commands

```sh
codex-auth list
codex-auth group list
codex-auth group <name> status
codex-auth group create <name> [<account>...]
codex-auth group <name> login [--device-auth]
codex-auth group <name> add <account> [<account>...]
codex-auth group <name> copy [<account>...]
codex-auth group <name> move [<account>...]
codex-auth group <name> remove <account> [<account>...]
codex-auth group <name> list [--live] [--api|--skip-api]
codex-auth group <name> import <path> [--alias <alias>]
codex-auth group <name> switch [--live] [--auto] [--api|--skip-api]
codex-auth group <name> switch <query>
codex-auth group <name> auto enable|disable
codex-auth group <name> auto --5h <percent> [--weekly <percent>]
codex-auth group <name> launch [resume [session]] [-- <codext-arg>...]
codex-auth group archive <name>
codex-auth group delete <name> --force
codex-auth group <name> path
codex-auth project show
codex-auth project set-group <name>
codex-auth project clear
codex-auth launch [resume [session]] [-- <codext-arg>...]
```

`group list <name>`, `group status <name>`, and `group path <name>` are also accepted as aliases for the preferred `group <name> ...` forms.

`list` shows the cross-group account dashboard from the default `CODEX_HOME`, separated into group sections and including a `GROUP` column for each account row. In color terminals, `default` uses gray and managed groups use their assigned display colors; active rows and group separators are bold/darker, while inactive rows use lighter tones from the same group color.

Use `group <name> list --skip-api` to print one group's accounts using local data. Use `group <name> switch` to open the interactive switcher inside one group, or `group <name> switch <query>` to switch directly. Use `group <name> status` for service and auto-switch status, and `group <name> path` for the group's `CODEX_HOME`.

## Logging In To A Group

Use group login when the account should be added directly to one pool:

```sh
codex-auth group work login
codex-auth group work login --device-auth
```

The command runs `codex login` with `CODEX_HOME` set to the `work` group folder, then imports that group's new `auth.json` into the same group registry.

Top-level login can target a group too:

```sh
codex-auth login --group work --device-auth
```

The group must already exist. Create it first with `codex-auth group create work` if you want to choose or create the backing folder.

## Creating a Group

`group create work` creates or reuses the managed Codex home for `work`.

In an interactive terminal, if there are unused folders under `~/codex-auth/groups/`, the command shows them and lets the user attach `work` to one of those folders. If no unused folder is selected, it asks for a new folder name and defaults to `work`.

In non-interactive mode, `group create work` uses `~/codex-auth/groups/work`.

## Adding Existing Accounts

Account selectors can be display numbers, aliases, email fragments, account names, or account keys.

When adding an account to a managed group, the command searches all known groups:

```sh
codex-auth group work add personal@example.com
```

If the match is in `default`, it is copied directly from `~/.codex` into the `work` Codex home.

If the match is in another non-default group, the command tells the user where it was found and asks for confirmation before copying it into the target group.

For explicit transfer commands:

```sh
codex-auth group trading copy beta@example.com
codex-auth group trading move work-only@example.com
```

`copy` leaves the source group unchanged, so a copied account can appear under both group sections in `codex-auth list`. That is expected: each group has its own `CODEX_HOME`, registry, sessions, and active account.

`move` imports the account into the target group, then removes it from the source group. It is the command to use when the account should belong to the new pool instead of the old pool.

With no account selector, `copy` and `move` open an interactive picker showing accounts from all other groups:

```sh
codex-auth group trading copy
codex-auth group trading move
```

## Launching Codext

Use `group <name> launch` instead of manually prefixing every command with `CODEX_HOME=...`:

```sh
codex-auth group work launch
codex-auth group work launch -- --model gpt-5.4
codex-auth group work launch resume
codex-auth group work launch resume 019db67d-2190-7563-a899-ce3082e491cf
```

The launched `codext` process receives `CODEX_HOME` for that group. Extra launch arguments are passed through to `codext`, so `launch resume` has the same behavior as running `codext resume` inside that group. The optional session argument can be any selector that `codext resume` normally accepts. Changing `CODEX_HOME` in another terminal does not affect a `codext` process that was already launched.

`group <name> launch` also remembers `<name>` for the current project directory. After that, plain launch uses the remembered group:

```sh
codex-auth launch
codex-auth launch -- --model gpt-5.4
codex-auth launch resume
codex-auth launch resume 019db67d-2190-7563-a899-ce3082e491cf
```

To manage the remembered project group directly:

```sh
codex-auth project show
codex-auth project set-group work
codex-auth project clear
```

If no group is remembered for the project, `launch` uses `default`, which maps to `~/.codex`.

## Per-Group Auto-Switch Settings

Each managed group has its own auto-switch and API settings in that group's `CODEX_HOME`:

```sh
codex-auth group work auto enable
codex-auth group work auto --5h 12 --weekly 8
codex-auth group work config api enable
codex-auth group work auto disable
```

The background runtime is one manager service for all enabled groups. It reads the group config, then checks each enabled group's own `CODEX_HOME` independently. Older per-group service identities are removed during enable/reconcile.

Use `group status` for a dashboard of all groups and `group <name> status` for the standard auto-switch status of one group.

## Archive And Delete

`group archive <name>` moves the managed group folder to `~/codex-auth/archive/<name>-<timestamp>` and removes the group/project mapping. It is meant as the reversible cleanup path before deleting a group permanently.

`group delete <name> --force` permanently removes the managed group folder and its mapping. The `default` group cannot be archived or deleted.

## Legacy Registry Groups

`group use <name>|none` still exists for the lower-level registry grouping inside the current `CODEX_HOME`. It stores an active group in that one registry and scopes plain `list`, `switch`, and auto-switch candidate selection inside that registry.

For separate account pools and separate Codex sessions, prefer the managed group commands above.
40 changes: 25 additions & 15 deletions docs/auto-switch.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ User-facing commands:
- `codex-auth config auto [--5h <percent>] [--weekly <percent>]`
- `codex-auth config api enable`
- `codex-auth config api disable`
- `codex-auth group <name> auto enable`
- `codex-auth group <name> auto disable`
- `codex-auth group <name> auto [--5h <percent>] [--weekly <percent>]`
- `codex-auth group <name> config api enable`
- `codex-auth group <name> config api disable`

Stored registry fields:

Expand All @@ -25,35 +30,37 @@ The feature is off by default.

## Runtime Model

When enabled, managed services run the long-lived watcher mode:
When enabled, the managed service runs the long-lived manager mode:

- `codex-auth daemon --watch`
- `codex-auth daemon --manager`

The watcher keeps a single process alive and runs roughly once per second.
Each cycle:
The manager keeps a single process alive, loads all managed groups, and runs roughly once per second.
For every group whose own registry has `auto_switch.enabled = true`, each cycle:

1. keeps an in-memory candidate index for all non-active accounts, keyed by the same candidate score used for switching
2. reloads `registry.json` only when the on-disk file changed, then rebuilds that in-memory index
3. syncs the currently active `auth.json` into the in-memory registry when the active auth snapshot changed
4. tries to refresh usage from the newest local rollout event first
5. if no new local rollout event is available, or the newest event has no usable rate-limit windows, and `api.usage = true`, falls back to the ChatGPT usage API at most once per minute for the current active account
6. keeps the candidate index warm with a bounded candidate upkeep pass instead of batch-refreshing every candidate
7. if the active account should switch, revalidates only the top few stale candidates before making the final switch decision
8. writes `registry.json` only when state changed
5. scans that group's rollout files for a fresh "hit your usage limit" message and marks the currently active account exhausted immediately when the message is newer than that account's activation time
6. if no new local rollout event is available, or the newest event has no usable rate-limit windows, and `api.usage = true`, falls back to the ChatGPT usage API at most once per minute for the current active account
7. keeps the candidate index warm with a bounded candidate upkeep pass instead of batch-refreshing every candidate
8. if the active account should switch, revalidates only the top few stale candidates before making the final switch decision
9. writes `registry.json` only when state changed

The watcher also emits English-only service logs for debugging:

- logs use compact `[local]`, `[api]`, and `[switch]` tags
- local rollout captures show the parsed window labels first, then the local-time event timestamp, then the real rollout basename; when the newest local event has no usable usage windows the same `[local]` line also marks `fallback-to-api`
- API refresh logs are reduced to `refresh usage | status=...`, where `status` is the HTTP status when available, `MissingAuth` when the active auth cannot call the ChatGPT usage API, or the direct transport error name such as `TimedOut` / `RequestFailed`

`daemon --once` still exists for tests and one-shot validation, but the managed service path uses `daemon --watch`.
`daemon --watch` and `daemon --once` still exist for tests, legacy service cleanup, and one-CODEX_HOME validation. The managed service path uses `daemon --manager`; `daemon --manager-once` runs one manager pass.

## Data Source Priority

The background watcher is intentionally not API-only, even when `api.usage = true`.

- Local rollout events are preferred because they arrive much faster than periodic usage API polling.
- Local limit-message detection is even more direct: it watches each enabled group's own `<CODEX_HOME>/sessions/**/rollout-*.jsonl` tree, so project directories and terminal tabs do not matter as long as the session uses that group `CODEX_HOME`.
- API refresh remains useful as a slower fallback and calibration path when rollout data is missing or stale.
- When `api.usage = false`, the watcher uses local rollout data only and makes no usage API requests.
- When a new rollout event arrives but its `rate_limits` payload is `null`, `{}`, or otherwise lacks usable 5h/weekly windows, the watcher keeps the previous `last_usage` snapshot and relies on the API fallback path instead of overwriting usage with empty data.
Expand All @@ -68,13 +75,14 @@ Local rollout attribution rules are unchanged:
- a newer `token_count` event with unusable `rate_limits` is still treated as a fresh signal for API fallback, but it does not overwrite the stored usage snapshot
- the event is applied only when `event_timestamp_ms >= active_account_activated_at_ms`
- each account remembers its own last consumed local rollout signature `(path, event_timestamp_ms)` so the same local event is not reapplied
- limit-message events also use the same activation-time guard; after a switch, the old limit message is older than the newly active account activation and will not immediately exhaust the replacement account

## Switching Rules

The watcher switches without foreground CLI output when the active account drops below either threshold:
The watcher switches without foreground CLI output when the active account reaches or drops below either threshold:

- `5h remaining < auto_switch.threshold_5h_percent`
- `weekly remaining < auto_switch.threshold_weekly_percent`
- `5h remaining <= auto_switch.threshold_5h_percent`
- `weekly remaining <= auto_switch.threshold_weekly_percent`

There is one extra near-real-time safety rule for free plans:

Expand All @@ -101,12 +109,14 @@ Platform bootstrap:

- Linux/WSL: `systemd --user` persistent service
- macOS: `LaunchAgent` with `KeepAlive`
- Windows: user scheduled task with an `ONLOGON` trigger, restart-on-failure settings, and an unlimited execution time for `codex-auth-auto.exe`, plus an immediate `schtasks /Run` during enablement
- Windows: user scheduled task with an `ONLOGON` trigger, restart-on-failure settings, and an unlimited execution time for `codex-auth-auto.exe`, plus an immediate task run during enablement

Service definition files stay in the platform-standard per-user locations. The managed watcher process uses the current `codex_home` root, so when `CODEX_HOME` is set during enablement the watcher keeps reading and writing that override after it starts in the background.
Service definition files stay in the platform-standard per-user locations. The managed manager process does not use one fixed `CODEX_HOME`; it reads `~/codex-auth/groups.json`, then runs each enabled group against that group's configured `CODEX_HOME`. The `default` group still maps to the normal `~/.codex`.
Foreground commands other than `help`, `version`, `status`, and `daemon` still reconcile the managed service definition after they complete.
`config auto enable` also prints a short usage-mode note so the user can see whether switching is currently running with default API-backed usage data or local-only fallback semantics.
When migrating from older Linux/WSL timer-based installs, enable/reconcile also removes the legacy `codex-auth-autoswitch.timer` unit file instead of leaving the old minute timer behind.
The manager uses one service identity for all enabled groups: `codex-auth-manager.service` on Linux, `com.loongphy.codex-auth.manager` on macOS, and `CodexAuthManager` on Windows.
When the manager service is installed or reconciled, the CLI resolves the current usable Node executable from `CODEX_AUTH_NODE_EXECUTABLE` or `PATH` and writes it into the service environment as `CODEX_AUTH_NODE_EXECUTABLE`. This avoids hard-coding a user-specific path while still letting macOS LaunchAgent/systemd run API refreshes under their limited default `PATH`.
When migrating from older service layouts, enable/reconcile also removes legacy one-CODEX_HOME and per-group service identities such as `codex-auth-autoswitch.service`, `com.loongphy.codex-auth.auto`, `codex-auth-autoswitch-work.service`, and `com.loongphy.codex-auth.auto.work`.

## Limits

Expand Down
Loading