Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
7.2 KiB
Runtime checks
Results of the verification list in docs/spec.md. Run on 2026-10-03 against opencode v2.0.22 (Windows), in an isolated profile (XDG_CONFIG_HOME, XDG_DATA_HOME, XDG_CACHE_HOME, XDG_STATE_HOME pointed at a temp folder, a private opencode serve on a local port, a mock fastCRW server on another), so the real opencode config was never touched.
Summary
| # | Check | Result |
|---|---|---|
| 1 | Built-in providers removable or disableable | Done: not removable by a plugin, but never used as a fallback |
| 2 | Throwing setup disables only this plugin |
Done: yes, loudly |
| 3 | Config websearch.provider vs editor.default.set |
Done: config wins |
| 4 | Install by name from the Gitea registry | Not run (needs a published release) |
| 5 | Release job token has package-write rights | Not run (needs a release run) |
| 6 | @opencode/plugin install weight |
Done |
| 7 | npm run smoke against the real fastCRW server |
Done |
| 8 | Typings, time shape, 429 cooldown |
Done: a 429 from our plugin triggers no cooldown |
How plugins load (read this first)
Read from the opencode binary and confirmed with hello-world plugins:
- A
pluginsentry inopencode.jsonthat points at a file is ignored with the warning "configured plugin path must be a directory". It must be a directory, or an npm package name. - A local directory is resolved by file path:
<dir>/serverthen<dir>/index. The directory'spackage.jsonexportsare not used. This repo has no rootindex.js(the entry isdist/index.js), so pointing a config entry at the repo or an unpacked package directory loads nothing and logs nothing. My first three attempts failed for this reason. - An npm package name resolves through
exports(<name>/server, then<name>), soexports["."]works for registry installs (still unverified end to end, check 4). - Auto-discovery in
plugins/folders (global config dir or project.opencode/) accepts only.tsand.jsfiles and directories..mjsfiles are ignored. ~paths are not expanded; use./relative, an absolute path orfile://. The~/.config/opencode/plugins/fastcrwexample in the original sketch does not work.- Loading is lazy: the plugin list is empty for several seconds after startup or
POST /api/location/reload.
With a one-line directory shim (index.js: export { default } from "file:///.../dist/index.js") the real plugin loaded, registered provider fastcrw, honoured baseURL and limit from the plugin options, and returned the mapped result for POST /api/websearch.
Findings
1. Built-in providers
The plugin API has no way to remove a provider (WebSearchEditor only has add and default), so the built-ins (exa, firecrawl, parallel, tavily, tinyfish) stay registered. In practice fastCRW is still the only one used:
- With no
websearchconfig, the plugin'seditor.default.set("fastcrw")makes it the default. - When fastCRW was unreachable or returned 429, opencode answered 503 "Web search request failed: fastcrw". It did not fall back to a built-in.
- If no default is set (for example the plugin failed to load), a query without a provider id is rejected with 400 "Web search provider is required".
2. Throwing setup
With baseURL: "nope", only this plugin ended up in state failed, with our message and stack (fastCRW: invalid baseURL "nope" (expected an http(s) URL)), and a WARN failed to load plugin line was logged. The other plugins stayed active and the server kept running. The provider is then not registered. The plan holds: fail loudly, no silent search elsewhere.
3. Precedence
Config websearch.provider wins over the plugin's editor.default.set: with the config set to exa, a query without a provider id went to Exa (and worked without a key, so the query left the machine). With the config unset or set to fastcrw, the plugin's provider was used. Consequence: do not set websearch.provider to anything else; setting it to fastcrw is harmless and explicit.
6. @opencode/plugin install weight
npm install of @opencode/plugin@2.0.22 adds about 100 MB of transitive dependencies (mainly effect, OpenTelemetry, @redis). Its peers solid-js, @opentui/core, @opentui/solid and @opencode/theme are optional and not installed. Decision for the first release: keep it in dependencies as the docs say; revisit if install size matters.
7. Smoke test
Run against http://crw.home and http://192.168.2.15:8013 (the same server; earlier attempts failed on DNS, a refused connection and an upstream 502 until the service came up). No token was needed. The envelope is { success, data } and data is an object, so this server returns the self-hosted shape data.results, not a flat array; the plugin's dual-shape parser handles it. Each result has url, title, description, snippet, position, score and category.
8. Typings, time, 429
From @opencode/schema 2.0.22: a result is { url: string, title?: string, content?: string, time: { published?: number } }, so time: {} is valid; execute receives ({ query }, { signal }); editor.default.set(selection: string | false). A 429 from fastCRW surfaced as a plain failure: three queries in a row all reached the server, so no provider cooldown was triggered. opencode also shows only the generic "Web search request failed: fastcrw" through the API; our detailed message was not visible there or in the debug log.
Follow-ups this implies
- Local installs need a root
index.js. Addindex.js(export { default } from "./dist/index.js") to the package andfiles, so a config entry pointing at an unpacked package directory works as well as an npm-name install. The release script stagespackage.jsonanddist/only and would need to include it. - Update the README and spec examples: no
~paths, and a directory (not a file) for localpluginsentries. - Optional: decide whether the generic user-facing error is acceptable, since our detailed messages are not surfaced.
- Still to verify after the first release: check 4 (install by name with a scoped
.npmrc) and check 5 (job token package-write rights).
Useful for later runs
opencode serve --hostname 127.0.0.1 --port <p>prints a generated server password; HTTP Basic auth with useropencodeworks.GET /openapi.jsonlists the API.- Relevant operations:
GET /api/plugin(showsstate, including load errors),GET /api/websearch/provider,POST /api/websearch(bodyquery, optionalproviderID),POST /api/location/reload. opencode debug confighung in an empty profile;opencode plugin addwrites to the global config, so avoid it on a real machine.