From e73250f54baf469f5312dadda0a9aede764d742a Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sat, 1 Aug 2026 05:07:35 +0800 Subject: [PATCH] feat(plugins): externalize OpenCode provider (#117035) * feat(plugins): externalize OpenCode provider * docs: refresh OpenCode provider map --- docs/docs_map.md | 2 +- docs/plugins/plugin-inventory.md | 8 +-- docs/plugins/reference/opencode.md | 2 +- docs/providers/opencode.md | 2 +- extensions/opencode/README.md | 16 +++++ extensions/opencode/index.ts | 2 +- extensions/opencode/package.json | 24 ++++++- package.json | 1 + .../official-external-provider-catalog.json | 63 +++++++++++++++++++ src/cli/plugins-location-bridges.test.ts | 1 + .../official-external-plugin-catalog.test.ts | 21 +++++++ .../bundled-plugin-build-entries.test.ts | 10 ++- 12 files changed, 141 insertions(+), 11 deletions(-) create mode 100644 extensions/opencode/README.md diff --git a/docs/docs_map.md b/docs/docs_map.md index 5dc0e9250055..352c9fddd966 100644 --- a/docs/docs_map.md +++ b/docs/docs_map.md @@ -8595,7 +8595,7 @@ Do not edit it by hand; run `pnpm docs:map:gen`. - Headings: - H2: Getting started - H2: Config example - - H2: Built-in catalogs + - H2: Provider catalogs - H3: Zen - H3: Go - H2: Advanced configuration diff --git a/docs/plugins/plugin-inventory.md b/docs/plugins/plugin-inventory.md index 0cdc8038ba36..df2ecc079383 100644 --- a/docs/plugins/plugin-inventory.md +++ b/docs/plugins/plugin-inventory.md @@ -51,7 +51,7 @@ Each entry lists the package, distribution route, and description. ## Core npm package -57 plugins +56 plugins - **[admin-http-rpc](/plugins/reference/admin-http-rpc)** (`@openclaw/admin-http-rpc`) - included in OpenClaw. OpenClaw admin HTTP RPC endpoint. @@ -133,8 +133,6 @@ Each entry lists the package, distribution route, and description. - **[openai](/plugins/reference/openai)** (`@openclaw/openai-provider`) - included in OpenClaw. Adds OpenAI model provider support to OpenClaw. -- **[opencode](/plugins/reference/opencode)** (`@openclaw/opencode-provider`) - included in OpenClaw. Adds OpenCode model provider support to OpenClaw. - - **[opencode-go](/plugins/reference/opencode-go)** (`@openclaw/opencode-go-provider`) - included in OpenClaw. Adds OpenCode Go model provider support to OpenClaw. - **[openrouter](/plugins/reference/openrouter)** (`@openclaw/openrouter-provider`) - included in OpenClaw. Adds OpenRouter model provider support to OpenClaw. @@ -169,7 +167,7 @@ Each entry lists the package, distribution route, and description. ## Official external packages -88 plugins +89 plugins - **[acpx](/plugins/reference/acpx)** (`@openclaw/acpx`) - npm; ClawHub. OpenClaw ACP runtime backend with plugin-owned session and transport management. @@ -281,6 +279,8 @@ Each entry lists the package, distribution route, and description. - **[novita](/plugins/reference/novita)** (`@openclaw/novita-provider`) - npm; ClawHub: `clawhub:@openclaw/novita-provider`. Adds Novita, Novita AI, Novitaai model provider support to OpenClaw. +- **[opencode](/plugins/reference/opencode)** (`@openclaw/opencode-provider`) - npm; ClawHub: `clawhub:@openclaw/opencode-provider`. Adds OpenCode model provider support to OpenClaw. + - **[openshell](/plugins/reference/openshell)** (`@openclaw/openshell-sandbox`) - npm; ClawHub. OpenClaw sandbox backend for the NVIDIA OpenShell CLI with mirrored local workspaces and SSH command execution. - **[parallel](/tools/parallel-search)** (`@openclaw/parallel-plugin`) - npm; ClawHub: `clawhub:@openclaw/parallel-plugin`. Adds web search provider support. diff --git a/docs/plugins/reference/opencode.md b/docs/plugins/reference/opencode.md index 9b37e98b6cd8..4a286020c55c 100644 --- a/docs/plugins/reference/opencode.md +++ b/docs/plugins/reference/opencode.md @@ -12,7 +12,7 @@ Adds OpenCode model provider support to OpenClaw. ## Distribution - Package: `@openclaw/opencode-provider` -- Install route: included in OpenClaw +- Install route: npm; ClawHub: `clawhub:@openclaw/opencode-provider` ## Surface diff --git a/docs/providers/opencode.md b/docs/providers/opencode.md index 16b982f71435..4c2fc132d78c 100644 --- a/docs/providers/opencode.md +++ b/docs/providers/opencode.md @@ -90,7 +90,7 @@ one OpenCode setup. } ``` -## Built-in catalogs +## Provider catalogs ### Zen diff --git a/extensions/opencode/README.md b/extensions/opencode/README.md new file mode 100644 index 000000000000..05099a52d083 --- /dev/null +++ b/extensions/opencode/README.md @@ -0,0 +1,16 @@ +# OpenCode Zen OpenClaw provider + +Official OpenClaw provider plugin for the OpenCode Zen model catalog, image +understanding, and native OpenCode session browsing. + +## Install + +```sh +openclaw plugins install @openclaw/opencode-provider +openclaw gateway restart +``` + +## Docs + +See `docs/providers/opencode.md` in the OpenClaw repository, or the published +docs at `https://docs.openclaw.ai/providers/opencode`. diff --git a/extensions/opencode/index.ts b/extensions/opencode/index.ts index 16aa63e333c5..1f566b56dc38 100644 --- a/extensions/opencode/index.ts +++ b/extensions/opencode/index.ts @@ -53,7 +53,7 @@ function isModernOpencodeModel(modelId: string): boolean { export default defineSingleProviderPluginEntry({ id: PROVIDER_ID, name: "OpenCode Zen Provider", - description: "Bundled OpenCode Zen provider plugin", + description: "OpenCode Zen provider plugin", manifest, provider: { label: "OpenCode Zen", diff --git a/extensions/opencode/package.json b/extensions/opencode/package.json index 27511d320844..30cf049a70f3 100644 --- a/extensions/opencode/package.json +++ b/extensions/opencode/package.json @@ -1,8 +1,11 @@ { "name": "@openclaw/opencode-provider", "version": "2026.7.2", - "private": true, "description": "OpenClaw OpenCode Zen provider plugin", + "repository": { + "type": "git", + "url": "https://github.com/openclaw/openclaw" + }, "type": "module", "devDependencies": { "@openclaw/plugin-sdk": "workspace:*" @@ -10,6 +13,23 @@ "openclaw": { "extensions": [ "./index.ts" - ] + ], + "install": { + "clawhubSpec": "clawhub:@openclaw/opencode-provider", + "npmSpec": "@openclaw/opencode-provider", + "defaultChoice": "npm", + "minHostVersion": ">=2026.7.2" + }, + "compat": { + "pluginApi": ">=2026.7.2" + }, + "build": { + "openclawVersion": "2026.7.2", + "bundledDist": false + }, + "release": { + "publishToClawHub": true, + "publishToNpm": true + } } } diff --git a/package.json b/package.json index 74e01cd249ce..15a31184f05c 100644 --- a/package.json +++ b/package.json @@ -294,6 +294,7 @@ "!dist/extensions/nextcloud-talk/**", "!dist/extensions/nostr/**", "!dist/extensions/novita/**", + "!dist/extensions/opencode/**", "!dist/extensions/parallel/**", "!dist/extensions/perplexity/**", "!dist/extensions/qianfan/**", diff --git a/scripts/lib/official-external-provider-catalog.json b/scripts/lib/official-external-provider-catalog.json index 56d0d50337ff..91f21ec83ff6 100644 --- a/scripts/lib/official-external-provider-catalog.json +++ b/scripts/lib/official-external-provider-catalog.json @@ -962,6 +962,69 @@ } } }, + { + "name": "@openclaw/opencode-provider", + "description": "OpenClaw OpenCode Zen provider plugin", + "source": "official", + "kind": "provider", + "openclaw": { + "plugin": { + "id": "opencode", + "label": "OpenCode Zen" + }, + "providerEndpoints": [ + { + "endpointClass": "opencode-native", + "hostSuffixes": [ + "opencode.ai" + ] + } + ], + "providers": [ + { + "id": "opencode", + "name": "OpenCode Zen", + "docs": "/providers/opencode", + "categories": [ + "cloud", + "llm" + ], + "envVars": [ + "OPENCODE_API_KEY", + "OPENCODE_ZEN_API_KEY" + ], + "authChoices": [ + { + "method": "api-key", + "choiceId": "opencode-zen", + "choiceLabel": "OpenCode Zen catalog", + "groupId": "opencode", + "groupLabel": "OpenCode", + "groupHint": "Shared API key for Zen + Go catalogs", + "optionKey": "opencodeZenApiKey", + "cliFlag": "--opencode-zen-api-key", + "cliOption": "--opencode-zen-api-key ", + "cliDescription": "OpenCode API key (Zen catalog)", + "onboardingScopes": [ + "text-inference" + ] + } + ] + } + ], + "contracts": { + "mediaUnderstandingProviders": [ + "opencode" + ] + }, + "install": { + "clawhubSpec": "clawhub:@openclaw/opencode-provider", + "npmSpec": "@openclaw/opencode-provider", + "defaultChoice": "npm", + "minHostVersion": ">=2026.7.2" + } + } + }, { "name": "@openclaw/groq-provider", "description": "OpenClaw Groq media-understanding provider.", diff --git a/src/cli/plugins-location-bridges.test.ts b/src/cli/plugins-location-bridges.test.ts index cb7b9b71b335..f7ac26c7988d 100644 --- a/src/cli/plugins-location-bridges.test.ts +++ b/src/cli/plugins-location-bridges.test.ts @@ -169,6 +169,7 @@ describe("listPersistedBundledPluginLocationBridges", () => { ["duckduckgo", "@openclaw/duckduckgo-plugin", false], ["mistral", "@openclaw/mistral-provider", true], ["novita", "@openclaw/novita-provider", true], + ["opencode", "@openclaw/opencode-provider", true], ["synthetic", "@openclaw/synthetic-provider", true], ["teams-meetings", "@openclaw/teams-meetings", true], ["volcengine", "@openclaw/volcengine-provider", true], diff --git a/src/plugins/official-external-plugin-catalog.test.ts b/src/plugins/official-external-plugin-catalog.test.ts index 1c8b1b412530..a82cead9a40f 100644 --- a/src/plugins/official-external-plugin-catalog.test.ts +++ b/src/plugins/official-external-plugin-catalog.test.ts @@ -1960,6 +1960,27 @@ describe("official external plugin catalog", () => { }); }); + it("lists OpenCode Zen with its model and media install surfaces", () => { + const opencode = expectCatalogEntry("opencode"); + const manifest = getOfficialExternalPluginCatalogManifest(opencode); + + expect(resolveOfficialExternalPluginId(opencode)).toBe("opencode"); + expect(resolveOfficialExternalPluginInstall(opencode)).toEqual({ + clawhubSpec: "clawhub:@openclaw/opencode-provider", + npmSpec: "@openclaw/opencode-provider", + defaultChoice: "npm", + minHostVersion: ">=2026.7.2", + }); + expect(manifest?.providers?.map((provider) => provider.id)).toEqual(["opencode"]); + expect(manifest?.contracts?.mediaUnderstandingProviders).toEqual(["opencode"]); + expect(manifest?.providerEndpoints).toEqual([ + { + endpointClass: "opencode-native", + hostSuffixes: ["opencode.ai"], + }, + ]); + }); + it("lists Synthetic as an official external provider", () => { const synthetic = expectCatalogEntry("synthetic"); diff --git a/test/scripts/bundled-plugin-build-entries.test.ts b/test/scripts/bundled-plugin-build-entries.test.ts index 107d7b413233..4aeb36016349 100644 --- a/test/scripts/bundled-plugin-build-entries.test.ts +++ b/test/scripts/bundled-plugin-build-entries.test.ts @@ -342,7 +342,15 @@ describe("bundled plugin build entries", () => { it("excludes externalized model providers from bundled artifacts", () => { const artifacts = listBundledPluginPackArtifacts(); - for (const pluginId of ["byteplus", "cohere", "meta", "mistral", "novita", "xiaomi"]) { + for (const pluginId of [ + "byteplus", + "cohere", + "meta", + "mistral", + "novita", + "opencode", + "xiaomi", + ]) { expect(artifacts).not.toContain(`dist/extensions/${pluginId}/index.js`); expect(artifacts).not.toContain(`dist/extensions/${pluginId}/openclaw.plugin.json`); expect(artifacts).not.toContain(`dist/extensions/${pluginId}/package.json`);