Skip to content

feat(ui): add instance-scoped provider manager from model selector#476

Merged
shantur merged 4 commits into
devfrom
provider-management
May 17, 2026
Merged

feat(ui): add instance-scoped provider manager from model selector#476
shantur merged 4 commits into
devfrom
provider-management

Conversation

@shantur
Copy link
Copy Markdown
Collaborator

@shantur shantur commented May 17, 2026

Summary

  • move provider authentication management out of global settings and into the model selector so it always runs against a live OpenCode instance
  • add an instance-scoped provider manager modal with API-key and OAuth flows, cancellable OAuth waiting, and provider discovery/configuration details
  • handle configured provider sources differently so config- and env-backed providers are explained in the UI while auth-backed providers can be disconnected safely

Validation

  • npm run typecheck --workspace @codenomad/ui
  • npm run build --workspace @codenomad/ui

Move provider authentication management out of global settings and into the model selector so the flow always runs against a live OpenCode instance with project-specific provider state. This lets users inspect configured providers, start new connections, and complete OAuth flows directly from the place where model selection fails or needs additional setup.

Implement a provider manager modal launched from the model selector footer, with source-aware configured provider handling, provider auth discovery, API-key and OAuth connection flows, cancellable OAuth waiting, and config-driven restrictions for providers declared in opencode.jsonc or environment variables. The UI keeps the existing square-corner visual language, shows richer provider details in the connect picker, and refreshes provider state through global dispose after auth changes so newly connected providers appear immediately.

Validation: npm run typecheck --workspace @codenomad/ui; npm run build --workspace @codenomad/ui
@shantur
Copy link
Copy Markdown
Collaborator Author

shantur commented May 17, 2026

@CodeNomadBot

I want you to perform the gatekeeper checks for latest PR state

check for regressions
check for better possible implementations
Be the PR gatekeeper
Be ruthless about code quality

@codenomadbot
Copy link
Copy Markdown
Contributor

codenomadbot Bot commented May 17, 2026

Gatekeeper review for latest PR state.

Understood request: review PR #476 ruthlessly for regressions and implementation quality. I did not make code changes.

Findings:

  1. Blocking: OAuth prompt values are collected but are not actually sent by the current SDK client.

packages/ui/src/components/provider-auth/provider-manager-modal.tsx:242-248 builds an inputs object and passes it into provider.oauth.authorize(...). The repo pins @opencode-ai/sdk@1.2.6, and that generated client only serializes providerID, directory, and method for /provider/{providerID}/oauth/authorize; extra inputs fields are dropped before the request body is built. The generated SDK type for ProviderAuthMethod also has only { type, label }, so the new prompts shape is not part of the installed contract.

Impact: any provider OAuth method that requires prompt data can render UI fields, accept user input, then authorize without those values. That flow will fail or silently use incomplete auth details. This needs either an SDK/API update that supports prompt inputs end-to-end, or a different request path that explicitly sends those fields.

  1. High: closing the modal with the header X bypasses OAuth wait cancellation and flow reset.

packages/ui/src/components/provider-auth/provider-manager-modal.tsx:385 calls props.onOpenChange(false) directly. The reset/abort logic only runs in the Dialog onOpenChange wrapper at packages/ui/src/components/provider-auth/provider-manager-modal.tsx:370-373, so this close path does not call resetFlow(null) and does not abort callbackAbortController.

Impact: if the user starts an auto OAuth wait and closes via the visible X button, the callback request can keep running after the modal is closed, then refresh providers/reset state later. Reopening can also expose stale flow state until another path resets it. The close button should go through the same close handler/reset path as Escape/overlay close.

  1. Medium: the config-update disconnect path is wired with the wrong SDK parameter shape.

packages/ui/src/components/provider-auth/provider-manager-modal.tsx:334-339 calls authClient.config.update({ ...configData(), disabled_providers: disabledProviders }). The installed v2 SDK expects config.update({ config: nextConfig, directory? }); the generated client maps only the config key into the PATCH body. Passing the config object directly means disabled_providers is not placed under the config parameter and will not be sent as intended.

This path is currently hidden for disable-in-config providers by packages/ui/src/components/provider-auth/provider-manager-modal.tsx:543, but the implementation is still incorrect and will break immediately if exposed or invoked from another UI path.

Quality concerns:

  • packages/ui/src/components/provider-auth/provider-manager-modal.tsx is already 554 lines. That crosses the repo’s source-file warning threshold. I would not block solely on size, but this modal now mixes SDK adapters, provider normalization, auth state machine, disconnect behavior, and all rendering in one component. The SDK-contract bugs above are exactly the kind of issue that a thin typed provider-auth adapter would have caught.

