Skip to content
18.1k

CLI Providers, Accounts, and Models

These commands configure upstream providers, authenticate accounts, manage credential pools, and control the model catalog exposed to Codex.

Leftover-argument errors from account list, account current, native main profiles, and provider commands redact values of credential options, including --code, --token, --api-key, --key, --secret, --password, and --admin-token, in both --option value and --option=value forms. Local provider registration uses ocx provider add <name> --api-key <key>; an inline --api-key=<key> is rejected with its value redacted. ocx provider edit does not accept --api-key.

Non-interactive provider management. Registry entries are seeded by name; a custom name requires both --adapter and --base-url.

Subcommand Supported flags Action
list --json, --jsonl List configured providers and the remaining registry entries; --jsonl emits one configured provider object per line.
add <name> --adapter <adapter>, --base-url <url>, --api-key <key>, --responses-path <path>, --auth-mode <key|forward|oauth|local>, --default-model <model>, --set-default, --force, --json, --sync, --live Save locally by default; --sync attempts client sync in either output mode. --live writes through the running proxy and cannot combine with --sync. --force permits overwrite.
edit <name> provider field flags, --headers <json>, --model-context-tier <model=default|long_context>, --upstream-http-version <http1.1|->, --fast <on|off>, --context-window <N|->, --json Edit validated live provider fields without replacing key pools. Repeat --model-context-tier for multiple Copilot models. --headers merges custom request headers; pass {} or - to clear them.
test <name> --model <id>, --yes, --json Check discovery; with --model, request one confirmed completion test.
show <name> --json Show config with API keys masked.
remove <name> --json, --live, --yes Local removal refuses the default and last provider. Live removal requires --yes and uses server dependency checks, default reassignment and account/custom-model cleanup.
set-default <name> --json, --live Select a local default, or update the running proxy with --live.
pacing <name> --json, --enabled <on|off>, --rpm <number>, --min-interval-ms <integer>, --max-concurrent <integer>, --file <FILE|-> Read configured rules and runtime status; update scalars or replace rules from a file. File and scalar flags are exclusive.
snapshot --json Read a redacted, validated public provider-editor document from the running proxy.
apply --baseline <FILE|->, --file <FILE|->, --yes, --json Apply a reviewed editor document with a baseline comparison; removals/renames require --yes.
selected <name> --set <ids>, --clear, --json Read or update the provider model allowlist.
quota --refresh, --json Read provider quota reports.
resets --limit <n>, --json List recently detected quota-window resets.
presets --json List dashboard provider presets.
account-mode pool, direct, --json Select pooled or direct Codex account routing.
import-cc-switch --source <FILE>, --provider <id>, --apply, --restore, --allow-private-network, --json Preview, selectively apply, or restore keyless provider entries from a CC-Switch SQLite database (cc-switch.db) or legacy JSON file.
Terminal window
ocx provider list --json
ocx provider list --jsonl # one configured provider object per line
ocx provider test ark
ocx provider add anthropic --set-default --sync
ocx provider add local-dev --adapter openai-chat --base-url http://localhost:11434/v1
ocx provider show anthropic --json
ocx provider edit github-copilot --model-context-tier gpt-5.6-luna=long_context
ocx models --provider anthropic --json
ocx models live --provider ark --json

--jsonl writes only configured providers, one JSON object per line, and omits the registryCount summary from --json. Each object has the same fields as an item in the configured array. Use it for scripts that process one configured provider object per line. --json and --jsonl cannot be combined.

Each configured row also has active. It is false when this device is linked to a Home (a Remote Link Child or a Hub-mode client): the row is local configuration that the Home’s providers shadow, so it does not serve requests while the link is connected. It is true when the row actually serves, including when the link state cannot be read. Rows never carry credential values: baseUrl drops embedded userinfo and masks credential-shaped query values such as key or token, while keeping ordinary parameters such as api-version.

Start with offline discovery: ocx provider --help, then the leaf’s --help. list and show read local configuration; add, remove, and set-default also use local configuration unless given --live. Other provider management operations below require a running proxy. Check ocx ready --json and ocx status --json before live work to confirm the intended target and version. A local list does not establish that the proxy adopted a saved change.

Local provider add <name> --json reports needsSync: true. Adding --sync actually attempts synchronization in both JSON and human output modes. Its receipt adds sync: {status, ok}; only status: "applied" with ok: true clears needsSync. No proxy (not-running), refused sync or failed sync returns nonzero while preserving the local save. Policy-skipped or catalog-only results may exit 0 with needsSync: true; they are not an applied client sync. Catalog ownership or saved-config safety refusals leave the catalog unchanged, return exit 1, and show recovery guidance in human output or sync.warning in JSON, even if config injection succeeded.

For authorized live changes:

Terminal window
ocx provider snapshot --json
ocx provider add anthropic --live --json
ocx provider set-default anthropic --live --json
ocx provider snapshot --json

Live add uses the target’s presets and refuses an observed existing provider without --force. The check and server upsert are not atomic: concurrent changes can race. Custom providers still need --adapter and --base-url. --live never falls back to a local save; combining it with --sync is invalid. Credentials belong in the supported human login or stdin flow, not CLI examples.

To remove an authorized provider, use ocx provider remove <name> --live --yes --json. The server checks dependencies and applies default reassignment and account/custom-model cleanup. Inspect the receipt and read back the target after a write.

A live receipt can be {"success":true,"name":"example","catalogRefresh":{"status":"committed","changed":true,"degraded":false,"notices":[]}}. success means configuration persisted; it does not by itself prove catalog convergence. For example, {"success":true,"name":"example","catalogRefresh":{"status":"skipped","reason":"busy","retryable":true}} returns exit 1 even though the save succeeded. Failed refresh, or skips for stale, refused and catalog-unavailable, also return nonzero. Preserve degraded notices. Null, absent or not-requested refresh can exit 0 without a client sync. Read back before recovery; do not repeat a persisted write to repair convergence. Errors use safe prose on stderr, including in JSON mode, rather than a JSON error envelope.

provider test <name> without --model checks upstream model discovery connectivity. A failed connection exits 1; success and a static catalog with no applicable discovery endpoint exit 0. applicable: false is an expected static-catalog result, not successful inference or a failed connection.

To send one completion request for a chosen model, use:

Terminal window
ocx provider test my-relay --model my-model
ocx provider test my-relay --model my-model --yes --json

This test may incur provider charges. An interactive terminal asks for confirmation; --yes accepts the charge warning. Non-interactive use and --json require --yes. Using --yes without --model is a usage error. Declining confirmation sends no request and exits 1. The command uses the running proxy’s management API.

The completion test sends the fixed prompt Reply exactly OK., requests a 32-token output cap, stops after 20 seconds or 64 KiB, and never retries or fails over. It supports key-auth or local OpenAI Chat and Responses providers, including per-model wire overrides. Disabled, OAuth, forwarded-auth and unsupported adapter or transport configurations return an applicability reason without sending a completion. TLS profiles, HTTP version pins, enabled request pacing and custom executors with a provider proxy override are currently unsupported.

Results include time to first text delta (TTFT), total latency, the requested and wire model ids, sanitized upstream model metadata and a distinct match, mismatch or unknown comparison. Missing or conflicting metadata is unknown. The comparison uses the upstream id before masking, against the id actually sent after alias and suffix handling. Provider-reported metadata cannot verify model identity: identityVerified is always false. Generated text is not returned. A failed completion exits 1; success and a not-applicable result exit 0.

provider edit is already live. Use --upstream-http-version http1.1 to pin that protocol, or --upstream-http-version - to clear it. --fast on|off toggles Fast; --context-window N sets a positive provider override and --context-window - clears it. Omitted settings stay unchanged; zero is not a clear operation.

Terminal window
ocx provider pacing anthropic --json
ocx provider pacing anthropic --enabled on --rpm 30 --min-interval-ms 1000 --max-concurrent 2 --json
ocx provider pacing anthropic --json

The read returns {provider, rules, status}. rules: null means unconfigured; status contains separate runtime queue/timing observations. Numeric flags alone do not enable an unconfigured block; --enabled off disables pacing. RPM accepts validated positive fractional values. Interval/concurrency require positive integers.

Scalar edits preserve the observed model rules but PATCH replaces the whole block without CAS, so a concurrent edit can be overwritten. Use snapshot/apply below when baseline comparison is required. For a complete rules object, including model-specific rules, use ocx provider pacing anthropic --file pacing.json --json. A minimal rules file is {"enabled":true,"requestsPerMinute":30}. File mode replaces the entire block and cannot be combined with scalar flags. --file - reads piped stdin; the input limits below apply.

Keep the same intended host and CLI context across these steps. Target pinning lasts within one invocation; snapshots contain no target identity token.

Terminal window
ocx provider snapshot --json > providers.baseline.json
cp providers.baseline.json providers.next.json

Edit the next file and review the diff while preserving the original baseline. Each document contains exactly {defaultProvider, providers}. Snapshot is a read-only redacted editor projection, not raw config export. Do not add secret, derived or unknown fields: credentials, any headers field and display markers (hasApiKey, hasHeaders, xaiResponsesOptInState, initialModelSelection) are not batch-editable. Raw configuration export may expose credentials and belongs in a human-operated terminal outside an agent session.

Terminal window
ocx provider apply --baseline providers.baseline.json --file providers.next.json --json
ocx provider snapshot --json

Add --yes only for reviewed, authorized removals or renames. Batch PUT preserves untouched private values and uses the public baseline to detect conflicts. It does not perform single-provider DELETE’s OAuth account cleanup. HTTP 409 returns exit 5: take a fresh snapshot, review concurrent changes and rebuild the proposed edit. Do not replace the baseline or retry automatically. Unknown write outcomes require inspection, not a rollback assumption.

