Files
openclaude/docs/advanced-setup.md
T
0xfandomandGitHub db01038d5c feat(model-picker): surface inactive provider profiles in /model (#1119 piece 2) (#1164)
* feat(model-picker): surface inactive provider profiles in /model

When a user configures multiple providerProfiles (Kimi + Z.AI + OpenRouter
+ SambaNova in the #1119 repro, but the pattern fits any multi-provider
setup), switching the main session between them currently requires
round-tripping through /provider — /model only shows the active
profile's models.

Make /model the single switcher:

- ModelOption gains an optional `switchToProfileId`. Existing options
  leave it unset and behave exactly as today.
- `getInactiveProviderProfileOptions` enumerates every configured
  profile that isn't the active one and emits a picker entry per model,
  labelled `<model> · <profile.name>` so the user can see the choice
  changes providers, not just models.
- Each option's `value` is encoded with `__switch_profile__:<id>:<model>`
  so the picker's plain-string `value` channel stays the source of truth
  and same-named models under different base URLs (`gpt-4o` on multiple
  OpenAI-compatible endpoints) stay disambiguated.
- /model's handleSelect detects the prefix, calls
  `setActiveProviderProfile` (same path /provider uses — applies env,
  persists active profile, refreshes startup file), then sets
  `mainLoopModel` to the bare model string.

Only surfaces inactive options when `CLAUDE_CODE_PROVIDER_PROFILE_ENV_APPLIED`
is set, so users who haven't opted into the multi-profile workflow at all
don't see the affordance.

Tests cover round-trip encoding (including OpenRouter-style colon-bearing
model strings), the active-filter, the multi-model explosion, and that
`getModelOptions()` 3P path includes the inactive options only when the
profile env is applied. Combined invocation with the rest of
`src/utils/model/` + `src/commands/model/` + `src/utils/providerProfiles.test.ts`
runs clean to guard against mock-leak (per the 2026-04-30 lesson —
spreads `import * as actual` for every `mock.module` factory).

Refs #1119

* fix(model-picker): run fast-mode cleanup on cross-profile switch

The new switch-profile branch returned before reaching the fast-mode
reconciliation, so a user with fastMode latched on Anthropic Opus could
switch to an OpenAI profile and silently keep fastMode on even though
the new model can't support it. Extract the cleanup into a pure helper
`reconcileFastModeForSwitch` and call it from both branches.

Refs #1119.

* fix(model-picker): decode cross-profile values before effort/display lookup

Inactive-profile entries encode the picker value as
`__switch_profile__:<profileId>:<model>`, but `resolveOptionModel`
forwarded the raw string straight to `parseUserSpecifiedModel`. For a
reasoning-capable cross-profile entry such as `gpt-5.4`,
`modelSupportsEffort()` then saw the prefixed string and reported
"Effort not supported", and `handleSelect` dropped the toggled effort
even when the underlying model accepts it.

Run `parseSwitchProfileValue` first; when it matches, hand the bare
target model to `parseUserSpecifiedModel` so effort capability,
default-effort lookup, and display-name resolution all key off the real
model id.

* fix(model-picker): include inactive profiles on local OpenAI-compatible scope

The inactive-profile compute lived after the
`getAdditionalModelOptionsCacheScope()?.startsWith('openai:')` early
return, so users with a local OpenAI-compatible profile active (Ollama,
lm-studio, any localhost endpoint) never saw the cross-profile switcher
in `/model`. They still had to round-trip through `/provider` to change
profile.

Hoist `profileEnvApplied`, the active-profile lookup, and
`getInactiveProviderProfileOptions(activeProfileId)` above the early
return, and append `inactiveProfileOptions` to the local-OpenAI branch
return value. Other branches (Claude.AI, MiMo, MiniMax, ant) were
already either irrelevant or have their own gating.

Test: new regression in modelOptions.crossProfile.test.ts pins
`getAdditionalModelOptionsCacheScope` to an `openai:` value and confirms
the inactive profile still surfaces with a parseable
`__switch_profile__` value.

* fix(model-picker): apply the allowlist to the decoded cross-profile model

filterModelOptionsByAllowlist evaluated cross-profile options by their encoded
__switch_profile__:<id>:<model> value, so an availableModels allowlist that
permits the bare target (e.g. glm-5.1) dropped every inactive-profile entry.
Check the allowlist against parseSwitchProfileValue(value)?.model ?? value, and
cover both the allowed and denied cases.

* fix(model-picker): only surface cross-profile switch options on the /model path

The inactive-profile entries come from the shared getModelOptions() list, but
only the /model command's onSelect decodes __switch_profile__ values and
activates the target profile. The prompt hotkey and Settings pickers wrote the
encoded value straight to mainLoopModel, sending an invalid model string.

Gate these options behind a new allowProfileSwitch prop that only the /model
command sets; inline pickers no longer surface an option they cannot honor.
Also apply the org allowlist to the decoded target model in the /model select
handler.

* test(model-picker): drop flaky cross-profile allowlist case

The decoded-allowlist assertion drove the org allowlist through the shared
session settings cache, which is racy across bun's single-process run and could
leak availableModels into sibling suites (the providerConfig cache-scope tests
went red in CI). The decode itself is a one-line guard already exercised by the
parseSwitchProfileValue round-trip coverage, so remove the unreliable case
rather than ship CI flake.

Also snapshot the real provider/auth modules before mocking so each harness
call rebuilds its mock from a clean base instead of a previous test's overrides
(bun live-repoints the imported namespace to the active mock).

* test(model-picker): stop cross-profile mocks leaking into provider suites

The cross-profile tests mock.module'd ../providerProfiles, ./providers,
../auth and ../../services/api/providerConfig per test. bun's mock.module is
process-wide and mock.restore() does not undo it, so these persisted into later
files — most damagingly the providerConfig mock, which replaced the module with
a single-function stub and stripped resolveProviderRequest /
getAdditionalModelOptionsCacheScope from providerConfig.local's suite (now
adjacent after the rebase onto #1706).

Install each mock once at module load, keep the full export surface, and gate
the overrides on module-level flags cleared in beforeEach/afterEach so the
persisted mocks are transparent passthroughs for every other suite. Same
pattern as the cross-spawn / install-surfaces leak fixes.

* fix(model): reconcile fast mode before activating the switched profile

In the cross-profile /model switch path, reconcileFastModeForSwitch ran after
setActiveProviderProfile. The reconciler gates on isFastModeEnabled(), which
reads the *active* provider — so once the target profile is activated it
reflects the new (fast-mode-less) provider and short-circuits to 'unchanged',
leaving fastMode latched on for a model that can't use it.

Compute the reconciliation before activating the profile, so it evaluates
against the source provider and correctly returns 'off' for an unsupported
target. Add a command-level regression test that drives handleSelect with a
__switch_profile__ value while setActiveProviderProfile flips the fast-mode
state, and asserts fastMode is set to false (it fails if the call order
regresses).

* fix(model): re-check fast mode after activating a switched profile

The pre-activation reconcile gates on the source provider, so its 'on' result
is stale when the target provider cannot run fast mode even though the target
model name passes the source-side support check (e.g. a third-party shim
exposing a claude-opus-* model). Re-evaluate isFastModeEnabled / supported /
available after setActiveProviderProfile and force fastMode off when it is no
longer genuinely supported. Add a command-level regression test for that path
and wrap the cross-profile test cleanup in try/finally so a failing assertion
still unmounts the Ink instance (jatmn review, #1119).

* test(model-picker): cover cross-profile allowlist with isolated settings

Re-add the regression dropped in 06a0c80: filterModelOptionsByAllowlist must
evaluate the allowlist against the decoded target model, not the encoded
__switch_profile__ wrapper. Uses this suite's per-test settings cache (reset in
afterEach) instead of the shared cache that made the earlier version flaky
(jatmn review, #1119).

* test(model-picker): make the cross-profile allowlist test leak-proof

The new allowlist test drove availableModels through setSessionSettingsCache,
but sibling suites (ModelPicker, ProviderManager, ...) mock.module both
settings.js (getSettings_DEPRECATED) and modelAllowlist.js (isModelAllowed)
process-wide, so in the full sequential run the leaked stubs defeated the cache
and the denied option was not filtered (smoke-and-tests red on the full suite,
green in isolation).

Drive the allowlist deterministically from this suite instead: install-once,
gated, passthrough mocks of getSettings_DEPRECATED (the filter gate) and
isModelAllowed (the per-option check), both keyed off a single
activeSettingsOverride and cleared in afterEach. Same gated-passthrough pattern
as the suite's existing providerConfig/providers/auth/profiles mocks and the
agent.test.ts allowlist approach.

* fix(model): keep cross-profile switch options out of the SDK models list

getModelOptions() now returns inactive-profile entries encoded as
__switch_profile__:<id>:<model>. print.ts mapped those straight into the
ModelInfo list returned to SDK/web callers, exposing UI-only values that
are not selectable model ids. Filter them with parseSwitchProfileValue
before building modelInfos. Add ModelPicker coverage for the
allowProfileSwitch filter (hidden inline, shown when allowed) and
document cross-profile /model switching in the provider-profile docs.

* test(model-picker): prove cross-profile switch options never reach SDK models

Extract selectSdkModelOptions as the single gate the SDK modelInfos
builder runs every getModelOptions() entry through, and cover it directly:
an encoded __switch_profile__:<id>:<model> option is dropped while real
model ids pass through. Fails if an inactive-profile affordance ever leaks
into the initialize.models response again (#1119).

* docs(model-picker): clarify the env gate for inactive-profile entries

The inactive-profile models only appear when the provider-profile env
workflow is active (CLAUDE_CODE_PROVIDER_PROFILE_ENV_APPLIED=1), not for
every multi-profile setup. Spell that out and restore the local-only
`--provider ollama` guidance that was folded into the paragraph.

* fix(model-picker): gate SDK option filter on switchToProfileId marker

selectSdkModelOptions filtered on the encoded __switch_profile__ value
prefix, which also reserved that prefix for every custom model id. A real
configured model whose id starts with __switch_profile__: would vanish
from the SDK models response and non-switching pickers. Key the gate on
the explicit switchToProfileId marker, which only synthesized switch
options carry, and add the collision regression.

Refs #1119

* fix(model-picker): reuse switch confirmation for cross-profile selections

The cross-profile branch built its own "Switched to" message and returned
before the regular path appended the selected effort and the
"Billed as extra usage" notice, hiding cost-impacting feedback when a
reasoning/extra-usage target was chosen through an inactive profile.
Append effort and the extra-usage check to the switch confirmation.

Refs #1119

* fix(model-picker): surface inactive profiles on the active Ollama path

The isOllamaProvider() early return ran before the inactive-profile
options were computed, so an active local Ollama profile saw only its own
models and lost the cross-profile switcher, forcing the /provider
round-trip this feature removes. Hoist the inactive-profile compute above
the Ollama branch and append it to the Ollama returns.

Refs #1119

* fix(model): surface inactive profiles on all provider branches; decode only real switch options

Two follow-ups to the #1119 unified /model switcher:

- inactiveProfileOptions was computed before the early-return branches but only
  appended on Ollama / local-scope / PAYG paths. The GitHub Copilot, NVIDIA NIM,
  MiniMax, Xiaomi MiMo, ant, and Claude-subscriber branches returned first, so a
  user with a saved profile active on any of those routes lost the cross-profile
  entries and had to round-trip through /provider. Append the (env-gated, so
  empty unless a profile is applied) inactive options on those branches too.

- filterModelOptionsByAllowlist decoded any value starting with
  `__switch_profile__:` via parseSwitchProfileValue, even a normal custom model
  id that merely shares that prefix, evaluating the allowlist against the wrong
  inner model. Gate the decode on the `switchToProfileId` marker (the type's
  documented contract) so non-switch ids are checked verbatim.

Extends the cross-profile harness with gated getAPIProvider / NVIDIA / subscriber
overrides and adds branch-append + verbatim-allowlist regressions (red-green).

* fix(model): key profile-switch handling on the marker across picker and command

The allowlist/SDK paths already used the switchToProfileId marker, but two
surfaces still keyed on the raw `__switch_profile__:` value prefix:

- ModelPicker's inline-picker filter hid any option whose value started with
  the prefix, so a real custom model id like `__switch_profile__:vendor:gpt-5.4`
  disappeared from prompt/settings pickers. It now filters on
  `switchToProfileId === undefined`.
- the /model command decoded parseSwitchProfileValue(model) for any prefixed
  string and tried to activate the encoded profile id, so selecting such a
  custom model activated a nonexistent profile instead of setting the literal
  model. It now only treats the value as a switch when the decoded profile id
  maps to a real configured provider profile — which every synthesized switch
  option does, and a prefix-colliding custom id does not.

Drops the now-unused SWITCH_PROFILE_VALUE_PREFIX import from ModelPicker. Adds a
picker regression (marked switch hidden, prefixed custom model stays visible) and
completes the cross-profile branch coverage (MiniMax, Xiaomi MiMo, ant) so every
branch that appends inactive-profile options is locked.

* test(model): register target profiles in cross-profile switch tests

The /model command now only treats a `__switch_profile__:` value as a switch
when its decoded profile id maps to a real configured provider profile. The
cross-profile switch tests set up setActiveProviderProfile but left the shared
getProviderProfiles mock empty, so the new guard classified their switch values
as literal models and the fast-mode / effort / extra-usage assertions no longer
ran. Register each test's target profile via getProviderProfiles so the switch
path executes as intended.

* fix(model): gate cross-profile switches on the selected option marker

Selecting a value that merely parses as `__switch_profile__:<profileId>:<model>`
activated the provider whenever <profileId> existed, so a literal custom model
id such as `__switch_profile__:profile_openai:gpt-5-mini` wrongly switched the
active provider instead of being applied verbatim.

Thread the picked option's `switchToProfileId` marker from ModelPicker.onSelect
(selectOptions already carries it) and only activate a profile when the marker
matches the decoded id. The effort/display resolver had the same gap — it
decoded every prefixed value; gate it on a genuine marker-backed switch option
too. Add a regression asserting a marker-less prefixed id is applied literally.

* test(model): cover Max/Team Premium and empty-catalog switch-append paths

The cross-profile branch-coverage suite exercised the populated-catalog returns
but not the Max/Team Premium subscriber early return nor the empty-catalog
fallbacks (NVIDIA/MiniMax/Xiaomi), which are the same paths that previously
dropped the inactive-profile switch options. Lock them so every changed return
that appends `...inactiveProfileOptions` is covered.

* fix(model): keep inactive-profile switch options in /model discovery overrides

The interactive /model command passes an optionsOverride into ModelPicker for
descriptor-backed and legacy OpenAI-compatible discovery contexts, built from
mergeActiveProfileModelOptions which only merges the ACTIVE profile's route
models. Because the picker renders optionsOverride ?? getModelOptions(), the
inactive-profile switch entries getModelOptions() appends never reached those
paths, so the unified switcher vanished for provider-profile routes
(OpenRouter/Kimi/MiniMax, refreshed local profiles). Re-append the same
inactive-profile switch options (allowlist-filtered on the decoded target) to
any override list before handing it to the picker.

* fix(model): base the switch marker on the presented option, treat ties as ambiguous

The picker derived switchToProfileId with selectOptions.find(value===...), and
the effort/display resolver decoded when any getModelOptions() entry with the
same value carried the marker. If a literal custom model id collided with an
encoded switch value, the literal could borrow a different same-value option's
marker and wrongly activate a provider. Add resolveSelectedSwitchProfileId,
which keys on the actual presented option and treats duplicate-value matches as
ambiguous (no switch), and route both the onSelect marker and the decode
decision through it.
2026-07-07 21:53:29 +08:00

25 KiB

OpenClaude Advanced Setup

This guide is for users who want source builds, Bun workflows, provider profiles, diagnostics, or more control over runtime behavior.

Install Options

OpenClaude requires Node.js >=22.0.0 for npm installs and runtime. Bun is only required when building or running from source.

Option A: npm

npm install -g @gitlawb/openclaude@latest

Option B: From source with Bun

Use Bun 1.3.13 or newer for source builds. Older Bun versions can fail during bun run build.

git clone https://github.com/Gitlawb/openclaude.git
cd openclaude

bun install
bun run build
npm link

Option C: Run directly with Bun

git clone https://github.com/Gitlawb/openclaude.git
cd openclaude

bun install
bun run dev

Provider Examples

OpenAI

export CLAUDE_CODE_USE_OPENAI=1
export OPENAI_API_KEY=sk-...
export OPENAI_MODEL=gpt-4o

Codex via ChatGPT auth

codexplan maps to GPT-5.5 on the Codex backend with high reasoning. codexspark maps to GPT-5.3 Codex Spark for faster loops.

If you use the in-app provider wizard, choose Codex OAuth to open ChatGPT sign-in in your browser and let OpenClaude store Codex credentials securely.

If you already use the Codex CLI, OpenClaude reads ~/.codex/auth.json automatically. You can also point it elsewhere with CODEX_AUTH_JSON_PATH or override the token directly with CODEX_API_KEY.

If you set CODEX_API_KEY manually and are not relying on auth.json or stored Codex OAuth credentials, also set CHATGPT_ACCOUNT_ID (or CODEX_ACCOUNT_ID).

export CLAUDE_CODE_USE_OPENAI=1
export OPENAI_MODEL=codexplan

# optional if you do not already have ~/.codex/auth.json
export CODEX_API_KEY=...
export CHATGPT_ACCOUNT_ID=...

openclaude

DeepSeek

export CLAUDE_CODE_USE_OPENAI=1
export OPENAI_API_KEY=sk-...
export OPENAI_BASE_URL=https://api.deepseek.com/v1
export OPENAI_MODEL=deepseek-v4-flash

Use deepseek-v4-pro when you want the stronger model. deepseek-chat and deepseek-reasoner remain available as DeepSeek's legacy API aliases.

Google Gemini

export CLAUDE_CODE_USE_GEMINI=1
export GEMINI_API_KEY=...
export GEMINI_MODEL=gemini-3-flash-preview

Claude on Vertex AI

The Vertex route uses Anthropic's Claude-on-Vertex API. It is not a general Vertex AI Model Garden adapter for Gemini or arbitrary partner models; use the Gemini provider for Gemini models and OpenAI-compatible routes for compatible third-party gateways.

Authentication uses Google Application Default Credentials through google-auth-library. There is no OPENAI_API_KEY-style API key for this route. For global npm installs, install the auth package on demand (it is not bundled by default — see Optional provider packages):

npm i -g google-auth-library

Authenticate with either local Application Default Credentials (ADC) or a service-account key file:

# Option 1 — local ADC (interactive, uses your own Google account):
gcloud auth application-default login

# Option 2 — service-account key file (headless / CI):
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json

Minimal setup:

export CLAUDE_CODE_USE_VERTEX=1
export ANTHROPIC_VERTEX_PROJECT_ID=my-gcp-project
export GOOGLE_CLOUD_PROJECT=my-gcp-project
export CLOUD_ML_REGION=us-east5

openclaude --model claude-sonnet-4-6

CLOUD_ML_REGION is optional and defaults to us-east5. Model-specific Vertex region override variables are also supported for Claude models; see src/utils/envUtils.ts for the current override names.

Gemini via OpenRouter

export CLAUDE_CODE_USE_OPENAI=1
export OPENAI_API_KEY=sk-or-...
export OPENAI_BASE_URL=https://openrouter.ai/api/v1
export OPENAI_MODEL=google/gemini-2.5-pro

OpenRouter model availability changes over time. If a model stops working, try another current OpenRouter model before assuming the integration is broken.

Ollama

ollama pull llama3.3:70b

export CLAUDE_CODE_USE_OPENAI=1
export OPENAI_BASE_URL=http://localhost:11434/v1
export OPENAI_MODEL=llama3.3:70b

Ollama Context Length

OpenClaude sends the current conversation history to Ollama on each turn and uses Ollama's native chat API for Ollama endpoints. Native chat lets OpenClaude send options.num_ctx with each request, so Ollama receives a 32768-token context window by default instead of falling back to the smaller context often used by Ollama's OpenAI-compatible /v1/chat/completions shim.

To choose a different request-level context size, set OPENCLAUDE_OLLAMA_NUM_CTX before launching OpenClaude:

export OPENCLAUDE_OLLAMA_NUM_CTX=65536

You can also start Ollama with a global context length:

macOS / Linux:

# Stop any existing Ollama app/server first, then run:
OLLAMA_CONTEXT_LENGTH=32768 ollama serve

Windows PowerShell:

# Quit any existing Ollama app/server first, then run:
$env:OLLAMA_CONTEXT_LENGTH="32768"
ollama serve

After a chat request, verify the loaded model is using the requested context:

ollama ps

Check the CONTEXT column. If it still shows a small value such as 4K after a new OpenClaude request, stop the existing Ollama app/server, start it again, and retry the request.

Use a concrete recall test after changing the setting, such as asking the model to repeat the first topic from the current chat. Questions like "do you remember our conversation?" can trigger generic local-model disclaimers even when history is present.

Atomic Chat (local, Apple Silicon)

export CLAUDE_CODE_USE_OPENAI=1
export OPENAI_BASE_URL=http://127.0.0.1:1337/v1
export OPENAI_MODEL=your-model-name

No API key is needed for Atomic Chat local models.

Or use the profile launcher:

bun run dev:atomic-chat

Download Atomic Chat from atomic.chat. The app must be running with a model loaded before launching.

LM Studio

export CLAUDE_CODE_USE_OPENAI=1
export OPENAI_BASE_URL=http://localhost:1234/v1
export OPENAI_MODEL=your-model-name

Together AI

export CLAUDE_CODE_USE_OPENAI=1
export OPENAI_API_KEY=...
export OPENAI_BASE_URL=https://api.together.xyz/v1
export OPENAI_MODEL=meta-llama/Llama-3.3-70B-Instruct-Turbo

Groq

export CLAUDE_CODE_USE_OPENAI=1
export GROQ_API_KEY=gsk_...
export OPENAI_BASE_URL=https://api.groq.com/openai/v1
export OPENAI_MODEL=llama-3.3-70b-versatile

GROQ_API_KEY matches the built-in Groq gateway preset. OPENAI_API_KEY also works as a fallback on the generic OpenAI-compatible path, but GROQ_API_KEY is the preferred variable for Groq-specific setup.

OpenCode Zen (pay-as-you-go)

export CLAUDE_CODE_USE_OPENAI=1
export OPENCODE_API_KEY=...
export OPENAI_BASE_URL=https://opencode.ai/zen/v1
export OPENAI_MODEL=gpt-5.4

openclaude

OpenCode Zen is a pay-as-you-go AI gateway with 48 models (GPT, Claude, Gemini, Qwen, MiniMax, GLM, Kimi, Grok, Big Pickle, DeepSeek, Nemotron). Uses the same OPENCODE_API_KEY as OpenCode Go. Get your key from https://opencode.ai.

OpenCode Go (subscription)

export CLAUDE_CODE_USE_OPENAI=1
export OPENCODE_API_KEY=...
export OPENAI_BASE_URL=https://opencode.ai/zen/go/v1
export OPENAI_MODEL=glm-5.1

openclaude

OpenCode Go is a $10/mo subscription for 13 open models (GLM, Kimi, DeepSeek, MiMo, MiniMax, Qwen). Uses the same OPENCODE_API_KEY as OpenCode Zen.

Gitlawb Opengateway

export CLAUDE_CODE_USE_OPENAI=1
export OPENAI_BASE_URL=https://opengateway.gitlawb.com/v1
export OPENGATEWAY_API_KEY=ogw_live_...
export OPENAI_MODEL=mimo-v2.5-pro

The Opengateway route is the fresh-install startup default and requires an API key from https://gitlawb.com/opengateway/keys. Keep the base URL at /v1 and switch models with /model or OPENAI_MODEL. Current partner models include:

  • mimo-v2.5-pro
  • google/gemini-3.1-flash-lite-preview

Xiaomi MiMo

export CLAUDE_CODE_USE_OPENAI=1
export MIMO_API_KEY=...
export OPENAI_BASE_URL=https://api.xiaomimimo.com/v1
export OPENAI_MODEL=mimo-v2.5-pro

The /provider Xiaomi MiMo preset uses the same endpoint and stores the key as MIMO_API_KEY. OPENAI_API_KEY also works as a compatibility fallback, but MIMO_API_KEY keeps the profile tied to the MiMo route.

NEAR AI

export CLAUDE_CODE_USE_OPENAI=1
export NEARAI_API_KEY=...
export OPENAI_BASE_URL=https://cloud-api.near.ai/v1
export OPENAI_MODEL=anthropic/claude-sonnet-4-6

openclaude

NEAR AI is a unified OpenAI-compatible gateway that proxies Anthropic, OpenAI, and Google models alongside TEE-hosted open models (GLM 5.1, Qwen3.5, Kimi K2.6). All models are accessible from a single endpoint with one API key. Get your key from https://cloud.near.ai/dashboard/organizations.

Model IDs use provider/model-name format (e.g. anthropic/claude-opus-4-7, openai/gpt-5.5, google/gemini-3.5-flash, zai-org/GLM-5.1-FP8).

For direct TEE completions (lower latency, verifiable privacy):

export OPENAI_BASE_URL=https://qwen35-122b.completions.near.ai/v1

Mistral

export CLAUDE_CODE_USE_MISTRAL=1
export MISTRAL_API_KEY=...
export MISTRAL_MODEL=devstral-latest

Azure OpenAI

export CLAUDE_CODE_USE_OPENAI=1
export OPENAI_API_KEY=your-azure-key
export OPENAI_BASE_URL=https://your-resource.openai.azure.com/openai/deployments/your-deployment/v1
export OPENAI_MODEL=gpt-4o

Microsoft Foundry / Azure OpenAI (resource URL + deployment)

When your endpoint is the resource base URL (not the full .../deployments/.../v1 path), set OPENAI_MODEL to the deployment name and AZURE_OPENAI_API_VERSION to your API version. The OpenAI shim builds:

{base}/openai/deployments/{OPENAI_MODEL}/chat/completions?api-version={AZURE_OPENAI_API_VERSION}

and sends the key in the api-key header for Azure hosts.

export CLAUDE_CODE_USE_OPENAI=1
export OPENAI_API_KEY=your-azure-key
export OPENAI_BASE_URL=https://your-resource.openai.azure.com
export OPENAI_MODEL=your-deployment-name
export AZURE_OPENAI_API_VERSION=2024-12-01-preview

If your hostname is not detected as Azure (for example some inference endpoints), force Azure URL and header behavior:

export OPENAI_AZURE_STYLE=1

Fireworks AI

Fireworks AI provides a fully OpenAI-compatible endpoint. Model IDs use the full path format accounts/fireworks/models/<model-name>.

export CLAUDE_CODE_USE_OPENAI=1
export FIREWORKS_API_KEY=fw_your_key_here
export OPENAI_BASE_URL=https://api.fireworks.ai/inference/v1
export OPENAI_MODEL=accounts/fireworks/models/llama-v3p1-70b-instruct

The OpenClaude VS Code extension can store the key in Secret Storage and set these variables for you when you launch from the Control Center. See vscode-extension/openclaude-vscode/README.md.

Optional provider packages

To keep the default npm i -g @gitlawb/openclaude install small and warning-free, a few provider SDKs and the native image library are not bundled. They are loaded on demand, and the CLI prints an npm install <pkg> hint (add -g for the global CLI) if you enable a feature whose package is missing. Install only what you need:

Feature Trigger Install
AWS Bedrock CLAUDE_CODE_USE_BEDROCK=1 npm i -g @anthropic-ai/bedrock-sdk. Profile-based auth (~/.aws/credentials) additionally needs @aws-sdk/credential-providers and @aws-sdk/client-sts; model listing needs @aws-sdk/client-bedrock. Proxy and skip-auth setups may also need @aws-sdk/credential-provider-node, @smithy/node-http-handler, or @smithy/core. The CLI prints the exact missing package if you hit one.
Azure Foundry CLAUDE_CODE_USE_FOUNDRY=1 npm i -g @anthropic-ai/foundry-sdk @azure/identity
Claude on Vertex AI / Gemini ADC CLAUDE_CODE_USE_VERTEX=1 / Gemini ADC auth npm i -g google-auth-library
Reading/processing images reading an image file npm i -g sharp

When installing OpenClaude from source (bun install), all of these are already present as dev dependencies, so source/dev builds need no extra steps.

Environment Variables

Variable Required Description
CLAUDE_CODE_USE_OPENAI OpenAI-compatible only Set to 1 to enable the OpenAI-compatible provider path
OPENAI_API_KEYS One of OPENAI_API_KEYS or OPENAI_API_KEY for non-local OpenAI-compatible cloud routes* Comma-separated OpenAI-compatible API key pool. Takes precedence over OPENAI_API_KEY and rotates to the next key on auth, quota, or rate-limit failures (* not needed for local models like Ollama, LM Studio, Atomic Chat, or other local OpenAI-compatible proxies).
OPENAI_API_KEY Required only when OPENAI_API_KEYS is unset or empty for non-local OpenAI-compatible cloud routes* Your API key (* not needed for local models like Ollama, LM Studio, Atomic Chat, or other local OpenAI-compatible proxies). A comma-separated list also enables key rotation.
OPENAI_MODEL OpenAI-compatible only Model name such as gpt-4o, deepseek-v4-flash, or llama3.3:70b
OPENAI_BASE_URL No API endpoint, defaulting to https://api.openai.com/v1
OPENAI_API_BASE No Compatibility alias for OPENAI_BASE_URL
OPENCLAUDE_OLLAMA_NUM_CTX Ollama only Request-level Ollama context window. Defaults to 32768; set a larger value for longer same-session history if your model and hardware can handle it.
CLAUDE_CODE_OPENAI_CONTEXT_WINDOWS No JSON map of OpenAI-compatible model names to context windows, such as {"custom-model":1000000}. Use this when a custom provider does not expose context metadata from /v1/models.
OPENCODE_API_KEY OpenCode Zen / Go Shared API key for OpenCode Zen (pay-as-you-go) and OpenCode Go (subscription); get yours from https://opencode.ai
MIMO_API_KEY Xiaomi MiMo route Xiaomi MiMo API key for https://api.xiaomimimo.com/v1; mirrored into the OpenAI-compatible auth env when the MiMo route is active
CLAUDE_CODE_USE_GEMINI Gemini only Set to 1 to enable the direct Gemini provider path
GEMINI_API_KEY / GOOGLE_API_KEY Gemini API-key auth Gemini API key for direct Gemini setup
GEMINI_MODEL Gemini only Model name such as gemini-3-flash-preview or gemini-2.5-pro
GEMINI_BASE_URL No Override the Gemini base URL
CLAUDE_CODE_USE_MISTRAL Mistral only Set to 1 to enable the dedicated Mistral provider path
MISTRAL_API_KEY Mistral only Mistral API key
MISTRAL_MODEL Mistral only Model name such as devstral-latest
MISTRAL_BASE_URL No Override the Mistral base URL
CODEX_API_KEY Codex only Codex or ChatGPT access token override
CHATGPT_ACCOUNT_ID / CODEX_ACCOUNT_ID Codex only Required for manual Codex env setup when the account id is not coming from auth.json or stored OAuth credentials
CODEX_AUTH_JSON_PATH Codex only Path to a Codex CLI auth.json file
CODEX_HOME Codex only Alternative Codex home directory
OPENCLAUDE_MAX_RETRIES No Maximum retry attempts for retryable API failures, capped at 100 (default: 10). Set to 0 to disable retries after the initial request. If unset, deprecated CLAUDE_CODE_MAX_RETRIES is still honored for compatibility.
OPENCLAUDE_RETRY_DELAY_MS No Base retry delay in milliseconds for APIs that do not send Retry-After; exponential backoff starts from this value, capped at 60000 (default: 500)
OPENCLAUDE_QUERY_HARD_MAX_MS No Foreground query hard maximum in milliseconds. Defaults to 1800000 (30 minutes). Use a larger positive integer for long autonomous sessions; invalid, zero, negative, fractional, or timer-overflow values are ignored with a warning.
OPENCLAUDE_DISABLE_CO_AUTHORED_BY No Suppress the default Co-Authored-By trailer in generated git commits
OPENCLAUDE_LOG_TOKEN_USAGE No When truthy (e.g. verbose), emits one JSON line on stderr per API request with input/output/cache tokens and the resolved provider. User-facing debug output — complements the REPL display controlled by /config showCacheStats. Distinct from CLAUDE_CODE_ENABLE_TOKEN_USAGE_ATTACHMENT, which is model-facing (injects context usage info into the prompt itself). Both can run together.

Model env vars are provider-scoped: first-party Anthropic sessions read ANTHROPIC_MODEL, OpenAI-compatible sessions read OPENAI_MODEL, Gemini reads GEMINI_MODEL, and Mistral reads MISTRAL_MODEL. For manual Bedrock, Vertex, or Foundry launches, select the model with --model.

Runtime Hardening

Use these commands to validate your setup and catch mistakes early:

# quick startup sanity check
bun run smoke

# validate provider env + reachability
bun run doctor:runtime

# print machine-readable runtime diagnostics
bun run doctor:runtime:json

# persist a diagnostics report to reports/doctor-runtime.json
bun run doctor:report

# print a redacted public issue report
openclaude doctor report --markdown

# write a redacted JSON issue report for attachment
openclaude doctor report --json --out openclaude-report.json

# write a deterministic task report from a session transcript
openclaude report --json --transcript ~/.openclaude/projects/-path-to-project/session-id.jsonl --out task-report.json

# print a human-readable task report from the latest session in the current project
openclaude report --markdown

# full local hardening check (smoke + runtime doctor)
bun run hardening:check

# strict hardening (includes project-wide typecheck)
bun run hardening:strict

Notes:

  • doctor:runtime fails fast if CLAUDE_CODE_USE_OPENAI=1 with a placeholder key or a missing key for non-local providers.
  • doctor:runtime also validates the dedicated Gemini and Mistral env paths when CLAUDE_CODE_USE_GEMINI=1 or CLAUDE_CODE_USE_MISTRAL=1.
  • Local providers such as http://localhost:11434/v1, http://10.0.0.1:11434/v1, and http://127.0.0.1:1337/v1 can run without OPENAI_API_KEY.
  • Codex profiles validate CODEX_API_KEY or the Codex CLI auth file and probe POST /responses instead of GET /models.
  • openclaude doctor report is redacted by default and is intended for GitHub issues. It summarizes provider/runtime/build/settings state without prompts, transcripts, raw settings files, API keys, MCP command details, or full home-directory paths.
  • openclaude report --json and openclaude report --markdown summarize observed session facts such as tool uses, Bash commands, validation commands, changed files, branch metadata, warnings, and linked issue/PR references. Use --transcript <file> for an explicit transcript, --session <id> for a stored session, or omit both to report the latest session for the current project. Large previews are truncated and credential-shaped strings are redacted. When no validation command is observed, the report keeps validations empty and includes a warning instead of claiming checks passed.

Provider Launch Profiles

Use profile launchers to avoid repeated environment setup:

# one-time profile bootstrap (prefer viable local Ollama, otherwise OpenAI)
bun run profile:init

# preview the best provider/model for your goal
bun run profile:recommend -- --goal coding --benchmark

# auto-apply the best available local/openai provider/model for your goal
bun run profile:auto -- --goal latency

# codex bootstrap (defaults to codexplan and ~/.codex/auth.json)
bun run profile:codex

# openai bootstrap with explicit key
bun run profile:init -- --provider openai --api-key sk-...

# gemini bootstrap with explicit key
bun run profile:init -- --provider gemini --api-key ...

# ollama bootstrap with custom model
bun run profile:init -- --provider ollama --model llama3.1:8b

# ollama bootstrap with intelligent model auto-selection
bun run profile:init -- --provider ollama --goal coding

# atomic-chat bootstrap (auto-detects running model)
bun run profile:init -- --provider atomic-chat

# codex bootstrap with a fast model alias
bun run profile:init -- --provider codex --model codexspark

# launch using persisted user-level provider profile
bun run dev:profile

# codex profile (uses CODEX_API_KEY or ~/.codex/auth.json)
bun run dev:codex

# OpenAI profile (uses the saved OpenAI profile, or OPENAI_API_KEYS / OPENAI_API_KEY from your shell)
bun run dev:openai

# Gemini profile (uses the saved Gemini profile, or GEMINI_API_KEY / GOOGLE_API_KEY from your shell)
bun run dev:gemini

# Ollama profile (defaults: localhost:11434, llama3.1:8b)
bun run dev:ollama

# Atomic Chat profile (Apple Silicon local LLMs at 127.0.0.1:1337)
bun run dev:atomic-chat

profile:recommend ranks installed Ollama models for latency, balanced, or coding, and profile:auto can persist the recommendation directly.

If no profile exists yet, dev:profile uses the same goal-aware defaults when picking the initial model.

Provider Profile Model Picker Mode

When a saved provider profile is active, /model can either show the provider's catalog/discovered models or only the models explicitly listed in the profile. Configure this in ~/.openclaude.json:

{
  "providerProfileModelPickerMode": "auto"
}

Supported values:

  • auto (default): single-model profiles show the provider catalog; multi-model profiles show the explicit profile list; native vendor routes keep their full provider catalog.
  • provider: show the provider catalog/discovery list first and append profile-only custom model IDs.
  • profile: show only explicitly configured profile models.

When the provider-profile env workflow is active (i.e. a profile has been applied and CLAUDE_CODE_PROVIDER_PROFILE_ENV_APPLIED=1 is set — as it is after launching with a saved profile) and you have more than one saved provider profile, /model also lists models from your inactive profiles, grouped under their profile name. Selecting one activates that provider profile and switches to the chosen model in a single step, reconciling fast mode if the target provider cannot run it. These cross-profile entries appear only in the interactive /model picker — they are never returned to SDK/automation callers and are hidden from inline pickers (such as the prompt hotkey or Settings), which cannot switch the active profile. Simply having multiple profiles configured without the env workflow active does not surface them.

Use --provider ollama when you want a local-only path. Auto mode falls back to OpenAI when no viable local chat model is installed.

Use --provider atomic-chat when you want Atomic Chat as the local Apple Silicon provider.

Use profile:codex or --provider codex when you want the ChatGPT Codex backend.

dev:openai, dev:gemini, dev:ollama, dev:atomic-chat, and dev:codex run doctor:runtime first and only launch the app if checks pass.

For dev:ollama, make sure Ollama is running locally before launch.

For dev:atomic-chat, make sure Atomic Chat is running with a model loaded before launch.

Message-Count Compaction Threshold

By default, OpenClaude compacts conversations based on token usage and also applies a safety hard cap of 1000 active messages. The hard cap catches long sessions that accumulate many small messages with negligible token cost.

This hard cap is a safety net: it can still trigger compaction even when DISABLE_COMPACT, DISABLE_AUTO_COMPACT, or a disabled auto-compact setting would otherwise prevent it. Set OPENCLAUDE_MAX_ACTIVE_MESSAGES_HARD_CAP=0 only when you need to suppress that safety cap for diagnostics.

If you frequently resume long sessions that accumulate hundreds of small tool-result messages with negligible token cost, you can opt in to message-count compaction via the in-app /config command:

/config

Select Message-count compaction and choose a threshold (100, 200, 500, or 1000). Setting it to off (default) leaves only the built-in hard cap.

This setting is intended for power users debugging specific edge cases. Most users should leave it at off.

The legacy OPENCLAUDE_MAX_ACTIVE_MESSAGES environment variable is still honored when the setting is off. OPENCLAUDE_MAX_ACTIVE_MESSAGES_HARD_CAP can override the safety cap; set it to 0 only for diagnostics.

Long-session memory guard validation

For changes that touch auto-compact, provider request conversion, transcript retention, or in-process teammates, run the focused long-session guard checks:

bun test --feature=UNATTENDED_RETRY src/query/autoCompactCooldown.test.ts src/utils/maxActiveMessages.test.ts src/services/api/openaiShim.test.ts

These tests cover repeated over-cap turns, auto-compact cooldown blocking, teammate active-message compaction, malformed hard-cap overrides, and pruned-history tool-call/tool-result pairing. They are not a substitute for a multi-hour manual soak, but they pin the bounded-history and conversion invariants that previously let long sessions grow until Node/V8 OOM.