Validation:

  • Attempted npm run typecheck --workspace @codenomad/ui, but this workspace does not have dependencies resolved (solid-js, @opencode-ai/sdk, Tauri packages, etc. are missing from node_modules), so local validation cannot be trusted here.
  • Inspected the pinned @opencode-ai/sdk@1.2.6 package contract separately to verify the SDK serialization issues above.

Gatekeeper verdict: changes requested. I would not merge until findings 1 and 2 are fixed; finding 3 should also be corrected while touching the same provider auth integration.

--
Yours,
CodeNomadBot

Prevent the provider manager modal from leaving stale OAuth work behind by routing the header close button through the shared dialog close handler, which now consistently resets local state and aborts any in-flight OAuth callback wait before the modal disappears.

Align the source-aware disconnect path with the pinned SDK contract by sending config updates under the expected config payload key. Also stop advertising prompt-based OAuth methods as working in this SDK build: when a provider requires extra OAuth prompt inputs, the modal now explains that the current client cannot submit them instead of collecting values that would be dropped from the request.

Validation: npm run typecheck --workspace @codenomad/ui; npm run build --workspace @codenomad/ui
@shantur
Copy link
Copy Markdown
Collaborator Author

shantur commented May 17, 2026

@CodeNomadBot

check again

@github-actions
Copy link
Copy Markdown

PR builds are available as GitHub Actions artifacts:

https://github.com/NeuralNomadsAI/CodeNomad/actions/runs/25995220743

Artifacts expire in 7 days.
Artifacts:

  • pr-476-838fac592a28fb8b87e11396f99036406a46714c-electron-windows
  • pr-476-838fac592a28fb8b87e11396f99036406a46714c-electron-linux

@codenomadbot
Copy link
Copy Markdown
Contributor

codenomadbot Bot commented May 17, 2026

Follow-up gatekeeper review for latest PR state (683688b).

Understood request: re-check the PR after the latest push and verify the prior gatekeeper findings. I did not make code changes.

Status of prior findings:

  • Prior blocking OAuth prompt serialization issue: addressed enough for this SDK version. The PR no longer sends dropped inputs; it disables OAuth methods that advertise prompt requirements and shows an unsupported-message instead.
  • Prior modal X close / OAuth wait leak: addressed. The header X now goes through handleModalOpenChange(false), which calls resetFlow(null) and aborts the callback wait.
  • Prior config.update parameter shape issue: addressed. The update now sends { config: { ... } }, matching the pinned SDK shape.

Remaining finding:

  1. High: browser OAuth launch can be silently blocked, leaving users stuck without a fallback link.

packages/ui/src/components/provider-auth/provider-manager-modal.tsx:249-263 awaits provider.oauth.authorize(...) and only then calls openExternalUrl(data.url, "provider-auth"). In the non-Tauri/browser path, openExternalUrl falls back to window.open(...) (packages/ui/src/lib/external-url.ts:18-20). Because this happens after an awaited network request, it is no longer reliably inside the original user activation; browser popup blockers commonly block that window.open. The helper also does not detect window.open(...) === null, and the modal does not render the authorization URL as a manual fallback.

Impact: on web/remote UI, clicking Continue can move the flow to code/waiting while no provider auth page opens and the user has no link to click or copy. This is especially risky because the feature is now launched from the model selector and is expected to work against live instances, including non-Tauri access.

Suggested fix: after authorization data is returned, render an explicit “Open authorization page” link/button using authorization().url in the code/waiting state, and ideally use that as the primary user-gesture action for browser hosts. At minimum, detect window.open failure/null and keep the user on an actionable state with a visible link.

Quality notes:

  • packages/ui/src/components/provider-auth/provider-manager-modal.tsx is now ~560 lines, still over the repo’s source-file warning threshold. Not a merge blocker by itself, but this component is carrying SDK calls, auth state machine, normalization, disconnect behavior, and rendering. A typed provider-auth adapter remains the better long-term implementation.
  • The locale files include the new keys, but the non-English locales still use English strings for these additions. That follows the existing fallback-ish pattern in this PR, but it is a product-quality gap.

Validation:

  • npm run typecheck --workspace @codenomad/ui could not be trusted in this workspace because dependencies are unresolved/missing from node_modules (solid-js, @opencode-ai/sdk, Tauri packages, etc.).
  • npm run build --workspace @codenomad/ui also fails for the same environment reason (vite cannot be resolved).
  • Re-inspected the pinned @opencode-ai/sdk@1.2.6 contract to confirm the previous SDK-shape issues are now handled.

Gatekeeper verdict: changes still requested because of the browser OAuth launch/fallback issue. The previous blocking SDK integration issues are fixed, but OAuth should not rely on a post-await window.open with no visible fallback URL.