Use regular UTF-8 JSON files or explicit piped -; at most one batch input can be stdin. Each input has a 4 MiB limit and a 30-second read deadline. The combined serialized {baseline,next} request must also fit 4 MiB. Interactive stdin, special files, conflicting flags, invalid fields and unconfirmed removals are refused before mutation with exit 2. Input errors never echo the submitted values.

Terminal window
ocx provider import-cc-switch --source ~/.cc-switch/cc-switch.db
ocx stop
ocx provider import-cc-switch --source ~/.cc-switch/cc-switch.db --provider my-provider --apply
# Start the proxy in another terminal (or run ocx ensure), then add credentials:
ocx start
printf '%s\n' "$API_KEY" | ocx account add-key my-provider
# Separate rollback path (stop the proxy first; removes unedited keyless entries only):
ocx stop
ocx provider import-cc-switch --restore

ocx provider import-cc-switch projects values only from an explicit allowlist of non-secret metadata fields (provider IDs, app types, base URLs, wire dialects, and default models) from a plain regular CC-Switch SQLite database or legacy JSON file. Source and embedded-config bytes are read transiently to scan the input, while secret fields, free-text display names, and TOML comments are skipped at the byte level, never decoded into strings, written, or logged, and transient byte buffers are zeroed in finally. Narrow base-URL path rules (http(s)://host[:port] with an empty path or 1–4 lowercase [a-z0-9._-] API path segments such as /v1, /api/v1, or /compatible-mode/v1) and identifier/model syntax checks act as defense in depth and reject non-conforming values without echoing them. Source files are opened once with O_NOFOLLOW | O_NONBLOCK descriptor verification and deserialized read-only in memory for SQLite without temporary files; SQLite files with multiple hard links (nlink > 1) are refused so sidecar checks cannot be bypassed via an alias path, and if a sibling -wal or -journal file is non-empty, import refuses and asks you to close CC-Switch (or checkpoint the database) and retry. Only import from a trusted local CC-Switch source, and verify every printed ID, model, and baseUrl before adding credentials:

  • Preview (default): requires --source <FILE>, writes nothing, and reports discovered, eligible, conflicting, and invalid entries.
  • Apply (--apply): requires --source <FILE> and a stopped proxy (ocx stop), and is refused on a Remote Link Child. Repeat --provider <id> to import only selected entries, or pass --allow-private-network to permit loopback or private-network base URLs. Even without a prior preview, --apply prints every imported provider ID, default model, and destination URL before writing keyless provider entries and recording a rollback journal at $OPENCODEX_HOME/cc-switch-provider-import-journal.json (stdout in text mode, stderr in --json mode); a second apply is refused while an active journal exists. Verify the printed entries, start the proxy (ocx start in another terminal or ocx ensure), and add credentials with printf '%s\n' "$API_KEY" | ocx account add-key <provider> (piped stdin) or ocx login <provider>.
  • Restore (--restore): separate rollback path (not a continuation of adding credentials) that requires a stopped proxy (ocx stop), is refused on a Remote Link Child, and takes no --source, --provider, or --allow-private-network. It inspects auth.json read-only at the byte level (failing closed if unreadable or malformed) and removes only providers from the active journal that still match their imported keyless state and are not selected as defaultProvider or referenced by routing, combos, fallback policy, OAuth accounts, or API keys.

ocx account list openai --quota --refresh --json includes a quotaRefresh object on the main-account row when that operation attempts a WHAM usage read. The existing GET /api/codex-auth/accounts?refresh=1 response exposes the same diagnostic.

Its status is ok, not_reported (no parseable quota in a successful response), http_error, timeout, network_error, invalid_response, or internal_error. Only http_error includes a numeric httpStatus. No raw response, error message, credential, or account identifier is included in this object. Cache-only reads, credential deferrals, and invalidated account snapshots omit it; older servers also omit it. Absence is not proof of success. A non-success HTTP status remains http_error even if its error body cannot be read; timeout and network_error describe failures before headers or while reading a successful response.

A valid login does not guarantee that this separate usage request succeeds. These categories do not change authentication, account selection, or quota freshness rules, and do not turn unknown quota into zero usage. This diagnostic currently covers the native main account, not pool-account refreshes. When reporting missing quota, share the category and HTTP status rather than credential files or a raw network capture.

Start the provider’s registered login flow. OAuth-style account providers open a browser and store credentials under ~/.opencodex/ (refreshable tokens rotate automatically; durable key grants such as OrcaRouter are reused until the provider revokes them); API-key login providers open their key dashboard, prompt for the key, validate it when possible, and save the resulting provider config. The command prints the currently accepted OAuth and API-key provider ids when the name is missing or unknown.

Failed OAuth logins exit with status 1 and print a fixed diagnostic: the HTTP status and standard OAuth error code (such as invalid_grant) when the provider returned one, network_error or timeout when the provider could not be reached, configuration_error with the reason when a local setting such as a Codex account namespace blocks the login, and oauth_authentication_failed otherwise. Pool 2 (anthropic2) also reports configuration_error before prompting when a custom provider owns that name or its config file cannot be read or parsed.

Use the same command to reauthenticate after ocx status / ocx doctor reports reauthentication required or a terminal refresh failure (or use Reauthenticate in the dashboard). Codex pool accounts are not one of those OAuth or API-key providers, but ocx login codex reaches them anyway: it routes to the account-pool login, so ocx login codex --reauth is the same thing as ocx account reauth codex. The dashboard Codex account pool (Reauthenticate) does it too. That route runs inside the proxy, so it needs a running one.

For the ChatGPT OAuth provider, an explicit terminal OAuth code confirming an expired or revoked refresh grant requires signing in again. Free-text descriptions alone never require reauthentication. Temporary token-service failures, timeouts and cancellation leave the account eligible for a later refresh. Browser token exchange and refresh each have a 30-second per-fetch deadline covering headers and body. HTTP-derived errors show only the status and an allowlisted OAuth code. Cancellation, timeout and transport failures use fixed messages without the original reason or cause.

Terminal window
ocx login xai
ocx login anthropic
ocx login orcarouter-oauth # browser consent + S256 PKCE
ocx login orcarouter # paste an existing API key
ocx login anthropic --headless # no browser, no local port

--headless runs the code-display login for hosts whose browser cannot reach the proxy’s loopback callback (containers, remote servers). The command prints an authorization URL; approve it in any browser, and the provider shows a code#state string to paste back at the prompt. No callback port is bound and no browser is launched. Only anthropic and anthropic2 support it today; any other provider exits with the list of providers that do. For Codex accounts use the device-code login instead, ocx account login codex --device — ocx login codex --headless is refused rather than passed on. The paste window is 10 minutes, and each login accepts one code: if the exchange fails, start a new login. A successful login becomes the provider’s active account, exactly like a browser login.

OAuth reauthentication preserves operator settings such as model selections, pricing overrides, and account failover preferences. Login-owned transport/authentication fields and registry-owned catalog metadata are refreshed. A live-discovery provider keeps its selected default model; a static provider can replace a default that no longer exists in its refreshed catalog.

For Antigravity, an upstream 401 can refresh the rejected account’s OAuth credential and retry the request once. The retry uses that credential’s Cloud Code Assist project. If refresh fails or no usable project is available, the request returns an authentication error; use the reauthentication flow above. A second 401 does not start another refresh/retry cycle. A 403 asking to verify the account quarantines that credential as needs-reauth(verify); when account failover is enabled, the request can retry on another eligible account. Complete Google’s account verification, then run ocx login google-antigravity. Silent token refresh does not clear this verification requirement. If OpenCodex cannot save the quarantine, this adapter exchange preserves the original 403 without another recovery send; the account has not been durably quarantined. If a sibling request cannot be built or admitted for sending, the exchange delivers the original 403 through normal error formatting. An enclosing combo or policy route can still apply its existing fallback rules.

For fetch-based web-search requests, automatic sibling rotation accepts only a complete, bounded Google error envelope containing error.details[].reason === "VALIDATION_REQUIRED". Verification wording alone does not rotate. This request-scoped recovery uses the sibling’s token and project without persisting a needs-reauth mark; cancellation, committed output, budget exhaustion or an unavailable sibling preserves the failure.

A proxy that is already running picks up the new credential without a restart: the CLI asks it to reload that one provider from disk, and the request carries no credential of its own. If the running proxy cannot accept that request — most often because it started from a build that predates attested reload — the login still succeeds and the credential is still written to disk, but the live process keeps serving the previous one. The CLI says so and asks you to restart:

⚠️ A proxy is running but could not reload this provider (unattested-target).
The credential is saved to disk; the running proxy keeps using the previous one.
Restart it to pick this up: ocx restart

Remove the stored OAuth credential locally by default. --json reports {schemaVersion:1, ok, provider, removed}; missing credentials return reason: "not_found" and exit 4, while removal returns exit 0.

For a requested logout on the running proxy, ocx logout <provider> --live --json accepts public OAuth providers and returns {schemaVersion:1, success:true, provider, live:true}. It does not sign out Codex/native-main or remove one selected account. It never deletes local credentials before the live request or falls back to local deletion after failure.

Pool policy, account thresholds and paid-credit intent

Section titled “Pool policy, account thresholds and paid-credit intent”

Read offline help, then the selected target’s policy before changing it:

Terminal window
ocx account pool --help
ocx account pool openai --json
ocx account pool anthropic --json

These are live management reads. The response includes provider, kind, supported, nullable stored policy, enabledEffective and optional inert. Only fields named by supported may be written; null is not false, and an inert policy is not active pooling. Threshold zero and enabled:false are independent. Use --enabled on|off, --threshold 0..100, --strategy, --sticky 1..100, and --quota-window only where supported. OpenAI does not support pool enabled/window writes; reset-first is for OpenAI and generic OAuth pools (never Anthropic), least-loaded Kiro-only, and Anthropic windows are five-hour, weekly, max-utilization. Omitted fields stay unchanged. Read DTO fields such as routes, per-account concurrency or Anthropic nativeMessages do not imply a setter in this command; use only the listed CLI options. A save that returns warning: "config_bookkeeping_failed" was persisted but needs a fresh read to confirm the live state.

Terminal window
ocx account pool anthropic --threshold 80 --quota-window five-hour --json
ocx account pool anthropic --json
ocx account auto-switch openai status --account <id-or-alias-or-main> --json

An explicit --account selects the OpenAI per-account threshold. Actions are status, on (80), off (0), inherit (null), or threshold N (0–100). Without --account, the existing auto-switch command remains pool-scoped. The result is {ok, id, autoSwitchThresholdOverride, autoSwitchThreshold}; read the nullable override and observed effective value separately.

Paid-credit use after included quota is a distinct policy requiring explicit spending intent. ocx account credits openai <id-or-alias-or-main> on|off --json targets one account. ocx account credits openai --all on|off --json is exclusive with that selector: on records current selectable IDs plus main, off clears the list. Receipts are {ok, id, creditsAfterLimit} or {ok, all, ids}; retain the actual returned IDs and read back ocx account list openai --json. Showing credit balances with --show-codex-credits does not enable this policy. Do not enable credits to recover from a failed login or quota read.

Quota activation and reset-grant observations

Section titled “Quota activation and reset-grant observations”
Terminal window
ocx account quota-activation openai <id-or-alias-or-main> --window fiveHour off --json
ocx account list openai --json
ocx account anthropic-reset-grants --json

Quota activation accepts fiveHour or weekly, deliberately different from Anthropic pool five-hour. It writes one account/window toggle; enabling can schedule quota refresh. {ok, id, window, enabled, available} reports policy and observed availability, not completed refresh. Unavailable enablement may return 409; disabling is still allowed. It never consumes a reset grant.

Anthropic reset-grants accepts an optional exact Anthropic account ID, not a Codex alias/main selector; omission uses the existing active/fallback choice. The GET may contact the upstream status service. It reports eligibility/reason, grant counts, nullable dates, cooldown, pending operation and journal availability. Empty grants are not the same as unavailable status. It never consumes a grant or resumes a pending spend. Consumption remains a human GUI operation.

To keep using a selected account past a proactive switching threshold or the main-account protection threshold, explicitly enable its remaining-quota preference:

Terminal window
ocx account use-remaining openai main on
ocx account use openai main
ocx account use-remaining openai main status --json
ocx account use-remaining openai main off

Replace main with a unique pool-account ID or alias to target an added account. Enabling this disables proactive usage switching for that account. For the main login, it also moves both effective hard-lock thresholds to 100%, so a 95% reading can still admit a request. The preference persists across restarts until turned off; original threshold settings remain stored and resume applying when it is disabled. It does not select an account itself or override round-robin scheduling, manual/automatic pause, entitlement checks, reauthentication, or upstream cooldowns. Paid-credit permission remains a separate setting; enabling this never opts an account into credits or redeems a reset grant. The default-on main hard lock still refuses at 100%.

GET /api/codex-auth/accounts/use-remaining?id=<account-id> reads the live policy. PUT /api/codex-auth/accounts/use-remaining accepts { id, useRemainingQuota: boolean }. Both require the existing management authentication. This control is currently exposed through the CLI and API.

In Codex settings → Multi-auth → Advanced settings, Block main account is on by default beside Ultra Fast. Switching it off applies immediately; turning it back on first shows the consequences, and cancelling does not change the setting. The main-account card shows monitoring, unknown usage, or a current policy block even when Advanced settings is closed.

The default follows from what a drained main account does to Codex Desktop: once the ChatGPT account window reports 0% remaining, Desktop disables its send button and the account stops accepting turns until the window resets. Holding ocx’s own traffic below that point keeps the account usable (#5694). The cost is Luna Reserve: while the block is in force, Reserve on that main account cannot activate. To let the main account run to exhaustion and hand over to Reserve, turn the switch off.

The 5h window and the weekly window each block on their own: the 5h window reaching 90% or the weekly window reaching 98% blocks immediately, even while the other still has headroom. Monthly-only accounts use their monthly window. The block releases automatically, with the switch still on, once every blocking window reports a fresh reading below its threshold (a 0% reset counts); the next observation at the threshold blocks again. Configure codexMainAccountHardLockThresholds in config.json with optional short and long percentages, for example { "short": 90, "long": 98 }. Both must be integers from 80 through 100, and short must not exceed long; omitted fields use the defaults. The settings API accepts the same object and returns the effective values in mainAccountHardLock.thresholds.

While the lock blocks, requests can fall back to another model instead of failing. The Fallback while blocked row sits beside the protection switch and works like the shadow-call row: picking a model — for example deepseek/deepseek-chat — turns the reroute on immediately, the optional thinking effort applies to it, and the switch pauses the reroute without forgetting the model. While the block is in force and no Codex account can serve a request for a native Codex model, that request is routed to the fallback model (with the configured effort) instead of being refused. A usable Pool account still serves the request first; provider-qualified selectors, combos, account-qualified rows and Luna Reserve keep their own destinations; and a fallback that itself names a native Codex model is ignored, because rerouting gpt-* to gpt-* would only re-enter the same refusal. The lock itself is unchanged: it releases on its own as before, and the reroute ends with it. Configure codexMainAccountHardLockFallback in config.json as { "model": "deepseek/deepseek-chat", "reasoningEffort": "low" }; enabled: false pauses the reroute while keeping the model. An absent section, an omitted model, or a malformed section means requests are refused as usual; reasoningEffort must be a declared effort. The settings API accepts the same object, rejects invalid writes before mutation, and null clears the section.

The dashboard and ocx status show possible usage outside opencodex when fresh readings rise by at least one point in the same reset episode without recent main-account activity through this proxy. This is an advisory observation, not proof: a long-running turn can also cause it. The lock may not prevent exhaustion from outside traffic. The process-local notice clears on identity or reset changes and expires after six hours or at the observed reset, whichever comes first. ocx status --json exposes the state, effective thresholds, blocking window kind, and optional externalUsage warning under mainAccountHardLock when live management evidence is available.

An unreadable 5h reading cannot hide a weekly block. Unknown usage does not fabricate a zero, and a missing reading does not erase an already measured blocking tuple. A predicted reset time alone does not unlock it. While blocked, recovery checks owned usage metadata on a capped 5/10/20/40/60-minute schedule, so a reset redeemed outside opencodex is noticed before the predicted reset; the first check waits five minutes, checks stay at least five minutes apart, and the next check is pulled in to the latest known blocking reset. A paused or reauthentication-required main receives no checks before its predicted reset. A longer Retry-After also delays profile and token preparation. Only fresh valid usage or authoritative window-absence evidence can lift the block. In Pool mode, a quota --refresh bypasses cache freshness but still honors failed-read pacing; a deferred read makes no new diagnostic attempt. Other pause, reauthentication, and upstream limits remain independent.

Protection treats one fresh valid WHAM usage response as a replacement for the old 5h reading when its primary window explicitly lasts at least 24 hours and reports valid usage, or its primary window is explicitly null and a measured secondary supplies the weekly usage. In either case, secondary/tertiary windows must be explicitly null or explicitly last at least 24 hours and report valid usage. This follows the parser’s short/long boundary, so a one-day window qualifies as well as weekly/monthly windows. The current window still uses the same configured long-window threshold (98% by default). This relies on the single reported snapshot; repeated observations are not required. Any omitted window field, an unknown duration, or partial response headers cannot clear a previous block. All-null responses carrying only credits and supplementary monthly-only readings also cannot establish recovery. The proxy checks the stored credential again before applying a delayed response. An unreadable file or replaced bearer cannot update the usage cache, release the lock, or quarantine the new credential, even for the same account with no second quota read. Its parsed ordinary usage can still be returned to the requesting caller, without shared-state updates or recovery evidence. The account card shows the published cached usage, keeping its quota aligned with the lock status; Direct provider quota omits an unpublished response and its older cached report. Conflicting account identities and stale 401/403 replies retain the current cached info and cannot clear or set the current account’s reauthentication state.

Responses to requests sent with the identified main credential refresh its cached usage from their quota headers, whether the proxy substituted the stored credential or the caller sent the same credential itself. A response is applied only if that credential is still the observed main credential when it arrives; a caller-owned credential for another account or workspace never updates the main account’s usage.

The persisted option is "codexMainAccountHardLock" in OpenCodex’s config.json. An absent key or true means on; only an explicit false turns it off, and that is what switching the setting off stores. The default changed here: the policy used to be opt-in and the old switch removed the key when it was turned off, so an install that had switched it off now reads as on. If you want the old behavior, switch it off once to record the opt-out. Protection covers new requests using the identified main account, not the last 2% itself: already-running requests, unmatched caller-owned keyring credentials, and traffic outside the proxy can still spend quota. Added accounts and other providers remain available.

With protection on, an owned startup restores the main credential’s in-memory identity binding after native-profile recovery and cleanup, so a persisted policy block survives a restart. Caller-owned Direct, exact-main, main-fallback, and main-pin requests can briefly receive 503 while that binding is pending; healthy stored Pool accounts stay eligible throughout. No credential is read from a foreign or unconfirmed service home for this initialization.

While this policy blocks main, Luna Reserve on that account is blocked too. Staying below ordinary quota exhaustion may prevent Reserve activation. Disabling the switch restores normal local handling, not additional upstream entitlement. Use the account quota refresh action to obtain a fresh observation; no reset credit is consumed automatically.

The optional authless Desktop mode keeps Desktop’s native Reserve-only picker gate inactive. It also disables Desktop’s automatic Reserve handling: Reserve is an explicit model choice, not an automatic fallback.

Keep the built-in OpenAI provider enabled in ChatGPT-forward mode, enable the account model picker, and configure a public selector for the stored main account. With effective loopback authless mode enabled, ocx sync includes <main-selector>/gpt-reserve alongside routed provider models. A bare gpt-reserve, an added-account selector, and API-key model discovery are not added to the catalog. The authless setting is ignored for remote-client routing or a listener that needs an admission header. When public and local listeners run together, Reserve compatibility applies only to requests admitted by the local listener. An authenticated public request stays on the normal path even if it originates from the same machine; request headers cannot select the local policy.

Enable authless Desktop mode with ocx system settings --desktop-authless on, run ocx sync, then fully quit and reopen Codex Desktop so it reloads the rewritten configuration and catalog. Follow the canonical authless Desktop workflow.

Each compatibility request checks a credential-bound server authorization, cached for at most 60 seconds. OpenCodex sends the Reserve capability header on an owned main-account usage read and requires ordinary usage to be disallowed, the Luna Reserve banner, and exactly one allowed Reserve bucket. Missing, denied, stale or mismatched evidence refuses the request; it does not switch accounts or silently use ordinary Luna. Passive usage can revoke authorization but cannot create it. Global cooldown, pause, reauthentication and the main-account hard lock still apply. Turn the hard lock off if you want to use Reserve on an exhausted main account; doing so does not grant server entitlement. This compatibility path supports conversation requests and compaction, not Reserve as a vision or web-search helper or a standalone search-relay model. Choose another model for those helpers.

The picker prefers actual Reserve metadata. When none has been observed, it uses an explicitly marked Luna metadata adaptation, following Desktop’s Reserve-or-Luna preset mapping. A visible entry is not proof of availability. Desktop source and fixture-backed paths were checked; a live Reserve-active account was not used to validate this compatibility path.

List and switch provider accounts and API-key pools through the running proxy. The shipped help surface is:

Usage: ocx account <list|history|current|use|clear|refresh|auto-switch|alias|priority|pause|resume|pause-exhausted|strategy|sticky|remove|clear-cooldown|add-key|import|import-orca|login|reauth|code|cancel|reset-credits|grok-reset-coupons|main> ...
list [provider] Codex account pool, OAuth accounts and API keys (identifiers shown masked as the API returns them).
history openai <pool-account-id> [--limit <1-200>] Recent routing decisions for one Codex pool account.
current <provider> Show the active account or key.
use <provider> <id|alias|main|auto> Switch the active credential; 'main' selects the Codex App login, 'auto' clears the selection unless an account carries that id.
clear <provider> Clear the manual Codex account selection unconditionally.
refresh <provider> Force-refresh Codex or provider quota reports.
auto-switch <provider> <on|off|status|threshold N> Control the Codex pool threshold.
alias <provider> <id|alias> <display-name|-> Set or clear an account's display name; '-' clears it.
pause <provider> <id|alias|main> Hold an account out of automatic selection.
resume <provider> <id|alias|main> Return a paused account to automatic selection.
pause-exhausted <provider> Pause every account whose quota is spent.
clear-cooldown <openai|anthropic> <id|alias|main> Drop a cooldown the proxy set after an upstream failure.
strategy <provider> [<quota|round-robin|fill-first|least-loaded|reset-first>] Pool placement strategy; least-loaded is Kiro-only.
sticky <provider> [<1-100>] Requests a bound thread keeps on one account; omit the value to read it.
priority <provider> <id|alias|main> [first|earlier|normal|later|last|-100..100|reset] Selection order; omit the value to read it.
remove <provider> <id|alias|main> --yes Remove a stored account or key after an existence check.
add-key <provider> [--label <label>] Add a key read only from piped stdin.
login/reauth/code/cancel Run browser or manual-code auth from a headless shell.
reset-credits <id|main> [--consume --yes] Inspect or consume Codex reset credits.
grok-reset-coupons [<id>] [--consume --yes] [--token-id <token-id>] [--operation-id <uuid>] Inspect or redeem Grok reset coupons.
import <provider> --format <format> (--file <path>|--stdin) Import credentials from a named external format.
import-orca --source <dir> --registry <file> [--apply] Preview or apply imports from Orca-managed Codex homes.
main <doctor|list|register|add|reauth|switch|recover> Manage the Codex App login the pool calls 'main'.
Switching the active account takes effect immediately; running threads move on their next request, and in-flight requests keep the account they captured.
A selection-order change applies from the next unbound request and never moves a bound thread.

Account subcommands require the proxy to be running and auto-resolve its recorded runtime port, except import-orca: preview is local-only, and import-orca --apply requires the proxy to be stopped. Successful operations exit 0. Invalid usage, an unknown provider or account/key id, an unreachable proxy, or an API failure exits 1. Credential fields are displayed exactly as the management API returns them (including its masking); raw API keys and OAuth tokens are never returned. Display conveniences are synthesized client-side, same as the dashboard: main is the CLI alias for the Codex App login in the openai account pool, OAuth accounts without an email appear as Account N, and the plan/label column falls back across plan, masked email, label, and masked key.

--json account rows use this common shape (optional fields are omitted when unavailable):

{
"provider": "openai",
"type": "codex | oauth | api-key",
"id": "__main__",
"label": "plus",
"email": "m***@example.com",
"plan": "plus",
"masked": "sk-ab****wxyz",
"priority": 0,
"active": true,
"needsReauth": false,
"quota": null
}

ocx account import-orca --source <orca-data-directory> --registry <orca-data.json> [--apply] [--json]

Section titled “ocx account import-orca --source <orca-data-directory> --registry <orca-data.json> [--apply] [--json]”

Reuse local Orca-managed Codex logins without another browser login. The source directory must contain codex-accounts/<account>/home/auth.json. Supply the Orca data directory and the chosen profile’s orca-data.json explicitly. Only accounts registered in that profile are considered; leftover or removed account homes are not imported. Each home must carry Orca’s matching .orca-managed-home marker. The command does not scan unrelated homes or remote hosts.

Terminal window
# Windows: preview only; no account is registered.
ocx account import-orca --source "$env:APPDATA\orca" --registry "$env:APPDATA\orca\profiles\local-default\orca-data.json" --json
# Finish active proxy requests and stop the proxy before applying.
ocx stop
ocx account import-orca --source "$env:APPDATA\orca" --registry "$env:APPDATA\orca\profiles\local-default\orca-data.json" --apply
ocx start

Older Orca profiles may use orca-data.json directly under the data directory. On other platforms, pass the local Orca data directory and profile registry with the same layout. The importer skips identities already present in the native main login, account pool, or credential store, including duplicates within the source. This deliberately uses the ChatGPT account ID as a conservative bucket: separate members sharing a workspace account ID are skipped too, not merged or individually imported. Repeating an import preserves existing accounts. If an earlier import stopped between saving credentials and registering the pool row, retry can finish that registration with the same ID only when its untouched pending record still matches the source. Invalid entries are counted by fixed reason code. A mixed result with at least one eligible account exits successfully; a result where every discovered entry is invalid exits nonzero. Output contains counts and reason codes, not emails, account identifiers, paths, or tokens.

Orca retains refresh ownership. OpenCodex stores a read-only source link and an access-token snapshot, never Orca’s refresh token. When resolving credentials, it reads the source again and verifies that it still belongs to the imported identity. Orca must keep that login available and refreshed. Missing, malformed, expired, or identity-mismatched source credentials fail closed; OpenCodex does not fall back to the old snapshot or refresh the source login. This is a local link, not a portable export or a transfer of refresh ownership. Requests already sent upstream keep the credentials they captured.

New accounts are registered as validation pending. After starting the proxy, open Codex Auth and click Refresh quotas to authorize the existing model validation step, which may consume a small amount of quota. Offline import and JWT parsing do not establish upstream authentication. CLI quota refresh alone does not perform that validation. The importer never starts or stops either app, changes Orca files, or changes the active pool account.

ocx account list [provider] [--json] [--all] [--quota [--refresh]]

Section titled “ocx account list [provider] [--json] [--all] [--quota [--refresh]]”

Stored ChatGPT pool accounts remain listable on hosts without a native Codex home, including in the dashboard and on account refresh. Listing does not create a Codex home or sign out the native main account. Disabling the Codex client integration does not disable an existing native login’s quota observation; invalid explicit CODEX_HOME paths still report a configuration error. Unsafe OPENCODEX_HOME storage reports an error even when the native Codex home is missing. Default-home resolution also reports errors for files, dangling links, and parent directories that cannot be traversed.

Without a provider, lists the Codex pool, OAuth accounts, and configured API-key pools. Empty providers are skipped unless --all is present. With a provider, lists only that credential family. Human output uses PROVIDER TYPE ID PLAN/LABEL PRIORITY STATUS; a manually chosen Codex row is marked selected. PRIORITY is the signed Codex selection order (0 when unset) and shows - for rows where ordering does not apply, such as OAuth accounts and API keys. With two stored Kiro accounts, rate, confirmed monthly-quota, and suspension refusals can rotate to an eligible account before output; positive cached model-list evidence is preferred among eligible accounts before the ordinary pool strategy. Reactive rotation is presence-driven and cannot be turned off — oauthAccountFailover.enabled: false declines pre-dispatch account preference, not refusal recovery, and a provider override takes precedence; ocx account login kiro adds accounts to the pool one at a time. An empty result is still success. --json returns:

{ accounts: AccountRow[], notes: string[] }

Codex JSON rows include slotId (the registered id) and slotKind (native-main or pool). When the proxy can verify both the upstream account and user identity, it also includes upstreamAccountKey (uai1_ followed by 64 lowercase hexadecimal characters). Equal keys within the openai provider identify slots for the same upstream user; keep each slot and its quota state separately. The key is local to this installation and can change when its administrator token rotates. If it is absent, do not infer a match from masked email or quota values. Raw upstream IDs, user claims and credentials are not listed.

An empty provider listing names the next command in human output and JSON notes: ocx account login <provider> for OAuth/Codex, or ocx account add-key <provider> with a human-controlled piped stdin source for API keys. An empty global listing points to ocx account login <provider> and ocx help account login.

Account rows include a validated health label and, when recovery is available, healthAction. Human output prints the recovery below the table. Reauthentication targets the stored account with ocx account reauth <provider> --id <id>; native main uses ocx account main reauth --device. Provider verification, credential conflicts, and pending validation retain distinct guidance. Codex rows preserve the server’s boolean creditsAfterLimit in list/current JSON; human rows show paid-credits: on only when permission is enabled. These reads do not change paid-credit permission.

--quota adds a QUOTA column with each account’s own usage, for providers that support a per-account probe (Anthropic, Kiro, Google Antigravity, and Devin today). It is opt-in because the proxy probes the upstream once per stored credential; the default listing stays a local read. --refresh bypasses the cached result. An account with no per-account quota shows -, and one whose probe failed shows unavailable — blank would read as “no usage” rather than “not measured”. --json carries the full breakdown per account, not just the summarized windows.

API-key pools also support ocx account list <provider> --quota [--refresh]. Without --quota, their listing does not request a quota probe. The opt-in read uses each stored key’s quota owner; --refresh requests fresh evidence from that owner and may contact the upstream provider. It does not replace stored keys or read the provider-wide aggregate returned by account refresh.

API-key JSON rows preserve quotaMode (probe, passive, unsupported), optional/null quota, and quotaUnavailable when returned. Public quota can include custom windows, USD credit balances, Kiro credits and update times, not only Codex-style percentages. Human output distinguishes unsupported, not measured, unavailable and actual measurements. Unknown is not zero; observed zero is retained. Malformed consumed fields or a returned key row without quota-mode evidence fail rather than fabricate support. An empty pool remains valid. Keys remain masked.

Google Antigravity rows carry the same Gem / Cla windows as the provider-level quota, computed from that account’s own credential and Cloud Code Assist project id. The per-account probe always talks to Google’s Cloud Code Assist host through the pinned outbound transport, regardless of a configured baseUrl: a custom base URL is a routing choice for requests, not a second source of Google’s accounting for a stored credential. An account without a project id, or one whose probe is redirected or fails, shows unavailable. When fallback model discovery supplies quota readings, detailed displays retain up to 256 model diagnostics with distinct bounded labels. These rows do not affect provider summary headlines, severity, account ranking, routing, or reset notifications.

Devin rows come from Cognition’s GetUserStatus for that account’s own key, sent to its allowlisted api-server host. If an older credential has no host, the probe uses the configured provider base URL when allowlisted, or the US default. They show the dated daily and weekly windows the plan exposes, and a monthly credit window only on a credit-billed plan that reports a credit balance; an unknown billing strategy uses that credit fallback only if both reset dates are absent. Expired daily and weekly windows stay hidden without becoming monthly credit quota. An unlimited balance, or a status with no balance at all, shows no credit window. Only a rejected key (401) clears a cached reading; other probe failures retain the last reading.

$ ocx account list anthropic --quota
PROVIDER TYPE ID PLAN/LABEL PRIORITY STATUS QUOTA
anthropic oauth 1278f8da a***r@examp***.com - 5h 7% wk 62%
anthropic oauth e112f28b k***1@examp***.net - active 5h 9% wk 45%

Kiro bills a monthly allowance and reports no shorter window, so its accounts render a mo figure instead:

$ ocx account list kiro --quota
PROVIDER TYPE ID PLAN/LABEL PRIORITY STATUS QUOTA
kiro oauth 3f0a91c2 a***r@examp***.com - active mo 15%
kiro oauth 8b24de70 k***1@examp***.net - mo 88%

ocx account list kiro marks an account excluded from automatic selection as not-auto-selected(<reason>). JSON carries autoSelectable and, when false, a closed skipReason (paused, needs_reauth, suspended, cooldown, or quota_exhausted). An active singleton or all-excluded pool may still send. Kiro providerCredits comes from measured meteringEvent values: the last reading within a physical response is retained, and separately billed sends add to the request spend. Credits are never estimated from tokens.

With two or more Kiro accounts logged in, request-rate, confirmed monthly-quota, and confirmed suspension refusals can rotate before output. Monthly exhaustion excludes only that login until reset or evidence expiry; completed service clears an older verdict. Quota refusals include the exact MONTHLY_REQUEST_COUNT and OVERAGE_REQUEST_LIMIT_EXCEEDED reasons on HTTP 400 or 429. Plans allowing overage can keep serving after the monthly gauge reaches 100%. Reactive rotation remains available when proactive account preference is off. Accounts are added one at a time — ocx account login kiro hands off to the Kiro CLI and appends the new account to the pool. In the dashboard, Login and Add account also offer Builder ID, Google, and GitHub device login alongside the Kiro CLI choice; native device login adds an account without signing the CLI out. Users of one organization (IAM Identity Center) profile share its profile ARN, so a Kiro CLI account is identified by that profile together with the email kiro-cli whoami reports for the session. Two users of the same organization therefore stay separate accounts, and signing the same user in again updates that user’s account. When kiro-cli whoami reports no user, the profile alone identifies the account as before. If that profile already has an account with a known user, such a login only refreshes the account whose stored Kiro CLI session it continues; any other login is refused instead of replacing an account, and an Add account login switches the Kiro CLI back to its previous session. Run kiro-cli whoami to confirm the signed-in user, then retry. Reauthenticating an account saved before its user was known records the user that signs in, unless that user already has another account of the same profile. To add an account without the Kiro CLI, use ocx account login kiro --method builder-id, --method google, or --method github. Open the printed verification URL, enter the user code, and wait for approval. --no-wait prints the flow ID; cancel it with ocx account cancel kiro --flow <flow-id>. Native login only adds accounts. To reauthenticate one, remove it and add it again. A repeated social profile ARN creates another slot and prints duplicate_profile_arn; the slots each carry their own quota and load state. Kiro can opt into proactive least-loaded placement with pool.kernel and account preference enabled. Proactive model preference also requires that account preference be explicitly enabled globally or for Kiro; an unset or false setting leaves a healthy active account in place. Model lists are learned after an account serves, so an inactive sibling may initially have no evidence. A model ID absent from every cached list is still sent upstream. Its optional maxConcurrentPerAccount cap is a bounded per-account queue: a full selected account waits up to 250 ms, then returns 503 account_capacity with Retry-After: 1. The cap is local to each proxy process and does not move a request; reactive rotation remains available after a refusal.

Shows the active account or key. A Codex pool with no manual pin reports the priority-aware automatic selection: the highest-priority eligible tier is chosen, and the lowest-usage account within that tier is selected under quota routing; another family with no active credential reports that state and still exits 0. --json returns:

{ provider, type, activeId: string | null, autoSwitchThreshold?: number, account: AccountRow | null }

ocx account use <provider> <account-or-key-id|alias|main|auto> [--json]

Section titled “ocx account use <provider> <account-or-key-id|alias|main|auto> [--json]”

auto clears the manual selection so the pool places work by its own strategy again — unless a Codex account literally carries the id auto, which wins by exact-id precedence; ocx account clear <provider> always restores automatic selection. Any Codex account can be named by the alias set with ocx account alias instead of its id; that holds for priority, pause, resume, clear-cooldown, remove and alias too. For Codex accounts, auto, main and __main__ are reserved regardless of case and cannot be assigned as aliases. OAuth and API-key display names keep their existing rules.

Selects an existing Codex account, OAuth account, or API key. For openai, main selects the Codex App login. A Codex Pool selection clears process-local affinity and applies to the next request, including one from an existing visible task; proxy restart or affinity eviction can also leave a task unbound, while in-flight requests keep their captured account. This controls Pool routing only; Direct mode keeps using the caller-owned/native main credential. Usage-based proactive switching, 401/403 reauthentication, 429/retry-after cooldowns, exclusion, and pre-output 429/402 failure recovery may later select another eligible Pool account. Those recovery paths remain active when usage-based switching is off. OpenCodex replays the conversation after an account change, but the provider-side prompt cache may be cold. Unknown providers or ids exit 1. On a 401/403, App login clears that account’s process-local affinity and requires reauthentication. On a 429, opencodex honors Retry-After, starts the account cooldown, clears affinity, and may rotate the request to another eligible Pool account. These failure transitions remain active with autoSwitchThreshold: 0; that setting disables only usage-based proactive switching. --json returns:

{ ok: true, provider, type, activeId }

Clear the manual Codex account selection without resolving an account id, so it works even when an account is literally named auto. Codex pools only; other provider types have no automatic selection to restore.

ocx account pause|resume <provider> <id|alias|main> [--json]

Section titled “ocx account pause|resume <provider> <id|alias|main> [--json]”

Accounts paused by the opt-in per-provider auto-pause policy use the same commands. Listing shows the fixed pause reason; resume clears it.

Pause or resume one account in the Codex, Anthropic, generic OAuth, or API-key provider pool, including google-antigravity. For the Codex pool, main identifies only the built-in Codex account; OAuth accounts must be identified by id or a unique alias. A paused OAuth account is excluded from request selection, 429 failover, and proactive token refresh, and cannot be selected manually. Pausing the active account switches to the next usable account when one exists. If every account is paused, requests that need that pool return 403 until an account is resumed.

For Anthropic and generic OAuth providers, identify the account by id or by a unique exact or case-insensitive alias. The JSON response reports the account id, pause state, and active account id.

Anthropic pause applies even when proactive pooling is disabled, including session affinity and 429 successors. It survives restart and reauthentication, preserves credentials and health, and does not interrupt a turn already sent. Removing the account removes its pause state. Per-account Anthropic auto-switch thresholds are not part of this control.

Anthropic’s automatic pause fallback keeps account order, skips paused accounts and accounts requiring reauthentication, and excludes Claude Code imports expiring within 60 seconds. Legacy accounts without a recorded source remain eligible using only their own stored credentials and normal stored-token refresh; they never adopt CLI-disk credentials. A still-valid Claude Code import with more time remaining can be selected. Later automatic re-adoption accepts a shared, nonempty access or refresh token. If both tokens rotate, OpenCodex requires authenticated account UUID proof for both the stored and imported bearer. It saves that proof privately when available from login, refresh, or profile lookup; account labels, email, organization and credential-file location cannot substitute for it.

When the old bearer has expired and no bound account proof was saved, automatic recovery may be impossible. An unavailable profile or unverified rotated pair leaves the stored account unchanged and does not replay a potentially consumed refresh token. Use explicit login to import the current Claude Code credential. Import preserves unrelated identityless slots and may create a separate account; select the intended account and remove obsolete slots only after checking them. A profile lookup failure can leave a new import identityless, with the same automatic-recovery limitation.

If no permitted fallback remains, quota and live model discovery wait for a usable active account. You can explicitly select an existing unpaused legacy account with ocx account use anthropic <account-id-or-alias>; its own valid credential and normal stored-token refresh remain available even when its original credential source was not recorded.

Terminal window
ocx account pause google-antigravity <account-id-or-alias>
ocx account resume google-antigravity <account-id-or-alias>

ocx account clear-cooldown <openai|anthropic> <id|alias|main> [--json]

Section titled “ocx account clear-cooldown <openai|anthropic> <id|alias|main> [--json]”

Drops a process-local failure cooldown without changing stored credentials. Use openai for a Codex pool account or anthropic for an Anthropic OAuth account; other providers are rejected. Both forms accept an account id or unique alias, while main is specific to the Codex pool.

Terminal window
ocx account clear-cooldown anthropic <id-or-alias>

The command reports success even when no cooldown is active, with cleared: false in JSON. Clearing an Anthropic cooldown also advances the account generation so an older in-flight quota probe cannot restore the cleared state or publish stale quota-derived eligibility afterward.

For the Codex pool, use ocx account refresh openai [--json]. It force-refreshes account quotas and prints available weekly/monthly percentages and reset times; missing quota data is reported as unknown, not 0%. Its JSON envelope is { accounts: AccountRow[] }, with quota on each Codex row.

For OAuth and API-key providers, this force-refreshes the provider quota-report endpoint; it is not a token re-login or a plain account-list re-read. --json returns { provider, report: ProviderQuotaReport | null }. A provider with no supported quota report prints no quota report available for <provider> and exits 0. Unknown providers and management-API failures exit 1; an upstream quota probe that fails or times out degrades to a null or stale report instead (exit 0), matching the dashboard’s quota bars.

ocx account auto-switch <provider> <on|off|status|threshold <0-100>> [--json]

Section titled “ocx account auto-switch <provider> <on|off|status|threshold <0-100>> [--json]”

Controls the openai Codex pool threshold, or stores a threshold for a generic OAuth pool. on stores 80%, off stores 0%, and threshold <n> accepts 0–100. A generic pool threshold steers selection only while pool.kernel is on with strategy: "fill-first"; with the flag off, saving one does not enable threshold-based switching. It never changes the provider enablement override or disables reactive 429 rotation. status and mutation output for generic pools use the confirmed server response. For generic pools, poolEnabled is the stored provider override (null means unspecified), not inherited effective state; inert: true means the threshold is stored but not applied, inert: false means the pool is applying it, and an absent inert is an unknown capability, which never reports enabled: true. API-key providers and invalid values are rejected.

ocx account auto-switch anthropic … --account <id>

Section titled “ocx account auto-switch anthropic … --account <id>”

For Anthropic OAuth, use ocx account auto-switch anthropic threshold 90 --account <id> (integer 0–100), off --account <id> (0), on --account <id> (80), inherit --account <id> (reset), or status --account <id> (read-only); append --json for structured output. The account card offers the same custom-threshold toggle. Missing/null inherits anthropicAccountPool.autoSwitchThreshold (default 80); 0 disables usage-driven switching for that account, not pause or reactive 429 recovery. Overrides survive restart and re-login and are removed with the account. With pooling enabled, quota and fill-first compare each source/candidate against its own threshold in the selected quota window. Manual/affinity precedence, identity-less round-robin/fill-first behavior, unknown-quota fallback and all-drained fallback remain unchanged. Round-robin is not usage-driven; disabled pools ignore these thresholds. Model-route allowlists still constrain every candidate.

openai: { provider, autoSwitchThreshold: number, enabled: boolean }
generic OAuth: { provider, autoSwitchThreshold: number | null, enabled: boolean, poolEnabled: boolean | null, inert: boolean | null }

ocx account preserve-signed-in openai [on|off|status] [--apply-to-bound] [--json]

Section titled “ocx account preserve-signed-in openai [on|off|status] [--apply-to-bound] [--json]”

Opt in to use other eligible accounts first and keep the signed-in ChatGPT/Codex Desktop identity as fallback. A bare invocation or status reads the running proxy’s policy, affected entry IDs, manual pin and next unbound choice. on persists codexPreserveSignedInAccount: true; off restores your existing custom account order without rewriting any priorities.

The reserved group includes native __main__ and ordinary pool entries with the same selected ChatGPT account/workspace and normalized member email. Sharing a workspace alone is insufficient. The group follows the latest physical login observed by the proxy’s native-login lifecycle; no incoming request credential can define it. If member/workspace evidence is unavailable, identityKnown: false reports that only main can be identified. Status obtains a fresh owned observation; a busy or unreadable native profile returns 503 without applying the change.

All four Codex strategies prefer other identities with quota headroom, preserving custom order within each group. Reserved entries remain fallback when preferred tiers drain. Fallback can consume the signed-in quota, subject to existing model access, pause, cooldown, reauthentication, hard-lock and paid-credit consent checks. This policy does not grant paid-credit consent.

Healthy existing bindings stay in place, and manual pins and exact account-qualified model routes override the preference. To apply it to ongoing work, run:

Terminal window
ocx account preserve-signed-in openai on --apply-to-bound

This explicitly releases only affected bindings and any affected manual pin for the next request; in-flight requests keep their captured account. Unrelated bindings and pins remain. Moving work may lose its warm prompt cache, and account-scoped uploads or continuation state can prevent a move. codexAccountPriorityFailback remains a separate opt-in, with its existing quota-strategy, positive-threshold and fresh-observation conditions; it can also move work toward a preferred group.

For never use this identity through the pool, use ocx account pause openai main instead: linked pause excludes main and matching pool entries without changing the desktop login file. Only requests that reach OpenCodex are controlled. This does not unlock an already-disabled desktop composer or promise to fix client-side quota gating.

ocx account priority <provider> <account-id|alias|main> [<-100..100|first|earlier|normal|later|last|reset>] [--json]

Section titled “ocx account priority <provider> <account-id|alias|main> [<-100..100|first|earlier|normal|later|last|reset>] [--json]”

Reads or sets one Codex pool account’s selection order: higher is used earlier, the default is 0, and the range is -100 through 100. Only the openai Codex pool is ordered, so other providers exit 1. main targets the Codex Desktop login, which is ordered like any other pool account. ocx account priority openai main last lowers only that entry; use preserve-signed-in above to reserve its matching ordinary pool entries too.

Preset words stand in for small integers: first is +2, earlier is +1, normal is 0, later is -1, and last is -2. reset returns the account to the default and drops its stored entry. Omitting the value reads the current order instead of writing one.

Ordering picks which accounts are considered first, not which are usable: selection still runs among eligible accounts, taking the highest order tier that still has quota headroom and leaving accountPoolStrategy to choose inside it. Pause, cooldown, and reauthentication are unaffected. Changes apply from the next unbound request, not only from newly started sessions: preemption moves an unbound request up as soon as a higher order regains headroom. Threads already bound to an account normally keep it until that account is drained; a reauthentication failure or a quota cooldown still releases the binding immediately. A transient-failure streak (5xx and other non-quota failures reaching upstreamFailoverThreshold, default 3) no longer deletes a live binding: the request is served by another account while the binding is kept, and the task returns to its own account as soon as that account serves again. If the account is still failing after 10 minutes the binding is released normally. This hold is independent of pool.cacheAffinity. Any accepted write also releases a manual “use this account now” pin, on whichever account held it, including a write that stores the order an account already had — this is the only way to clear a pin while keeping the account that is currently selected. (Clearing the active account through the management API releases a pin too, but it drops that selection along with it.) An unreachable proxy, an unknown account id, or a value outside the accepted set exits 1. --json returns:

{ ok: true, provider, id, priority: number, preset: string | null }

Run browser-based or manual-code account authentication from a headless shell. Use ocx account --help for the provider-specific command shape. If a Codex account login is saved but its validation or model-catalog refresh remains pending, both human and JSON modes exit 1. The saved login remains visible; do not restart authentication just because follow-up work is pending. Human output prints fixed ocx sync recovery guidance on stderr for a pending catalog. --json keeps stdout parseable and preserves the pending flags without the human warning.

The new login options are flow-specific:

Flow --open-browser on|off --add-account on|off
Fresh ordinary provider OAuth Supported for browser-capable providers Supported; omitted defaults to adding an account
Ordinary provider reauth Browser-capable flows only Rejected; omission retains reauth behavior
Codex browser login Supported Rejected
Codex device, or native device-only kimi/nous/github-copilot Rejected Rejected for Codex; ordinary OAuth rules for other providers
Native Kiro --method Rejected Rejected; this flow is add-only

Explicit add-account off permits the existing import preference; it does not remove an account. Omission preserves previous CLI defaults. The operator must complete browser/device verification. Keep flowId, account ID and verification code separate; a returned device grant is not a successful browser launch. --no-wait --json reports the handoff, not completed login. Preserve pending, expired, cancelled, validation-pending and catalog-pending state; never silently restart a flow, capture credentials or complete verification for the operator.

ocx account login openai --device runs OpenAI’s device-code login instead of the browser callback. Use it when the proxy host has no browser, or when nothing can reach its localhost:1455 — a container, a VPS, or any hub reached over SSH:

Terminal window
ocx account login openai --device --no-wait --json
# Read the returned flowId, URL and deviceCode; the operator completes verification.

Open that URL on any other machine, enter the short code, and the login completes. Without --no-wait the command polls until you finish; the device grant lives 15 minutes, and the command waits that long rather than the 5 minutes a browser login allows, because the point is that you walk away to another device. kimi, nous, and github-copilot accept the flag as a no-op because their only login is already a device flow; a provider with no device grant rejects it.

In the dashboard, the same login is selected by the “Don’t open a browser on the proxy machine” checkbox on the add-account modal. That setting already means the operator is not sitting at the proxy host, which is exactly when a callback URL is useless — so ticking it switches the Codex login to the device flow and shows a copyable code instead.

ocx account remove <provider> <id|alias|main> --yes [--json]

Section titled “ocx account remove <provider> <id|alias|main> --yes [--json]”

This guarded, non-interactive deletion requires --yes. Before deleting, it verifies that the id exists; a missing id exits 1 without sending DELETE. The main Codex App login cannot be removed, so remove openai main --yes is refused. After deletion, the family is read again: removing the pinned Codex account clears the pin and returns to automatic selection; OAuth promotes the first remaining account or reports none; API-key pools promote the first remaining key or report none. --json success and failure shapes are:

{ ok: true, provider, id, removedActive: boolean, promotedActiveId: string | null, catalogRefreshPending?: boolean }
{ error: string } // stderr, exit 1

catalogRefreshPending is present on Codex removals only. When it is true, the account deletion is already saved; human output prints generic ocx sync recovery guidance on stderr and still exits 0. OAuth-account and API-key removal envelopes do not gain this field.

ocx account add-key <provider> [--label <label>] [--json]

Section titled “ocx account add-key <provider> [--label <label>] [--json]”

Adds and activates a key for an API-key provider. The key is read only from non-TTY piped/redirected stdin; interactive TTY input, empty input, OAuth/Codex providers, and API failures exit 1. The key is never echoed, including when it appears inside a label. Prefer a secret manager or a here-string:

Terminal window
ocx account add-key openrouter --label personal <<< "$OPENROUTER_API_KEY"
security find-generic-password -w openrouter | ocx account add-key openrouter --json

--json returns { ok: true, id: string | null, label?: string } and never includes the key.

ocx account reset-credits <id|main> [--consume --yes]

Section titled “ocx account reset-credits <id|main> [--consume --yes]”

Inspect Codex reset credits for an account. Consuming a credit is destructive and requires both --consume and --yes.

After a confirmed reset, fresh usage can recover the same account’s eligible existing shared reset-derived cooldown. Paused accounts, accounts needing reauthentication and cooldowns owned by an in-flight probe remain excluded from this recovery. A failed or busy usage refresh after confirmed consumption does not require another credit: check usage again instead of repeating --consume. Consume success does not guarantee routability; see the management API recovery contract for reset/replay, freshness and scope limits.

ocx account grok-reset-coupons [<account-id>] [--consume --yes [--token-id <id>] [--operation-id <uuid>]] [--json]

Section titled “ocx account grok-reset-coupons [<account-id>] [--consume --yes [--token-id <id>] [--operation-id <uuid>]] [--json]”

Inspects remaining reset coupons or redeems one for an xAI / Grok account.

When invoked without --consume, returns the available coupon tokens and validity windows:

Terminal window
ocx account grok-reset-coupons
ocx account grok-reset-coupons acc_xai_01 --json

Redeeming a reset coupon mutates billing state and permanently exhausts one coupon token. --consume strictly requires --yes:

Terminal window
ocx account grok-reset-coupons --consume --yes
ocx account grok-reset-coupons --consume --yes --token-id <token-id>

Pass --operation-id <uuid> (must be a valid UUIDv4) to guarantee idempotent settlement. If the network drops or the command is retried, identical operation IDs replay the durably recorded outcome instead of consuming a second coupon.

The command exits nonzero unless the settled code is redeemed (including a replayed redeemed). When the outcome is unconfirmed (a missing or unrecognized settled code, attempt_unresolved, attempt_in_progress, attempt_reconcile_failed, operation_state_changed, operation_token_mismatch, attempt_mark_failed, or ledger_unavailable/capacity with an operation ID), it prints the --operation-id to preserve; rerun with that same ID instead of starting a new operation. A recorded refusal (auth_failed, fetch_resets_failed, no_coupons_available, coupon_unavailable, or token_unresolved), such as a replayed failure, prints Coupon was not redeemed (<code>); any other settled code counts as unconfirmed.

Manage named native Codex main-login profiles without changing OpenCodex account-pool routing:

ocx account main doctor [--json]
ocx account main list [--json]
ocx account main register <label> [--json]
ocx account main add <label>
ocx account main reauth --device [--no-wait] [--json]
ocx account main reauth status --flow <id> [--json]
ocx account main reauth cancel --flow <id> [--json]
ocx account main switch <profile-id-or-label> --yes [--json]
ocx account main recover [--rollback --yes] [--json]

An empty native-profile list suggests ocx account main add <label> in human output and JSON notes; it does not assume an existing native login is available.

ocx account main reauth --device --no-wait --json writes one JSON object to stdout on success, without the human-readable follow up: line. Use its flowId with ocx account main reauth status --flow <id> --json to check progress.

Each mutating command reports the canonical effective CODEX_HOME returned by the running proxy. This path can differ from the caller’s CODEX_HOME; commands that support JSON expose the same value as effectiveCodexHome.

Version 1 supports file-based Codex authentication, encrypts stored profiles with AES-256-GCM, and keeps the encryption key in the operating-system credential store. add stages the official Codex login flow before importing the resulting credential. Close Codex before switching profiles; a successful switch preserves local tasks and history, then requires Codex to be restarted. Use doctor to inspect profile state and recover to finish or roll back an interrupted transition. switch accepts either the profile ID or its label.

reauth re-authenticates the existing native main identity with an OpenAI device code (#3898) instead of enrolling a new profile. It is the headless-hub recovery path: no local Codex App, no codex binary, and no OS keyring are required. The device login must complete for the same ChatGPT account that already holds the native main slot; the credential write is fenced by the exclusive claim and a path/hash/inode snapshot, and the command output carries only the flow id, the verification URL, the device code, and status. The pool login route stays pool-only and keeps rejecting __main__; the equivalent dashboard surface is the Codex Auth main card’s Re-login with device code control.

The v1 recovery matrix covers an OpenCodex process exiting after a transaction file has been published by rename. It does not claim durability across an OS or kernel crash or sudden power loss: atomicWriteFileAsync() does not fsync either the file or its parent directory.

The encrypted vault, switch journal, recovery marker, and journal quarantine live in the canonical <real CODEX_HOME>/.opencodex-native-main-profiles directory, so every OpenCodex instance sharing that Codex home observes one owner and one recovery state. Plaintext login staging remains isolated under each <OPENCODEX_HOME>/native-main-profile-staging directory.

Before native-main traffic or journal recovery is admitted, the lifetime owner takes the exclusive credential claim and removes only exact auth.json.ocx.<pid>.<sequence>.tmp crash residues. Each candidate must remain a single-linked regular file under the unchanged canonical CODEX_HOME; it is truncated, flushed, and then unlinked. Link/reparse substitutions, identity changes, and other ambiguity keep native-main traffic closed, while near-miss names are never removed automatically. This protects against cooperative OpenCodex crashes, not a malicious process already running as the same OS user. That user and the filesystem containing CODEX_HOME remain trusted, and truncation does not promise physical erasure from copy-on-write storage, snapshots, or SSD remanence.

Preview builds used <OPENCODEX_HOME>/native-main-profiles. That layout is never imported silently. If doctor reports legacy profile state, stop every OpenCodex proxy sharing the same CODEX_HOME. Then either back up and move the matching *.vault.json, *.journal.json, recovery marker, and any referenced journal-quarantine file together into the canonical directory while preserving owner-only permissions, or remove the old preview set and run ocx account main register again. Do not choose between multiple old roots or run both layouts while any sharing proxy is active. On Windows, preview state keyed by the former case-folded home identity must be reset rather than moved because its encrypted AAD and operating-system keyring identity are intentionally not reused.

ocx models [subcommand] · ocx model <subcommand>

Section titled “ocx models [subcommand] · ocx model <subcommand>”

ocx model is an alias of ocx models. With no subcommand, list the models statically seeded in configured providers. --provider filters one configured provider and --json returns model metadata. live reads the running catalog; add, edit, remove, and list-custom manage manual catalog entries; enable, disable, and provider control visibility; selected controls a provider allowlist; context controls provider context caps; and shadow manages background shadow-call interception.

Model prices are estimates in USD per million tokens. ocx models --json includes a price object with cost4 rates and their source; ocx models price --json keeps cost for the saved override and reports resolved rates in effectiveCost. Manual prices (including zero) take precedence, followed by the shared catalog and verified official-price fallbacks. Unknown models return null; no price is invented. Automatic defaults are derived on read and do not populate modelCosts in your config, so catalog updates remain effective. Use set-price to save provider-specific rates.

price --json also preserves a saved promptLengthPricing policy in cost; the four effectiveCost rates are the base rates, before evaluating a request’s prompt length or tier. Use Models → Price or the management API to edit a prompt-length policy. Existing set-price commands replace the four base rates and keep the model’s saved Flat or Custom threshold policy; --auto removes both the rates and any policy.

Discover command syntax with ocx models --help before live work. list-custom reads local configuration; add and remove save locally unless given --live. Local custom writes opportunistically synchronize when a proxy exists. The remaining model-management operations use the running proxy. Check readiness and version before live calls; offline inspection does not require starting a proxy. Display-name overrides for discovered models have their own command, separate from custom-model edit.

Subcommand Supported flags Action
list (default) --provider <name>, --json List models seeded in configured providers, with estimated input/output prices.
live --provider <name>, --json Read the running catalog, including models discovered at runtime. Rows are flagged native/routed, custom, and enabled/disabled.
price <provider/model> --json Read the saved manual override and effective price, including automatic catalog defaults.
set-price <provider/model> --input <rate>, --output <rate>, --cache-read <rate>, --cache-write <rate>, --auto, --json Set display prices in USD per 1M tokens. Input/output are required when setting; omitted cache rates become zero. --auto removes only this model’s override.
add <provider> <modelId> --display-name <name>, --context-window <tokens>, --modalities <text,image,audio>, --reasoning-efforts <levels>, --default-reasoning-effort <level>, --live, --json Register a custom model locally or, with --live, on the running proxy.
edit <custom-id> --model-id <id>, --display-name <name|->, --context-window <tokens|0>, --modalities <text,image,audio|->, --json Edit a custom model. - clears a field; 0 clears the context window.
remove <custom-id|provider/modelId> --yes, --live, --json Delete a custom model locally or on the live target. Live removal and local JSON removal require --yes; local text mode retains interactive confirmation.
display-name <provider/raw-model-id> --set <text> or --clear, --json Set or clear the discovered model’s display override; preserve the raw upstream ID.
order status --json Read saved order/mode, routed candidates and known featured state.
order set --models <CSV> or --mode <default|alphabetical|provider|most-used>, --json Set a complete routed permutation or derive a preset order.
order reset --json Clear saved order and mode, retaining featured selection.
list-custom --json Show all custom models with the custom-id the other subcommands take.
enable <provider/model|native-model> --native, --json Make one model visible to Codex.
disable <provider/model|native-model> --native, --json Hide one model from Codex.
provider <name> <on|off> --json Enable or disable every model of one provider in a single write.
selected <provider> --set <id,id...>, --clear, --json Read or replace the provider model allowlist. --clear removes the allowlist so every model is offered.
preset show --provider <name>, --json Read shipped preset versions and the applied mode, optionally for one provider.
preset apply <provider> --all, --json Apply the curated preset; --all clears the allowlist. A zero-match preset leaves selection unchanged and reports fallback: "preset-empty".
new-policy [on|off] --provider <name>, --json Read or change exposure policy for newly discovered models; omission of provider selects global policy. A provider read can report inherit.
new-arrivals --json Read recorded recent discoveries by provider; does not force a new upstream discovery.
context <status|value <tokens> [--set-all]|provider <name> on [--value <tokens>]|provider <name> off|all <on|off>> --json Read or set the context-window cap, globally or per provider. value <tokens> --set-all also re-points every routed provider (like the dashboard toggle); without it the value only becomes the default. provider ... on --value <tokens> sets an explicit cap for that provider only (--value is valid with on only).
shadow <status|set> [model|-] --enabled <on|off>, --json Read or set the replacement model for Codex’s background helper calls. - clears the model. status also reports sourceModels, the helper slugs the proxy intercepts (default: gpt-6-luna, gpt-5.6-luna; clients through 0.144.x used gpt-5.4-mini, which an explicit sourceModels override can restore).
Terminal window
ocx models live --json # what Codex can actually see right now
ocx models disable anthropic/claude-haiku-4 # hide one routed model
ocx models enable gpt-5.6-sol # no slash, so it is treated as native
ocx models provider zenmux off # hide a noisy provider wholesale
ocx models selected anthropic --set claude-opus-5,claude-fable-5
ocx models selected anthropic --clear # drop the allowlist again
ocx models add deepseek deepseek-v4 --display-name 'DeepSeek V4' --context-window 128000 --modalities text,image
ocx models list-custom --json # read the custom-id for edit/remove
ocx models remove deepseek/deepseek-v4 --yes

A model selector with a slash is routed (anthropic/claude-opus-5); a bare id is treated as a native OpenAI model, so --native is only needed to force that reading for an id that would otherwise look routed.

--modalities accepts only text, image, and audio. Codex parses that field as a closed enum and rejects an entire catalog containing any other value, so add, edit, and the management API all refuse the bad value rather than storing something the catalog writer would have to strip later (#759).

Live custom models and local save outcomes

Section titled “Live custom models and local save outcomes”

Use configured provider names and raw upstream model IDs. For an authorized live addition or deletion, inspect the intended target and read back afterward:

Terminal window
ocx ready --json
ocx status --json
ocx models add <provider> <raw-model-id> --live --display-name 'Research model' --context-window 128000 --modalities text --json
ocx models live --json

Keep the returned stored id. Removal uses ocx models remove <complete-stored-id> --live --yes --json or an unambiguous provider/model selector from that target. Display labels and ID prefixes are not selectors. There is no local fallback, revision-protected deletion or alternate-ID retry. models list-custom --json is a local list, not a live registry read for another target.

Without --live, add/remove JSON returns {action, model, needsSync, sync}. No proxy means sync: {"status":"not-attempted","ok":false}, needsSync: true and exit 0: the local save succeeded and opportunistic sync was not attempted. Attempted sync failures/refusals are nonzero while preserving the save. Only complete applied sync clears needsSync; policy-skipped success may exit 0 with it still true. JSON removal requires --yes even on an interactive terminal.

Terminal window
ocx models display-name <provider>/<raw-model-id> --set 'Research model' --json
ocx models live --json

This command splits on the first slash and retains every remaining slash in the raw upstream model ID. Do not substitute an encoded public picker ID or alias. --clear is the alternative to --set and sends null. Label overrides do not change pricing or public identity. A recognized saved-but-failed-refresh result has saved: true and exit 1; inspect state before another write.

Terminal window
ocx models order status --json
ocx models live --json
ocx models order set --models '<complete-public-id-permutation>' --json
ocx models order status --json

Manual ordering uses the public IDs in pickerAvailable, every routed candidate exactly once, with current featured models in their exact required leading order. Blank, duplicate, missing, unknown and ambiguous entries refuse. The CLI re-reads settings and model identities immediately before the manual PUT; changed state returns conflict. This is an observational check, not CAS, so read back afterward. It never silently drops native identities or modifies featured selection.

Alternatively select --mode alphabetical, --mode provider, or --mode most-used. These presets do not prepend the manual featured prefix. Most-used reads all-time, all-surface usage, retains unranked candidates, and refuses incomplete history. Both manual and non-default modes refuse saved orders containing bare native IDs. If clearing that saved order is intended, use ocx models order reset --json or ocx models order set --mode default --json first. Do not auto-reset on refusal.

Live custom and order writes include catalogRefresh. Only committed, nondegraded refresh exits 0; skipped, failed or degraded refresh returns the saved receipt with exit 1. Order receipt applied is the featured roster, not client convergence. Unknown outcomes require inspection, not an assumption of rollback.

Terminal window
ocx models preset show --provider anthropic --json
ocx models preset apply anthropic --json
ocx models selected anthropic --json
ocx models new-policy --provider anthropic --json
ocx models new-policy off --provider anthropic --json
ocx models new-policy --provider anthropic --json
ocx models new-arrivals --json

The writes require intent to change selection or discovery policy. Read back the selection and policy; a saved preset fallback can leave the previous selection intact. preset apply <provider> --all is the supported all-models mode; the dashboard’s disabled custom preset does not add another selectable mode.

Use ocx provider add mine --adapter openai-chat --base-url https://example.com/v1 --default-model model-a --text-only when registering a provider, or ocx provider edit mine --model model-a --text-only for an existing provider. Add can use --model or its default model; edit requires --model. The flag updates only that exact model’s modelCapabilities.inputModalities to ["text"], preserving other models and axes.

Automatic OpenAI account exhaustion checks distinguish purchased usage credits from reset tickets. Spending remains off by default: enable Use credits for that account (stored in creditCodexAccountIds). Only that opt-in together with a fresh positive spendable balance or unlimited credits can keep an account eligible after included usage reaches 100%; an upstream refusal or overage limit still blocks that evidence. Credit evidence expires after five minutes, and usage headers do not renew its clock. pause-exhausted uses this rule. Reset tickets alone never grant automatic headroom, and a credits-only response cannot clear a request cooldown. The default-on main-account hard lock remains a separate local policy and is not lifted by the credits switch.

ocx account history openai <pool-account-id> [--limit 1-200] [--json] reads stored observations without contacting the provider. The output separates actual observation time, WHAM or response-header source, window family and usage percentage. At most 200 observations per account are retained for 30 days, with global storage bounds.

Ordinary token refresh preserves history. Reauthentication, removal or account replacement retires the old publication. Native main and probes performed before a login is published are not included. Missing history means insufficient observations, not zero usage. This command does not spend quota. Effective estimates, when supported by observations, carry the limitations below.

The history output also includes effective reported-token estimates when same-window observations and attributable usage support them. Each estimate includes a sample count and low confidence. Quota rounding, external usage and assumed log-label continuity limit the inference; it is not your provider’s token allowance. Missing or truncated ledger evidence returns insufficient evidence. --limit controls displayed history, not the bounded estimate input.

Read the saved Anthropic OAuth model routes, replace them from a local JSON array, or clear them:

Terminal window
ocx account routes anthropic --json
ocx account routes anthropic --file routes.json
ocx account routes anthropic --clear

The file is limited to 64 KiB. The server validates each route and stores it under anthropicAccountPool.routes; writes require the running proxy. Use stored account IDs from ocx account list anthropic --json. Rules only affect the enabled pool and never claim that an account is entitled to a model. Request logs identify a matched rule as route:#<n> (1-based list position), without its operator name.

Vision and web-search helpers match their own model independently and can fail locally when their strict route has no eligible account. See Anthropic helper account routing.