@hasna/switcher
v0.2.10
Published
Universal provider and model switcher for coding harnesses with an API, SDK and self-hosted storage
Readme
id: "switcher-readme" title: "Switcher" type: "package-documentation" owner: "codex-fixer" created_at: "2026-09-05T12:50:21.698672Z" updated_at: "2026-09-07T06:18:32.384233+00:00" status: "active" source_task: "01a07181-ca8d-70c1-99a2-b276dc5770f3"
Switcher
Launch Claude Code, Codex, Grok Build, OpenCode 2, legacy OpenCode, Kilo, Pi, OMP, DeepSeek Harness, Cline, Hermes, Prime Agent, Antigravity CLI, Junie, Gemini CLI or Aider with a chosen compatible provider and its model catalog. An authenticated API owns profiles and run metadata; the CLI starts the native harness on your computer. Import the same HTTP client from @hasna/switcher/sdk.
Requires Bun 1.3.14 or newer. Install the native harnesses separately.
Switcher never installs or upgrades a native harness. switcher doctor reports
the executable name, verified version requirement, upstream project and
installation guidance for every adapter. If a harness is installed outside
PATH, pass its trusted absolute executable path with --executable PATH.
| Harness | Executable and verified version | Install target | Official instructions |
| --- | --- | --- | --- |
| Claude Code | claude, >=2.1.257 | Claude Code official distribution | Quickstart |
| Codex CLI | codex, >=0.153.0 | OpenAI Codex official distribution | Project |
| Grok Build | grok, >=1.0.13 | xAI Grok Build official project | Project |
| OpenCode (legacy) | opencode, >=1.18.0 | opencode-ai | CLI guide |
| OpenCode 2 | opencode2, beta-19157 or newer (including stable >=2.0.0) | OpenCode 2 official distribution | v2 docs |
| Pi Coding Agent | pi, >=0.85.1 | @earendil-works/pi-coding-agent | Coding agent README |
| OMP (oh-my-pi) | omp, >=18.1.11 | @oh-my-pi/pi-coding-agent | Project |
| DeepSeek Harness | dsh, >=0.1.2-rc.1 | @deepseek-ai/dsh | CLI reference |
| Cline | cline, >=3.0.61 | cline | CLI project |
| Hermes Agent | hermes, >=0.21.0 | NousResearch Hermes Agent official installer | Quick install |
| Prime Agent | prime-agent, >=0.9.2 | PrimeIntellect versioned release artifact | Project |
| Antigravity CLI | agy, exactly 1.2.5 | Google official distribution | Installation |
| Junie CLI | junie, build 3196.5 | JetBrains official distribution | Documentation |
| Gemini CLI | gemini, exactly 0.58.0 | @google/gemini-cli | Project |
| Aider | aider, exactly 0.86.2 | aider-chat | Installation |
| Kilo Code | kilo, >=7.5.15 | @kilocode/cli | Release v7.5.15 |
For project distributions without a verified package command, follow the linked upstream instructions and pass the resulting executable explicitly. Launch rejects unsupported native versions before starting a coding session.
npm install -g @hasna/switcher
switcher --help
switcher doctorChatGPT desktop with a provider
From 0.1.9, use the installed macOS ChatGPT app with a selected provider and model:
switcher launch chatgpt --provider deepseek --model deepseek-flash
switcher launch chatgpt --provider openrouter --model anthropic/claude-sonnet-4.6
switcher launch chatgpt --provider my-provider --model my-model
switcher launch chatgpt --provider deepseek --model deepseek-flash --dry-run
switcher launch chatgpt --provider deepseek --model deepseek-flash --reasoning max \
--dangerously-bypass-approvals-and-sandboxThis runs local Codex conversations inside the unified ChatGPT app through
Switcher's provider gateway. ChatGPT cloud Chat/Work, Preview Edit and other
account-only features use their own service and are outside this routing path.
The classic ChatGPT app (com.openai.chat) cannot use this launcher. The current
unified ChatGPT app and its former Codex name (com.openai.codex) are supported;
the bundled Codex runtime must meet the Codex CLI minimum above.
Providers must support the Responses protocol and the selected model's tool
calling. DeepSeek, OpenRouter, OpenAI and other Responses-compatible presets
use the same provider/model catalog and credential bindings as terminal Codex.
Use switcher models PROVIDER to find exact model IDs. Custom providers can be
registered with switcher providers add NAME --url URL --protocol openai-responses.
There is no automatic conversion from Chat Completions or Anthropic Messages.
For third-party Responses endpoints, Switcher converts the desktop's unpaired
codex_app task-message outputs to ordinary user input, preserving their full
text and images. This supports creating a task and sending follow-up messages
without the missing-call_id errors from strict providers. Paired tool results
keep their original format; OpenAI's own endpoint receives native messages.
--reasoning EFFORT sets the initial effort (also supported for direct terminal
Codex launches). DeepSeek's picker exposes none, low, high and max; its Responses
API maps minimal to low and medium/xhigh to high. Other models can declare an
exact reasoningEfforts array through models add/update --file FILE. An explicit
effort also enables that choice for models whose catalog omits effort metadata;
the provider must support it. The app can change effort per conversation, and
routing events record the value actually sent upstream.
--dangerously-bypass-approvals-and-sandbox explicitly selects full access:
commands can edit files and use the network without approval prompts or a
sandbox. Without that flag, desktop launches start with workspace-write and
on-request approvals. These are startup defaults; the app's own permission
controls and managed requirements still apply. Start a new conversation after
changing launch defaults; existing conversations can retain their own settings.
Browser and Computer Use are local app tools, separate from the inference provider. Switcher preserves the app's sandbox-helper arguments so the installed tool runtime can start. Enable the app's browser/computer plugins and the browser extension in the desired profile; website permissions, macOS Accessibility/Screen Recording permissions, workspace policy and model eligibility still apply. Full access does not override those controls.
Models need function calling; screenshot workflows also need image input.
DeepSeek's Responses API supports both for deepseek-flash, but ignores OpenAI's
built-in computer_use tool. The desktop's local MCP tools are a separate path.
Successful inference does not establish browser or computer compatibility for
every provider. See the browser extension guide
and DeepSeek compatibility details.
Switcher starts a separate app instance with private login and Electron state.
That private overlay remains provider/profile-scoped. --share-native-state (or
an explicit shared-state marker below) projects only the allowlisted native corpus
into that overlay, so local conversations and instructions can remain available
without reusing another provider/account's cookies, settings or authentication.
The canonical Codex corpus is normally ~/.codex. Your regular ChatGPT app's
signed-in state is preserved; authentication and cookies are not copied. A second
launch of the same active provider profile is refused.
Keep the launching terminal running until you quit that instance: Switcher owns
its inference gateway and stops its own app process on interruption or timeout.
Upstream API keys remain in the Switcher gateway. Only a temporary loopback
credential reaches the child; its private auth file is removed after exit.
Switcher refuses to overwrite authentication added manually to a provider
profile. It does not modify or re-sign the installed app. Select a nonstandard
installation with --app-path /absolute/path/ChatGPT.app; native CLI arguments,
--executable and --backend are not accepted for desktop launches.
Shared native conversations
Sharing is opt-in. Pass --share-native-state, set
HASNA_CODEX_STATE_HOME/HASNA_CLAUDE_STATE_HOME, or set the compatible
SUBSCRIPTIONS_SHARED_HOME_CODEX/SUBSCRIPTIONS_SHARED_HOME_CLAUDE marker.
Without one of those signals, Switcher keeps its historical provider/account state
layout and creates no shared links. The command flag selects ~/.codex or
~/.claude; a custom canonical corpus must be named by a marker. Ambient
CODEX_HOME/CLAUDE_CONFIG_DIR is treated only as the current private overlay,
never silently promoted into the cross-account identity. If both marker forms are
present they must resolve to the same directory. Markers are consumed by Switcher
and are not forwarded to provider processes or nested launchers. Directories must
be absolute, owned and free of writable/symlink redirection. This contract is
currently supported on Linux and macOS; Windows refuses before creating state.
Codex overlays share sessions, archived sessions and thread-writer-locks. Native
skill directories are deliberately excluded: Skills-owned migration and hook
controls remain authoritative and account-synced skill copies must not enter a
cross-account corpus. The entire SQLite store uses the canonical
configuration's sqlite_home, or the canonical root; inherited account-specific
CODEX_SQLITE_HOME is ignored. There are no per-database or WAL symlinks.
session_index.jsonl stays an overlay-local native index; legacy index-only names
remain pending explicit migration. Authentication,
Electron cookies, .codex-global-state.json, plugin caches and worktree metadata
are not projected between homes. Claude's projects, todos, history and explicit CLAUDE.md/.hasna/instructions
policy roots use its common corpus. Commands, agents, rules, credentials, settings,
native skills and plugin state remain private. Skills continue through the
reviewed Skills bridge.
Both tools share only .hasna/instructions, never the rest of .hasna. Codex's
optional AGENTS.override.md takes native precedence over AGENTS.md; Switcher
does not create empty versions or dangling links. A later launch links an optional
file only after the canonical regular file exists and passes validation.
The desktop config projects only the canonical instructions,
developer_instructions, model_instructions_file, compact_prompt,
experimental_compact_prompt_file, the four
include_*_instructions/include_environment_context switches, and
project_doc_max_bytes/project_doc_fallback_filenames. Relative model and
compact instruction files resolve inside the canonical config directory and must be readable trusted
regular files; they cannot escape into authentication or unrelated state. Native file/config instruction precedence is preserved; routing
and private authentication settings are rendered separately. Unsupported legacy
profile selection refuses launch rather than silently losing its instructions.
Codex CLI normally reads these keys directly from its canonical home. When nested
inside a private authentication home, its audited instruction keys must match the
canonical projection, including resolved model and compact instruction file paths; missing
or stale keys visibly refuse launch. Switcher does not rewrite that account's
configuration or place instruction text in process arguments. Refresh the account
overlay's instruction projection before retrying a conflicting nested launch.
The desktop adapter requests native thread/list with all providers and filesystem
read-repair, then starts, resumes or forks with the current launch provider and model.
Shared state requires Codex 0.154.0 or newer and the directly verified adapter;
Ori shared-state/resume support remains refused until separately accepted.
It disables the supported provider-model fallback on thread/start, preserves
native permissions, and does not rewrite saved transcripts or tool-call IDs. For CLI resume:
switcher launch codex --provider PROVIDER --model MODEL --share-native-state -- resume
switcher launch codex --provider PROVIDER --model MODEL --share-native-state -- resume --last
switcher launch codex --provider PROVIDER --model MODEL --share-native-state -- resume --all
switcher launch codex --provider PROVIDER --model MODEL --share-native-state -- resume SESSION_IDThe first three forms use Switcher's cross-provider native catalog discovery,
then execute native Codex with the exact chosen ID. Shared-state consent also
means the selected conversation history will be sent through the provider/model
chosen for this launch; native metadata does not reliably expose an original
provider/account label, so Switcher does not claim provenance it cannot verify.
--all includes other
workspaces; normal discovery keeps the current workspace filter. Explicit IDs
pass directly to native Codex. Discovery sends no inference prompt. Native
Codex's own picker outside these Switcher forms can still filter by provider.
Existing nonempty legacy overlay directories are preserved and are never
silently replaced with links; conflicting state requires an explicit migration.
Normal launch reports legacy desktop data as pending migration. Inspect and stage an explicit noncredential snapshot with the local native-state command:
switcher state import codex --from /absolute/legacy/profile/codex
# Review planDigest from the dry-run, then bind the exact plan to the mutation:
switcher state import codex --from /absolute/legacy/profile/codex --entry sessions --apply --plan-digest SHA256
switcher state import claude --from /absolute/legacy/claude/profile --entry projects --apply --plan-digest SHA256The default is a read-only plan with a path-redacting planDigest. --apply
requires that exact digest, binding explicit mutation consent to the reviewed
source, destination, selection and file identities. It copies unique allowlisted files, skips byte-identical duplicates, and refuses
divergent collisions before starting the copy. Recognized native Codex session IDs are also checked across active/archived
rollouts: divergent versions with different filenames refuse import, while exact
duplicates are not published twice. --entry may be repeated to limit a snapshot.
Source files stay unchanged; symlinks, hardlinked aliases and
credential/config/SQLite/index/plugin-cache/native-skill entries cannot be imported.
All source files are checked again and staged before publishing any new copy.
No-replace publication never overwrites or deletes a racing writer. If a
collision appears after publication starts, already published reviewed files are
preserved and the command requires a new dry-run to reconcile the remainder. Imports are bounded
to 100,000 entries and 2 GiB per operation.
This is copy-only staging, not complete migration: the old home can still receive writes. Legacy SQLite metadata and index-only display names stay in the original, and the pending warning remains. Codex's supported catalog scan repairs copied transcript discovery; this does not prove every legacy desktop preference or title has migrated. Stop old writers and review their remaining state before retiring any old profile. Switcher does not retire or delete it automatically.
--dry-run validates app detection and provider/model discovery without opening
the app. The SDK offers local installation discovery through detectChatGPTApp();
provider/profile/catalog operations continue to use the existing HTTP API.
The opt-in test:native-chatgpt-runtime script exercises the installed app's
bundled runtime against a real provider using a fresh isolated profile. Set
SWITCHER_NATIVE_CHATGPT_PROVIDER and SWITCHER_NATIVE_CHATGPT_MODEL to run it.
It verifies selected-model configuration, direct responses, delegated task
creation, follow-up delivery, and history replay against the real provider.
It is separate from visual desktop acceptance.
test:native-chatgpt-tools checks the installed browser/computer tool kernel
through the generated launcher without provider calls or UI actions. Set
SWITCHER_TEST_CUA_CONFIG to the installed unified-computer-use plugin's
.mcp.json; the check uses disposable state and leaves that configuration intact.
References: OpenAI custom provider configuration, community desktop custom-model profiles, reported signed-in provider routing issue, and Preview Edit routing limitation, and DeepSeek thinking controls.
Claude desktop with a provider
Claude Code's official Chrome integration requires direct Anthropic sign-in; API-key and third-party-provider sessions cannot use it. Routing Claude desktop inference through Switcher does not remove that restriction. A separately configured, provider-compatible browser MCP server is another integration path. See Anthropic's Chrome prerequisites.
switcher launch claude-desktop --provider deepseek --model deepseek-flash
switcher launch claude-desktop --provider my-messages-provider --model vendor/model
switcher launch claude-desktop --provider deepseek --model deepseek-flash --dry-runRequires macOS and Claude desktop 1.52386.0 or newer. This uses the app's
supported third-party gateway mode and its own downloaded Claude Code engine;
a separately installed Claude Code CLI is not required. launch claude keeps
launching the terminal CLI. detectClaudeDesktopApp() is available in the SDK.
The provider must implement Anthropic Messages, including streaming and tool calling for the chosen model. DeepSeek and compatible gateway presets select the Messages endpoint automatically. Saved provider IDs retain their explicit protocol. OpenCode Zen and Go additionally use the Claude translation adapter described below for models served on another native wire. A configured provider works in both desktop apps when it offers both required APIs; a preset's existence is not proof that every model supports every feature.
Claude uses its vendor-supported ~/Library/Application Support/Claude-3p
profile, separate from the regular signed-in Claude profile. Only one third-party
instance can run at a time; quit it before switching providers. Switcher adds a
temporary configuration-library entry, leases that profile, and restores the
previous selection on exit. Its conversations remain in Claude-3p. Configuration
changes made outside Switcher are preserved and reported for review. Managed
Claude configurations are not overridden. No app patching, re-signing or
restricted development flags are used.
The selected model fills Claude's normal, planning and fast model slots using
explicit gateway aliases. --role-model planning=MODEL and --role-model
fast=MODEL can select different models from the same provider. The gateway
records the actual upstream model. Display labels identify your provider model;
the aliases do not change that model into an Anthropic model.
Choose effort and permissions in Claude's own controls; the model must support
what Claude sends. Switcher's --reasoning and
--dangerously-bypass-approvals-and-sandbox flags currently apply to Codex and
ChatGPT, and are rejected for Claude desktop. Cowork and account-dependent
features retain their native requirements. Only a temporary scoped loopback
credential is written to the private configuration file; the upstream provider
key stays in Switcher. Keep Switcher running while using the launched instance.
Live acceptance on macOS used DeepSeek deepseek-flash: ChatGPT local Codex
returned the requested test marker with reasoning and Full access visible;
Claude's third-party chat returned its requested marker, with successful gateway
traces identifying deepseek-flash. Other compatible providers have adapter
coverage, not a claim of live acceptance on every model.
References: Claude third-party gateway, configuration library and model tiers, and CC Switch's Claude desktop integration.
Direct launch
The direct launch flow is available from 0.1.1. The additional OMP, DeepSeek Harness, Cline, Hermes, Prime Agent, legacy OpenCode, Kilo, Gemini CLI and Aider adapters are introduced in 0.1.2. Version 0.1.0 requires explicit API/provider/profile setup.
Version 0.1.3 automatically injects model guidance and enforces allowed model IDs through a per-launch gateway. Native child and utility models default to your selected provider model. Use --role-model ROLE=ID or --model-policy-file FILE for explicit assignments, aliases and fallbacks. The full catalog stays visible; using another catalog entry requires an allowed assignment or a new launch. See model policy for native role support, routing traces and enforcement scope.
Supply the provider key through environment injection (DEEPSEEK_API_KEY, OPENROUTER_API_KEY, or an explicit SWITCHER_PROVIDER_* reference), or configure a local credential binding below. Switcher never saves the value.
switcher providers presets
switcher models deepseek
switcher launch claude --provider deepseek
# Deterministic automation: choose an exact ID from the discovered catalog.
switcher launch claude --provider deepseek --model deepseek-v4-pro
switcher launch codex --provider openrouter --model anthropic/claude-sonnet-4.6An interactive terminal can choose or search the catalog when --model is omitted. Noninteractive launches require an explicit model. --dry-run resolves and saves the provider/profile and fresh catalog, then prints the launch plan without starting the harness or creating a run record. Existing switcher launch PROFILE commands remain supported. Direct launches create or reuse records without overwriting customized providers or profiles.
With no Switcher API credential configured, the CLI and switcher-mcp exit non-zero and name the sources they consulted (Keychain item hasna.credentials.switcher.api-key, ~/.hasna/switcher/config/credentials, HASNA_SWITCHER_API_KEY); they never open local data by default. Set HASNA_SWITCHER_LOCAL=1 (alias SWITCHER_LOCAL=1; affirmative values are 1, true, yes, or on) to deliberately run on the box: each invocation then starts an authenticated loopback API on an allocated port, stores SQLite data in ~/.hasna/switcher, prints one switcher: LOCAL mode line on stderr, and closes its own listener on completion. Its random operator key remains in memory. Under that opt-in, use HASNA_SWITCHER_HOME to choose another owner-only home, HASNA_SWITCHER_SQLITE_PATH for an explicit database, or HASNA_SWITCHER_DATABASE_URL for PostgreSQL. A configured API URL or key outranks the flag. API and SDK data access remains HTTP.
Remote API configuration is resolved through Contracts, including canonical credential stores and the default gateway URL. Invalid, unavailable or unauthorized remote services fail without opening local SQLite.
The registry contains DeepSeek, OpenCode Zen, OpenCode Go, OpenRouter, Anthropic, OpenAI, xAI, Ollama, LM Studio, Groq, Cerebras, Mistral, Together AI, Fireworks, Moonshot/Kimi, DashScope, Z.AI, MiniMax, SiliconFlow, and generic protocol entries. switcher providers presets ID exposes documented routes, aliases and limitations; this is not a claim that every combination has passed live tests. Remaining adapters and acceptance gates are tracked in TODOS.md.
OpenCode and OpenRouter providers
From 0.2.1, opencode selects OpenCode Zen and opencode-go selects its Go
subscription. Both discover their complete live /models catalogs. Supply
OPENCODE_API_KEY through environment injection or bind each preset to your
existing vault/keychain reference. Go requires an active Go subscription.
SWITCHER_PROVIDER_OPENCODE and SWITCHER_PROVIDER_OPENCODE_GO can bind
different accounts explicitly. OpenRouter uses OPENROUTER_API_KEY or its
SWITCHER_PROVIDER_OPENROUTER binding.
switcher models opencode --limit 1000
switcher models opencode-go --limit 1000
switcher models openrouter --limit 1000
switcher launch claude --provider opencode --model big-pickle
switcher launch claude --provider opencode-go --model kimi-k3
switcher launch claude --provider openrouter --model deepseek/deepseek-v4-flashUse the exact API model ID from the catalog. OpenCode IDs are unprefixed;
OpenRouter IDs include their provider prefix. --limit controls the displayed
page, and --offset accesses subsequent pages. The stored catalog includes all
pages and modalities; coding selections exclude models explicitly lacking text
output or tools. OpenRouter's declared reasoning efforts are retained.
Claude Code and claude-desktop speak Messages to Switcher. For OpenCode,
Switcher forwards Claude/Qwen Messages requests natively, uses Messages for
Go's MiniMax models, and translates the other documented model families to
Chat Completions, Responses, or Gemini generateContent. Translation preserves
streaming text, tool arguments/results, images, reasoning text, and token usage.
It keeps Gemini tool signatures within the current launch. Restarting a launch
does not restore that transient signature cache, so start a fresh conversation
for Gemini tool workflows. Token counting on translated routes and provider
server tools are rejected explicitly; model capabilities and entitlements still
apply. Other harnesses use the preset's selected native protocol and require a
model compatible with it. OpenRouter provides its own Messages translation.
OpenCode inference receives Switcher's own user agent and a stable conversation identifier. Native session headers are preserved from an explicit allowlist; Claude's session metadata is used when available, with a per-launch fallback. Credential and session handling is scoped to the exact OpenCode service URLs.
bun run test:native-claude-providers uses installed Claude Code with controlled
upstream fixtures and verifies actual file reads through Chat Completions,
Responses, Gemini, Go Messages, and OpenRouter Messages. This is separate from
paid-provider acceptance. Development live checks discovered all three catalogs
and received answers from OpenCode's free Chat endpoint; the saved paid keys
were rejected and the free endpoint subsequently rate-limited native testing.
Sources: OpenCode Zen, OpenCode Go, and OpenRouter Claude Code integration.
Canonical API configuration
The CLI, clientFromEnv() SDK helper and standalone MCP client resolve Switcher API URL/key through @hasna/contracts/client, following the same shared convention as Conversations. A key alone selects https://api.hasna.com/switcher; HASNA_SWITCHER_API_URL selects a self-hosted endpoint. The SWITCHER_API_URL and SWITCHER_API_KEY aliases are supported. Deliberate overrides/profile selections take precedence; otherwise the resolver reads macOS Keychain, canonical disk configuration and then environment. A live client rereads credentials before each request and refuses an authority change until rebuilt.
The canonical file is ~/.hasna/switcher/config/credentials (owner-only mode 0600), using assignments named HASNA_SWITCHER_API_URL and HASNA_SWITCHER_API_KEY. Provision actual values through your approved secret manager. macOS Keychain uses service hasna.credentials.switcher.api-key and optional api-url, with account HASNA_STATION or the short hostname. Provider keys remain in the provider's vault or secure store; this file authenticates the Switcher API only.
HASNA_HOME replaces ~/.hasna; HASNA_CONFIG_HOME places credentials at <root>/switcher/credentials and <root>/secrets/credentials. These shared overrides must be absolute and nonblank. HASNA_PROFILE selects credentials-<profile>; API authority stays in the common credentials file. HASNA_SWITCHER_HOME changes Switcher's local database, bindings and launch state only, and does not relocate shared credentials. HOME is respected by both paths.
Configured but missing, unsafe, conflicting or inaccessible remote credentials fail before local data is opened, and a Keychain item that exists but cannot be read is a terminal error, never treated as absent. Complete absence of Switcher remote configuration also fails closed; the owned local API is reachable only through HASNA_SWITCHER_LOCAL=1 and uses a random in-memory key. Server-side authentication remains configured separately through HASNA_SWITCHER_API_KEY on switcher-serve.
Credential bindings
Bind an existing vault key once, then launch without an external wrapper. New bindings resolve the Secrets operator key and API URL through @hasna/contracts/client. A configured Keychain item or canonical ~/.hasna/secrets/config/credentials supplies the operator without shell exports. The default Secrets API URL is https://api.hasna.com/secrets; --vault-url can select a custom vault but must agree with any configured Secrets authority.
Real launches now preflight the local inference credential before catalog refresh and before an interactive model picker. An existing environment credential is preserved without creating a binding. An existing binding remains authoritative, including its exact Secrets operator account and allowed provider origins; a locked source, an unauthorized destination, or rejected provider credential is terminal and never falls through to another account.
When a provider credential is missing in an interactive terminal, Switcher uses the trusted secrets executable on PATH to search metadata only. It displays the selected Secrets account/source and matching vault key references, asks which reference to bind, saves that reference through the existing immutable binding store, resolves the value through the authenticated loopback delivery path, and performs a bounded provider authentication check before continuing the original launch. OpenRouter uses its authenticated GET /api/v1/key metadata endpoint; its public model catalog is never treated as proof that a key works. Custom providers may declare a relative credentialCheck (GET or HEAD) in provider JSON. Catalog configuration alone is never inferred to validate a credential because a catalog may be public or ignore invalid authorization.
OpenCode Zen checks its supplied key with one fixed synthetic question to the documented free jev-1.13-free model. Before sending the selected key, Switcher requires rejection of invalid keys through both the legacy and migrated routes: an existing legacy key may also have been migrated. Anonymous public credentials and whitespace-containing tokens are rejected. The check uses a shared ten-second deadline and bounded JSON responses, follows no redirects, sends no user prompt or project data, and has no paid-model fallback. The free alias is a limited-time provider offering; if the check becomes unavailable, launch stops with the selected binding preserved. This proves key authentication, not access to every paid model. The coding model and provider chosen for the actual launch remain unchanged. An explicitly configured credentialCheck retains precedence. See OpenCode's Jev endpoint and free-model pricing.
Discovery requests Secrets' full metadata array with a 257-row limit, preserving compatibility with compact-default and older Secrets CLIs. The extra row detects searches exceeding the 256-reference limit; discovery refuses these results before deduplication and also caps the combined references across searches. Each response remains limited to 256 KiB. This search never requests secret values; broader matches require an explicit vault-key binding.
If no matching key exists, Secrets is unavailable, the selected Keychain/vault cannot be read, or the provider rejects the credential, the structured error names the exact recovery without changing accounts. The first-run binding pins the exact Contracts source and vault authority that were displayed; if that Keychain account, environment tier, profile, or canonical file changes, later launches stop rather than selecting another account. First-run bindings also retain a requirement for a supported provider authentication check, so an unsupported check cannot be bypassed by retrying. Older explicit bindings remain compatible when no safe check is declared; adding credentialCheck upgrades them to authenticated preflight without changing their account or origins. Noninteractive launches never prompt and return credential_setup_required with explicit binding syntax or the provider environment reference. --dry-run preserves its planning contract: it does not search Secrets, create a binding, retrieve a provider credential, or perform provider authentication merely to describe a launch plan. Public and credentialless catalogs may refresh. Authenticated catalogs are read from the saved Switcher snapshot; if none exists, the error names the explicit switcher models PROVIDER --refresh command that performs the credential-consuming refresh.
For stations that update global tools with Bun, give the vault binding a private, version-pinned npm installation. A later global Bun installation can reset the permissions of its secrets executable, including one already repaired, and break every provider using it. The private installation keeps that update path separate. This example uses Secrets 0.4.2; choose an exact verified release and a new, unused prefix for each upgrade:
SWITCHER_VAULT_PREFIX="$HOME/.hasna/switcher/vault-runtimes/secrets-0.4.2-private"
(
set -e
umask 077
test ! -e "$SWITCHER_VAULT_PREFIX"
mkdir -p "$SWITCHER_VAULT_PREFIX"
npm install --prefix "$SWITCHER_VAULT_PREFIX" --save-exact \
--ignore-scripts --no-audit --no-fund --umask=077 @hasna/[email protected]
)Both the shell and npm umasks matter: a shell umask of 0002 can leave npm package directories group-writable, which the vault guard also rejects. --ignore-scripts prevents lifecycle scripts from running during this installation. Verify the installed package against its integrity-checked release archive before binding it, as described below. Do not run Bun installs inside this prefix. Preserve the previous runtime until the new binding passes a real provider launch.
To migrate an existing binding, record its locator with credentials list, then explicitly remove and recreate that same reference with the new --vault-cli. Preserve its vault key, URL, operator/account and authorized origins; only the executable path should change. Keep the original locator available to restore if verification fails.
switcher credentials bind deepseek \
--vault-key providers/deepseek/live/api_key \
--vault-cli "$SWITCHER_VAULT_PREFIX/node_modules/@hasna/secrets/dist/index.js"
switcher credentials check deepseek
switcher launch claude --provider deepseek --model deepseek-v4-pro--vault-cli /absolute/path/to/secrets selects a particular installation; omitting it binds the current secrets on PATH, which may be replaced by a global tool update. Vault lookup uses secrets exec to inject the value into a short-lived receiver, which delivers it over an authenticated loopback connection. Values stay in process memory. The lookup has a 20-second deadline and owns a separate process group; it finishes before the native harness starts. Each lookup reads the vault again. Conflicting Secrets service URL configuration fails explicitly. Vault CLI bindings currently require POSIX; Windows callers can inject provider environment variables.
If launch reports vault_exec_permissions, verify the installed Secrets package against its trusted release artifact before repairing it. Bun 1.3.14's bin-link installer can change an executable member from mode 0755 to 0777, including with --ignore-scripts. Changing the shell umask does not correct that installer behavior.
After validating the package archive against its registry integrity, compute the SHA256 of the executable member inside that verified archive. Use that expected digest to finalize the existing binding's installed executable:
switcher credentials repair-executable deepseek --sha256 EXPECTED_EXECUTABLE_SHA256For a fresh installation whose unsafe executable prevents creating a binding, use switcher credentials repair-executable --vault-cli /absolute/path/to/secrets --sha256 EXPECTED_EXECUTABLE_SHA256 first, then bind normally. Select either a binding reference or an explicit path.
The digest is a release-artifact checksum, not a credential. Do not substitute the installed file's own hash: that would trust any altered bytes. Repair checks the exact bytes, file owner/type and every path ancestor, then creates an identical private copy and atomically replaces a writable or hardlinked executable with group/public write permission removed. Cache hardlinks and already-open writers retain the old inode and cannot change the repaired executable. It then runs the ordinary launch checks again. Safe files with a private inode are unchanged; digest mismatches, unsafe ancestors and unowned files are refused. The command never starts Secrets, contacts the vault, changes bindings, or repairs permissions automatically during launch. Run this explicit step after each affected installation; a later Bun install can reset bin modes again. Reinstall a verified package if its bytes differ.
For provider keys already stored in macOS Keychain, use --keychain-service SERVICE --keychain-account ACCOUNT instead of vault options. Bindings contain only references and authorized origins under ~/.hasna/switcher/config/credential-bindings, in owner-only files. They remain local even when Switcher uses a remote API. A configured binding takes precedence over environment aliases; an unavailable binding never falls back to another account.
credentials list displays bindings; credentials remove PRESET_OR_REFERENCE removes only the locator. Replacement requires explicit removal. Custom credential references require --origin URL (repeatable); preset bindings authorize their documented origins by default. credentials check reports availability, length and hash, not successful provider authentication. When the selected Switcher API is hosted, catalog discovery still runs in the local launcher after credential authentication; only validated catalog metadata is committed to the API. Provider values are never sent to or stored by the hosted Switcher service.
Explicit --vault-account ACCOUNT pins one macOS Keychain account and requires --vault-url; it never falls back when that account is missing or locked. --vault-operator env preserves the per-process HASNA_SECRETS_API_KEY mode and also requires --vault-url. Existing bindings keep their original operator mode. To adopt canonical resolution for an old binding, explicitly remove and rebind its same provider key reference. A Secrets operator cannot bootstrap itself through HASNA_SECRETS_API_KEY_REF; use a literal operator from its canonical store or explicit override. On Linux the shared resolver reads the owner-only canonical credentials file or process environment. The binding stores only the locator and optional vault authority. Alternatively, let the authenticated secrets CLI inject a provider credential for one command:
secrets exec providers/deepseek/live/api_key --as DEEPSEEK_API_KEY -- \
switcher launch claude --provider deepseek --model deepseek-v4-flash --dry-runAuthenticate that Secrets process through your existing secret manager or service environment. --dry-run refreshes public/credentialless catalogs, reuses saved authenticated catalog snapshots, creates provider/profile records as needed and returns a launch plan without starting the native harness or inference. It never retrieves a provider credential solely for planning. Every new process needs its own runtime injection. credentials check inspects configured bindings, not environment aliases. Keychain bindings are macOS-only; on Linux they fail with keychain_unavailable and recommend a vault binding or runtime environment. Native subscription/OAuth login is separate from Switcher's provider-key mode; Switcher does not import or reuse it.
OpenCode 2 configuration
OpenCode 2 requires beta-19157 or newer. Each launch isolates its provider configuration, home, configuration directory and cache. Switcher snapshots native global and project tool permissions, safe agent prompts and permissions, and the global and working-directory ancestor AGENTS.md files. Provider/model overrides, agent request headers and bodies, plugins, and live configuration reloads are excluded. Unsupported permission forms, unreadable policy files and malformed JSONC or agent YAML stop the launch instead of dropping rules.
The original XDG_DATA_HOME is retained so existing native sessions continue to work. Switcher reads only the native database's remote-configuration registration key and, when needed, migration status; it never edits that database. Registered remote configuration or a pending legacy credential migration blocks launch because those sources could reintroduce provider settings. Use an explicitly isolated XDG_DATA_HOME or complete the native migration separately before launching. Path permissions using ~ or $HOME retain their original home-directory meaning.
For bounded installed-native checks, set SWITCHER_TEST_NATIVE_EXECUTABLE to OpenCode 2 and run bun run test:native-opencode2-authority openai-chat, openai-responses, or anthropic-messages from the package directory. These checks use local fixtures, actual file reads, deleted-file resume, an agent deny rule, and the settled native catalog.
Pi
Pi 0.85.1 or newer supports all three wire protocols. Use switcher launch pi --provider deepseek --model deepseek-v4-flash. Switcher supplies the provider catalog and scopes the native picker and model cycling to that provider. Pi's --list-models diagnostic still lists global model definitions. Catalog IDs that differ only by letter case are rejected because Pi cannot select them unambiguously.
Pi sessions persist under Switcher state per profile; pass native --continue or --session PATH after --. Each launch uses a temporary Pi agent directory. Global settings, keybindings, extensions, themes and skills are not loaded, and changes to that temporary configuration disappear at exit. Project customization follows Pi's native behavior.
OMP
OMP 18.1.11 or newer supports the three Switcher wire protocols through its native models.yml provider API. Switcher writes a per-launch provider catalog and config.yml under PI_CODING_AGENT_DIR, selects the exact provider-qualified model, and keeps the API key in the child environment. OMP's native project instructions, tools and permissions stay enabled. Sessions persist under the profile's Switcher-owned --session-dir; model, provider, profile and config overrides are reserved by the launch profile.
DeepSeek Harness
Use the official @deepseek-ai/dsh executable, version 0.1.2-rc.1 or newer, with its supported Node runtime. This is the native DeepSeek Harness, separate from Claude Code using a DeepSeek provider. Switcher supplies the full coding catalog through DSH's native llm-pi-ai adapter, supporting Chat Completions, Responses and Messages. The selected model becomes the default for new sessions; native model choices remain available within that catalog.
# Native browser UI, bound to an allocated loopback port.
switcher launch dsh --provider deepseek --model deepseek-v4-flash
# Print the authenticated local URL without opening a browser.
switcher launch dsh --provider deepseek --model deepseek-v4-flash -- --no-open
# Native one-shot task; headless has no resume flag.
switcher launch dsh --provider deepseek --model deepseek-v4-flash -- --profile headless "Inspect this project"
# Standard ACP over stdin/stdout for a programmatic client.
switcher launch dsh --provider deepseek --model deepseek-v4-flash -- --profile acpDSH sessions and attachments persist under Switcher state per launch profile. Resume through the native browser UI or ACP session/list and session/resume; a resumed session retains its native conversation and model selection. Each launch uses a temporary DSH home. Existing global profiles, settings, plugins and saved credentials are not loaded, and edits to the temporary configuration disappear on exit. Project customization remains native. Native browser authentication uses a launch URL and a host/port-bound cookie; its own temporary signing grant is removed with that home. Provider keys remain environment references in configuration.
Only the shipped web, headless and acp profiles are supported. SDK initialization can mount a different provider independently of the launch profile, so SDK/custom profiles and arbitrary patches are rejected. Listener overrides are also rejected. Ori's dsh command performs setup only and is not accepted as a launch backend.
The opt-in test:native-dsh script runs the actual Switcher CLI and installed DSH against local inference fixtures, verifies a real file-read tool and second-process resume after deleting the file, and rejects model choices outside the catalog. SWITCHER_TEST_DSH_PROTOCOL selects a wire protocol, SWITCHER_TEST_DSH_AUTH selects bearer, x-api-key or none, and SWITCHER_TEST_DSH_MODE=headless checks the native one-shot path. test:native-dsh-web checks the browser catalog, authentication, Host/Origin protections and shutdown without opening a browser. Set SWITCHER_TEST_DSH_EXECUTABLE to the official executable and SWITCHER_TEST_ROOT to an owned scratch directory. These checks do not make paid provider calls.
Aider
The direct Aider adapter is verified with aider-chat 0.86.2. Install that native Python application separately and select its executable if needed. Other versions, GUI mode and Ori launching are rejected. The GUI uses a different initialization path that defaults to blanket confirmation; this adapter preserves native CLI confirmations.
switcher launch aider --provider deepseek --model deepseek-v4-flash
# Native read-only context and a bounded request; confirmations remain native.
switcher launch aider --provider deepseek --model deepseek-v4-flash -- \
--read CONVENTIONS.md --file app.py --message "Implement the requested change."
# Continue the same profile's last closed conversation.
switcher launch aider --provider deepseek --model deepseek-v4-flash -- \
--restore-chat-historyAider uses native file context and text edit formats, rather than an autonomous file-read function tool. Text-input/output models can be selected without advertised function-tool support. Switcher registers the full catalog using openai/ID, openai/responses/ID, or anthropic/ID; these are Aider/LiteLLM transport names, and the provider receives its original model ID. All three protocols have installed-native edit/history fixture coverage. Chat and Messages support upstream streaming; Aider/LiteLLM buffers Responses requests even when native streaming is requested. /models and --list-models also enumerate built-in definitions; this is an available-model listing, not an isolated interactive picker. Use the full generated name with /model. Chat catalog IDs beginning responses/ are rejected because LiteLLM reserves that prefix for a different transport. Responses IDs containing responses/ are also rejected because LiteLLM removes those segments.
Every launch has a private home, configuration and cache. An authenticated loopback bridge owns the upstream key and admits only catalog model IDs. The native child receives only the temporary bridge token. Dotenv loading, provider credentials, aliases and model transport overrides are excluded; no provider key is written to native files. Original Git global configuration is loaded through Git include directives; global writes target a private file, retaining identity and repository policy. Ordinary global, Git-root and project Aider preferences retain native precedence; read-only paths using ~/ retain their original home meaning.
Preflight rejects conflicting configured startup commands, blanket approval, upgrade/test/lint/commit startup switches, ambiguous YAML/options, unreadable read-only context, custom model transport/callback settings, non-UTF-8 history and editable files belonging to a different Git root. Select the intended repository with --cwd. Explicit native task options and confirmations remain available. These checks do not sandbox concurrent project edits; keep native startup configuration unchanged while launching.
Aider has no session ID. Each launch writes a separate owner-only transcript under Switcher state, and --restore-chat-history copies the last closed conversation for that profile into a new transcript before native restoration. Concurrent runs keep independent history; diagnostic-only runs do not replace it. This does not import an existing standalone Aider transcript. Set SWITCHER_TEST_AIDER_EXECUTABLE and run bun run test:native-aider for controlled installed-native protocol, edit, history, catalog, hostile-routing and dry-run checks. Paid-provider, Linux and interactive-terminal acceptance remain separate gates.
Antigravity CLI and Junie
switcher launch antigravity --provider gemini --model gemini-2.5-flash
switcher launch junie --provider openrouter --model '~anthropic/claude-sonnet-latest'Antigravity 1.2.5 uses the native Gemini API. Switcher creates a private native home, supplies the launch directory with --add-dir, preserves permission settings and global GEMINI.md, and keeps conversations in the profile session directory. Its built-in Flash Lite helper is routed to the policy's fast model, which defaults to main. Use --role-model fast=MODEL to assign it explicitly. Other non-main roles are unsupported. Global plugins and shared app authentication are not imported, and ~ inside native tools resolves to the private home. Inherited custom routing settings fail preflight.
Junie build 3196.5 uses an isolated custom:switcher JSON model profile for Chat Completions, Responses or Anthropic Messages. Its primary and faster slots use the managed gateway, with fast defaulting to main. Switcher snapshots native user/project settings, disables inherited model/config discovery and keeps a durable profile cache and sessions for native resume. Native permissions, project guidelines and Junie's noninteractive trust behavior remain active. Provider/config/model override flags are reserved. Neither adapter receives the upstream provider key; the child receives an ephemeral gateway credential.
The native Gemini preset now uses the distinct gemini-generate-content provider ID. Existing saved providers retain their IDs and remain usable by name.
Claude Code with Amazon Bedrock
switcher launch claude --provider bedrock \
--url https://bedrock-mantle.us-east-1.api.aws/anthropic/v1 \
--model anthropic.claude-sonnet-5Use your account's regional Mantle endpoint and a Bedrock API key through SWITCHER_PROVIDER_BEDROCK, AWS_BEARER_TOKEN_BEDROCK, or a scoped credential binding. Switcher verifies authentication with the same origin's /v1/models, discovers the regional catalog, and sends native Anthropic Messages through its managed gateway. AWS credentials and native Bedrock mode are not inherited by the child. IAM/model access, regional availability, billing and key expiry remain provider requirements; catalog visibility alone does not prove inference entitlement. See Amazon's Messages API contract.
Gemini CLI
The native Gemini adapter supports exactly Gemini CLI 0.58.0 and the gemini-generate-content protocol with x-api-key authentication (the native wire header is x-goog-api-key). Use switcher launch gemini --provider gemini --model MODEL, selecting an ID from the discovered catalog. A compatible custom gateway must implement the same Gemini protocol and discovery contract. Chat Completions, OAuth, Vertex/ADC and Ori are separate interfaces and are not provided by this native adapter.
The API retains Gemini's full catalog and its documented supportedGenerationMethods metadata. Models that explicitly lack generateContent, such as embedding or prediction-only entries, are excluded from coding selections. Absent metadata remains unknown; support for generateContent alone does not establish text output or function-tool support. See the Gemini model catalog contract.
Switcher holds the upstream key in an authenticated loopback bridge; the native CLI receives a temporary local token. The bridge accepts only catalog model IDs and the generateContent, streamGenerateContent and countTokens routes, preserves the configured deployment prefix, and cancels unfinished streams on exit. It does not allow a native client to replace the upstream endpoint or headers. Native helper requests for models outside the catalog fail closed.
Each launch gets private user, default and system settings. The complete compatible catalog replaces native picker visibility, and explicit model resolution preserves upstream IDs. Native profile sessions remain durable across fresh launches: pass -- --resume latest --prompt PROMPT for headless continuation. Concurrent launches share session storage but have separate configuration and native session IDs.
Workspace trust remains a native user decision; Switcher never enables it automatically. Original system/default/user settings, native user policies, project instructions and project permission handling remain active. Global Markdown context files and nested imports inside the original .gemini directory are copied with their import paths preserved. Context imports outside that directory, non-Markdown or nested global context filenames, symlinked configuration, and project policy paths using ~ fail explicitly; use ordinary Markdown names and explicit absolute policy paths. System/default/user and native --policy home paths retain their original home meaning. Private user settings, trust and policy changes disappear at exit; project changes remain native. Global extensions, custom agents, skills and keybindings are not copied into the private home.
Inherited nonempty modelConfigs, custom agent model transport settings and incompatible enforced authentication policies stop launch before catalog refresh or credential resolution. Keep routing configuration stable during launch; this preflight does not sandbox concurrent project edits. ACP is rejected because its client can replace authentication and routing. Normal native approval flags and literal prompt arguments remain available.
For local acceptance, set SWITCHER_TEST_NATIVE_EXECUTABLE to the installed Gemini executable and run bun run test:native-gemini. The fixture uses native plan permissions and a narrow trusted-folder entry for its owned project, verifies file reading, deleted-file resume, two concurrent sessions, global/project imports, user deny rules and cleanup. Set SWITCHER_TEST_GEMINI_PACKAGE to the official 0.58.0 package directory to run the native settings/catalog loader regression with bun test. Real-provider, interactive-terminal and Linux Gemini acceptance remain separate gates.
Kilo
Kilo 7.5.15 or newer supports Chat Completions, Responses and Anthropic Messages through a scoped parent bridge. Use switcher launch kilo --provider PROVIDER --model EXACT_MODEL; pass native arguments after --. Switcher supplies the exact provider/model catalog and preserves supported project instructions and permission rules. Each launch uses a private native home/config/cache and profile session directory.
The upstream provider key stays in the Switcher bridge; Kilo receives only a short-lived local token. Kilo 7.5.15 can load legacy project MCP configuration, and those trusted MCP processes may inherit that scoped bridge capability. This adapter does not claim a complete MCP sandbox. Conflicting native provider/model/endpoint/auth flags, unsupported policy forms, remote instruction URLs and system-managed configuration are rejected before native launch.
Cline, Hermes and Prime Agent
Use switcher launch cline, switcher launch hermes or switcher launch prime-agent with the same --provider and --model options. These adapters support Chat, Responses and Messages with full provider catalogs and per-profile native history. Cline requires 3.0.61 or newer, Hermes 0.21.0 or newer and Prime Agent 0.9.2 or newer.
Cline uses its native ACP backend with per-launch configuration, durable sessions and native permission requests. Hermes uses its native custom-provider interface and keeps state.db and sessions under profile state; its model menu also retains built-in free-provider/MOA entries. Prime keeps its foreground supervisor and workers within the launch lifetime. It automatically chooses a shorter private runtime directory if the system temporary path cannot hold native Unix sockets. Native history and resume remain profile-specific.
Legacy OpenCode
switcher launch opencode --provider PROVIDER --model MODEL selects legacy OpenCode 1.18.0 or newer, tested with 1.18.29. It is distinct from opencode2 and uses the legacy singular-provider configuration schema. The adapter supplies the provider catalog, preserves supported native instructions and permission rules, and stores sessions per profile. Chat, Responses and Messages have controlled native tool/read/resume coverage. Use native models for its full provider-qualified catalog; visual picker acceptance is recorded separately.
Run a persistent service
For a private local/self-hosted service, inject a random operator token of at least 24 characters as HASNA_SWITCHER_API_KEY. For a hosted PostgreSQL service, inject HASNA_SWITCHER_API_SIGNING_KEY (or the shared HASNA_API_SIGNING_KEY / API_KEY_SIGNING_SECRET) and issue revocable, scoped switcher:read / switcher:write client keys through @hasna/contracts. Exactly one authentication mode is accepted. Provider credentials stay in each launcher process; hosted catalog refresh uploads metadata only.
# SQLite: private persistent API with a static operator token.
switcher-serve --data-dir ~/.hasna/switcher --port 8080
# PostgreSQL self-hosting with the same private token:
switcher-serve --port 8080
# Hosted PostgreSQL: inject HASNA_SWITCHER_DATABASE_URL and
# HASNA_SWITCHER_API_SIGNING_KEY. Apply the terminating migration first:
switcher-serve migrate
switcher-serve --host 0.0.0.0 --port 8080Choose exactly one backend. There is no automatic fallback. SQLite HTTP hosting is an explicit switcher-specific product requirement; clients always use HTTP. Hosted signed-key mode requires PostgreSQL and refuses a SQLite path. The migration command applies the domain and API-key lifecycle schema with the owner DSN and exits. The long-running hosted service validates that exact schema with its application DSN and performs no DDL.
Set HASNA_SWITCHER_API_URL=http://127.0.0.1:8080 for a self-hosted client and inject its operator token. A configured client key with no URL selects https://api.hasna.com/switcher; remote URLs require HTTPS. A selected hosted authority that is missing, unreachable or unauthorized fails closed and never opens local SQLite. Values 0, false, no, and off do not enable local mode; unknown nonblank local-flag values are rejected. The service binds loopback by default; put a TLS reverse proxy in front of an explicitly hosted listener.
For containers, build the package first, then use docker compose --profile sqlite up --build. For PostgreSQL, inject SWITCHER_POSTGRES_PASSWORD and a matching URI in HASNA_SWITCHER_DATABASE_URL using hostname postgres, database/user switcher; run docker compose --profile postgres up --build. URI-encode password characters. Choose one profile. Compose exposes only loopback port 8080 and persists named volumes.
Add a provider and launch
switcher providers add openrouter-responses --preset openrouter \
--protocol openai-responses --credential-env SWITCHER_PROVIDER_OPENROUTER
switcher providers refresh openrouter-responses
switcher models openrouter-responses --search claude --limit 1000
switcher profiles add coding --provider openrouter-responses \
--harness codex --model anthropic/claude-sonnet-4.6
switcher launch coding -- --help
# Interactive native launch:
switcher launch codingThe model ID is an example; choose an exact ID from the current catalog and verify account access. Use --url https://provider.example/api/v1 instead of the preset for any compatible endpoint. The base URL includes the provider API version/path. Presets declare the appropriate discovery URL separately; DeepSeek discovers models at its root while Messages inference uses its Anthropic path. Select a separate provider profile for each wire protocol. The launch adapters normalize native endpoint conventions.
For Claude use --harness claude with anthropic-messages; for Grok, Hermes or OpenCode 2 use their supported protocol. Pass native arguments after --, such as switcher launch coding -- exec "Reply with exactly: connected". --backend direct is the default; the optional --backend ori is OpenRouter-only and accepts --ori-executable PATH, while --executable remains the direct adapter option. --cwd, --state-dir, and --timeout SECONDS are local launcher options. Native approval and sandbox settings remain in effect. See the Ori backend contract for its supported target and catalog boundaries.
When the API runs remotely, provider authentication and model discovery happen in the local launcher. The launcher commits the resulting validated catalog through the versioned API, then uses the same prepared credential for direct inference. The hosted service never receives or stores the provider key and refuses server-side provider refreshes; authenticated callers cannot make the hosted service contact provider URLs. An external compatible gateway can be the configured provider. Switcher does not translate between wire protocols.
Catalog refresh and offline use
switcher models PROVIDER reads an existing stored catalog without contacting the provider. When no catalog exists, it discovers one. --refresh and every launch request fetch the upstream catalog. Failed refreshes preserve the previous snapshot, but launch fails instead of silently using that snapshot. API launch plans warn when their catalog is more than five minutes old.
Discovery allows at most two retries per page for network failures and HTTP 408, 425, 429, 500, 502, 503 or 504. A request has a 20-second limit and the refresh has a 60-second aggregate limit. Valid numeric or HTTP-date Retry-After values are honored; a delay beyond the remaining budget fails immediately. Otherwise, retry delays are 100 ms and 200 ms. Redirects and other HTTP failures are terminal. Existing limits remain 16 MiB per response, 100 pages and 10,000 unique models. Credentials, origin restrictions and parser validation stay the same on every retry.
Optional Ori backend
switcher launch codex --provider openrouter --model MODEL --backend ori uses installed Ori 0.12.x. Add --ori-executable PATH to choose its installation. --dry-run validates the Ori contract without resolving a launch credential. The Codex picker uses Switcher's complete compatible catalog. Grok is supported through Ori's Chat route and its entitled OpenRouter catalog. Direct adapters remain the default. Ori launches reject other provider authorities, Claude's global-configuration mutations, and the legacy OpenCode target; use the direct Claude and
