Skip to content

Wait for OpenCode plugin activation before integration calls - #486

Open
chriswritescode-dev wants to merge 1 commit into
mainfrom
fix/opencode-2-3-real-binary-tests
Open

chriswritescode-dev wants to merge 1 commit into
mainfrom
fix/opencode-2-3-real-binary-tests

Conversation

@chriswritescode-dev

@chriswritescode-dev chriswritescode-dev commented Nov 13, 2026 •

Copy link
Copy Markdown
Owner

User description

Summary

OpenCode 2.3.0 no longer waits for plugin activation in its integration.list handler. On a directory instance that has just started, the integration endpoints therefore act on a half-loaded registry for about 100ms:

  • integration.list returns an empty or partial catalogue, without plugin-provided forms such as GitHub Copilot's deploymentType/enterpriseUrl.
  • integration.connect.key for Azure succeeds without the required resourceName field and stores an invalid credential.

OpenCode's provider.list handler still waits for plugin activation (awaitPlugins, failing with ServiceUnavailableError after 5s) in both 2.2.5 and 2.3.0. A new shared helper, awaitOpenCodePlugins, calls it first. The provider credential routes, the OAuth auth-methods and authorize routes, and the frontend's getIntegrations now await it before touching integrations.

This replaces runWhenIntegrationReady, which retried only on IntegrationNotFoundError and could not catch the partial state, so it and its tests are removed.

Validation

  • With OpenCode 2.3.0 installed, the two real-binary tests that failed on main now pass: "connects an azure key together with its required resourceName form field" (its manual warm-up call is removed, so it proves the route waits) and "maps the real integration catalogue and starts and cancels an OAuth attempt".
  • New unit tests check that provider.list runs before connect.key and integration.list, and that a failed activation wait does not connect the key.
  • Backend vitest run --coverage: 4,281 passed, none skipped. Statements 90.18%, branches 85.3%.
  • Frontend: 4,055 passed. CLI: 468 passed.
  • pnpm lint and pnpm typecheck are clean.

PR Type

Bug fix, Enhancement


Description

  • Add shared awaitOpenCodePlugins helper calling provider.list

  • Await plugin activation before credential and OAuth integration calls

  • Remove retry-based runWhenIntegrationReady helper and its tests

  • Stub provider.list; add activation-wait and failure tests


Diagram Walkthrough

flowchart LR
  REQ["Backend / frontend integration request"] -- "call helper" --> AWAIT["awaitOpenCodePlugins(api)"]
  AWAIT -- "provider.list" --> PLUGINS["OpenCode plugin registry ready"]
  PLUGINS -- "then proceed" --> INTEG["integration.list / get / connect.key / oauth.connect"]
  PLUGINS -- "activation failure" --> ERR["502 error, no integration call"]
Loading

File Walkthrough

Relevant files
Bug fix
3 files
oauth.ts
Await plugin activation before OAuth integration calls     
+8/-8     
providers.ts
Gate credential routes on plugin activation                           
+8/-10   
oauth.ts
Await plugins before listing integrations                               
+5/-1     
Enhancement
1 files
index.ts
Add awaitOpenCodePlugins, drop not-found error export       
+5/-1     
Tests
4 files
oauth.test.ts
Test activation wait, remove retry test                                   
+13/-13 
providers.test.ts
Add activation-wait tests, drop retry coverage                     
+17/-40 
stub-opencode-client.ts
Stub provider namespace list method                                           
+4/-0     
integration-ready.test.ts
Delete obsolete integration-ready helper tests                     
+0/-98   
Miscellaneous
1 files
integration-ready.ts
Delete retry-based integration readiness helper                   
+0/-45   

Comment on lines +239 to +241
export async function awaitOpenCodePlugins(api: Pick<OpenCodeApi, 'provider'>): Promise<void> {
await api.provider.list()
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heavy readiness probe

awaitOpenCodePlugins calls provider.list() and discards the result, so it is used only for its wait-for-plugins side effect. That call returns the whole provider and model catalogue, which is large. It now runs before every credential status check, credential delete, key connect, auth-methods request, authorize request, and frontend getIntegrations. A UI that checks status for each provider downloads the catalogue N times. Consider waiting once per OpenCode instance or directory and caching the result, or using a lighter readiness signal if one exists.

Comment on lines +116 to +117
await awaitOpenCodePlugins(openCodeClient.api)
await openCodeClient.api.integration.connect.key(connectInput)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Status code mapping

A plugin-activation timeout (ServiceUnavailableError) goes through handleOpenCodeError and comes out as a 502. The new test asserts this. The condition is temporary and safe to retry, so a 503 would be more accurate and would let clients retry. This is minor and only matters if a client treats 502 and 503 differently.

@lil-worker-cstech

Copy link
Copy Markdown
Contributor

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 2 🔵🔵⚪⚪⚪
⚠️ Risk level: Low
✅ Merge recommendation: Needs review
🔎 Failure modes

What: Each credential and OAuth request now downloads OpenCode's full provider catalogue just to wait for plugins to load. The response body is thrown away. A screen that checks credential status for many providers repeats this download once per provider. That adds latency and payload size to every call.
Where: shared/src/opencode/index.ts:awaitOpenCodePlugins
Trigger: The UI calls `/providers/:id/credentials/status` once for each provider, or calls `getIntegrations` often.
Detected by: nothing
Covered in this PR: No

What: When plugin activation takes longer than OpenCode's 5s limit, `provider.list` fails with `ServiceUnavailableError`. The routes now pass that error to the client as a 502 with no retry. The removed `runWhenIntegrationReady` used to retry briefly before giving up.
Where: backend/src/routes/providers.ts:POST /:id/credentials
Trigger: A directory instance starts cold and its plugins load slowly, for example on a first run or a slow disk.
Detected by: Unit test "returns the plugin-activation failure without connecting the key". It checks that the error is passed through, not that the request recovers.
Covered in this PR: Yes

What: The frontend's `getIntegrations` now depends on `provider.list` succeeding. Any failure there, even one unrelated to plugin loading, stops the integrations list from loading at all.
Where: frontend/src/api/oauth.ts:getIntegrations
Trigger: `provider.list` returns an error, for example from an upstream provider-config problem, while `integration.list` would have worked.
Detected by: nothing
Covered in this PR: No

🧪 PR contains tests
🔒 No security concerns identified

@lil-worker-cstech

Copy link
Copy Markdown
Contributor

PR Code Suggestions ✨

No code suggestions found for the PR.

This branch has not been deployed

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant