* docs: PRD for authenticated providers in Path engine * fix: show authenticated providers in Path engine
8.6 KiB
PRD — Authenticated Provider Discovery (Path Engine)
Summary
In OpenWork, when the engine source is set to Path (i.e. OpenWork spawns the user-installed opencode binary), the model picker can incorrectly show only free Zen (opencode) models and omit providers/models the user is already authenticated to.
Users should at minimum see all providers the engine is authenticated to ("connected"), and ideally see the full provider catalog with clear connection status.
This PRD proposes:
- Parity: Path engine should use the same credentials/config as the user’s normal
opencodeCLI. - Visibility: OpenWork should surface why providers are missing (missing env, missing auth store, wrong binary).
- Recovery: Provide an in-app way to restore the expected provider list (import env, select auth location, or connect providers).
Problem
What users experience
- Toggle Engine source → Path.
- Connect/start the engine successfully.
- In the model picker, only free Zen models appear.
- Providers/models the user can use in the terminal (OpenAI, Anthropic, etc.) do not appear.
This breaks a core expectation: OpenWork is a UI for OpenCode, not a different runtime.
Why it matters
- The UI appears “broken” or “limited to free”.
- Users can’t pick the model/provider they already pay for.
- It increases support burden (“works in terminal, not in app”).
Root Cause (Current Behavior)
OpenWork’s model list is driven by OpenCode server APIs:
client.config.providers()(OpenCodeGET /config/providers) returns configured/available providers + default model mapping.- On the server this uses
Provider.list().
- On the server this uses
OpenCode determines which providers are “connected” primarily via:
- Environment variables (e.g.
OPENAI_API_KEY,ANTHROPIC_API_KEY, etc.) - OpenCode auth store (OpenCode
Auth.all()→${XDG_DATA_HOME}/opencode/auth.jsonby default) - Config (
opencode.jsonprovider options)
In Path engine mode, OpenWork spawns opencode serve from a GUI process (Tauri). Two common issues arise:
-
GUI apps don’t inherit shell env
- Provider env vars set in
.zshrc,.bashrc,direnv, etc. are not present. - Result: providers configured via env are “missing”.
- Provider env vars set in
-
XDG path mismatch can hide
auth.json- If the user’s terminal session sets
XDG_DATA_HOME(or related XDG vars) but the GUI app does not, OpenCode looks in a different directory forauth.json. - Result: providers configured via
opencode auth loginappear missing.
- If the user’s terminal session sets
Additionally, a third issue can confuse debugging:
- OpenWork may be running a different
opencodebinary than the user expects- If multiple installs exist (brew vs installer vs older binary), OpenWork may resolve a different executable.
- The user may have authenticated using a different installation context.
All three present the same symptom: only free Zen models are returned because OpenCode believes it has no provider credentials.
Goals
- In Path engine mode, OpenWork shows all providers the engine is authenticated to.
- OpenWork provides clear feedback when providers are missing:
- which
opencodebinary is running - which auth/config directories are being used
- whether any provider credentials were detected
- which
- Users can fix the situation without leaving the app.
Non-Goals
- Implementing a full provider marketplace or registry.
- Replacing OpenCode’s auth/config storage.
- Solving remote (Client mode) provider auth across machines (separate PRD).
Proposed Solution
1) Switch model picker to “provider.list + connected” semantics
Today, config.providers is useful but opaque: it only returns providers the server believes are configured/available.
Instead, OpenWork should prefer:
client.provider.list()(OpenCodeGET /provider) which returns:all: full provider catalogconnected: provider IDs that are authenticateddefault: default model per provider
UI behavior
- Show “Connected” providers first.
- Show non-connected providers as disabled, with a clear “Connect” CTA.
- Preserve “Zen” as the default/fallback (free works out of the box), but don’t hide the rest.
This change ensures the user always sees the catalog and can understand “you’re not connected” vs “OpenWork is broken”.
2) Add a provider/auth diagnostics surface (Settings → Providers)
Add a compact diagnostics block (no secrets displayed):
- Engine source: Path | Sidecar
- Resolved
opencodebinary (fromengine_doctor.resolvedPath) - Server version (
/global/health.version) - Effective workspace directory (what OpenCode sees via
client.path.get()) - Connected providers count + list of IDs
- Detected auth store location (see section 3)
Include a “Refresh providers” action that re-fetches provider.list().
3) Make Path engine use the same auth/config directories as the user’s CLI
3a) Detect likely auth store locations
On start/connect, OpenWork should locate OpenCode’s auth store (without requiring the user to know XDG).
Detection strategy (ordered):
- If
XDG_DATA_HOMEis present in the OpenWork process env, use it. - If absent, compute default XDG data dir for the platform.
- Additionally, probe common legacy locations (if any exist) and present the chosen one in diagnostics.
3b) Optional: import XDG env from login shell (user-consented)
If providers are unexpectedly missing, offer a safe recovery step:
- Button: Import shell environment (recommended)
- Explain: “GUI apps don’t inherit shell env. This will read your login shell environment variables and restart the engine. Only provider-related variables are imported.”
Implementation intent:
- Spawn the user’s login shell (e.g.
$SHELL -lc env) and parse. - Import only an allowlist:
XDG_*variables- Provider key env vars (derived from OpenCode ModelsDev provider env metadata)
- Restart engine with the merged env.
4) Provide an in-app provider connection path
Even with env import, the best long-term UX is first-class provider connection inside OpenWork.
Minimum viable flow:
- For providers that support API key auth:
- Prompt for key → call
client.auth.set({ providerID, type: "api", key })
- Prompt for key → call
- For providers that support OAuth:
- Display guided steps and deep link to authorization URL using
client.provider.auth()+ OAuth endpoints.
- Display guided steps and deep link to authorization URL using
This avoids reliance on shell env and makes Path engine work identically to Sidecar.
User Flows
Flow A — User already has opencode auth credentials
- User selects Engine source → Path.
- Starts engine.
- OpenWork calls
provider.list(). - Connected providers appear.
If connected providers are missing:
- Settings → Providers shows:
- binary path
- auth store location
- connected providers = 0
- User clicks “Import shell environment” (or “Select auth store…” if we add it).
- Engine restarts.
- Connected providers appear.
Flow B — User relies on env vars only (no auth store)
- User starts Path engine.
- Providers show as “Not connected”.
- OpenWork offers:
- “Import shell environment” (brings env vars into engine)
- or “Connect” (stores key via
auth.set)
Flow C — Multiple opencode installs
- OpenWork shows resolved binary path.
- If it differs from the user’s expected install, user can:
- set
OPENCODE_BIN_PATH(already supported by engine doctor), or - choose a binary via a future “Pick engine binary…” UI.
- set
Acceptance Criteria
- With Engine source = Path:
- If the user has provider credentials configured via
opencode auth login, OpenWork shows those providers as connected. - If the user has provider credentials configured only via shell env, OpenWork can import those variables (with consent) and providers become connected.
- If no credentials exist, OpenWork shows the provider catalog but clearly marks providers as “Not connected” (no more “only free exists” ambiguity).
- If the user has provider credentials configured via
- The model picker includes paid Zen models when the engine has an opencode key.
- Diagnostics page never prints secret values.
Open Questions
- Should OpenWork ever persist imported env vars, or only apply them for the engine process lifetime?
- Where should OpenWork store user-entered keys?
- In OpenCode’s
auth.jsonviaauth.set(simple, portable) - In OS keychain, then sync into
auth.seton engine start (more secure)
- In OpenCode’s
- How should Sidecar and Path engines share auth?
- Prefer a shared auth store for parity.
- If isolation is required, provide an explicit “Import credentials from system OpenCode” action.