Skip to content

About

Run many Codex accounts on one machine, fully isolated.

Topics

Resources

Stars

12 stars

Watchers

0 watching

Forks

Latest commit

 

History

45 Commits

Folders and files

Repository files navigation

codex-alias

codex-alias runs multiple Codex accounts/profiles with separate homes. Each profile gets an isolated CODEX_HOME and a wrapper command (for example codex-work) that forwards to the original codex binary, so auth, config, and history stay separated.

Profiles can optionally run a local codex-relay bridge. This keeps Codex on its Responses API while translating requests for providers that only expose Chat Completions.

It ships as a Python package with two parts:

  • a reusable, UI-free library (codex_alias) that does all the filesystem work
  • a rich + click CLI (codexa) built on top of it

Install

Requires uv.

# One-shot install onto PATH
make install

# Equivalently, via uv directly
uv tool install .

# Or work inside the project
uv sync
uv run codexa doctor

Other make targets: make test, make sync, make uninstall, make clean (run make help for the list).

uv tool install . puts codexa on your PATH and also installs the compatibility aliases codexalias and codex-alias. From there:

codexa add work
codex-work

During add, interactive prompts let you:

  1. Copy plugins/skills/rules from the source home
  2. Copy global instructions (AGENTS.md + AGENTS.override.md)
  3. Copy current config (auth.json + config.toml)
  4. Select root-profile hooks to share with the new profile
  5. Share sessions with the root home (symlink)
  6. Otherwise migrate sessions into the new profile

The choices are recorded as ordered sync types. A later codexa sync <profile> re-runs the corresponding migration handlers in that order. Pass --no-bootstrap to skip the prompts.

Commands

# Manage profile homes and their wrapper commands (default wrapper: codex-<profile>)
codexa profile manage add <profile> [command-name]
codexa profile manage list [--json] [--details]
codexa profile manage path <profile>
codexa profile manage rename <profile> <new-profile> \
  [--command-name <old-wrapper>] [--new-command-name <new-wrapper>]
codexa profile manage remove <profile> [command-name] [--keep-data] [--yes]
codexa profile manage refresh-wrappers

# Top-level lifecycle commands remain available for compatibility
codexa add <profile> [command-name]

# Import one session from default ~/.codex into current/target home
codexa import <session-id> [target|@current]

# Repair stale provider/model metadata
codexa fix-session <session-id> [home|@current] \
  [--provider <provider>] [--model <model>]

# Copy a session for default/another profile, then resume the copy
codexa resume <session-id> [--profile default|<profile>]

# Detect the latest session used in the current directory and show its final output
codexa detect

# Detect the latest session and resume it directly in its profile
codexa detect resume [codex args...]

# Interactive session migration into the current home
codexa migrate session

# Copy all sessions from one home into another
codexa migrate copy <source|@source> [target|@current]

# Copy one session from one home into another
codexa migrate one <source|@source> <session-id> [target|@current]

# Share sessions with a source home via symlink (existing profile)
codexa share-sessions <profile> [source|@source]

# Run codex once with an existing profile (without creating a wrapper)
# Create it first with `codexa add <profile>` if needed.
codexa run <profile> [codex args...]

# Inspect or manually pin/unpin an optional profile relay
codexa relay start <profile>
codexa relay status [profile]
codexa relay stop <profile>
codexa relay start --all
codexa relay status --all
codexa relay stop --all

# Shortcut: run an existing profile and forward all remaining args to Codex
codexa <profile> [codex args...]

# List profiles (compatibility alias; prefer `profile manage list`)
codexa list

# Print the absolute home path of a profile (compatibility alias)
codexa path <profile>

# Remove a profile (compatibility alias)
codexa remove <profile> [command-name]

# Keep the profile data and remove only the wrapper command
codexa remove <profile> [command-name] --keep-data

# Environment and sanity checks
codexa doctor

# Select root hooks for a profile
codexa hooks

