Skip to content

feat(cli): let TUI /setup request a custom slug/name when creating a connection #4605

Description

@me2seeks

Background

Multi-account model-provider onboarding has landed:

  • feat(runtime-host): own multi-account API-key onboarding #3882 — Runtime Host owns multi-account API-key onboarding; the onboarding target is either { kind: 'existing', connectionId } or { kind: 'create', providerType }.
  • On create, the storage coordinator allocates the identity itself: connectionId = randomUUID() and slug = deriveConnectionSlug(providerType, existingSlugs) (packages/storage/src/runtime-policy/coordinator.ts), so a second OpenAI account becomes openai-2, openai-3, …
  • The TUI /setup wizard exposes both targets (one row per existing connection plus an "add account" row per provider), but has no step to choose a slug or display name.

Desktop already offers custom slug/name, but only by bypassing managed onboarding: the renderer derives a slug with existingSlugs, lets the user edit it, validates format/uniqueness in provider-add-submission.ts, then drives connection.catalog.create + credential.vault.set + model fetch as separate Host operations (with a compensating delete on failure).

Result: a TUI user with two OpenAI accounts gets openai and openai-2 and cannot tell them apart in /model, which displays name · slug. Naming is a Desktop-only privilege today, and Desktop pays for it with a multi-step, non-transactional flow.

Proposal

Let callers optionally request a slug and display name on the managed onboarding create target, keeping the Host as the allocating authority:

  1. Protocol (packages/runtime-host/src/protocol/connection-effects.ts): extend the create target with optional slug / name fields. The decoder is currently a closed shape (['kind', 'providerType']), so this needs a compatibility decision (epoch bump vs. capability negotiation) — older Hosts must not silently ignore a requested name.
  2. Storage (coordinator.ts create branch): if a slug is requested, validate it with the existing slug codec and check uniqueness against the catalog the basis already reads; on collision, reject with a typed failure (mirroring Desktop's duplicate form error) instead of silently deriving a different slug. No requested slug → keep today's deriveConnectionSlug behavior.
  3. TUI (OnboardingWizard): add an optional name/slug step for create targets, prefilled with the Host-style derived suggestion (openai-2); copy goes through the locale catalogs (en / zh-CN).

Design the fields as auth-kind-agnostic so a future OAuth onboarding path gets naming for free — OAuth multi-account arguably needs display names even more (two GitHub Copilot accounts are otherwise indistinguishable).

Non-goals

  • Renaming existing connections. slug is immutable post-create by design (catalog update pins slug/connectionId/providerType); editing name for existing connections could be a follow-up via connection.catalog.update.
  • OAuth login in the TUI itself — that is a separate feature on top of the existing oauth.login.start/query/cancel Host operations and does not conflict with this change; the only shared files are the provider-list projection / picker items.
  • Migrating Desktop's add-provider form onto managed onboarding (would give it single-transaction semantics); nice follow-up, not required here.

Acceptance

  • From TUI /setup, a user can create a second OpenAI connection named e.g. openai-work / "Work OpenAI"; it appears with that name · slug in /setup and /model.
  • Requesting a slug that already exists produces a typed, localized error in the wizard; nothing is silently renamed.
  • Omitting the slug reproduces today's derived-slug behavior exactly.
  • Protocol change is covered by contract tests, including the old-Host/new-surface compatibility story.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions