multi-codex 0.7.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- multi_codex-0.7.0/LICENSE +21 -0
- multi_codex-0.7.0/PKG-INFO +452 -0
- multi_codex-0.7.0/README.md +436 -0
- multi_codex-0.7.0/pyproject.toml +30 -0
- multi_codex-0.7.0/setup.cfg +4 -0
- multi_codex-0.7.0/src/multi_codex/__init__.py +3 -0
- multi_codex-0.7.0/src/multi_codex/__main__.py +5 -0
- multi_codex-0.7.0/src/multi_codex/accounts.py +245 -0
- multi_codex-0.7.0/src/multi_codex/actions.py +76 -0
- multi_codex-0.7.0/src/multi_codex/apps.py +74 -0
- multi_codex-0.7.0/src/multi_codex/binding.py +66 -0
- multi_codex-0.7.0/src/multi_codex/cli.py +994 -0
- multi_codex-0.7.0/src/multi_codex/completion.py +254 -0
- multi_codex-0.7.0/src/multi_codex/config.py +327 -0
- multi_codex-0.7.0/src/multi_codex/doctor.py +244 -0
- multi_codex-0.7.0/src/multi_codex/fsutil.py +250 -0
- multi_codex-0.7.0/src/multi_codex/identity.py +171 -0
- multi_codex-0.7.0/src/multi_codex/launcher.py +151 -0
- multi_codex-0.7.0/src/multi_codex/lock.py +47 -0
- multi_codex-0.7.0/src/multi_codex/migrate.py +672 -0
- multi_codex-0.7.0/src/multi_codex/platform.py +184 -0
- multi_codex-0.7.0/src/multi_codex/shared.py +127 -0
- multi_codex-0.7.0/src/multi_codex/switch.py +286 -0
- multi_codex-0.7.0/src/multi_codex/usage.py +475 -0
- multi_codex-0.7.0/src/multi_codex.egg-info/PKG-INFO +452 -0
- multi_codex-0.7.0/src/multi_codex.egg-info/SOURCES.txt +41 -0
- multi_codex-0.7.0/src/multi_codex.egg-info/dependency_links.txt +1 -0
- multi_codex-0.7.0/src/multi_codex.egg-info/entry_points.txt +2 -0
- multi_codex-0.7.0/src/multi_codex.egg-info/top_level.txt +1 -0
- multi_codex-0.7.0/tests/test_accounts.py +415 -0
- multi_codex-0.7.0/tests/test_apps.py +133 -0
- multi_codex-0.7.0/tests/test_binding.py +201 -0
- multi_codex-0.7.0/tests/test_config.py +72 -0
- multi_codex-0.7.0/tests/test_config_copy.py +88 -0
- multi_codex-0.7.0/tests/test_ergonomics.py +268 -0
- multi_codex-0.7.0/tests/test_fsutil.py +61 -0
- multi_codex-0.7.0/tests/test_insight.py +481 -0
- multi_codex-0.7.0/tests/test_install.py +255 -0
- multi_codex-0.7.0/tests/test_migrate.py +587 -0
- multi_codex-0.7.0/tests/test_onboarding.py +270 -0
- multi_codex-0.7.0/tests/test_onboarding_commands.py +172 -0
- multi_codex-0.7.0/tests/test_platform.py +23 -0
- multi_codex-0.7.0/tests/test_switch.py +303 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 multi-codex contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: multi-codex
|
|
3
|
+
Version: 0.7.0
|
|
4
|
+
Summary: Run several Codex CLI accounts side by side: separate CODEX_HOME directories, launchers and proxies.
|
|
5
|
+
License: MIT
|
|
6
|
+
Project-URL: Homepage, https://github.com/jakoes-wu/multi-codex
|
|
7
|
+
Classifier: Environment :: Console
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Operating System :: MacOS
|
|
10
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Requires-Python: >=3.8
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Dynamic: license-file
|
|
16
|
+
|
|
17
|
+
# multi-codex
|
|
18
|
+
|
|
19
|
+
**English** | [简体中文](README.zh-CN.md)
|
|
20
|
+
|
|
21
|
+
Use several [Codex CLI](https://github.com/openai/codex) accounts on one machine, at the same time. Each account keeps its own login, settings, history and, if you like, its own proxy. No more logging out and in again.
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
codex-work # Codex, logged in with your work account
|
|
25
|
+
codex-personal # Codex, logged in with your personal account, in another terminal
|
|
26
|
+
multi-codex list # which account is logged in as whom
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## How it works
|
|
30
|
+
|
|
31
|
+
Codex keeps everything (settings, credentials, sessions) in one directory, `CODEX_HOME`, which is `~/.codex` by default. multi-codex gives every account its own directory and a small launcher command, `codex-<name>`, that starts Codex with that directory:
|
|
32
|
+
|
|
33
|
+
```text
|
|
34
|
+
codex-work -> CODEX_HOME=~/.cx/work HTTPS_PROXY=http://127.0.0.1:7901
|
|
35
|
+
codex-personal -> CODEX_HOME=~/.cx/personal (inherits your shell's proxy settings)
|
|
36
|
+
codex -> ~/.codex, which can itself become one of the accounts
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The launchers are plain shell scripts. They keep working even if you uninstall multi-codex.
|
|
40
|
+
|
|
41
|
+
## Install
|
|
42
|
+
|
|
43
|
+
You need macOS or Linux (Windows support is planned; inside WSL, use the Linux instructions), Python 3.8 or newer (no extra packages), `tar`, `curl` or `wget`, and the Codex CLI on your `PATH`.
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
curl -fsSL https://raw.githubusercontent.com/jakoes-wu/multi-codex/main/install.sh | sh
|
|
47
|
+
multi-codex --version
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The `multi-codex` command and the `codex-<name>` launchers go to `~/.local/bin`. If your shell says `command not found`, that directory is not on your `PATH` yet; the installer prints a hint but never edits your shell profile. Add this line to `~/.zshrc` or `~/.bashrc` and open a new terminal:
|
|
51
|
+
|
|
52
|
+
```sh
|
|
53
|
+
export PATH="$HOME/.local/bin:$PATH"
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
In fish, run `fish_add_path ~/.local/bin` once instead.
|
|
57
|
+
|
|
58
|
+
Installing from a clone, with pipx or into another directory, and how downloads are verified: see [More installation options](#more-installation-options).
|
|
59
|
+
|
|
60
|
+
## Quick start
|
|
61
|
+
|
|
62
|
+
### 1. Create one account per login
|
|
63
|
+
|
|
64
|
+
```sh
|
|
65
|
+
multi-codex add work
|
|
66
|
+
multi-codex add personal
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Each command creates a directory (`~/.cx/work`) and a launcher (`codex-work`). A name starts with a letter or digit and may contain letters, digits and `._@+-`; an e-mail address works too.
|
|
70
|
+
|
|
71
|
+
After `add`, multi-codex prints the next step: the login command, and a warning if `~/.local/bin` is not on your `PATH` yet. Running `multi-codex` without arguments shows these steps again.
|
|
72
|
+
|
|
73
|
+
If an account should go through a proxy, give it a local port or a URL, for example `multi-codex add work --proxy 7901` (the same as `http://127.0.0.1:7901`). See [Proxy values](#proxy-values).
|
|
74
|
+
|
|
75
|
+
### 2. Log in once per account
|
|
76
|
+
|
|
77
|
+
```sh
|
|
78
|
+
multi-codex login work
|
|
79
|
+
multi-codex login personal
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
`multi-codex login NAME` runs `codex login` with the account's environment and works even before `~/.local/bin` is on your `PATH`; `codex-work login` does the same.
|
|
83
|
+
|
|
84
|
+
### 3. Use the launchers instead of `codex`
|
|
85
|
+
|
|
86
|
+
```sh
|
|
87
|
+
codex-work # all arguments are passed to codex
|
|
88
|
+
codex-personal resume
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### 4. Check that everything is right
|
|
92
|
+
|
|
93
|
+
```sh
|
|
94
|
+
multi-codex list # accounts, launcher state, logged-in e-mail and plan
|
|
95
|
+
multi-codex usage # 5-hour and weekly usage; empty until you have used an account (--live asks right away)
|
|
96
|
+
multi-codex doctor # finds problems and prints the command that fixes each one
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Already using Codex? Keep your current login
|
|
100
|
+
|
|
101
|
+
Your existing `~/.codex` can become an account as well, so you do not have to log in again:
|
|
102
|
+
|
|
103
|
+
```sh
|
|
104
|
+
# Close Codex first: terminals, VS Code, the desktop app
|
|
105
|
+
multi-codex migrate-default
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Without a name, the account is named after the e-mail address in `~/.codex/auth.json`; if there is none (API key, not logged in, keyring), pass a name, for example `multi-codex migrate-default main`. This moves `~/.codex` to `~/.cx/<name>`, leaves a link at `~/.codex` and creates `codex-<name>`. Plain `codex`, VS Code, the desktop app and old absolute paths under `~/.codex` keep working as before. Later, `multi-codex use work` makes another account the default. If Codex keeps your login in the system keyring, the command stops and explains why. Details and how to undo it: [Migrating `~/.codex`](#migrating-codex).
|
|
109
|
+
|
|
110
|
+
## Common tasks
|
|
111
|
+
|
|
112
|
+
| I want to | Command | Details |
|
|
113
|
+
| ---- | ---- | ---- |
|
|
114
|
+
| Open VS Code with an account | `multi-codex code work ~/src/project` | [VS Code and the desktop app](#vs-code-and-the-desktop-app-experimental) |
|
|
115
|
+
| Open the desktop app with an account (macOS) | `multi-codex app work` | [VS Code and the desktop app](#vs-code-and-the-desktop-app-experimental) |
|
|
116
|
+
| Change the account that plain `codex` and the Dock apps use | `multi-codex use work` (after `migrate-default`) | [Default account](#default-account) |
|
|
117
|
+
| Always use one account inside a project | In the project directory: `multi-codex bind work`, then `multi-codex run` | [Directory bindings](#directory-bindings) |
|
|
118
|
+
| Set or change an account's proxy | `multi-codex proxy work 7901` | [Proxy values](#proxy-values) |
|
|
119
|
+
| Share `AGENTS.md`, skills and rules between accounts | Put them in `~/.codex-shared`, run `multi-codex init --shared-dir ~/.codex-shared`, then `multi-codex add work --shared` | [Shared resources](#shared-resources) |
|
|
120
|
+
| Start a new account with another account's settings | `multi-codex add new --config-from work` | [Copying settings](#copying-settings-from-another-account) |
|
|
121
|
+
| Give an account extra environment variables | `multi-codex env work KEY=VALUE` | [Environment variables](#per-account-environment-variables) |
|
|
122
|
+
| Set up all accounts on a new machine | `curl -fsSL https://raw.githubusercontent.com/jakoes-wu/multi-codex/main/install.sh \| sh -s -- --config accounts.json` | [Declarative setup](#declarative-setup-with-apply) |
|
|
123
|
+
| Get tab completion | `eval "$(multi-codex completion zsh)"` | [Shell completion](#shell-completion) |
|
|
124
|
+
| See what a command would change | add `--dry-run` | [Commands](#commands) |
|
|
125
|
+
| Remove an account | `multi-codex remove work` (the directory is kept) | [Commands](#commands) |
|
|
126
|
+
|
|
127
|
+
More questions are answered in the [FAQ](#faq).
|
|
128
|
+
|
|
129
|
+
## Good to know
|
|
130
|
+
|
|
131
|
+
- **Safe to re-run.** Every command can be run again. Write commands print only what they change, or `already up to date`; add `-v` to see every item, including unchanged ones.
|
|
132
|
+
- **Never overwrites your files.** If a file that multi-codex did not create is in the way, it reports a conflict and changes nothing.
|
|
133
|
+
- **Interrupted migrations resume.** Run the same command again and it continues from the actual state on disk.
|
|
134
|
+
- **Not a security boundary.** Separate directories keep the accounts' local state apart, but any program running as your user can read every account directory.
|
|
135
|
+
|
|
136
|
+
## Commands
|
|
137
|
+
|
|
138
|
+
| Command | What it does |
|
|
139
|
+
| ---- | ---- |
|
|
140
|
+
| `multi-codex init [--root DIR] [--bin-dir DIR] [--shared-dir DIR] [--shared-items A,B]` | Create or change global settings. |
|
|
141
|
+
| `multi-codex migrate-default [NAME] [--source DIR] [--copy] [--keep-backup] [--proxy P] [--skip-process-check] [--accept-relogin]` | Turn the default directory into an account. Without NAME, the e-mail address in its `auth.json` is used. |
|
|
142
|
+
| `multi-codex add NAME [--proxy P] [--shared \| --no-shared] [--adopt] [--config-from OTHER]` | Add an account, adopt an existing directory, or change its options. `--config-from` copies `config.toml` from another account once. |
|
|
143
|
+
| `multi-codex login NAME [-- ARGS]` | Run `codex login` with an account's environment; arguments after `--` go to `codex login`. Does not need `~/.local/bin` on `PATH`. |
|
|
144
|
+
| `multi-codex proxy NAME PORT\|URL\|off\|inherit` | Set an account's proxy. |
|
|
145
|
+
| `multi-codex remove NAME` | Unregister an account and delete its launcher. **The account directory is kept.** |
|
|
146
|
+
| `multi-codex apply [-f FILE]` | Converge everything to the configuration (or to `FILE`). |
|
|
147
|
+
| `multi-codex list [--json]` | Show accounts, the state of their launchers, and who is logged in. |
|
|
148
|
+
| `multi-codex usage [NAME ...] [--live] [--timeout SEC] [--json]` | Show rate-limit usage. |
|
|
149
|
+
| `multi-codex doctor [--json]` | Check the installation, configuration and accounts. Read-only. |
|
|
150
|
+
| `multi-codex run [NAME] [-- COMMAND ...]` | Run a command (default: `codex`) with an account's environment. Without NAME, the account bound to the current directory is used. |
|
|
151
|
+
| `multi-codex bind [NAME [DIR]]` / `unbind [DIR]` | Bind a directory to an account, list bindings, or remove one. |
|
|
152
|
+
| `multi-codex code NAME [PATH] [-- ARGS]` | Open VS Code for an account (experimental). |
|
|
153
|
+
| `multi-codex app NAME` | Open the Codex desktop app for an account (macOS, experimental). |
|
|
154
|
+
| `multi-codex path NAME` | Print an account's directory. |
|
|
155
|
+
| `multi-codex env NAME [KEY=VALUE ...] [--unset KEY] [--clear]` | List or change an account's extra environment variables. |
|
|
156
|
+
| `multi-codex use [NAME] [--skip-process-check]` | Show or change the default account (where `~/.codex` points). |
|
|
157
|
+
| `multi-codex restore NAME [--skip-process-check] [--accept-relogin]` | Undo `migrate-default`: move the account back to `~/.codex`. |
|
|
158
|
+
| `multi-codex completion bash\|zsh\|fish` | Print a shell completion script. |
|
|
159
|
+
|
|
160
|
+
Every write command accepts `--dry-run`. `init`, `add`, `proxy`, `remove`, `apply`, `bind`, `unbind` and `env` print only the items they change (or `already up to date`); `-v` / `--verbose` also prints unchanged items. `list`, `usage` and `doctor` never change anything; with `--json` they print a single JSON object on stdout (with a `"version": 1` field) and keep warnings on stderr. Use `--json` in scripts: the table layout is not guaranteed to stay the same.
|
|
161
|
+
|
|
162
|
+
### Login and usage
|
|
163
|
+
|
|
164
|
+
`list` adds two columns, LOGIN and PLAN, read from each account's local `auth.json`. No token is ever printed, and nothing is sent anywhere. LOGIN is the e-mail address, `api-key`, `-` (not logged in), `keyring` (credentials are in the system keyring and cannot be read from files) or `unreadable`. PLAN is the plan recorded when the current token was issued; it is updated the next time Codex refreshes the token. If two accounts are logged in as the same ChatGPT user and workspace, `list` warns you: they share one quota.
|
|
165
|
+
|
|
166
|
+
`multi-codex usage` reads the most recent rate-limit snapshot from each account's session logs (`sessions/` and `archived_sessions/`). It is offline but can be out of date; a window that has reset since the snapshot is shown as `reset since snapshot`.
|
|
167
|
+
|
|
168
|
+
`multi-codex usage --live` runs `codex app-server` through the account's launcher (so the account's proxy applies) and asks it for the current usage (`account/rateLimits/read`, Codex 0.48.0 or newer). multi-codex never reads or sends tokens itself. As with any Codex run, Codex may refresh the account's token and write it back to that account's directory. Accounts are queried one after another; `--timeout` limits each one (default 30 seconds).
|
|
169
|
+
|
|
170
|
+
### Health check
|
|
171
|
+
|
|
172
|
+
`multi-codex doctor` prints one line per check (`ok`, `warn` or `fail`) and a `fix:` line with the command to run. It checks the `codex` executable and its version, the configuration, unfinished migrations, whether `bin_dir` is on `PATH`, environment variables that break isolation, where `~/.codex` points, whether the files match the configuration (the same plan `apply` would execute), each account's login, and duplicate logins. It does not repair anything and does not use the network. The exit code is 1 if any check fails, otherwise 0.
|
|
173
|
+
|
|
174
|
+
### Shell completion
|
|
175
|
+
|
|
176
|
+
```sh
|
|
177
|
+
eval "$(multi-codex completion bash)" # in ~/.bashrc
|
|
178
|
+
eval "$(multi-codex completion zsh)" # in ~/.zshrc, after compinit
|
|
179
|
+
multi-codex completion fish | source # in ~/.config/fish/config.fish
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Subcommands, options and registered account names (including e-mail addresses) are completed.
|
|
183
|
+
|
|
184
|
+
### Running other commands
|
|
185
|
+
|
|
186
|
+
`multi-codex run NAME -- COMMAND ...` runs any command with exactly the environment of `codex-NAME` (`CODEX_HOME`, proxy, extra variables). Without a command it runs `codex`. Everything after the first `--` is passed through unchanged; the exit code is the command's. `multi-codex path NAME` prints the account directory.
|
|
187
|
+
|
|
188
|
+
### Directory bindings
|
|
189
|
+
|
|
190
|
+
```sh
|
|
191
|
+
cd ~/work/project && multi-codex bind work # this directory and everything below it use "work"
|
|
192
|
+
multi-codex run -- codex resume # no account name needed here
|
|
193
|
+
multi-codex bind # list bindings; * marks the one in effect here
|
|
194
|
+
multi-codex unbind # remove the binding of the current directory
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
`run` without an account name walks up from the current directory and uses the nearest bound directory. Bindings are stored in `config.json` (not in your project), keyed by the real path of the directory (links resolved, the on-disk letter case used on case-insensitive file systems). `apply -f` keeps the current bindings; removing an account (`remove`, `restore`, `apply -f`) also removes its bindings. Older versions of multi-codex drop the `bindings` field on their next write.
|
|
198
|
+
|
|
199
|
+
### Copying settings from another account
|
|
200
|
+
|
|
201
|
+
`multi-codex add new --config-from work` copies `config.toml` from `work` into `new` once; afterwards the two files are independent. An existing `config.toml` with different content is a conflict (nothing is written), and so is copying into an account that shares `config.toml`. The copy includes everything in the file, such as `cli_auth_credentials_store` or absolute paths that point into the other account.
|
|
202
|
+
|
|
203
|
+
### Per-account environment variables
|
|
204
|
+
|
|
205
|
+
```sh
|
|
206
|
+
multi-codex env work OPENAI_BASE_URL=https://example.com/v1 TERM_PROGRAM=vscode
|
|
207
|
+
multi-codex env work # list
|
|
208
|
+
multi-codex env work --unset TERM_PROGRAM
|
|
209
|
+
multi-codex env work --clear
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
The variables are stored in `config.json` (`accounts.<name>.env`) and written into the launcher. Values are used literally (no `$VAR` expansion). `CODEX_HOME` and the proxy variables are reserved: use `multi-codex proxy` for proxies. A launcher with environment variables is readable only by you (mode 0700), and `list --json` shows only the variable names; still, the values are stored in plain text, so do not put secrets there that need stronger protection. Older versions of multi-codex ignore the `env` field and drop it on their next write; run `multi-codex env NAME --clear` before downgrading.
|
|
213
|
+
|
|
214
|
+
### VS Code and the desktop app (experimental)
|
|
215
|
+
|
|
216
|
+
```sh
|
|
217
|
+
multi-codex code work ~/src/project # a separate VS Code window that uses account "work"
|
|
218
|
+
multi-codex app work # a separate Codex desktop app instance (macOS)
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
These rely on undocumented behavior (verified with VS Code 1.139.1, the OpenAI extension 26.928.31416 and the Codex desktop app 26.831.11858) and may break after an update.
|
|
222
|
+
|
|
223
|
+
- `code` runs `code --user-data-dir <root>/.apps/<name>/vscode` with the account's environment (like `run`). Each account gets its own VS Code settings; extensions are shared from `~/.vscode/extensions`. On macOS the `code` command passes the whole environment, including the account's variables, to `open --env`, so the values are briefly visible in the process list.
|
|
224
|
+
- `app` starts `/Applications/ChatGPT.app` (bundle id `com.openai.codex`) through `open -n` with `CODEX_HOME` set to the account directory and its own data directory `<root>/.apps/<name>/desktop`; output goes to `desktop.log` there. The desktop app loads your login shell's environment, so it uses the shell's proxy settings, not the account's, and the account's extra environment variables are not passed. Log in to one instance at a time: sign-in uses a fixed local callback port. While an account's instance is running, opening the app normally (Dock, Finder, `open -a`) only brings that instance to the front; to run your default account next to it, start it with `open -n -a /Applications/ChatGPT.app`.
|
|
225
|
+
|
|
226
|
+
### Default account
|
|
227
|
+
|
|
228
|
+
After `migrate-default`, `~/.codex` is a link to one account, and plain `codex`, the Codex desktop app and IDE extensions use that account. `multi-codex use` shows which one; `multi-codex use NAME` points the link at another account atomically.
|
|
229
|
+
|
|
230
|
+
A Codex process started without `CODEX_HOME` re-opens files under `~/.codex` while it runs, so switching underneath it would mix the files of two accounts. `use` therefore refuses (exit code 4) while any process has the current default account open — including sessions started with `codex-<name>`, which cannot be told apart. Close Codex (the CLI, the desktop app, IDE extensions and the app-server daemon), switch, then restart them. The check sees only files that are open at that moment, so treat it as a safety net, not a guarantee.
|
|
231
|
+
|
|
232
|
+
If you `remove` the account that `~/.codex` points to, the link is left pointing at an unregistered directory; `doctor` reports it.
|
|
233
|
+
|
|
234
|
+
### Default locations
|
|
235
|
+
|
|
236
|
+
| Item | Default |
|
|
237
|
+
| ---- | ---- |
|
|
238
|
+
| Account directories | `~/.cx/<name>` |
|
|
239
|
+
| Launchers | `~/.local/bin/codex-<name>` |
|
|
240
|
+
| Configuration | `~/.config/multi-codex/config.json` (honours `XDG_CONFIG_HOME`) |
|
|
241
|
+
|
|
242
|
+
Account names may contain letters, digits and `._@+-`, must start with a letter or digit, and are case-insensitive (`Work` and `work` are the same account). An email address works as a name.
|
|
243
|
+
|
|
244
|
+
### Proxy values
|
|
245
|
+
|
|
246
|
+
| Value | Effect in the launcher |
|
|
247
|
+
| ---- | ---- |
|
|
248
|
+
| `inherit` (default) | Leaves proxy variables exactly as they are in your shell. |
|
|
249
|
+
| `off` | Unsets `HTTPS_PROXY`, `HTTP_PROXY`, `ALL_PROXY`, `NO_PROXY` and their lowercase forms. |
|
|
250
|
+
| `7901` | Same as `http://127.0.0.1:7901`. |
|
|
251
|
+
| `http://host:port`, `https://…`, `socks5://…`, `socks5h://…` | Sets `HTTPS_PROXY`/`HTTP_PROXY` (both cases). `ALL_PROXY` is set only for SOCKS proxies; for other proxies an inherited `ALL_PROXY` is removed. `localhost,127.0.0.1,::1` is appended to your existing `NO_PROXY`. |
|
|
252
|
+
|
|
253
|
+
Proxy URLs must not contain a user name or password: launchers are plain, world-readable files.
|
|
254
|
+
|
|
255
|
+
> **Prefer HTTP proxies.** Codex's documentation does not list which proxy variables it honours. Tested with codex-cli 0.159.0 on Linux:
|
|
256
|
+
>
|
|
257
|
+
> - `HTTPS_PROXY` / `HTTP_PROXY` and a lone `ALL_PROXY` are honoured. Every connection (chatgpt.com, ab.chatgpt.com, oaiusercontent.com) went through the proxy, and none bypassed it.
|
|
258
|
+
> - `off` works: with proxy variables set in the parent shell, Codex connected directly.
|
|
259
|
+
> - With a `socks5h://` URL, most connections were still sent as HTTP `CONNECT` requests to that port. A SOCKS proxy therefore only works when its port also speaks HTTP (for example a "mixed" port); a SOCKS-only port will break most requests. multi-codex prints a warning whenever you set a SOCKS proxy.
|
|
260
|
+
|
|
261
|
+
### Declarative setup with `apply`
|
|
262
|
+
|
|
263
|
+
```json
|
|
264
|
+
{
|
|
265
|
+
"version": 1,
|
|
266
|
+
"root": "~/.cx",
|
|
267
|
+
"bin_dir": "~/.local/bin",
|
|
268
|
+
"shared": {"dir": "~/.codex-shared", "items": ["AGENTS.md", "skills"]},
|
|
269
|
+
"accounts": {
|
|
270
|
+
"work": {"proxy": "http://127.0.0.1:7901", "shared": true},
|
|
271
|
+
"personal": {"proxy": "inherit"}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
```sh
|
|
277
|
+
multi-codex apply -f accounts.json
|
|
278
|
+
# or, on a new machine, in one step:
|
|
279
|
+
./install.sh --config accounts.json
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
`apply -f` replaces the configuration with the file. Accounts missing from the file are unregistered (their directories are kept). If anything conflicts, nothing is written at all.
|
|
283
|
+
|
|
284
|
+
## Migrating `~/.codex`
|
|
285
|
+
|
|
286
|
+
`multi-codex migrate-default main`:
|
|
287
|
+
|
|
288
|
+
1. refuses to start when Codex stores the credentials in the system keyring (`cli_auth_credentials_store = "keyring"` in `config.toml` or `/etc/codex/config.toml`, or `"auto"` without an `auth.json`): the keyring entry is tied to the directory path, so you would be logged out after the move. Pass `--accept-relogin` to migrate anyway and log in again afterwards;
|
|
289
|
+
2. refuses to start while any process has files, its working directory or its executable inside `~/.codex` (close Codex, IDE extensions and the ChatGPT browser extension host first);
|
|
290
|
+
3. renames `~/.codex` to `~/.cx/main` when both are on the same file system, otherwise copies, verifies every file by SHA-256, and parks the original as `~/.codex.multi-codex-bak.<timestamp>`;
|
|
291
|
+
4. creates the link `~/.codex -> ~/.cx/main` and registers the account.
|
|
292
|
+
|
|
293
|
+
Progress is recorded in `~/.config/multi-codex/migrate-journal.json`. If the migration is interrupted, run the same command again and it continues from the actual state on disk. While a migration is unfinished, other write commands refuse to run.
|
|
294
|
+
|
|
295
|
+
Sockets and FIFOs (runtime files such as `ipc.sock`) are not copied in copy mode. On macOS, copy mode does not preserve extended attributes.
|
|
296
|
+
|
|
297
|
+
If `CODEX_HOME`, `CODEX_SQLITE_HOME`, `CODEX_API_KEY` or `CODEX_ACCESS_TOKEN` is set in your environment, multi-codex warns you: these variables override or bypass per-account isolation.
|
|
298
|
+
|
|
299
|
+
### Undoing a migration
|
|
300
|
+
|
|
301
|
+
```sh
|
|
302
|
+
multi-codex restore main
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
`restore` removes the `~/.codex` link, moves `~/.cx/main` back to `~/.codex` and unregisters the account (its launcher is deleted; shared links inside the directory stay and keep working). It checks the same things as `migrate-default`: whether the directory is in use, and whether credentials are in the system keyring (moving the directory back logs you out in that case; `--accept-relogin` proceeds anyway). If it is interrupted, run the same command again. While a restore is unfinished, other write commands refuse to run. If `~/.codex` currently points to another account, run `multi-codex use main` first. The account directory and `~/.codex` must be on the same file system; otherwise restore by hand:
|
|
306
|
+
|
|
307
|
+
```sh
|
|
308
|
+
rm ~/.codex # remove the link (only the link)
|
|
309
|
+
mv ~/.cx/main ~/.codex # move the data back
|
|
310
|
+
multi-codex remove main # unregister the account
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
With `--keep-backup` in copy mode, the original directory stays at `~/.codex.multi-codex-bak.<timestamp>`.
|
|
314
|
+
|
|
315
|
+
If a migration stops with an error and you want to abandon it: the error message says where the complete data is; move it back to `~/.codex` and delete `~/.config/multi-codex/migrate-journal.json`.
|
|
316
|
+
|
|
317
|
+
## Shared resources
|
|
318
|
+
|
|
319
|
+
```sh
|
|
320
|
+
multi-codex init --shared-dir ~/.codex-shared --shared-items AGENTS.md,skills,rules,agents
|
|
321
|
+
multi-codex add work --shared
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
Put the items you want to share into the shared directory first: items missing from it are skipped (`skip`), and nothing is linked for them. multi-codex creates the missing links and remembers which links it created. Turning sharing off removes only those links; links you made yourself are left alone. A real file or directory at a link location is a conflict and is never overwritten.
|
|
325
|
+
|
|
326
|
+
If you already linked an account to the shared directory by hand, `multi-codex add NAME --shared --adopt` takes those links over without recreating them: from then on, turning sharing off removes them as well. Only links that already point to the matching shared item are adopted.
|
|
327
|
+
|
|
328
|
+
### What can be shared
|
|
329
|
+
|
|
330
|
+
Based on the Codex source code (openai/codex at `6b4daafd`):
|
|
331
|
+
|
|
332
|
+
| Item | What it is | Share? | Why |
|
|
333
|
+
| ---- | ---- | ---- | ---- |
|
|
334
|
+
| `AGENTS.md` | Global instructions | Yes (default) | Read fresh on every load; Codex does not write it. |
|
|
335
|
+
| `agents/` | Custom agent roles | Yes (default) | Read-only. |
|
|
336
|
+
| `rules/` | Exec policy ("always allow" commands) | Yes (default) | Appends are file-locked. An approval given in one account then applies to all sharing accounts. |
|
|
337
|
+
| `skills/` | Skills | Yes (default) | On start-up Codex rewrites `skills/.system` when its built-in skills differ; harmless as long as all accounts use the same Codex version. |
|
|
338
|
+
| `config.toml` | Settings | With care | Codex writes through the link and replaces the target atomically, so the link survives; but there is no cross-process lock, so two accounts changing settings at the same time can lose one change. |
|
|
339
|
+
| `history.jsonl` | Prompt history | Yes | Reads and writes are file-locked; the histories of the accounts are merged. |
|
|
340
|
+
| `auth.json`, `secrets/`, `.credentials.json`, `.env` | Credentials | **No** | They are the account. |
|
|
341
|
+
| `installation_id` | Installation identifier | No | Sent with requests; sharing makes several accounts look like one installation. |
|
|
342
|
+
| `*.sqlite` (`state_5.sqlite`, …) | Threads, logs, memories | **No** | `state_5.sqlite` records account IDs. |
|
|
343
|
+
| `sessions/`, `archived_sessions/`, `session_index.jsonl` | Session logs | No | The index has only an in-process lock; sessions record the account that created them. |
|
|
344
|
+
| `models_cache.json`, `cache/` | Caches | Not needed | Keyed by the account; a mismatch is a cache miss. |
|
|
345
|
+
| `app-server-control/`, `app-server-daemon/`, `packages/`, `tmp/`, `.tmp/`, `log/`, `shell_snapshots/` | Runtime state | No | Per process or per session. |
|
|
346
|
+
|
|
347
|
+
Separate directories keep the local state of the accounts apart. They are **not** a security boundary: any program running as your user can read every account directory.
|
|
348
|
+
|
|
349
|
+
## FAQ
|
|
350
|
+
|
|
351
|
+
### Which account do VS Code and the desktop app use when I start them from the Dock?
|
|
352
|
+
|
|
353
|
+
The one in `~/.codex`. Apps started from the Dock or Finder get no `CODEX_HOME` from your terminal; both the OpenAI extension and the Codex desktop app then fall back to `~/.codex` (`process.env.CODEX_HOME ?? ~/.codex` in their code). They do read your login shell's environment, so a `CODEX_HOME` exported in your shell profile would apply, but setting it there is not recommended: it also changes what plain `codex` uses in every terminal.
|
|
354
|
+
|
|
355
|
+
### How do I change that default account?
|
|
356
|
+
|
|
357
|
+
Let multi-codex manage `~/.codex`, then switch with `use`:
|
|
358
|
+
|
|
359
|
+
```sh
|
|
360
|
+
# Close Codex everywhere first (VS Code, the desktop app, codex sessions in terminals)
|
|
361
|
+
multi-codex migrate-default main # turn the current ~/.codex into an account named "main"
|
|
362
|
+
multi-codex use work # ~/.codex now points to account "work"
|
|
363
|
+
multi-codex use # show the current default account
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
From then on, apps started from the Dock use the account `use` points to. To undo the migration, point `~/.codex` back at it first: `multi-codex use main`, then `multi-codex restore main` (`restore` refuses while `~/.codex` points to another account). `use` and `restore` refuse to run while a process is using the directories involved, so close Codex first; see [Default account](#default-account).
|
|
367
|
+
|
|
368
|
+
### How do I open VS Code with a particular account?
|
|
369
|
+
|
|
370
|
+
```sh
|
|
371
|
+
multi-codex code work ~/src/project
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
This starts a separate VS Code instance with its own user data directory and the account's environment. A separate data directory is required: with the same one, `code` only hands the request to the VS Code that is already running, whose Codex extension keeps using the environment it started with. The extension has no setting for choosing an account. See [VS Code and the desktop app (experimental)](#vs-code-and-the-desktop-app-experimental).
|
|
375
|
+
|
|
376
|
+
### How do I open the Codex desktop app with a particular account?
|
|
377
|
+
|
|
378
|
+
```sh
|
|
379
|
+
multi-codex app work
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
To do it by hand, both variables are needed: without `CODEX_ELECTRON_USER_DATA_PATH` the desktop app replaces `CODEX_HOME` with your login shell's value after it starts, and shares its data directory with the default instance.
|
|
383
|
+
|
|
384
|
+
```sh
|
|
385
|
+
D="$HOME/.cx/.apps/work/desktop"; mkdir -p "$D"
|
|
386
|
+
open -n --env CODEX_HOME="$HOME/.cx/work" --env CODEX_ELECTRON_USER_DATA_PATH="$D" \
|
|
387
|
+
-a /Applications/ChatGPT.app --args --user-data-dir="$D"
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### Where do I run `open -n -a /Applications/ChatGPT.app`?
|
|
391
|
+
|
|
392
|
+
In any terminal window (Terminal, iTerm, Warp, …), from any directory. It is the macOS `open` command, not part of multi-codex. `-n` starts a new instance even if one is running; without `CODEX_HOME` the new instance uses `~/.codex`. You need it to run the default account next to an account instance started with `multi-codex app`, because opening the app normally (Dock, Finder, `open -a`) only brings the running instance to the front.
|
|
393
|
+
|
|
394
|
+
### `codex login status` says I am logged in, but the desktop app asks me to sign in. Why?
|
|
395
|
+
|
|
396
|
+
`codex login status` only checks that the credentials file exists; it does not check that the token still works. If a directory has not been used for a while, its token may no longer be accepted, and the app shows the sign-in page. Sign in again for that directory (for an account: `codex-<name> login`). `multi-codex usage --live NAME` asks Codex for live usage and fails if the login is no longer valid.
|
|
397
|
+
|
|
398
|
+
### How do I upgrade multi-codex?
|
|
399
|
+
|
|
400
|
+
Run the installer again; configuration, accounts and launchers are not touched:
|
|
401
|
+
|
|
402
|
+
```sh
|
|
403
|
+
curl -fsSL https://raw.githubusercontent.com/jakoes-wu/multi-codex/main/install.sh | sh
|
|
404
|
+
multi-codex --version
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
## Exit codes
|
|
408
|
+
|
|
409
|
+
| Code | Meaning |
|
|
410
|
+
| ---- | ---- |
|
|
411
|
+
| 0 | Success, or already in the desired state |
|
|
412
|
+
| 1 | Runtime error (I/O, invalid configuration file, failed verification, lock held by another command, an unfinished migration or restore blocks the command); `usage`: at least one account failed; `doctor`: at least one check failed |
|
|
413
|
+
| 2 | Invalid command-line arguments |
|
|
414
|
+
| 3 | Conflict with files multi-codex does not own; `migrate-default` or `restore` refused because credentials are in the system keyring; `use` / `restore` found `~/.codex` in an unexpected state or on another file system. Nothing was changed |
|
|
415
|
+
| 4 | The directory to migrate, switch away from or restore is in use |
|
|
416
|
+
|
|
417
|
+
## More installation options
|
|
418
|
+
|
|
419
|
+
From a clone:
|
|
420
|
+
|
|
421
|
+
```sh
|
|
422
|
+
git clone https://github.com/jakoes-wu/multi-codex.git
|
|
423
|
+
cd multi-codex
|
|
424
|
+
./install.sh
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
With pipx: `pipx install git+https://github.com/jakoes-wu/multi-codex`.
|
|
428
|
+
|
|
429
|
+
The tool goes to `~/.local/share/multi-codex` and the `multi-codex` command to `~/.local/bin`. Use `--prefix DIR` to install somewhere else. Run `./install.sh --help` for all options.
|
|
430
|
+
|
|
431
|
+
**Verified downloads.** From v0.5.0 on, every release publishes `multi-codex-<tag>.tar.gz` and `SHA256SUMS`. The remote installer downloads that archive and checks its SHA-256 before installing anything; a mismatch stops the installation. Branches and older releases are installed unverified (the installer says so); set `MULTI_CODEX_REQUIRE_CHECKSUM=1` to refuse them. The checksum is published next to the archive, so it protects against a damaged or altered download, not against a compromised GitHub account.
|
|
432
|
+
|
|
433
|
+
## Uninstalling
|
|
434
|
+
|
|
435
|
+
```sh
|
|
436
|
+
./install.sh --uninstall # from a clone
|
|
437
|
+
curl -fsSL https://raw.githubusercontent.com/jakoes-wu/multi-codex/main/install.sh | sh -s -- --uninstall # without a clone
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
This removes the tool only. Your configuration, account directories and `codex-<name>` launchers stay; the launchers keep working because they do not depend on multi-codex.
|
|
441
|
+
|
|
442
|
+
## Contributing
|
|
443
|
+
|
|
444
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md). Run the tests with:
|
|
445
|
+
|
|
446
|
+
```sh
|
|
447
|
+
python3 -m unittest discover -s tests -t tests
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
## License
|
|
451
|
+
|
|
452
|
+
[MIT](LICENSE)
|