@hasna/switcher 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +73 -12
- package/dist/catalog.d.ts +3 -2
- package/dist/cli/index.js +5448 -198
- package/dist/credentials.d.ts +178 -0
- package/dist/direct-launch.d.ts +6 -0
- package/dist/domain.d.ts +118 -8
- package/dist/generated/api.d.ts +150 -5
- package/dist/grok-args.d.ts +1 -0
- package/dist/harness-arguments.d.ts +5 -0
- package/dist/harness-environment.d.ts +1 -0
- package/dist/harness-process.d.ts +11 -0
- package/dist/harness-types.d.ts +1 -0
- package/dist/harnesses.d.ts +33 -2
- package/dist/index.js +163 -2
- package/dist/launcher.d.ts +38 -3
- package/dist/mcp/index.js +123 -3
- package/dist/opencode2-config.d.ts +60 -0
- package/dist/ori-backend.d.ts +89 -0
- package/dist/presets.d.ts +63 -0
- package/dist/runtime.d.ts +20 -0
- package/dist/sdk.d.ts +77 -9
- package/dist/sdk.js +163 -2
- package/dist/serve/index.js +656 -70
- package/dist/server.d.ts +16 -0
- package/dist/service.d.ts +2 -1
- package/dist/terminal-descriptors.d.ts +5 -0
- package/hasna.contract.json +1 -1
- package/openapi.json +320 -6
- package/package.json +9 -4
package/README.md
CHANGED
|
@@ -4,14 +4,14 @@ title: "Switcher"
|
|
|
4
4
|
type: "package-documentation"
|
|
5
5
|
owner: "codex-fixer"
|
|
6
6
|
created_at: "2026-09-05T12:50:21.698672Z"
|
|
7
|
-
updated_at: "2026-09-
|
|
7
|
+
updated_at: "2026-09-06T11:12:08Z"
|
|
8
8
|
status: "active"
|
|
9
9
|
source_task: "01a07181-ca8d-70c1-99a2-b276dc5770f3"
|
|
10
10
|
---
|
|
11
11
|
|
|
12
12
|
# Switcher
|
|
13
13
|
|
|
14
|
-
Launch Claude Code, Codex, Grok Build
|
|
14
|
+
Launch Claude Code, Codex, Grok Build, OpenCode 2 or Pi 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`.
|
|
15
15
|
|
|
16
16
|
Requires Bun 1.3.14 or newer. Install the native harnesses separately.
|
|
17
17
|
|
|
@@ -21,12 +21,67 @@ switcher --help
|
|
|
21
21
|
switcher doctor
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
##
|
|
24
|
+
## Direct launch
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
The direct launch flow is available from 0.1.1. Version 0.1.0 requires explicit API/provider/profile setup.
|
|
27
|
+
|
|
28
|
+
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.
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
switcher providers presets
|
|
32
|
+
switcher models deepseek
|
|
33
|
+
switcher launch claude --provider deepseek
|
|
34
|
+
# Deterministic automation: choose an exact ID from the discovered catalog.
|
|
35
|
+
switcher launch claude --provider deepseek --model deepseek-v4-pro
|
|
36
|
+
switcher launch codex --provider openrouter --model anthropic/claude-sonnet-4.6
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
An 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.
|
|
40
|
+
|
|
41
|
+
When no remote API configuration is present, each CLI invocation starts an authenticated loopback API on an allocated port, stores SQLite data in `~/.hasna/switcher`, and closes its own listener on completion. Its random operator key remains in memory. 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. API and SDK data access remains HTTP.
|
|
42
|
+
|
|
43
|
+
If either remote API variable is configured, both `HASNA_SWITCHER_API_URL` and `HASNA_SWITCHER_API_KEY` are required. An unreachable or misconfigured remote API fails; it never selects a local database instead. The SDK always requires an explicitly configured API.
|
|
44
|
+
|
|
45
|
+
The registry contains DeepSeek, 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](TODOS.md).
|
|
46
|
+
|
|
47
|
+
## Credential bindings
|
|
48
|
+
|
|
49
|
+
Bind an existing vault key once, then launch without an external wrapper. Use your vault's URL and the installed `secrets` CLI. On macOS, `--vault-account` selects the operator's `hasna.credentials.secrets.api-key` Keychain item. Without that option, inject `HASNA_SECRETS_API_KEY` for each process.
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
switcher credentials bind deepseek \
|
|
53
|
+
--vault-key providers/deepseek/live/api_key --vault-url https://vault.example \
|
|
54
|
+
--vault-account my-station
|
|
55
|
+
switcher credentials check deepseek
|
|
56
|
+
switcher launch claude --provider deepseek --model deepseek-v4-pro
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`--vault-cli /absolute/path/to/secrets` selects a particular installation. 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.
|
|
60
|
+
|
|
61
|
+
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.
|
|
62
|
+
|
|
63
|
+
`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. Provider credentials needed by a remote API's catalog discovery must still be configured on that server independently.
|
|
64
|
+
|
|
65
|
+
## OpenCode 2 configuration
|
|
66
|
+
|
|
67
|
+
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.
|
|
68
|
+
|
|
69
|
+
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.
|
|
70
|
+
|
|
71
|
+
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.
|
|
72
|
+
|
|
73
|
+
## Pi
|
|
74
|
+
|
|
75
|
+
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.
|
|
76
|
+
|
|
77
|
+
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.
|
|
78
|
+
|
|
79
|
+
## Run a persistent service
|
|
80
|
+
|
|
81
|
+
Inject a random operator token of at least 24 characters as `HASNA_SWITCHER_API_KEY` through your secret manager. Inject provider credentials separately, using names beginning `SWITCHER_PROVIDER_`. Only environment references are persisted. Explicitly hosted servers read `SWITCHER_PROVIDER_*` references; the local launcher also accepts the standard aliases declared by each built-in preset.
|
|
27
82
|
|
|
28
83
|
```sh
|
|
29
|
-
# SQLite:
|
|
84
|
+
# SQLite: persistent hosted service and database.
|
|
30
85
|
switcher-serve --data-dir ~/.hasna/switcher --port 8080
|
|
31
86
|
|
|
32
87
|
# PostgreSQL: inject HASNA_SWITCHER_DATABASE_URL, then:
|
|
@@ -53,12 +108,16 @@ switcher launch coding -- --help
|
|
|
53
108
|
switcher launch coding
|
|
54
109
|
```
|
|
55
110
|
|
|
56
|
-
The 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
|
|
111
|
+
The 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.
|
|
57
112
|
|
|
58
|
-
For Claude use `--harness claude` with `anthropic-messages`; for Grok or OpenCode 2 use their supported protocol. Pass native arguments after `--`, such as `switcher launch coding -- exec "Reply with exactly: connected"`. `--
|
|
113
|
+
For Claude use `--harness claude` with `anthropic-messages`; for Grok 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](docs/ori-backend-integration.md) for its supported target and catalog boundaries.
|
|
59
114
|
|
|
60
115
|
When the API runs remotely, inject the provider credential into the API process for authenticated catalog discovery and into the local launcher for direct inference. The API never returns a provider key. An external compatible gateway can be the configured provider. Switcher does not translate between wire protocols.
|
|
61
116
|
|
|
117
|
+
## Optional Ori backend
|
|
118
|
+
|
|
119
|
+
`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 OpenCode 2 adapters. Ori live acceptance remains tracked separately from fixture checks.
|
|
120
|
+
|
|
62
121
|
## Models and native pickers
|
|
63
122
|
|
|
64
123
|
| Harness | Required wire protocol | Native catalog |
|
|
@@ -66,15 +125,17 @@ When the API runs remotely, inject the provider credential into the API process
|
|
|
66
125
|
| Claude Code ≥2.1.242 | Anthropic Messages | Per-launch `modelPicker` on compatible Claude versions |
|
|
67
126
|
| Codex ≥0.153.0 | OpenAI Responses | Startup `model_catalog_json` |
|
|
68
127
|
| Grok Build ≥1.0.13 | Chat Completions, Responses or Messages | Authenticated loopback remote catalog with upstream model IDs |
|
|
69
|
-
| OpenCode 2 (tested beta-
|
|
128
|
+
| OpenCode 2 (tested beta-19157) | Chat Completions, Responses or Messages | Version 2 provider/model configuration and standalone server |
|
|
129
|
+
|
|
130
|
+
The CLI catalog includes all provider output modalities. Native coding pickers exclude unavailable models and those explicitly lacking text output or tool support; unknown metadata remains unknown. This is a capability filter, not a guarantee of successful tool use. Catalog refresh happens before every launch. Native pickers are startup snapshots, not promised live reloads. OpenCode requires a complete capability object; missing fields use text-only/tool-enabled native defaults with a warning. Its beta `models --standalone` command may return an early empty snapshot; the native `/api/model` API and interactive picker expose the settled catalog.
|
|
70
131
|
|
|
71
|
-
The
|
|
132
|
+
The Claude adapter sets `ANTHROPIC_DEFAULT_MODEL` and the default subagent model to the selected provider model. Explicit subagent definitions and managed model restrictions still apply. These follow the [native model precedence rules](https://code.claude.com/docs/en/model-config).
|
|
72
133
|
|
|
73
134
|
Claude Code with a non-Claude model is experimental and unsupported by Anthropic. Codex requires Responses, not Chat Completions. Upstream reasoning, tool schemas, context limits and stateless Responses behavior still need provider-specific validation. Switcher never silently falls back to a different provider.
|
|
74
135
|
|
|
75
|
-
For a provider without discovery, use `providers add ID --file provider.json` with `manualModels`. Each model needs `id` and `name`; optional fields are `contextWindow`, `maxOutputTokens`, `inputModalities`, `outputModalities`, and `
|
|
136
|
+
For a provider without discovery, use `providers add ID --file provider.json` with `manualModels`. Each model needs `id` and `name`; optional fields are `contextWindow`, `maxOutputTokens`, `inputModalities`, `outputModalities`, `supportedParameters`, and `available`. Use `catalogBaseUrl` and `modelsPath` for a separate discovery root/path; CLI equivalents are `--catalog-url` and `--models-path`. Use `catalogFormat: "ollama"` for `/api/tags`. Mistral presets select a capability-aware parser, including archived status. Together presets select its native bare-array parser. Fireworks requires `--catalog-account-id ID` or an explicit catalog URL and retains count evidence across its paginated account catalog. DashScope requires an explicit region/workspace `--catalog-url` with `--catalog-format dashscope`. Z.AI currently requires an explicit catalog or manual models because its documented API has no model-list contract. MiniMax defaults to its `.cn` Open Platform endpoints; use an explicit authority and credential reference for another product or region. A different authenticated catalog origin requires an explicit `catalogCredentialEnv`; a public catalog can declare `catalogAuthStyle: "none"`. Standard credential aliases are resolved only for the matching built-in provider origin. The default parser follows Anthropic-style `has_more/last_id` pagination and otherwise expects an OpenAI-style `data` array. HTTP redirects are rejected.
|
|
76
137
|
|
|
77
|
-
Grok uses a per-launch authenticated loopback bridge because its environment overlay cannot define providers. The bridge serves model metadata and forwards the selected protocol unchanged. It holds upstream credentials only in memory; Grok receives an ephemeral local token. The same bridge handles credentialless endpoints and OpenCode auth-header mismatches. Bridged requests are limited to 4 MiB and four minutes. Grok resume, and
|
|
138
|
+
Grok uses a per-launch authenticated loopback bridge because its environment overlay cannot define providers. The bridge serves model metadata and forwards the selected protocol unchanged. It holds upstream credentials only in memory; Grok receives an ephemeral local token. The same bridge handles credentialless endpoints and OpenCode auth-header mismatches. Bridged requests are limited to 4 MiB and four minutes. Grok resumes retain the selected profile model. Use `-- --resume SESSION_ID -p PROMPT` for headless continuation, or omit the prompt and type after the interactive session loads. Interactive resume with an inline positional prompt is rejected because the native client can send it before applying the selected model. Grok 1.0.13 passed source and installed development CLI resume checks against a controlled Messages fixture and live DeepSeek Flash. OpenCode's provider identity stays stable across temporary bridge ports; `-- run --session SESSION_ID PROMPT` resumes with fresh launch settings. The installed beta-19157 passed two-process Messages resume checks against a controlled local upstream and live DeepSeek Flash, including a proof-file read and preserved history. Other provider/protocol and registry-release cells remain tracked separately in COMPATIBILITY.md.
|
|
78
139
|
|
|
79
140
|
Native history and credentials stay with the harness. Resume only with the same profile/provider/model configuration unless the harness explicitly supports a change; cross-provider reasoning/session migration is not provided. Temporary nonsecret picker/config files are removed when the child exits. Run records contain launch/end metadata and initial model, not transcripts or a claim about later native picker selections.
|
|
80
141
|
|
|
@@ -93,7 +154,7 @@ console.log(plan.catalog.models.length);
|
|
|
93
154
|
|
|
94
155
|
The SDK supports Node and Bun. It has no database or launcher imports. Explicit credentials may be supplied as a string or a fresh resolver function; `clientFromEnv()` uses the shared contracts resolver with environment-only inputs and no disk fallback.
|
|
95
156
|
|
|
96
|
-
Public lifecycle endpoints: `GET /health`, `/ready`, `/version`. Authenticated OpenAPI: `GET /v1/openapi.json`. Provider/profile CRUD, catalog list/refresh, launch-plan validation and run metadata are under `/v1`. SDK types are generated from that OpenAPI document. API errors include code, message and request ID.
|
|
157
|
+
Public lifecycle endpoints: `GET /health`, `/ready`, `/version`. Authenticated OpenAPI: `GET /v1/openapi.json`. Provider/profile CRUD, `/v1/provider-presets`, catalog list/refresh, launch-plan validation and run metadata are under `/v1`. The SDK includes `listProviderPresets()`, `getProviderPreset()`, `health()`, `ready()`, and `version()`. SDK types are generated from that OpenAPI document. API errors include code, message and request ID.
|
|
97
158
|
|
|
98
159
|
All mutations require `Idempotency-Key`. Reuse the same key and payload after an uncertain response; changing the payload returns 409. Updates/deletes also require the numeric record version in `If-Match`. SDK methods supply these headers and accept a caller-provided idempotency key. Run creation requires the `planToken` from a launch plan; a changed provider, profile or catalog rejects the stale plan with 409 before local execution. List endpoints accept `limit` (1–1000), `offset` and `search`. Referenced providers/profiles cannot be deleted while children exist.
|
|
99
160
|
|
package/dist/catalog.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
import { type Catalog, type Provider } from "./domain";
|
|
2
|
-
export
|
|
1
|
+
import { type Catalog, type Provider, type ProviderInput } from "./domain";
|
|
2
|
+
export type CatalogCredentialResolver = (provider: ProviderInput) => Promise<string | undefined>;
|
|
3
|
+
export declare function discover(provider: Provider, env?: Record<string, string | undefined>, resolveCredential?: CatalogCredentialResolver): Promise<Catalog>;
|