One provider profile, six coding surfaces
Launchpad separates provider policy from tool configuration so six coding surfaces can share one model catalog and credential.
| Resource | Link |
|---|---|
| Source | ravikanchikare/launchpad |
| Provider design reference | OpenCode |
| LiteLLM proxy | LiteLLM proxy documentation |
| OpenAI model catalog | List models API |
| Claude Code settings | Claude Code settings documentation |
A shared model provider does not create a shared configuration format. Claude Code reads Anthropic environment variables, Codex accepts provider definitions, ChatGPT persists a profile, and Claude Desktop expects fixed model slots. Pointing every tool at one URL leaves the difficult part unresolved.
Launchpad makes the provider profile the stable boundary. Model discovery, endpoint resolution, and credential selection happen once. Six application surfaces then receive the narrow configuration each one understands.
| Concern | Current implementation |
|---|---|
| Provider kinds | litellm, openai-compatible |
| Credential sources | LAUNCHPAD_PROVIDER_API_KEY, then macOS Keychain |
| Provider overrides | URL, kind, and optional models URL |
| Terminal surfaces | Claude Code, Codex, OpenCode, Copilot CLI |
| Desktop surfaces | ChatGPT, Claude Desktop |
| Model selection | Live catalog with keyboard filtering and navigation |
| Persistent recovery | ChatGPT profile capture and restore |
A provider profile owns endpoint rules
The first implementation treated the provider URL as an origin and appended paths inside each launcher. That rule breaks as soon as a URL already ends in /v1 or includes a tenant prefix. The same configured value can produce /v1/v1/models in one adapter and lose the prefix in another.
The replacement introduces one provider profile with three inputs: provider kind, base URL, and an optional models URL. The profile validates them and derives the OpenAI base, Anthropic base, and discovery endpoints.
This follows the provider seam used by OpenCode: generic settings remain separate from provider-specific discovery behavior. A launcher does not need to know where a LiteLLM catalog lives. It asks the profile for a resolved endpoint.
Discovery follows provider kind
LiteLLM exposes richer model-group metadata outside the OpenAI API surface. A generic OpenAI-compatible provider usually exposes only the standard model list. Probing both indiscriminately turns an authentication failure into an ambiguous fallback.
| Provider kind | Primary catalog | Fallback | Response shape |
|---|---|---|---|
litellm |
/model_group/info |
/v1/models for an unavailable management route |
LiteLLM, then OpenAI |
openai-compatible |
/v1/models |
None | OpenAI |
Either with modelsUrl |
Exact configured URL | None | OpenAI |
The explicit models URL is for deployments where inference and discovery use different hosts or prefixes. It is not a second provider base. Launch requests still use the OpenAI or Anthropic base derived from the provider URL.
Configuration has one precedence order
Launchpad owns its environment contract instead of borrowing LiteLLM-specific variable names. This keeps the configuration accurate when the provider is not LiteLLM.
export LAUNCHPAD_PROVIDER_API_KEY=...
export LAUNCHPAD_PROVIDER_URL=https://provider.example.com/v1
export LAUNCHPAD_PROVIDER_KIND=openai-compatible
launchpad config show
launchpad launch claudeThe URL, kind, and models URL use environment values first, then saved settings. The key resolves from LAUNCHPAD_PROVIDER_API_KEY first and macOS Keychain second. No LITELLM_* compatibility aliases remain.
The same profile can be saved without environment variables:
launchpad config set-provider https://provider.example.com/v1 \
--kind openai-compatible \
--models-url https://catalog.example.com/modelsThe models URL is optional. Omitting it restores provider-specific discovery.
Each surface gets a narrow adapter
The provider profile ends where tool configuration begins. Adapters receive resolved endpoints, a model ID, and a credential. They do not rediscover models or append API versions.
Anthropic environment
Sets the Anthropic base, auth token, selected model, default model slots, and model discovery flag in the child process.
OpenAI Responses provider
Passes a temporary provider definition through -c arguments, selects the model with -m, and supplies the key only to the child.
Managed profile block
Captures the existing profile, writes one delimited provider block and model catalog, then retains enough state for an exact restore.
In-memory provider configuration
Builds an OpenAI-compatible provider document and passes it through OPENCODE_CONFIG_CONTENT without changing the user's config file.
Provider environment
Maps the resolved OpenAI base, key, provider type, and selected model into Copilot's process environment.
Local compatibility service
Advertises Claude model slots locally, rewrites each selected slot to a provider model, and forwards Anthropic-compatible requests.
Model IDs pass through unchanged. The picker selects provider-model-id; Claude receives that value as its model, Codex receives it through -m, and OpenCode places it under the Launchpad provider namespace.
Credentials remain process-scoped
Saved settings contain provider kind, URL, and optional models URL. They do not contain the provider key. Terminal adapters construct a child environment from the current process, remove Launchpad’s provider variables, remove conflicting tool credentials, then add only the variables required by the selected integration.
ChatGPT and Claude Desktop need credentials after the initiating CLI process exits. Their flows persist the management key in macOS Keychain and configure a helper or local service instead of writing the key into general settings JSON.
Persistent changes require restoration
ChatGPT shares configuration with Codex under CODEX_HOME. Launchpad captures the existing root model fields, writes a delimited provider block, and records the generated model catalog before restarting the desktop app.
The restore command removes only Launchpad’s managed block, restores the captured values, removes the generated catalog, and asks before restarting ChatGPT:
launchpad launch chatgpt --restoreThis is not cleanup around the implementation. It is part of the adapter contract: a persistent integration is incomplete until the previous state can be recovered.
Verify the boundary
The tests exercise provider URLs with and without /v1, path prefixes, LiteLLM fallback, direct OpenAI discovery, explicit catalog URLs, environment precedence, child credential isolation, Claude request rewriting, and ChatGPT restore.
go test -race -timeout 60s ./...
go vet ./...
npm --prefix ui run buildA smoke test can stop at picker cancellation. That proves credential resolution and live catalog discovery without starting a model request.
Takeaways
A provider is more than a URL
Provider kind, catalog endpoint, wire protocol, and credential source form one profile. Treating them as independent strings creates invalid combinations.
Endpoint derivation needs one owner
The provider profile handles path prefixes and /v1 exactly once. Tool adapters consume resolved OpenAI and Anthropic bases.
Discovery and execution stay separate
The catalog selects a model ID. Each adapter translates that selection into the tool's native process or profile contract.
Restore is part of configuration safety
Persistent profile changes need captured state, explicit confirmation, and a tested path back to the original configuration.