# Reapply the profile's saved migration types from the source home
codexa sync [profile] [--yes]

# One-shot sync one or more independently managed content types
codexa sync --all --type skills --source ~/.codex --yes
codexa sync --all --type plugins --type rules --type prompts --source ~/.codex --yes

# Sync only selected skills; repeat --skill or use a file
codexa sync --all --type skills \
  --skill review-mr --skill domain-modeling --source ~/.codex --yes
codexa sync --all --type skills --skills-file ./skills.allowlist \
  --exclude-skill grilling --source ~/.codex --yes

# Persist the selector for future `codexa sync <profile>` calls
codexa sync --all --type skills --skill review-mr --save --source ~/.codex --yes

# Preview or clean stale user skills (never removes .system)
codexa sync --all --type skills --skill review-mr --dry-run --source ~/.codex
codexa sync --all --type skills --skill review-mr --prune-skills --source ~/.codex --yes

# Show all independently selectable sync types
codexa sync --list-types

# Interactive skill table (selection is persisted and unselected user skills are removed)
codexa sync <profile> --select-skills --source ~/.codex --yes

# Machine/AI-readable inventory
codexa list --json
codexa sync --list-skills --json --source ~/.codex
codexa sync --list-types --json

# Enable global instruction sync for an existing profile, then sync it
codexa sync <profile> --instructions --yes

@source refers to the configured source home; @current refers to the current CODEX_HOME (falling back to the source home when unset). A bare profile name or an absolute path also works anywhere a home is expected.

codexa profile manage remove prompts for confirmation before deleting a profile home (auth, config, sessions, and everything else under the profile). Pass --yes to skip the prompt or --keep-data to keep the home and only remove the wrapper. Deleting a profile that is the configured source home or the current CODEX_HOME is refused.

profile manage rename moves the profile home without changing its data and updates the generated wrapper. Use the wrapper-name options when the existing wrapper was created with a custom command name. The top-level add, list, path, remove, and refresh-wrappers commands remain available as compatibility aliases.

By default, profile commands resolve codex through the user's login shell, as if codex ... had been entered directly. This preserves fish/bash/zsh functions and aliases as well as PATH-based executable wrappers such as Superset. Existing generated profile commands pick up wrapper changes automatically; refreshing them is only necessary when the generated wrapper format itself changes.

Environment variables

  • CODEXALIAS_PROFILE_ROOT: profile root directory (default: ~/.codex/profiles)
  • CODEXALIAS_BIN_DIR: output directory for wrappers (default: ~/.local/bin)
  • CODEXALIAS_CODEX_CMD: original Codex command (default: codex)
  • CODEXALIAS_CODEX_WRAPPER: executable Codex wrapper; takes precedence over CODEXALIAS_CODEX_CMD for run, resume, and generated profile commands
  • CODEXALIAS_CODEX_ARGS: fixed arguments prepended to every Codex invocation
  • CODEXALIAS_RELAY_COMMAND: relay executable (default codex-relay)
  • CODEXALIAS_SOURCE_HOME: source home used by add/@source (default: $CODEX_HOME or ~/.codex)
  • CODEXALIAS_MANAGER_BIN_NAME: manager binary name used by generated profile commands (default: codexalias, a compatibility alias)

To explicitly override normal shell resolution with a standalone executable:

export CODEXALIAS_CODEX_WRAPPER="$HOME/.superset/bin/codex"
export CODEXALIAS_CODEX_ARGS="--dangerously-bypass-approvals-and-sandbox"
codexa resume <session-id>

The explicit override must be an executable name or path. Without it, shell aliases and functions are inherited automatically.

Chat-only providers with codex-relay

Install the bridge separately (it is intentionally not a dependency of codex-alias):

uv tool install codex-relay

Opt a profile in by creating $CODEXALIAS_PROFILE_ROOT/<profile>/relay.toml:

upstream = "https://api.commandcode.ai/provider/v1"
# Omit port to let codex-alias allocate a free local port.
api_key_file = "auth.json"
api_key_field = "OPENAI_API_KEY"

