You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Problem: When a user re-runs Amicode onboarding (or installs fresh on a machine with existing LLM tool configs), they must manually re-enter API keys they've already configured elsewhere — in opencode's own auth store, shell environment variables, or other AI coding tools. This is friction that shouldn't exist.
Approach: Add an "Import existing credentials" alternative path to the Stage 0 onboarding webview. The extension host scans flat-file credential sources in priority order, deduplicates by provider, tests each key in the background, and presents a preview card (provider + source + live test status). The user picks which provider becomes their default model, confirms with one click, and all detected providers are written to opencode.json. Keys never leave the host process — the webview sees only provider names and test status.
Approaches Considered:
Approach
Verdict
Webview-only scanner (chosen)
Scans flat-file sources from the host, reuses existing testConnection and writeOnboardingConfig, keys stay in Node, cross-platform
Keychain integration (macOS security CLI)
Finds more credentials (Claude Code OAuth, Copilot tokens) but platform-specific, OAuth tokens aren't portable as static API keys, Keychain prompts confuse users
Passive notification on activation
Zero-click discovery but notifications are dismissible/missable, harder to present model selection, feels presumptuous
Scope:
New credential_scanner.ts module: scans sources in priority order, returns DetectedCredential[]
New message types: scan-credentials, scan-results, test-status-update, confirm-import
Webview UI: "Import existing credentials" link below manual form, preview card with provider rows and radio for default selection
Batch-write variant of writeOnboardingConfig (writes multiple providers in one pass)
Does NOT include: Keychain access, OAuth token import, passive auto-detection notification
Assumptions:
The opencode auth store (~/.local/share/opencode/auth.json and account.json) is the primary source and uses plaintext keys
Shell RC files use export VAR=value patterns parseable by regex without eval
Claude Code's .credentials.json fallback may contain type: "api" entries (importable) or type: "oauth" entries (skipped)
At least one detected provider must pass the connection test for "Confirm & Save" to enable
Acceptance Criteria
"Import existing credentials" link appears below the manual provider/key form in the onboarding webview
Clicking it triggers a scan and shows a status indicator: pulsing orange dot + "Searching..." (matches the devtools rebuild indicator pattern — same CSS classes, same animation)
On scan completion, the status transitions to static green dot + "Found N providers!" (or inline "No credentials found" message if empty)
On scan failure, the status transitions to static red dot + error message
Preview card shows each detected provider with: name, source label, and live connection test status (spinning/checkmark/X)
User can select which provider becomes the active model via radio selection
Default model per provider is auto-selected (first entry in PROVIDER_MODELS)
"Confirm & Save" writes all detected providers to opencode.json with correct schema (provider.<id>.options.apiKey, env as string[])
No key material is ever sent to the webview — only provider names, source labels, and test results
If no credentials are found, an inline message appears and the manual form remains available
If a source file is unreadable or malformed, it is skipped silently (logged to output channel)
Duplicate providers across sources are deduplicated (first source wins per priority order)
Connection tests run in parallel in the background and update the preview asynchronously
"Back" link returns to the manual form and drops held credentials from memory
Panel close mid-scan aborts without writing config
Scan Status Indicator (reuses devtools rebuild pattern)
Three states, matching the RebuildStatusIndicator in developer-tools.tsx:
State
Dot
Text
Trigger
searching
8px orange, pulsing (devtools-pulse keyframe)
"Searching..."
User clicks "Import existing credentials"
found
8px green, static
"Found N providers!"
Scan completes with results
failed / empty
8px red, static (failed) or no dot (empty)
Error message / "No credentials found"
Scan errors or finds nothing
Reuse the existing CSS classes: devtools-status-dot--orange, devtools-status-dot--green, devtools-status-dot--red, and the devtools-pulse keyframe animation. The onboarding webview is a separate document context, so the relevant CSS must be inlined or duplicated (not imported from the app bundle).
Security Model
Keys exist only in the extension host's memory (the DetectedCredential[] array)
Webview messages contain { provider, source, modelDefault } — never key material
Shell RC parsing uses strict regex: /^\s*export\s+([\w]+)=["']?([^"'\s]+)/ — no eval, no subshell
On "Back" or panel close, the held credential array is dropped immediately
A security test asserts no postMessage payload from host→webview contains a string field matching /key|secret|token|credential/i with value length > 8
Data Contract (host → webview messages)
// scan-status (sent immediately on scan start, then on completion){type: "scan-status",payload: {state: "searching"|"found"|"empty"|"failed",count?: number,error?: string}}// scan-results (sent once after scan completes successfully){type: "scan-results",payload: {providers: Array<{provider: string,source: string,model: string}>}}// test-status-update (sent per-provider as connection tests complete){type: "test-status-update",payload: {provider: string,ok: boolean,error?: string}}
Important
Problem: When a user re-runs Amicode onboarding (or installs fresh on a machine with existing LLM tool configs), they must manually re-enter API keys they've already configured elsewhere — in opencode's own auth store, shell environment variables, or other AI coding tools. This is friction that shouldn't exist.
Approach: Add an "Import existing credentials" alternative path to the Stage 0 onboarding webview. The extension host scans flat-file credential sources in priority order, deduplicates by provider, tests each key in the background, and presents a preview card (provider + source + live test status). The user picks which provider becomes their default model, confirms with one click, and all detected providers are written to
opencode.json. Keys never leave the host process — the webview sees only provider names and test status.Approaches Considered:
testConnectionandwriteOnboardingConfig, keys stay in Node, cross-platformsecurityCLI)Scope:
credential_scanner.tsmodule: scans sources in priority order, returnsDetectedCredential[]scan-credentials,scan-results,test-status-update,confirm-importwriteOnboardingConfig(writes multiple providers in one pass)Assumptions:
~/.local/share/opencode/auth.jsonandaccount.json) is the primary source and uses plaintext keysexport VAR=valuepatterns parseable by regex without eval.credentials.jsonfallback may containtype: "api"entries (importable) ortype: "oauth"entries (skipped)Acceptance Criteria
modelvia radio selectionPROVIDER_MODELS)opencode.jsonwith correct schema (provider.<id>.options.apiKey,envasstring[])Key Decisions
Credential Source Priority
First hit per provider wins. No Keychain access.
Provider ID Normalization
amazon-bedrock(account.json)amazon-bedrockopencode-go/opencode(account.json)opencodeANTHROPIC_API_KEY(env)anthropicOPENAI_API_KEY(env)openaiGOOGLE_API_KEY(env)googleOPENROUTER_API_KEY(env)openrouterOPENCODE_API_KEY(env)opencodeScan Status Indicator (reuses devtools rebuild pattern)
Three states, matching the
RebuildStatusIndicatorindeveloper-tools.tsx:searchingdevtools-pulsekeyframe)foundfailed/emptyReuse the existing CSS classes:
devtools-status-dot--orange,devtools-status-dot--green,devtools-status-dot--red, and thedevtools-pulsekeyframe animation. The onboarding webview is a separate document context, so the relevant CSS must be inlined or duplicated (not imported from the app bundle).Security Model
DetectedCredential[]array){ provider, source, modelDefault }— never key material/^\s*export\s+([\w]+)=["']?([^"'\s]+)/— no eval, no subshellpostMessagepayload from host→webview contains a string field matching/key|secret|token|credential/iwith value length > 8Data Contract (host → webview messages)
Data Contract (webview → host messages)
Constraints & Invariants
eval,child_process, or subshell executiontype: "api"entriesprovider.<id>.options.apiKey(nested),envasstring[], valid model IDs fromPROVIDER_MODELSPrior Art
RebuildStatusIndicatorindeveloper-tools.tsx— the orange/green/red dot + text pattern this feature replicatesdevtools-status-dotCSS classes anddevtools-pulsekeyframe inamicode.css(lines 1487-1525)hasProviderEnvVar()inonboarding_routing.ts(exported but unused — checks env vars exist)writeOnboardingConfig()inonboarding_panel.ts(existing config writer, correct schema)testConnection()inonboarding_panel.ts(existing per-provider HTTP test)Source
Sub-issue of #359 (Enhanced Onboarding: Orient > Demo > Collect)
Sub-issues