Repository navigation
Conversation
plugin.list read the registry without waiting for plugin activation, so a cold-location call could return an empty list while the configured plugins were still registering (same race already fixed for mcp.list, skill.list, agent.list and model.list). Await plugin activation before listing, matching model.list, and add the endpoint's ServiceUnavailableError to the protocol contract (regenerating the client). A regression test registers a local plugin that becomes available only after setup completes; without the await, plugin.list returns [] and the test fails.
Keeps these endpoints consistent with plugin.list so a cold location can't fail with a false 'not in the current server inventory'. Declares ServiceUnavailableError on plugin.check and regenerates the client/openapi.
|
The following comment was made by an LLM, it may be inaccurate: |
There was a problem hiding this comment.
This fixes #54654. On the base, the reproduction script printed "No plugins found" on the first plugin list in every new directory. With this PR the plugin is listed on the first call. The new test fails on the base and passes here, and typechecks pass for server, protocol and client.
Public API: GET /api/plugin (plugin.list) and POST /api/plugin/check (plugin.check) now declare a 503 ServiceUnavailableError in the protocol, openapi.json and the generated client. Status lists change from [400, 401, 404] to [400, 401, 404, 503]. This is backwards compatible: nothing is removed, but clients may now get a 503 from these endpoints.
Design question for maintainers: commit 8905af5 ("refactor(protocol): remove plugin activation wait") deliberately removed the activation waits from plugin.check and plugin.update. Its audit note says "Activation timing is an internal server concern. Catalog reads remain non-blocking." This PR brings the waits back and adds one to plugin.list, so a maintainer should confirm this is wanted. The description also says the same race is already fixed this way for mcp.list, skill.list and agent.list, but none of those handlers wait for activation on dev (see #51282, #51680, #54192). There is also a possible regression, noted inline: a 503 when activation takes longer than 5 seconds.
| "plugin.list", | ||
| () => | ||
| Effect.gen(function* () { | ||
| yield* awaitPlugins |
There was a problem hiding this comment.
awaitPlugins waits for the whole activation, including the second pass that installs missing package plugins. If it takes more than 5 seconds it fails with a 503 ("Location plugins did not finish activating"). Before this change, plugin list in a new directory with a slow or stuck npm install still showed the local plugins and any failures. Now it errors, and this is the command people use to diagnose plugin problems. Could plugin.list fall back to the current inventory on timeout instead of failing? check and update have the same issue: they would 503 instead of acting on the plugins already loaded.
…nfigured' race opencode plugin list/check/update against a not-yet-activated background service reads an empty plugin inventory (anomalyco/opencode#54654, fix pending in anomalyco/opencode#56998), so the first call after a cold start can report a configured plugin as missing. Re-running succeeds.
Issue for this PR
Closes #54654
Type of change
What does this PR do?
plugin.listread the plugin registry without waiting for plugin activation, so the first call on a cold location returned an empty list while configured plugins were still registering (opencode plugin list→ "No plugins found", exit 0; the second call lists everything). Same cold-location race already fixed formcp.list,skill.list,agent.list, andmodel.list.plugin.list,plugin.check, andplugin.updatenowyield* awaitPluginsbefore reading the inventory, matching themodel.listpattern.ServiceUnavailableErroronplugin.listandplugin.checkin the protocol (the await can fail if activation times out);plugin.updatealready declared it.This is a re-open of #54676, which was auto-closed for a non-compliant PR description — code is unchanged apart from the rebase onto current dev.
How did you verify your code works?
packages/server/test/plugin-endpoint.test.tsuses a fixture plugin that only becomes available after its async setup completes: without the await,plugin.listreturns[]and the test fails; with it, the plugin is listed. Passes 1/1.bun typecheckclean inpackages/protocol,packages/server,packages/client.bun run generateinpackages/clientproduces no further diff.Screenshots / recordings
N/A (no UI change)
Checklist
Added by the opencode bot (AI-generated).
Related: #51282, #51680, #54192, #53724
Public API: plugin.list and plugin.check now declare a 503 ServiceUnavailableError (backwards compatible)