--
Yours,
CodeNomadBot

@github-actions
Copy link
Copy Markdown

PR builds are available as GitHub Actions artifacts:

https://github.com/NeuralNomadsAI/CodeNomad/actions/runs/25995396978

Artifacts expire in 7 days.
Artifacts:

  • pr-476-683688b9cff47e5c0a9058fe9f03cdcff281320b-tauri-linux
  • pr-476-683688b9cff47e5c0a9058fe9f03cdcff281320b-tauri-macos
  • pr-476-683688b9cff47e5c0a9058fe9f03cdcff281320b-tauri-windows
  • pr-476-683688b9cff47e5c0a9058fe9f03cdcff281320b-electron-macos
  • pr-476-683688b9cff47e5c0a9058fe9f03cdcff281320b-tauri-macos-arm64
  • pr-476-683688b9cff47e5c0a9058fe9f03cdcff281320b-electron-windows
  • pr-476-683688b9cff47e5c0a9058fe9f03cdcff281320b-electron-linux

Improve provider authentication behavior for web and remote browser hosts by pre-opening a popup synchronously on the user gesture, then navigating that popup once the OAuth authorization URL is returned. This avoids relying on post-await window.open behavior that popup blockers commonly reject.

Keep the OAuth code and waiting states actionable even when the popup cannot be opened by surfacing explicit fallback controls to open the authorization page manually, redirect the current tab, or copy the link. The external URL helper now reports whether a browser-host open attempt actually succeeded so the modal can show clear blocked-popup guidance instead of leaving users stuck after Continue.

Validation: npm run typecheck --workspace @codenomad/ui; npm run build --workspace @codenomad/ui
@shantur
Copy link
Copy Markdown
Collaborator Author

shantur commented May 17, 2026

@CodeNomadBot

Check again

@codenomadbot
Copy link
Copy Markdown
Contributor

codenomadbot Bot commented May 17, 2026

Follow-up gatekeeper review for latest PR state (1e2edfc).

Understood request: re-check after the latest push and verify the prior OAuth fallback blocker. I did not make code changes.

Status of prior findings:

  • OAuth prompt/SDK serialization: still addressed by disabling unsupported prompted OAuth methods instead of sending dropped inputs.
  • Modal close / OAuth wait cancellation: still addressed via the shared close/reset handler.
  • config.update SDK parameter shape: still addressed with { config: ... }.
  • Browser OAuth popup/fallback issue: addressed. The flow now pre-opens a browser window during the user gesture, updates it after the async authorization call, detects blocked launch attempts via openExternalUrl(...), and renders explicit fallback actions (Open authorization page, Open in this tab, Copy link) when authorization data is available.

Remaining non-blocking concern:

  1. Medium/cleanup: a pre-opened OAuth popup can remain as a blank “Preparing authorization page...” window if provider.oauth.authorize(...) fails before an authorization URL is returned.

packages/ui/src/components/provider-auth/provider-manager-modal.tsx:354-356 creates pendingOauthPopup before submitOAuthAuthorize(...). If the SDK call throws before launchAuthorizationUrl(...) runs, the catch at packages/ui/src/components/provider-auth/provider-manager-modal.tsx:357-364 sets the modal error state but does not close the pre-opened popup or clear pendingOauthPopup. The next explicit reset will close it, but the immediate user experience is a stranded blank auth tab/window.

Suggested fix: in the catch path for OAuth authorization failures, close and clear pendingOauthPopup before setting the error state. This is small and keeps the fallback implementation tidy.

Quality notes:

  • packages/ui/src/components/provider-auth/provider-manager-modal.tsx is now ~646 lines, above the repo source-file warning threshold. Still not a merge blocker by itself, but this component has grown into a combined SDK adapter, auth state machine, and renderer. A future split would reduce the chance of SDK-contract bugs recurring.
  • New non-English locale keys are still English strings. That matches the current PR pattern but remains a localization quality gap.

Validation:

  • npm run typecheck --workspace @codenomad/ui still cannot be trusted in this workspace because dependencies are unresolved/missing from node_modules (solid-js, @opencode-ai/sdk, Tauri packages, etc.).
  • npm run build --workspace @codenomad/ui still fails for the same environment reason (vite cannot be resolved).
  • GitHub Actions artifacts were posted for the earlier 683688b build; I did not verify CI artifacts for 1e2edfc from this local environment.

Gatekeeper verdict: no remaining merge-blocking findings from my review. I would prefer the popup cleanup fix before merge, but the previous blocker is materially addressed.

--
Yours,
CodeNomadBot

@github-actions
Copy link
Copy Markdown

PR builds are available as GitHub Actions artifacts:

https://github.com/NeuralNomadsAI/CodeNomad/actions/runs/25996595695