The key file is read only when the alias starts. The relay process receives it through its environment; the persisted relay state never contains the key. command, provider, api_key_env, and extra_args are also supported. For example, command = "uvx --from codex-relay codex-relay" selects a one-shot uvx installation.

When a profile has relay.toml, codexa run, generated profile wrappers, codexa resume, and codexa detect resume automatically start its relay and stop it when the alias exits. Profiles without relay.toml keep the normal launch path unchanged. A relay is shared only when the effective upstream, API key, port/host, command, and extra arguments match; profiles with different upstreams or keys get separate processes. codexa relay start pins a relay for manual use, and codexa relay stop removes that pin (an active alias lease is never interrupted).

Shared relay logs and process state live under $CODEXALIAS_PROFILE_ROOT/.codexalias-relays/; treat that directory and each profile's auth file as sensitive.

Hook sharing

Codex reads hooks from $CODEX_HOME/hooks.json. Because each profile has its own CODEX_HOME, codexa add offers a table of hooks from the configured source home ($CODEXALIAS_SOURCE_HOME/hooks.json, default ~/.codex/hooks.json). Use Space to toggle a row, Enter to review the selection, and confirm to write it. The standalone codexa hooks command first asks which profile to edit and then opens the same table. Enabled plugin hooks remain selectable even when the source home has no standalone hooks.json.

The table also includes hooks from enabled Codex plugins in the root config.toml (for example, agent-trace). When copied to a profile, plugin hooks are bound to their root plugin directory so ${PLUGIN_ROOT} continues to work outside the plugin's own context.

The ordered migration types chosen during add are stored in the profile's .codexalias.json. New profiles can manage skills, plugins, agents, mcp, rules, prompts, instructions, config, hooks, sessions_shared, and sessions_migrate independently. The bundle type is available for the old all-in-one behavior. For backward compatibility, a profile whose saved state contains the historical plugins type still runs that bundle; an explicit --type plugins means only plugins/ and .plugins/. Running codexa sync <profile> walks the saved types in order.

Use --type TYPE for a one-shot sync that does not change saved settings; repeat the option to run multiple types. Use --all (or --all-profiles) to target every profile, and --source PATH to pin the source home explicitly when the current process already has a profile-specific CODEX_HOME.

Skills are selectable by --skill NAME, --exclude-skill NAME, or --skills-file PATH. An empty include list means all non-system skills; .system is excluded unless --include-system-skills is explicit. --save persists the selector per profile, --dry-run previews file operations, and --prune-skills removes only non-selected user skills from the target; it never removes .system. Use --list-skills to inspect available skill names. --select-skills opens the same keyboard-driven table used for hooks, saves the resulting allowlist, and enables pruning of unselected user skills. It never removes .system. --json is available for profile, skill, and sync-type inventories so an AI or shell script can inspect state without parsing Rich output, then issue an explicit --type/--skill/--save command.

Plugin/instruction/config sync asks for confirmation before overwriting profile files; pass --yes for explicit non-interactive approval. Instruction sync mirrors both global instruction filenames and removes a stale target override when it no longer exists in the source home. Profile-local hooks are preserved; hook-specific ownership snapshots remain internal to the hook migration so changed root hooks can be replaced safely.

Library usage

The core is importable and never prints or exits — it returns value objects or raises CodexAliasError subclasses, so you can drive it from your own tooling:

from codex_alias import CodexAlias, Config

mgr = CodexAlias(Config.from_env())
mgr.add_profile("work")

for profile in mgr.list_profiles():
    print(profile.name, "shared" if profile.sessions_shared else "isolated")

# Copy one session between homes
src = mgr.resolve_home_ref("@source").path
dst = mgr.resolve_home_ref("work").path
result = mgr.copy_session_by_query(src, "019d1df0-8f1e-7393-b54a-0f0b511c5a33", dst)
print(result.status)

