Refactor UX#17
Merged
mostlygeek merged 15 commits intomainfrom May 1, 2026
Merged
Conversation
Replace the previous flow (profile → backend → launch) with a profile-centric workflow that surfaces provider choice and model selection instead of internal backend types. - Add CompatibleProviders and BackendsForProvider to Manager for provider-centric filtering - Add DedupBackends to collapse backends sharing compat key signatures (e.g. Anthropic and ZAI both use anthropic_messages) - Add ModelSelector interface and ClaudeCodeProfile.ApplyModel for applying user-chosen default models via ANTHROPIC_MODEL - Add ProviderInfo.DisplayName falling back to ID when Name is empty - Add stepSelectProvider and stepSelectModel to the TUI state machine - Show model picker (with FQN provider_id/model_id) when provider has multiple models and profile supports ModelSelector - Persist LastProviderID and LastModel in launcher state
CodexProfile was missing the ModelSelector interface, so the TUI skipped the model picker. WriteConfig also didn't set the API base URL in config.toml, causing Codex to connect to api.openai.com instead of the aperture proxy, and defaulted to WebSocket transport. - Add ApplyModel to implement ModelSelector for model picker support - Write config.toml with a custom "aperture" provider that sets base_url to the aperture host and forces HTTPS transport via supports_websockets = false
OpenCode's UX previously asked the user to pick a backend after picking a provider, and the generated temp config was hardcoded per backend type rather than reflecting the chosen aperture provider. Collapse OpenCode's SupportedBackends to one abstract entry so the backend step is skipped, and add a ProviderConfigWriter interface that lets profiles generate config tailored to the chosen ProviderInfo. OpenCode's WriteProviderConfig now picks the AI SDK npm package and options from the provider's compatibility map (anthropic_messages, bedrock_converse, google_*, openai_responses, openai_chat) and writes the provider's id, name, and full model list into the temp config. For Vertex providers, set apiKey to trigger the SDK's express mode so it skips google-auth-library's ADC lookup, and embed the full project-scoped path (with the _aperture_auto_*_ placeholders aperture rewrites server-side) in baseURL so the request URL matches aperture's vertex router pattern.
Reorder pickOpenCodeSDK so providers exposing multiple compat keys land on the preferred SDK: openai_responses → anthropic_messages → openai_chat → google_* → bedrock_* → gemini_generate_content. Add handlers for bedrock_model_invoke and gemini_generate_content (mapped to @ai-sdk/google against /v1beta). Pin the model list to what aperture reports by writing a "whitelist" field alongside the models map. Without it, OpenCode merges its built-in models.dev database entries on top of our config when the provider key matches a known ID like "openai" or "anthropic". The whitelist makes provider.ts:1374 strip the database-sourced models so only ours remain. Use fully qualified "<provider-id>/<model-name>" identifiers as the model map keys and display names, keeping the bare model name in the "id" field so OpenCode's API requests still hit the upstream model.
Claude Code on Bedrock embeds ANTHROPIC_MODEL in the request URL path, so passing the FQN "providerID/modelID" produced /bedrock/model/bedrock/<id>/invoke-with-response-stream and Aperture rejected the resulting action.
Claude Code on Bedrock resolves models from
ANTHROPIC_DEFAULT_{OPUS,SONNET,HAIKU}_MODEL set in ProviderEnv and the
user can switch with /model at runtime, so the launch-time picker adds
no value. Add an optional BackendModelSelector interface so a profile
can opt out of the picker per backend, and route the TUI's existing
ModelSelector checks through a single helper that consults it.
Each agent CLI (Claude Code, Codex, Gemini, OpenCode) now lives in its own internal/clients sub-package that owns its install, launch, env, and config-writing logic end-to-end. The TUI becomes a generic menu-stack engine driven by internal/menu descriptors; app-level state (active Aperture endpoint, YOLO mode, last-launch record, provider list) moves to internal/config. Claude Desktop remains in internal/profiles for now with a thin adapter to the new clients.Client contract; a follow-up will port it out and delete the package.
OpenCode exposes a native model switcher, so selecting a model in the Aperture TUI was redundant. The flow is now Main Menu → OpenCode → Pick Provider → launch; the user picks a model inside OpenCode itself.
Long menus (e.g. the OpenCode provider list after all models roll up) ran past single-digit shortcuts, leaving items unreachable by key. Auto-numbering now draws from 1-9, a-z, A-Z in order, skipping any key already claimed by an explicit Shortcut or Digit. On terminals wider than 80 columns, menus with 10+ rows render in two columns when the widest row fits twice across. Left/Right (and h/l) move between columns; Up/Down stay within the current column. The key handler and renderer share a single menuLayout helper so navigation can't disagree with what's drawn.
- gemini: require an https:// FQDN Aperture endpoint and abort with a clear message otherwise. Gemini CLI 0.40+ rejects non-https custom base URLs (except literal localhost / 127.0.0.1) and strips any trailing slash. Also set GOOGLE_GEMINI_BASE_URL alongside the legacy GEMINI_BASE_URL so the new CLI picks up the gateway. - tui: allow hidden shortcut rows (Settings "s", Install "i", endpoint "a" add / "d" delete) to fire from shortcut keys, and don't move the cursor onto a hidden row when activating it — that was causing the endpoints "d" handler to read a nonsense cursor and hang. - tui: drop the redundant "Add endpoint" row from the endpoints menu; "a" shortcut in the footer is the single entry point. - codex, gemini: keep the pre-refactor config paths (aperture/codex-home, aperture/gemini-home) so existing on-disk state is still found.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Huge refactor to restructure the code so each harness (internal/clients) has its own implementation. This allows specific logic to easier to manage and in an intuitive location.