Artifacts expire in 7 days.
Artifacts:

  • pr-476-1e2edfca4a4e7121d69bad43f733a363e2476485-tauri-linux
  • pr-476-1e2edfca4a4e7121d69bad43f733a363e2476485-tauri-macos
  • pr-476-1e2edfca4a4e7121d69bad43f733a363e2476485-tauri-windows
  • pr-476-1e2edfca4a4e7121d69bad43f733a363e2476485-electron-macos
  • pr-476-1e2edfca4a4e7121d69bad43f733a363e2476485-tauri-macos-arm64
  • pr-476-1e2edfca4a4e7121d69bad43f733a363e2476485-electron-windows
  • pr-476-1e2edfca4a4e7121d69bad43f733a363e2476485-electron-linux

Improve provider OAuth handling for remote and browser-hosted sessions by pre-opening a popup synchronously on the user gesture and navigating it only after the authorization URL is returned. This preserves browser user activation for popup-based flows while still keeping local desktop behavior unchanged.

Add explicit fallback actions in the OAuth waiting and code-entry states so users can continue even if popup launch fails: open the authorization page in a new tab, redirect the current tab, or copy the link. The external URL helper now reports whether browser-host launches actually succeed, allowing the modal to surface blocked-popup guidance instead of leaving the user stuck without a path forward.

Validation: npm run typecheck --workspace @codenomad/ui; npm run build --workspace @codenomad/ui
@github-actions
Copy link
Copy Markdown

PR builds are available as GitHub Actions artifacts:

https://github.com/NeuralNomadsAI/CodeNomad/actions/runs/25996972423

Artifacts expire in 7 days.
Artifacts:

  • pr-476-790b717d5bbc0bf1102b09c4090d4f30037b7091-tauri-linux
  • pr-476-790b717d5bbc0bf1102b09c4090d4f30037b7091-tauri-windows
  • pr-476-790b717d5bbc0bf1102b09c4090d4f30037b7091-electron-macos
  • pr-476-790b717d5bbc0bf1102b09c4090d4f30037b7091-tauri-macos
  • pr-476-790b717d5bbc0bf1102b09c4090d4f30037b7091-tauri-macos-arm64
  • pr-476-790b717d5bbc0bf1102b09c4090d4f30037b7091-electron-windows
  • pr-476-790b717d5bbc0bf1102b09c4090d4f30037b7091-electron-linux

@shantur
Copy link
Copy Markdown
Collaborator Author

shantur commented May 17, 2026

@CodeNomadBot

And now again

@codenomadbot
Copy link
Copy Markdown
Contributor

codenomadbot Bot commented May 17, 2026

Follow-up gatekeeper review for latest PR state (790b717).

Understood request: re-check after the latest push and verify the prior popup-cleanup concern. I did not make code changes.

Status of prior findings:

  • OAuth prompt/SDK serialization: still addressed by disabling unsupported prompted OAuth methods instead of sending dropped inputs.
  • Modal close / OAuth wait cancellation: still addressed via the shared close/reset handler.
  • config.update SDK parameter shape: still addressed with { config: ... }.
  • Browser OAuth popup/fallback issue: still addressed with pre-opened popup, blocked-launch detection, and explicit open/copy/same-tab fallback actions.
  • Pre-opened popup cleanup on authorization failure: addressed. submitAuth now closes and clears pendingOauthPopup in the catch path before returning to prompt/error state.

Findings:

  • No remaining merge-blocking findings from this review.

Quality notes:

  • packages/ui/src/components/provider-auth/provider-manager-modal.tsx is now ~650 lines, above the repo source-file warning threshold. This is not a merge blocker, but the component now combines SDK integration, auth flow state, popup lifecycle, provider normalization, disconnect behavior, and rendering. I would split this in a follow-up, not inside this PR unless maintainers want to pay that cost now.
  • New non-English locale keys remain English strings. This is a localization quality gap, but consistent with the PR’s current pattern.

Validation:

  • Local npm run typecheck --workspace @codenomad/ui still cannot be trusted in this workspace because dependencies are unresolved/missing from node_modules (solid-js, @opencode-ai/sdk, Tauri packages, etc.).
  • Local npm run build --workspace @codenomad/ui still fails for the same environment reason (vite cannot be resolved).
  • GitHub Actions artifacts were posted for 790b717, which indicates the external build workflow ran for this latest head, but I did not inspect the artifacts themselves from this environment.

Gatekeeper verdict: approve from review standpoint. I do not see a reason to block merge on the current code state.

--
Yours,
CodeNomadBot

@shantur shantur merged commit 4ddd256 into dev May 17, 2026
11 checks passed
@shantur shantur deleted the provider-management branch May 17, 2026 17:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant