claude-multiacc 2.0.61 → 2.0.65
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.
- package/README.md +6 -2
- package/bin/claude-accounts +4 -4
- package/bin/codex +54 -13
- package/bin/codex-accounts +28 -7
- package/docs/ACCOUNT_OPERATIONS.md +2 -2
- package/docs/AUTORESUME.md +16 -2
- package/docs/CODEX.md +58 -2
- package/docs/CODEX_MCP_AUTH.md +58 -0
- package/lib/__pycache__/audit.cpython-314.pyc +0 -0
- package/lib/__pycache__/autoresume.cpython-314.pyc +0 -0
- package/lib/__pycache__/autoresume_cross.cpython-314.pyc +0 -0
- package/lib/__pycache__/autoresume_cross_dispatch.cpython-314.pyc +0 -0
- package/lib/__pycache__/autoresume_cross_receipt.cpython-314.pyc +0 -0
- package/lib/__pycache__/autoresume_handoff.cpython-314.pyc +0 -0
- package/lib/__pycache__/autoresume_probe.cpython-314.pyc +0 -0
- package/lib/__pycache__/autoresume_receipt.cpython-314.pyc +0 -0
- package/lib/__pycache__/claude_client_error.cpython-314.pyc +0 -0
- package/lib/__pycache__/claude_reset.cpython-314.pyc +0 -0
- package/lib/__pycache__/claude_token_identity.cpython-314.pyc +0 -0
- package/lib/__pycache__/codex_autoresume_mode.cpython-314.pyc +0 -0
- package/lib/__pycache__/codex_config_edit.cpython-314.pyc +0 -0
- package/lib/__pycache__/codex_credit_controls.cpython-314.pyc +0 -0
- package/lib/__pycache__/codex_credit_local_scope.cpython-314.pyc +0 -0
- package/lib/__pycache__/codex_credit_selection.cpython-314.pyc +0 -0
- package/lib/__pycache__/codex_credits.cpython-314.pyc +0 -0
- package/lib/__pycache__/codex_limits_lock.cpython-314.pyc +0 -0
- package/lib/__pycache__/codex_mcp_oauth.cpython-314.pyc +0 -0
- package/lib/__pycache__/codex_python.cpython-314.pyc +0 -0
- package/lib/__pycache__/codex_resume_cwd.cpython-314.pyc +0 -0
- package/lib/__pycache__/codex_speed.cpython-314.pyc +0 -0
- package/lib/__pycache__/codex_trust.cpython-314.pyc +0 -0
- package/lib/__pycache__/codex_usage_fetch.cpython-314.pyc +0 -0
- package/lib/__pycache__/keychain.cpython-314.pyc +0 -0
- package/lib/__pycache__/mcp_mirror.cpython-314.pyc +0 -0
- package/lib/__pycache__/mcp_oauth.cpython-314.pyc +0 -0
- package/lib/__pycache__/mcp_registry.cpython-314.pyc +0 -0
- package/lib/__pycache__/model_catalog.cpython-314.pyc +0 -0
- package/lib/__pycache__/model_match.cpython-314.pyc +0 -0
- package/lib/__pycache__/report.cpython-314.pyc +0 -0
- package/lib/__pycache__/selector_credit_scope.cpython-314.pyc +0 -0
- package/lib/__pycache__/selector_credits.cpython-314.pyc +0 -0
- package/lib/__pycache__/selector_funding.cpython-314.pyc +0 -0
- package/lib/__pycache__/selector_policy.cpython-314.pyc +0 -0
- package/lib/__pycache__/selector_primitives.cpython-314.pyc +0 -0
- package/lib/__pycache__/session_settings.cpython-314.pyc +0 -0
- package/lib/__pycache__/shim_path.cpython-314.pyc +0 -0
- package/lib/autoresume.py +34 -13
- package/lib/autoresume_cross.py +2 -1
- package/lib/autoresume_handoff.py +13 -5
- package/lib/codex_exec_stdin.sh +45 -0
- package/lib/codex_mcp_oauth.py +204 -0
- package/lib/codex_mcp_oauth.sh +29 -0
- package/lib/codex_mcp_oauth_locks.py +158 -0
- package/lib/codex_mcp_oauth_native.py +102 -0
- package/lib/codex_mcp_oauth_store.py +149 -0
- package/lib/codex_resume_cwd.py +57 -0
- package/lib/codex_retry_process.sh +37 -0
- package/lib/codex_speed.py +351 -0
- package/lib/codex_speed.sh +39 -0
- package/lib/common.sh +16 -3
- package/lib/model_match.py +4 -1
- package/lib/session_settings.py +25 -6
- package/lib/shim_path.py +6 -0
- package/package.json +10 -5
- package/tests/__pycache__/autoresume_cross_receipt_support.cpython-314.pyc +0 -0
- package/tests/__pycache__/autoresume_cross_tmux_environment.cpython-314.pyc +0 -0
- package/tests/__pycache__/autoresume_cross_tmux_support.cpython-314.pyc +0 -0
- package/tests/__pycache__/autoresume_cross_unit_support.cpython-314.pyc +0 -0
- package/tests/__pycache__/autoresume_tmux_support.cpython-314.pyc +0 -0
- package/tests/__pycache__/packaged_command_support.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_autoresume.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_autoresume_catalog.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_autoresume_catalog_cli.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_autoresume_codex_records.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_autoresume_codex_trust.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_autoresume_cross_claude_holders.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_autoresume_cross_guards.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_autoresume_cross_holders.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_autoresume_cross_origin.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_autoresume_cross_policy.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_autoresume_cross_receipt.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_autoresume_cross_tmux.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_autoresume_cross_tmux_races.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_autoresume_cross_tokens.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_autoresume_cursor.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_autoresume_cwd.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_autoresume_cwd_handoff.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_autoresume_cwd_reader.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_autoresume_cwd_tmux.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_autoresume_fallback.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_autoresume_handoff_argv.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_autoresume_handoff_history.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_autoresume_handoff_origins.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_autoresume_memory.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_autoresume_memory_tmux.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_autoresume_mode.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_autoresume_probe.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_autoresume_receipt.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_autoresume_relaunch_settings.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_autoresume_settings.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_autoresume_sidechain.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_autoresume_sidechain_tmux.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_autoresume_tmux.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_autoresume_tmux_modes.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_autoresume_trust_tmux.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_claude_reset.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_claude_token_identity.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_codex_exec_stdin.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_codex_mcp_oauth.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_codex_reset.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_codex_reset_credits.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_codex_reset_credits_polling.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_codex_reset_credits_report.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_codex_reset_fetch.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_codex_reset_polling.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_codex_reset_reporting.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_codex_reset_windows.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_mcp_mirror.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_mcp_mirror_args.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_mcp_oauth.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_mcp_registry.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_selector.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_selector_cached_credits.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_selector_credit_scope.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_selector_credit_shim.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_selector_credits.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_selector_spread.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_token_identity_ceremony.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_token_identity_integration.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_token_identity_retry.cpython-314.pyc +0 -0
- package/tests/__pycache__/test_token_identity_sync.cpython-314.pyc +0 -0
- package/tests/fake_codex_tui.py +23 -1
- package/tests/run-tests.sh +23 -7
- package/tests/test_autoresume_cwd.py +71 -0
- package/tests/test_autoresume_cwd_handoff.py +92 -0
- package/tests/test_autoresume_cwd_reader.py +105 -0
- package/tests/test_autoresume_cwd_tmux.py +54 -0
- package/tests/test_codex_exec_stdin.py +178 -0
- package/tests/test_codex_mcp_oauth.py +146 -0
- package/tests/test_codex_mcp_oauth_adopt.py +87 -0
- package/tests/test_codex_mcp_oauth_migration.py +141 -0
- package/tests/test_codex_mcp_oauth_native.py +109 -0
- package/tests/test_codex_mcp_oauth_safety.py +130 -0
- package/tests/test_codex_mcp_oauth_shim.py +188 -0
- package/tests/test_codex_retry_process.py +143 -0
- package/tests/test_codex_speed.py +236 -0
- package/tests/test_mutate_lock.py +107 -0
- package/tests/test_shim_path.py +44 -4
- package/lib/__pycache__/audit.cpython-312.pyc +0 -0
- package/lib/__pycache__/autoresume.cpython-312.pyc +0 -0
- package/lib/__pycache__/autoresume_cross.cpython-312.pyc +0 -0
- package/lib/__pycache__/autoresume_cross_dispatch.cpython-312.pyc +0 -0
- package/lib/__pycache__/autoresume_cross_receipt.cpython-312.pyc +0 -0
- package/lib/__pycache__/autoresume_handoff.cpython-312.pyc +0 -0
- package/lib/__pycache__/autoresume_probe.cpython-312.pyc +0 -0
- package/lib/__pycache__/autoresume_receipt.cpython-312.pyc +0 -0
- package/lib/__pycache__/claude_client_error.cpython-312.pyc +0 -0
- package/lib/__pycache__/claude_reset.cpython-312.pyc +0 -0
- package/lib/__pycache__/claude_token_identity.cpython-312.pyc +0 -0
- package/lib/__pycache__/codex_autoresume_mode.cpython-312.pyc +0 -0
- package/lib/__pycache__/codex_config_edit.cpython-312.pyc +0 -0
- package/lib/__pycache__/codex_credit_controls.cpython-312.pyc +0 -0
- package/lib/__pycache__/codex_credit_local_scope.cpython-312.pyc +0 -0
- package/lib/__pycache__/codex_credit_selection.cpython-312.pyc +0 -0
- package/lib/__pycache__/codex_credits.cpython-312.pyc +0 -0
- package/lib/__pycache__/codex_limits_lock.cpython-312.pyc +0 -0
- package/lib/__pycache__/codex_python.cpython-312.pyc +0 -0
- package/lib/__pycache__/codex_trust.cpython-312.pyc +0 -0
- package/lib/__pycache__/codex_usage_fetch.cpython-312.pyc +0 -0
- package/lib/__pycache__/keychain.cpython-312.pyc +0 -0
- package/lib/__pycache__/mcp_mirror.cpython-312.pyc +0 -0
- package/lib/__pycache__/mcp_oauth.cpython-312.pyc +0 -0
- package/lib/__pycache__/mcp_registry.cpython-312.pyc +0 -0
- package/lib/__pycache__/model_catalog.cpython-312.pyc +0 -0
- package/lib/__pycache__/model_match.cpython-312.pyc +0 -0
- package/lib/__pycache__/report.cpython-312.pyc +0 -0
- package/lib/__pycache__/selector_credit_scope.cpython-312.pyc +0 -0
- package/lib/__pycache__/selector_credits.cpython-312.pyc +0 -0
- package/lib/__pycache__/selector_funding.cpython-312.pyc +0 -0
- package/lib/__pycache__/selector_policy.cpython-312.pyc +0 -0
- package/lib/__pycache__/selector_primitives.cpython-312.pyc +0 -0
- package/lib/__pycache__/session_settings.cpython-312.pyc +0 -0
- package/lib/__pycache__/shim_path.cpython-312.pyc +0 -0
- package/tests/__pycache__/autoresume_cross_receipt_support.cpython-312.pyc +0 -0
- package/tests/__pycache__/autoresume_cross_tmux_environment.cpython-312.pyc +0 -0
- package/tests/__pycache__/autoresume_cross_tmux_support.cpython-312.pyc +0 -0
- package/tests/__pycache__/autoresume_cross_unit_support.cpython-312.pyc +0 -0
- package/tests/__pycache__/autoresume_tmux_support.cpython-312.pyc +0 -0
- package/tests/__pycache__/packaged_command_support.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_catalog.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_catalog_cli.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_codex_records.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_codex_trust.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_cross_claude_holders.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_cross_guards.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_cross_holders.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_cross_origin.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_cross_policy.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_cross_receipt.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_cross_tmux.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_cross_tmux_races.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_cross_tokens.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_cursor.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_fallback.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_handoff_argv.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_handoff_history.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_handoff_origins.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_memory.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_memory_tmux.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_mode.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_probe.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_receipt.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_relaunch_settings.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_settings.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_sidechain.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_sidechain_tmux.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_tmux.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_tmux_modes.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_trust_tmux.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_claude_reset.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_claude_token_identity.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_codex_reset.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_codex_reset_credits.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_codex_reset_credits_polling.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_codex_reset_credits_report.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_codex_reset_fetch.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_codex_reset_polling.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_codex_reset_reporting.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_codex_reset_windows.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_mcp_mirror.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_mcp_mirror_args.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_mcp_oauth.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_mcp_registry.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_selector.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_selector_cached_credits.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_selector_credit_scope.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_selector_credit_shim.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_selector_credits.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_selector_spread.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_token_identity_ceremony.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_token_identity_integration.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_token_identity_retry.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_token_identity_sync.cpython-312.pyc +0 -0
package/README.md
CHANGED
|
@@ -685,8 +685,12 @@ claude-accounts mcp auth-forget higgsfield # log it out of EVERY account on th
|
|
|
685
685
|
cannot open) stays ignored and is cleared by a later pass.
|
|
686
686
|
- **Kill switches**: `CLAUDE_MULTIACC_MCP_OAUTH=0` stops sharing (the shim's merge and the
|
|
687
687
|
limits pass's sync); `CLAUDE_MULTIACC_MCP_OAUTH_REFRESH=0` never refreshes — and so shares
|
|
688
|
-
nothing. `auth-status` and `auth-forget` always run.
|
|
689
|
-
|
|
688
|
+
nothing. `auth-status` and `auth-forget` always run. These commands describe the Claude pool.
|
|
689
|
+
|
|
690
|
+
Codex shares its native MCP credential file and refresh locks locally, so a login in
|
|
691
|
+
one pooled account works in the others. Run `codex-accounts mcp auth-sync` to repair
|
|
692
|
+
sharing now; use native `codex mcp login NAME`, `list`, and `logout NAME` for authentication.
|
|
693
|
+
See [Codex MCP authentication](docs/CODEX_MCP_AUTH.md) for storage constraints and recovery.
|
|
690
694
|
|
|
691
695
|
app-robot tasks are different: the panel launches Claude with `--strict-mcp-config` and a
|
|
692
696
|
merged MCP document (`agent_mcp.servers_for`) and Codex with the same document as `-c`
|
package/bin/claude-accounts
CHANGED
|
@@ -1708,7 +1708,7 @@ token_identity_receipt() { # Unknown results must never overwrite a peer's prove
|
|
|
1708
1708
|
limits_distribute_now() {
|
|
1709
1709
|
local lock="$ACC_ROOT/tmp/limits-push.lock"
|
|
1710
1710
|
mkdir -p "$ACC_ROOT/tmp" 2>/dev/null || return 0
|
|
1711
|
-
if !
|
|
1711
|
+
if ! atomic_mkdir "$lock" 2>/dev/null; then
|
|
1712
1712
|
# A DETACHED push that is killed (logout, reboot, pkill) never runs its EXIT trap,
|
|
1713
1713
|
# and mkdir can never take a lock dir nobody will remove: one stranded lock
|
|
1714
1714
|
# silently stopped ALL telemetry distribution on the live pool from 2026-09-03
|
|
@@ -1721,7 +1721,7 @@ limits_distribute_now() {
|
|
|
1721
1721
|
# exactly as before.
|
|
1722
1722
|
[ $(( $(epoch_now) - $(file_mtime "$lock") )) -gt 600 ] || return 0
|
|
1723
1723
|
rm -rf "$lock" 2>/dev/null
|
|
1724
|
-
|
|
1724
|
+
atomic_mkdir "$lock" 2>/dev/null || return 0
|
|
1725
1725
|
log_to sync.log "stale limits-push lock broken (older than 600s); distributing"
|
|
1726
1726
|
fi
|
|
1727
1727
|
trap 'rmdir "$lock" 2>/dev/null || true' EXIT
|
|
@@ -1792,7 +1792,7 @@ cmd_limits() {
|
|
|
1792
1792
|
done
|
|
1793
1793
|
local lock="$ACC_ROOT/.locks/limits"
|
|
1794
1794
|
mkdir -p "$ACC_ROOT/.locks"
|
|
1795
|
-
if !
|
|
1795
|
+
if ! atomic_mkdir "$lock" 2>/dev/null; then
|
|
1796
1796
|
local age=$(( $(epoch_now) - $(file_mtime "$lock") ))
|
|
1797
1797
|
if [ "$age" -lt 120 ]; then
|
|
1798
1798
|
# A --json caller still gets the document (built from the state on disk) —
|
|
@@ -1802,7 +1802,7 @@ cmd_limits() {
|
|
|
1802
1802
|
return 0
|
|
1803
1803
|
fi
|
|
1804
1804
|
rm -rf "$lock"
|
|
1805
|
-
if !
|
|
1805
|
+
if ! atomic_mkdir "$lock" 2>/dev/null; then
|
|
1806
1806
|
if [ "$json" = "1" ]; then emit_report_json limits; return $?; fi
|
|
1807
1807
|
return 0
|
|
1808
1808
|
fi
|
package/bin/codex
CHANGED
|
@@ -93,6 +93,11 @@ fi
|
|
|
93
93
|
# the servers themselves. Same contract as bin/claude's mcp_ensure.
|
|
94
94
|
MCP_PY="$SELF_DIR/../lib/mcp_registry.py"
|
|
95
95
|
MCP_PYBIN="${CLAUDE_MULTIACC_PYTHON:-python3}"
|
|
96
|
+
mcp_oauth_ensure() {
|
|
97
|
+
[ -f "$SELF_DIR/../lib/codex_mcp_oauth.sh" ] || return 0
|
|
98
|
+
bash "$SELF_DIR/../lib/codex_mcp_oauth.sh" "$ACC_ROOT" "$MCP_PYBIN" \
|
|
99
|
+
"$SELF_DIR/../lib/codex_mcp_oauth.py" "$1" "$REAL" >/dev/null 2>&1 </dev/null || true
|
|
100
|
+
}
|
|
96
101
|
mcp_sig() { # $1 acct dir -> "m:z,m:z,m:z" (registry, overlay, account config; - = absent)
|
|
97
102
|
local f out="" s
|
|
98
103
|
for f in "$ACC_ROOT/mcp-servers.json" "$ACC_ROOT/mcp-servers.local.json" "$1/config.toml"; do
|
|
@@ -151,6 +156,20 @@ cli_positional_at() { # argv -> prints the index of its first positional argumen
|
|
|
151
156
|
return 1
|
|
152
157
|
}
|
|
153
158
|
|
|
159
|
+
# Account/model capabilities are resolved only after the serving account is known.
|
|
160
|
+
# The helper is optional so a damaged install retains the shim's fail-open contract.
|
|
161
|
+
codex_native_argv() {
|
|
162
|
+
CODEX_SPEED_ARGV=("$@")
|
|
163
|
+
if [ -f "$SELF_DIR/../lib/codex_speed.sh" ]; then
|
|
164
|
+
. "$SELF_DIR/../lib/codex_speed.sh"
|
|
165
|
+
codex_speed_prepare "$@" || return $?
|
|
166
|
+
fi
|
|
167
|
+
}
|
|
168
|
+
codex_native_exec() {
|
|
169
|
+
codex_native_argv "$@" || exit $?
|
|
170
|
+
exec "$REAL" ${CODEX_SPEED_ARGV[@]+"${CODEX_SPEED_ARGV[@]}"}
|
|
171
|
+
}
|
|
172
|
+
|
|
154
173
|
# Whether argv is `codex [global options] mcp add|remove …`.
|
|
155
174
|
mcp_mutation_argv() {
|
|
156
175
|
local i j
|
|
@@ -243,8 +262,11 @@ if [ -n "${CODEX_HOME:-}" ] \
|
|
|
243
262
|
printf 'codex-multiacc: auto-resume cannot enter account selection.\n' >&2
|
|
244
263
|
exit 2
|
|
245
264
|
fi
|
|
265
|
+
if [ "${CODEX_MULTIACC_DISABLE:-0}" != "1" ] && [ -n "${CODEX_HOME:-}" ]; then
|
|
266
|
+
mcp_oauth_ensure "$CODEX_HOME"
|
|
267
|
+
fi
|
|
246
268
|
mcp_nested_mirror "$@" || true
|
|
247
|
-
|
|
269
|
+
codex_native_exec "$@"
|
|
248
270
|
fi
|
|
249
271
|
|
|
250
272
|
now="$(date +%s)"
|
|
@@ -1066,6 +1088,9 @@ ar_argv_ok() { # the argv codex is about to get
|
|
|
1066
1088
|
model:*) case "$a" in ''|-*) return 1 ;; esac ;;
|
|
1067
1089
|
effort:model_reasoning_effort=?*)
|
|
1068
1090
|
case "${a#model_reasoning_effort=}" in *[!$AR_LOWER]*) return 1 ;; esac ;;
|
|
1091
|
+
effort:service_tier=*)
|
|
1092
|
+
case "${a#service_tier=}" in default|fast|priority|ultrafast|'"default"'|'"fast"'|'"priority"'|'"ultrafast"'|"'default'"|"'fast'"|"'priority'"|"'ultrafast'") ;; *) return 1 ;; esac ;;
|
|
1093
|
+
effort:features.fast_mode=true) ;;
|
|
1069
1094
|
*) return 1 ;;
|
|
1070
1095
|
esac
|
|
1071
1096
|
want=""
|
|
@@ -1258,11 +1283,13 @@ if [ -n "${CODEX_ACCOUNT:-}" ] && [ "$AR_PROBE" != "1" ]; then
|
|
|
1258
1283
|
share_state_index "$d"
|
|
1259
1284
|
export CODEX_HOME="$d"
|
|
1260
1285
|
export CODEX_SHIM_ACTIVE=1
|
|
1286
|
+
unset CODEX_MULTIACC_SELECTED_HOME
|
|
1261
1287
|
# A pinned account carries every registered MCP server too, and a pinned
|
|
1262
1288
|
# `codex mcp add|remove` is mirrored like any other (exits inside).
|
|
1263
1289
|
mcp_ensure "$d"
|
|
1290
|
+
mcp_oauth_ensure "$d"
|
|
1264
1291
|
mcp_mirror_run "$d" "$@" || true
|
|
1265
|
-
|
|
1292
|
+
codex_native_exec "$@"
|
|
1266
1293
|
fi
|
|
1267
1294
|
sel_log "pin-invalid account=$CODEX_ACCOUNT (no such dir; random fallback)"
|
|
1268
1295
|
fi
|
|
@@ -1333,7 +1360,7 @@ if [ "${#valid[@]}" -eq 0 ]; then
|
|
|
1333
1360
|
[ -t 2 ] && printf 'codex-multiacc: no pool account is usable (%s) — see: codex-accounts expired, then: codex-accounts relogin\n' \
|
|
1334
1361
|
"${ids# }" >&2
|
|
1335
1362
|
fi
|
|
1336
|
-
|
|
1363
|
+
codex_native_exec "$@"
|
|
1337
1364
|
fi
|
|
1338
1365
|
|
|
1339
1366
|
# ---- rotation ----------------------------------------------------------------
|
|
@@ -1551,6 +1578,7 @@ if [ "$AR_TIER" = "credits" ]; then
|
|
|
1551
1578
|
fi
|
|
1552
1579
|
# The picked account carries every registered MCP server before the client starts.
|
|
1553
1580
|
mcp_ensure "$pick"
|
|
1581
|
+
mcp_oauth_ensure "$pick"
|
|
1554
1582
|
|
|
1555
1583
|
# Opportunistic limits refresh: non-blocking, throttled, backgrounded. The windows are
|
|
1556
1584
|
# deliberately wide (10m, matching the 5m scheduled pass): the usage endpoint rate-limits
|
|
@@ -1581,6 +1609,9 @@ sel_log "$acct weekly=$(fresh_field "$pick" weekly_percent || echo '?')%" \
|
|
|
1581
1609
|
"session-ok=${PICK_GATE_COUNT} pwd=$PWD${credit_funding_log}"
|
|
1582
1610
|
|
|
1583
1611
|
export CODEX_SHIM_ACTIVE=1
|
|
1612
|
+
# An isolated child job can distinguish this automatic selection from a caller pin.
|
|
1613
|
+
# Consumers must also require CODEX_HOME to match and respect explicit pins/disable.
|
|
1614
|
+
export CODEX_MULTIACC_SELECTED_HOME="$pick"
|
|
1584
1615
|
|
|
1585
1616
|
# A `codex mcp add|remove` is run as a child under the picked account and mirrored to
|
|
1586
1617
|
# every other one (exits inside); anything else falls through to exec.
|
|
@@ -1591,9 +1622,9 @@ if mcp_mutation_argv "$@"; then
|
|
|
1591
1622
|
fi
|
|
1592
1623
|
|
|
1593
1624
|
# Auto-retry applies only to `codex exec` runs with an alternative account available,
|
|
1594
|
-
# and
|
|
1595
|
-
#
|
|
1596
|
-
#
|
|
1625
|
+
# and stdin is a file/device or an explicit `exec ... -` prompt. That prompt needs
|
|
1626
|
+
# EOF in the native CLI too. Other pipes stay unbuffered so a non-consuming command
|
|
1627
|
+
# can finish even when its service leaves stdin open.
|
|
1597
1628
|
wants_retry=0
|
|
1598
1629
|
if [ "${CODEX_SHIM_RETRY:-1}" != "0" ] \
|
|
1599
1630
|
&& { [ "${#eligible[@]}" -ge 2 ] || [ "$AR_TIER" = "credits" ]; }; then
|
|
@@ -1603,19 +1634,26 @@ if [ "${CODEX_SHIM_RETRY:-1}" != "0" ] \
|
|
|
1603
1634
|
if [ "$wants_retry" = "1" ]; then
|
|
1604
1635
|
# A TTY cannot be buffered or replayed: `codex exec` with no prompt argument reads
|
|
1605
1636
|
# stdin, and the retry path would hand it /dev/null. Plain exec instead —
|
|
1606
|
-
# stdin is inherited untouched.
|
|
1607
|
-
#
|
|
1608
|
-
#
|
|
1637
|
+
# stdin is inherited untouched. Explicit stdin prompts can replay a pipe: an
|
|
1638
|
+
# AppInspire/Node child receives a complete prompt through a socket, not a file.
|
|
1639
|
+
# Keep unknown commands and pipes the CLI need not consume on direct exec.
|
|
1609
1640
|
if [ -t 0 ]; then
|
|
1610
1641
|
wants_retry=0
|
|
1611
1642
|
elif [ -f /dev/fd/0 ] || [ -c /dev/fd/0 ]; then
|
|
1612
1643
|
:
|
|
1644
|
+
elif bash "$SELF_DIR/../lib/codex_exec_stdin.sh" "$@" 2>/dev/null; then
|
|
1645
|
+
:
|
|
1613
1646
|
else
|
|
1614
1647
|
wants_retry=0
|
|
1615
1648
|
fi
|
|
1616
1649
|
fi
|
|
1617
1650
|
fi
|
|
1618
1651
|
|
|
1652
|
+
# Keep a damaged installation fail-open rather than wrapping an unowned child.
|
|
1653
|
+
if [ "$wants_retry" = 1 ] && ! . "$SELF_DIR/../lib/codex_retry_process.sh" 2>/dev/null; then
|
|
1654
|
+
wants_retry=0
|
|
1655
|
+
fi
|
|
1656
|
+
|
|
1619
1657
|
share_state_index "$pick"
|
|
1620
1658
|
if [ "$wants_retry" = "0" ]; then
|
|
1621
1659
|
export CODEX_HOME="$pick"
|
|
@@ -1636,14 +1674,14 @@ if [ "$wants_retry" = "0" ]; then
|
|
|
1636
1674
|
fi
|
|
1637
1675
|
ar_scrub_env
|
|
1638
1676
|
[ "$AR_NO_DAEMON" != 1 ] || set -- --no-daemon "$@"
|
|
1639
|
-
|
|
1677
|
+
codex_native_exec "$@"
|
|
1640
1678
|
fi
|
|
1641
1679
|
|
|
1642
1680
|
# Retry path: buffer stdio so a retried call never double-emits partial output.
|
|
1643
1681
|
mkdir -p "$ACC_ROOT/tmp" 2>/dev/null || true
|
|
1644
1682
|
tmpd="$(mktemp -d "$ACC_ROOT/tmp/shim.XXXXXX" 2>/dev/null)" || {
|
|
1645
1683
|
export CODEX_HOME="$pick"
|
|
1646
|
-
|
|
1684
|
+
codex_native_exec "$@"
|
|
1647
1685
|
}
|
|
1648
1686
|
trap 'rm -rf "$tmpd"' EXIT
|
|
1649
1687
|
|
|
@@ -1652,7 +1690,7 @@ trap 'rm -rf "$tmpd"' EXIT
|
|
|
1652
1690
|
# nonzero — a hard failure. Verify now, fall back to plain exec if we cannot.
|
|
1653
1691
|
if ! : > "$tmpd/out" 2>/dev/null || ! : > "$tmpd/err" 2>/dev/null; then
|
|
1654
1692
|
export CODEX_HOME="$pick"
|
|
1655
|
-
|
|
1693
|
+
codex_native_exec "$@"
|
|
1656
1694
|
fi
|
|
1657
1695
|
|
|
1658
1696
|
stdin_file=""
|
|
@@ -1687,7 +1725,9 @@ cur="$pick"
|
|
|
1687
1725
|
rc=0
|
|
1688
1726
|
while :; do
|
|
1689
1727
|
if [ -n "$stdin_file" ]; then exec 3< "$stdin_file"; else exec 3< /dev/null; fi
|
|
1690
|
-
CODEX_HOME="$cur"
|
|
1728
|
+
CODEX_HOME="$cur" codex_native_argv "$@" || exit $?
|
|
1729
|
+
CODEX_HOME="$cur" CODEX_MULTIACC_SELECTED_HOME="$cur" \
|
|
1730
|
+
codex_retry_run "$REAL" ${CODEX_SPEED_ARGV[@]+"${CODEX_SPEED_ARGV[@]}"} <&3 > "$tmpd/out" 2> "$tmpd/err"
|
|
1691
1731
|
rc=$?
|
|
1692
1732
|
exec 3<&-
|
|
1693
1733
|
if [ "$rc" -ne 0 ] && [ "$attempt" -eq 1 ] \
|
|
@@ -1749,6 +1789,7 @@ while :; do
|
|
|
1749
1789
|
# away from — not the one that bounced.
|
|
1750
1790
|
remember_pick "$cur"
|
|
1751
1791
|
mcp_ensure "$cur"
|
|
1792
|
+
mcp_oauth_ensure "$cur"
|
|
1752
1793
|
attempt=2
|
|
1753
1794
|
continue
|
|
1754
1795
|
fi
|
package/bin/codex-accounts
CHANGED
|
@@ -106,6 +106,10 @@ USAGE
|
|
|
106
106
|
every install and self-update runs it, and each limits pass refreshes its own pool's
|
|
107
107
|
copy once it is an hour old. --if-stale leaves a younger copy alone; --quiet prints
|
|
108
108
|
nothing. show prints the merged catalog.
|
|
109
|
+
codex-accounts speed --account-dir DIR --model MODEL --speed normal|fast|ultrafast [--json]
|
|
110
|
+
resolve speed from this account's native model catalog. Ultrafast verifies
|
|
111
|
+
account access through native model/list without inference; unknown or
|
|
112
|
+
unavailable access falls back to Fast. Never changes account configuration.
|
|
109
113
|
codex-accounts post-sync (server side) seed dirs, fix perms, quick verify
|
|
110
114
|
codex-accounts mcp add <name> [--provider claude|codex|both] [--scope user]
|
|
111
115
|
[-e KEY=VAL]... [-H 'Header: v']... [--transport stdio|http]
|
|
@@ -133,6 +137,8 @@ USAGE
|
|
|
133
137
|
re-apply the registry now (repair drift)
|
|
134
138
|
codex-accounts mcp import-local --owner NAME (stdin JSON) machine-local overlay for
|
|
135
139
|
a runner daemon; applied here, never synced
|
|
140
|
+
codex-accounts mcp auth-sync share native MCP files and refresh locks locally
|
|
141
|
+
Sign in with `codex mcp login NAME`; sign out with `codex mcp logout NAME`.
|
|
136
142
|
|
|
137
143
|
ENV
|
|
138
144
|
CODEX_ACCOUNTS_ROOT pool root, overriding ~/.codex-accounts — one isolated pool
|
|
@@ -142,10 +148,13 @@ ENV
|
|
|
142
148
|
'none' = local-only, nothing is pushed anywhere
|
|
143
149
|
CODEX_MULTIACC_SYNC_ROOT / _SYNC_REPO remote pool root / addon repo for it
|
|
144
150
|
CODEX_ACCOUNT pin the shim to one account
|
|
151
|
+
CODEX_SPEED normal|fast|ultrafast for this launch; unavailable Ultrafast uses Fast
|
|
152
|
+
CODEX_SPEED_MODEL model hint for app-server launches that choose their model over RPC
|
|
145
153
|
CODEX_SHIM_RETRY=0 disable the `codex exec` auto-retry
|
|
146
154
|
CODEX_MULTIACC_DISABLE=1 bypass the shim entirely
|
|
147
155
|
CODEX_MULTIACC_AUTO_RESET=0 disable automatic usage-reset redemption
|
|
148
156
|
CODEX_MULTIACC_MCP=0 disable the MCP registry (no reconcile at seed/launch, no mirror)
|
|
157
|
+
CODEX_MULTIACC_MCP_OAUTH=0 stop preparing shared native MCP storage and locks
|
|
149
158
|
CODEX_MULTIACC_MODEL_CATALOG=0 never refresh the codex pool's model-catalog.json
|
|
150
159
|
(MULTIACC_MODEL_CATALOG=0: neither pool's)
|
|
151
160
|
EOF
|
|
@@ -153,6 +162,12 @@ EOF
|
|
|
153
162
|
|
|
154
163
|
require_manifest() { [ -f "$MANIFEST" ] || die "no manifest at $MANIFEST — run claude-multiacc install first"; }
|
|
155
164
|
|
|
165
|
+
cmd_speed() {
|
|
166
|
+
local native
|
|
167
|
+
native="$(find_real_codex "$_self" 2>/dev/null)" || native=""
|
|
168
|
+
"$PYBIN" "$LIB_DIR/codex_speed.py" --native-cli "$native" "$@"
|
|
169
|
+
}
|
|
170
|
+
|
|
156
171
|
cmd_configure() {
|
|
157
172
|
require_manifest
|
|
158
173
|
local apply=0 check=0 arg rc
|
|
@@ -254,11 +269,11 @@ PYEOF
|
|
|
254
269
|
# later contender legitimately re-took.
|
|
255
270
|
login_lock_acquire() { # $1 = lock dir; nonzero when someone else holds it
|
|
256
271
|
local L="$1"
|
|
257
|
-
if !
|
|
272
|
+
if ! atomic_mkdir "$L" 2>/dev/null; then
|
|
258
273
|
[ $(( $(epoch_now) - $(file_mtime "$L") )) -gt 1800 ] || return 1
|
|
259
274
|
mv "$L" "$L.stale.$$" 2>/dev/null || return 1
|
|
260
275
|
rm -rf "$L.stale.$$" 2>/dev/null
|
|
261
|
-
|
|
276
|
+
atomic_mkdir "$L" 2>/dev/null || return 1
|
|
262
277
|
fi
|
|
263
278
|
: > "$L/owner.$$" 2>/dev/null || true
|
|
264
279
|
return 0
|
|
@@ -1848,6 +1863,7 @@ PYEOF
|
|
|
1848
1863
|
trap - EXIT
|
|
1849
1864
|
# Every account's MCP servers, adopting what one holds that the registry does not know.
|
|
1850
1865
|
mcp_registry_pass
|
|
1866
|
+
codex_mcp_oauth_ensure "$ACC_ROOT" "$PYBIN" "$LIB_DIR/codex_mcp_oauth.py"
|
|
1851
1867
|
# This pool's model catalog, once its copy is an hour old (lib/common.sh).
|
|
1852
1868
|
model_catalog_backstop
|
|
1853
1869
|
# The refresher fails open per account; a NON-zero status means the pass itself
|
|
@@ -2264,10 +2280,14 @@ cmd_mcp() {
|
|
|
2264
2280
|
local args=()
|
|
2265
2281
|
case "$sub" in
|
|
2266
2282
|
add|add-json|remove|list|apply|import-local) shift ;;
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2283
|
+
auth-sync)
|
|
2284
|
+
shift
|
|
2285
|
+
local real
|
|
2286
|
+
real="$(find_real_codex "$_self")" || die "real codex binary not found"
|
|
2287
|
+
"$PYBIN" "$LIB_DIR/codex_mcp_oauth.py" ensure --root "$ACC_ROOT" --codex-bin "$real" "$@"
|
|
2288
|
+
return $? ;;
|
|
2289
|
+
auth-status|auth-forget)
|
|
2290
|
+
die "use codex mcp list for native auth status, or codex mcp logout NAME to clear shared MCP authentication" ;;
|
|
2271
2291
|
"") usage; return 1 ;;
|
|
2272
2292
|
*) die "unknown mcp subcommand: $sub (add, add-json, remove, list, apply, import-local)" ;;
|
|
2273
2293
|
esac
|
|
@@ -2469,7 +2489,7 @@ PYEOF
|
|
|
2469
2489
|
# distribution for as long as the arg loops rejected the flag. An UNKNOWN verb must still
|
|
2470
2490
|
# FAIL, or the probe stops meaning what it says — so this list must hold exactly the verbs
|
|
2471
2491
|
# the dispatcher below implements, and a test pins that both ways.
|
|
2472
|
-
_KNOWN_VERBS="list status add import export-credential export-cred import-credential import-cred adopt dedupe remove login expired relogin re-login sync configure verify limits post-sync health self-update init-pool mcp model-catalog"
|
|
2492
|
+
_KNOWN_VERBS="list status add import export-credential export-cred import-credential import-cred adopt dedupe remove login expired relogin re-login sync configure verify limits post-sync health self-update init-pool mcp model-catalog speed"
|
|
2473
2493
|
case " $_KNOWN_VERBS " in
|
|
2474
2494
|
*" ${1:-help} "*)
|
|
2475
2495
|
# ...but never past a literal `--`: from there on the words belong to an MCP
|
|
@@ -2502,6 +2522,7 @@ case "${1:-help}" in
|
|
|
2502
2522
|
init-pool) shift; cmd_init_pool "$@" ;;
|
|
2503
2523
|
mcp) shift; cmd_mcp "$@" ;;
|
|
2504
2524
|
model-catalog) shift; cmd_model_catalog "$@" ;;
|
|
2525
|
+
speed) shift; cmd_speed "$@" ;;
|
|
2505
2526
|
help|--help|-h) usage ;;
|
|
2506
2527
|
*) usage; exit 1 ;;
|
|
2507
2528
|
esac
|
|
@@ -260,8 +260,8 @@ What to expect, and how to read `auth-status`:
|
|
|
260
260
|
re-authentication in ANY one account revokes the shared login at the server, so the pool
|
|
261
261
|
retires it in every account (`login: none — retired … (cleared in acct-NN file)`); one
|
|
262
262
|
sign-in in any account shares the new one. `mcp auth-forget NAME` is the same logout from
|
|
263
|
-
the operator's side.
|
|
264
|
-
|
|
263
|
+
the operator's side. Codex uses native login/logout over shared local storage;
|
|
264
|
+
`codex-accounts mcp auth-sync` repairs it. See [Codex MCP authentication](CODEX_MCP_AUTH.md).
|
|
265
265
|
- **Rolling it out**: the first share runs at install time (the limits agent starts on
|
|
266
266
|
install), and a build older than this one reads a shared MCP-only file as a live login:
|
|
267
267
|
every setup-token account would then say "Not logged in". Install it only where no older
|
package/docs/AUTORESUME.md
CHANGED
|
@@ -164,7 +164,12 @@ shell: runs the shim again → token → marker on the old account → normal
|
|
|
164
164
|
1. It consumes the token. A token is single-use and valid for 10 minutes.
|
|
165
165
|
2. It writes the old account's marker with the pool's existing writers (see the tables
|
|
166
166
|
below).
|
|
167
|
-
3. It `cd`s to the session's directory.
|
|
167
|
+
3. It `cd`s to the session's directory. For Codex, this is the latest directory saved
|
|
168
|
+
by that session, including a change since its first turn. Starting or resuming it
|
|
169
|
+
from another shell directory therefore does not stop the automatic restart at
|
|
170
|
+
**Working directory · resume**. The selected account trusts this same directory
|
|
171
|
+
before startup. If the saved directory is unavailable, an existing initial session
|
|
172
|
+
directory or launch directory is used as a fallback.
|
|
168
173
|
4. It runs **normal selection**, with one addition: the accounts this chain of
|
|
169
174
|
relaunches left are skipped until their own reset. The selection policy is
|
|
170
175
|
otherwise untouched.
|
|
@@ -220,7 +225,16 @@ The prompt is submitted automatically:
|
|
|
220
225
|
- **To replace the whole text**, set `CLAUDE_MULTIACC_AUTORESUME_PROMPT` or
|
|
221
226
|
`CODEX_MULTIACC_AUTORESUME_PROMPT`.
|
|
222
227
|
|
|
223
|
-
### Model and
|
|
228
|
+
### Model, effort, and Codex speed
|
|
229
|
+
|
|
230
|
+
Codex relaunches also carry the most recently saved `service_tier` from the
|
|
231
|
+
thread's own settings or turn context. Normal, Fast, and Ultrafast remain the same
|
|
232
|
+
across account rotation; Ultrafast becomes Fast when the selected destination
|
|
233
|
+
account/model does not advertise it. Claude receives no speed flag. The shim
|
|
234
|
+
updates the named thread's tier through native `thread/resume` before launching
|
|
235
|
+
the CLI, because native resume restores a saved tier ahead of `-c` configuration.
|
|
236
|
+
See [Session speed](CODEX.md#session-speed) for capability discovery and the
|
|
237
|
+
`CODEX_SPEED` launch override.
|
|
224
238
|
|
|
225
239
|
The watcher reads the model and effort from the session's own history when it decides to
|
|
226
240
|
relaunch, so a `/model` or `/effort` switch made after the launch is honoured:
|
package/docs/CODEX.md
CHANGED
|
@@ -18,8 +18,21 @@ then the 30-point weekly-headroom band with random spread, ≥90% any-bucket
|
|
|
18
18
|
exclusion, peers rotate), same marker semantics (`.limited` cooldowns, `.expired` parks with
|
|
19
19
|
credential/policy scoping and soft expiry), same fail-open guarantees, same sync
|
|
20
20
|
safety guards. The `codex` shim engages the buffered auto-retry only for
|
|
21
|
-
`codex exec` runs with
|
|
22
|
-
|
|
21
|
+
`codex exec` runs with file/device stdin and explicit `codex exec ... -` prompts from pipes.
|
|
22
|
+
The latter require EOF in the native CLI too; the shim saves and replays the exact prompt
|
|
23
|
+
on its one account retry. Unknown commands, interactive stdin and non-consuming live pipes
|
|
24
|
+
remain unbuffered. Explicit `CODEX_HOME` and `CODEX_ACCOUNT` pins still bypass rotation.
|
|
25
|
+
Cancelling a buffered run forwards TERM/INT/HUP to its active native process and reaps it
|
|
26
|
+
before removing the temporary prompt/output files.
|
|
27
|
+
Interactive `codex` in tmux gets [auto-resume](AUTORESUME.md) instead, like `claude`.
|
|
28
|
+
|
|
29
|
+
Automatic selection exports `CODEX_MULTIACC_SELECTED_HOME` equal to the serving
|
|
30
|
+
`CODEX_HOME`, including after a retry. Selecting an explicit `CODEX_ACCOUNT` clears the marker;
|
|
31
|
+
a home passthrough never creates it. Nested invocations still pass through.
|
|
32
|
+
An isolated job launcher may request fresh pool selection only when `CODEX_SHIM_ACTIVE=1`,
|
|
33
|
+
the marker exactly matches `CODEX_HOME`, no `CODEX_ACCOUNT` is set and
|
|
34
|
+
`CODEX_MULTIACC_DISABLE` is not `1`. Omit the inherited home/guard/marker only for that
|
|
35
|
+
child. Unmarked older sessions and overridden homes retain their existing pin behavior.
|
|
23
36
|
|
|
24
37
|
Codex-specific notes:
|
|
25
38
|
|
|
@@ -79,6 +92,45 @@ Codex-specific notes:
|
|
|
79
92
|
- **API-key logins are rejected** — ChatGPT subscription accounts only, matching the
|
|
80
93
|
addon's no-API-keys rule.
|
|
81
94
|
|
|
95
|
+
## Session speed
|
|
96
|
+
|
|
97
|
+
Set `CODEX_SPEED=normal`, `fast`, or `ultrafast` for a Codex launch. Normal explicitly
|
|
98
|
+
uses the standard tier, even when an account's configuration defaults to Fast.
|
|
99
|
+
The setting also works with `CODEX_ACCOUNT` and `CODEX_HOME` pins. Explicit native
|
|
100
|
+
`-c service_tier=...` overrides take precedence. Claude has no corresponding option.
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
CODEX_SPEED=fast codex
|
|
104
|
+
CODEX_SPEED=ultrafast codex exec -m gpt-6.1-sol 'Review the changed code'
|
|
105
|
+
codex-accounts speed --account-dir ~/.codex-accounts/acct-18 --model gpt-6.1-sol --speed ultrafast --json
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
The shim reads **the selected account's** native model catalog, never an e-mail or
|
|
109
|
+
a plan-name guess. Ultrafast falls back to Fast when that account/model does not
|
|
110
|
+
advertise it. Every Ultrafast launch verifies the native account's `model/list`
|
|
111
|
+
with no inference, so even a copied cache cannot grant another account access;
|
|
112
|
+
an inconclusive read falls back to Fast.
|
|
113
|
+
Catalog probes use a temporary SQLite index, preserving the account's normal
|
|
114
|
+
session-index work. The inspection command above uses the same native check for
|
|
115
|
+
Ultrafast.
|
|
116
|
+
|
|
117
|
+
Quota retries resolve the speed again for the next account. Automatic quota
|
|
118
|
+
resumes carry the speed recorded in the session history. For those resumes and
|
|
119
|
+
explicit speed requests on a named session UUID, the shim applies the resolved
|
|
120
|
+
tier through native `thread/resume` before launching the resumed CLI: Codex
|
|
121
|
+
currently restores its saved tier ahead of process configuration. That
|
|
122
|
+
preparation sends no turn and preserves the conversation. A failed preparation
|
|
123
|
+
stops the launch instead of silently selecting another speed.
|
|
124
|
+
|
|
125
|
+
App-server callers may supply `CODEX_SPEED_MODEL` when their model is chosen over
|
|
126
|
+
RPC. Read the resolved `config.service_tier` with native `config/read` and pass it
|
|
127
|
+
as `serviceTier` on `thread/start`, `thread/resume`, and `turn/start`; otherwise
|
|
128
|
+
saved thread settings can override the process configuration.
|
|
129
|
+
|
|
130
|
+
The [official speed documentation](https://learn.chatgpt.com/docs/agent-configuration/speed)
|
|
131
|
+
describes Fast's higher usage and Ultrafast access for Pro $500 and eligible
|
|
132
|
+
Enterprise/Edu plans. Actual account/model capabilities remain authoritative.
|
|
133
|
+
|
|
82
134
|
## Spendable Codex credits
|
|
83
135
|
|
|
84
136
|
The existing authenticated usage GET also supplies the account's spendable Codex
|
|
@@ -215,6 +267,10 @@ claude-multiacc codex status # same status command via the npm wrapper
|
|
|
215
267
|
|
|
216
268
|
## MCP servers for every account
|
|
217
269
|
|
|
270
|
+
OAuth logins also need shared storage; see
|
|
271
|
+
[Codex MCP authentication](CODEX_MCP_AUTH.md). `codex-accounts mcp auth-sync` repairs
|
|
272
|
+
local credential and refresh-lock sharing without touching account `auth.json` files.
|
|
273
|
+
|
|
218
274
|
`codex-accounts mcp add|add-json|remove|list|apply|import-local` manages the pool's MCP
|
|
219
275
|
registry (`~/.codex-accounts/mcp-servers.json`); `add`/`remove` default to `--provider both`,
|
|
220
276
|
so the claude pool beside it gets the same change, and `mcp remove` retires the server in
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Codex MCP authentication across pooled accounts
|
|
2
|
+
|
|
3
|
+
The MCP registry shares server configuration. Codex stores OAuth logins separately:
|
|
4
|
+
the direct OS keyring is shared, but its fallback `.credentials.json` belongs to
|
|
5
|
+
`CODEX_HOME`. On 2026-10-23 MotionSites was configured in all 18 accounts, while its
|
|
6
|
+
login existed only in `acct-06`. The other 17 reported `not_logged_in`.
|
|
7
|
+
|
|
8
|
+
The addon prepares one machine-local native MCP credential file and one directory
|
|
9
|
+
of native refresh/store locks under `<pool>/.codex-mcp-oauth/`. Accounts hardlink
|
|
10
|
+
`.credentials.json` to that file and link `mcp-oauth-locks` to the shared locks.
|
|
11
|
+
Codex reads, refreshes, saves and logs out using its own implementation. The addon
|
|
12
|
+
never refreshes a token or reads the OS keyring. Account `auth.json` files remain
|
|
13
|
+
independent, and none of this MCP state is included in fleet sync.
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
codex-accounts mcp auth-sync # repair storage sharing now
|
|
17
|
+
codex mcp login motionsites # authorize once on this machine
|
|
18
|
+
codex mcp list # native authentication status
|
|
19
|
+
codex mcp logout motionsites # clear the shared login
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Preparation runs before pooled launches, during seeding, and after limits passes.
|
|
23
|
+
It requires native Codex 0.163.0 or newer, whose storage and lock contract is checked
|
|
24
|
+
before preparation; older clients keep their existing stores.
|
|
25
|
+
A first login saved after preparation is picked up at the next launch or explicit
|
|
26
|
+
`auth-sync`. Restart an already running session to reload its MCP connections.
|
|
27
|
+
`CODEX_MULTIACC_MCP_OAUTH=0` stops preparation; it does not undo existing shared
|
|
28
|
+
storage. Each machine still needs its own authorization.
|
|
29
|
+
|
|
30
|
+
## Native storage constraints
|
|
31
|
+
|
|
32
|
+
Codex 0.163.0 refuses final-component credential symlinks with `O_NOFOLLOW`, so a
|
|
33
|
+
regular hardlinked file is necessary. Its file writer updates that inode in place
|
|
34
|
+
under `mcp-oauth-locks/file-store.lock`. A tokenless reserved entry keeps the native
|
|
35
|
+
document nonempty after its last real login is removed: otherwise Codex unlinks
|
|
36
|
+
only the account's filename, leaving the other hardlinks with the old login.
|
|
37
|
+
The reserved entry is not a configured server and cannot authenticate a request.
|
|
38
|
+
|
|
39
|
+
The refresh lock must be shared too: native locks normally live under each
|
|
40
|
+
`CODEX_HOME`, even when the grant is in the globally shared direct OS keyring.
|
|
41
|
+
Sharing a rotating grant without its lock lets simultaneous sessions reuse an
|
|
42
|
+
obsolete refresh token. Preparation preserves existing lock inodes, defers busy
|
|
43
|
+
stores and refuses independent credential documents rather than choosing a grant.
|
|
44
|
+
Initial migration atomically exchanges native lock directories with their shared
|
|
45
|
+
links. It retains discarded lock files until preexisting client handles close;
|
|
46
|
+
an operation racing that first migration can make preparation wait for its native
|
|
47
|
+
lock timeout. Later launches use the existing shared paths without that migration.
|
|
48
|
+
Linux excludes an inaccessible process only after proving that it has exited with
|
|
49
|
+
no living threads; inaccessible live or uncertain handle state still defers migration.
|
|
50
|
+
Only manifest accounts are eligible, including the supported `acct-NN → ~/.codex`
|
|
51
|
+
adoption. Foreign directory symlinks, duplicate homes and unsupported scoped
|
|
52
|
+
credential entries are refused. Encrypted secrets storage is not merged.
|
|
53
|
+
|
|
54
|
+
Implementation: `lib/codex_mcp_oauth.py` and its supporting modules. Native contract:
|
|
55
|
+
[OAuth storage](https://github.com/openai/codex/blob/rust-v0.163.0/codex-rs/rmcp-client/src/oauth.rs)
|
|
56
|
+
and [refresh locks](https://github.com/openai/codex/blob/rust-v0.163.0/codex-rs/rmcp-client/src/oauth/refresh_lock.rs).
|
|
57
|
+
The supported store settings are documented in the
|
|
58
|
+
[OpenAI configuration reference](https://learn.chatgpt.com/docs/config-file/config-reference).
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|