Session sharing

By default each profile has isolated sessions. To share history across profiles (useful when different provider configs access the same conversations), share sessions during creation (answer yes to "Share sessions with root home") or for an existing profile:

codexa share-sessions work

This symlinks ~/.codex/profiles/work/sessions (plus history.jsonl and the state_5.sqlite / logs_1.sqlite metadata databases) to the source home, so sharing profiles see the same conversation history while keeping separate auth/config. Existing real files are backed up to *.backup.N before being replaced with a symlink.

Repairing a session

Codex persists model-provider and model metadata both inside each JSONL session and in the state_5.sqlite thread index. If a provider is later renamed or a profile uses a different model, codex resume can fail before the TUI starts. Repair both persisted copies with:

# Preview the repair; "custom" is inferred from ~/.codex/config.toml
codexa fix-session 019f8938-544e-7160-901c-af1ffb2657a5 --dry-run

# Apply it, but only where the stale value is exactly "aicoding"
codexa fix-session 019f8938-544e-7160-901c-af1ffb2657a5 \
  --from-provider aicoding \
  --model deepseek-v4-pro

The command validates every JSONL record before writing, creates unique *.backup.N copies for changed JSONL and SQLite files, atomically replaces the JSONL, and conditionally updates only the matching SQLite thread row. Use --provider to override the provider inferred from the selected home's top-level model_provider setting, and --model to repair the persisted model as well.

Resuming with another profile

codexa resume <session-id> [codex args...] shows a numbered Rich list containing default and every added profile. Arguments after the session id are forwarded to the final Codex resume command, for example codexa resume <session-id> --profile luna-high --yolo. After the profile is selected, it asks whether to fix the copied session's provider and model. A y reads both values from the target profile's top-level config.toml, repairs the new session's JSONL and SQLite metadata, and then launches Codex. Profiles using Codex's built-in auth may omit model_provider; in that case the source session's provider is preserved. An n keeps the existing behavior and leaves the copied model unchanged. The source session is never modified. This also works when profiles share session storage through symlinks because the cloned session has a distinct ID.

The clone also applies registered response-item compatibility mappings, regardless of the fix prompt. The current gpt-5* rule clears non-empty plaintext reasoning.content; GPT-5 Responses endpoints reject that replay shape. Rules use model and wire-API capabilities, not hard-coded provider names.

Encrypted history has a separate portability boundary. Codexa compares a normalized wire_api + base_url backend fingerprint when both sides are known. It preserves encrypted reasoning for aliases of the same backend, and treats foreign encrypted reasoning as an explicitly reported lossy mapping without dropping its record. It does not guess when either backend is unknown. For a known foreign backend it keeps the reasoning record (and its paginated ordinal) while clearing the backend-bound encrypted_content; this preserves the history cursor without replaying unreadable ciphertext. Foreign encrypted compaction blocks the repair because deleting it could remove the only copy of earlier context. Incomplete or orphan historical tool calls are reported as diagnostics but are not changed. Add future rules to src/codex_alias/session_mappings.py and classify each one as lossless or lossy. When a lossy mapping is needed, the CLI asks for confirmation before writing a clone or repairing an existing session. Library callers can pass allow_lossy=False to require the same explicit decision in their own UI.

Use --profile cpa to skip the profile picker or --no-launch to create the copy without starting Codex. The fix confirmation is still shown after the profile is known. The installed executable is codexa, with compatibility aliases codexalias and codex-alias available as well.

codexa detect searches the configured source home and every profile for the most recently updated session whose working directory is the current directory. It reports the inferred profile, session id, rollout path, and last assistant output. Use --cwd PATH to inspect another directory. codexa detect resume uses that session's profile home directly for codex resume; it does not copy the session or prompt for another profile. The old resume detect, detach, and --detach spellings are no longer supported.

Development

uv sync
uv run pytest

About

Run many Codex accounts on one machine, fully isolated.

Topics

Resources

Stars

12 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages