diff --git a/docs/plugins/plugin-inventory.md b/docs/plugins/plugin-inventory.md index 234c44048d11..94c608528c99 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 -54 plugins +55 plugins - **[admin-http-rpc](/plugins/reference/admin-http-rpc)** (`@openclaw/admin-http-rpc`) - included in OpenClaw. OpenClaw admin HTTP RPC endpoint. @@ -131,6 +131,8 @@ 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-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. - **[policy](/plugins/reference/policy)** (`@openclaw/policy`) - included in OpenClaw. Adds policy-backed doctor checks for workspace conformance. @@ -163,7 +165,7 @@ Each entry lists the package, distribution route, and description. ## Official external packages -91 plugins +90 plugins - **[acpx](/plugins/reference/acpx)** (`@openclaw/acpx`) - npm; ClawHub. OpenClaw ACP runtime backend with plugin-owned session and transport management. @@ -279,8 +281,6 @@ Each entry lists the package, distribution route, and description. - **[opencode](/plugins/reference/opencode)** (`@openclaw/opencode-provider`) - npm; ClawHub: `clawhub:@openclaw/opencode-provider`. Adds OpenCode model provider support to OpenClaw. -- **[opencode-go](/plugins/reference/opencode-go)** (`@openclaw/opencode-go-provider`) - npm; ClawHub: `clawhub:@openclaw/opencode-go-provider`. Adds OpenCode Go 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-go.md b/docs/plugins/reference/opencode-go.md index 5e85c69c8237..37a5c4453171 100644 --- a/docs/plugins/reference/opencode-go.md +++ b/docs/plugins/reference/opencode-go.md @@ -12,7 +12,7 @@ Adds OpenCode Go model provider support to OpenClaw. ## Distribution - Package: `@openclaw/opencode-go-provider` -- Install route: npm; ClawHub: `clawhub:@openclaw/opencode-go-provider` +- Install route: included in OpenClaw ## Surface diff --git a/docs/providers/opencode-go.md b/docs/providers/opencode-go.md index f81530153b37..57665588f088 100644 --- a/docs/providers/opencode-go.md +++ b/docs/providers/opencode-go.md @@ -9,24 +9,21 @@ title: "OpenCode Go" OpenCode Go is the Go catalog inside [OpenCode](/providers/opencode). It shares the `OPENCODE_API_KEY` credential with the Zen catalog, but keeps its own runtime provider id (`opencode-go`) so upstream per-model routing stays -correct. OpenClaw provides it as the official external -`@openclaw/opencode-go-provider` plugin. +correct. OpenCode Go is bundled in the OpenClaw package for this release, so +onboarding and configuration are sufficient; no separate plugin install is +required. | Property | Value | | ---------------- | -------------------------------------------------- | | Runtime provider | `opencode-go` | -| Plugin | `@openclaw/opencode-go-provider` | +| Plugin | Bundled (`opencode-go`) | | Auth | `OPENCODE_API_KEY` (alias: `OPENCODE_ZEN_API_KEY`) | | Parent setup | [OpenCode](/providers/opencode) | ## Getting started -Install the official plugin and restart the Gateway: - -```bash -openclaw plugins install @openclaw/opencode-go-provider -openclaw gateway restart -``` +OpenCode Go is already included with OpenClaw for this release. Continue with +interactive onboarding or pass the shared OpenCode API key directly. diff --git a/docs/providers/opencode.md b/docs/providers/opencode.md index c7d0ae7836ae..b5f3f2acf907 100644 --- a/docs/providers/opencode.md +++ b/docs/providers/opencode.md @@ -55,11 +55,9 @@ one OpenCode setup. **Best for:** the OpenCode-hosted Kimi, GLM, MiniMax, Qwen, and DeepSeek lineup. - - ```bash - openclaw plugins install @openclaw/opencode-go-provider - openclaw gateway restart - ``` + + OpenCode Go is included with OpenClaw for this release, so no separate + plugin installation or Gateway restart is required. ```bash diff --git a/extensions/opencode-go/README.md b/extensions/opencode-go/README.md index ac22575a8b6c..f9eada90e229 100644 --- a/extensions/opencode-go/README.md +++ b/extensions/opencode-go/README.md @@ -2,12 +2,8 @@ Official OpenClaw provider plugin for the OpenCode Go model catalog. -Install from OpenClaw: - -```bash -openclaw plugins install @openclaw/opencode-go-provider -openclaw gateway restart -``` +OpenCode Go is bundled with OpenClaw for this release; no separate plugin +installation is required. Configure `OPENCODE_API_KEY` or `OPENCODE_ZEN_API_KEY`, then select an `opencode-go/...` model. diff --git a/extensions/opencode-go/package.json b/extensions/opencode-go/package.json index 4ee0b490f7fc..e3ec044adf56 100644 --- a/extensions/opencode-go/package.json +++ b/extensions/opencode-go/package.json @@ -25,7 +25,7 @@ }, "build": { "openclawVersion": "2026.8.1", - "bundledDist": false + "bundledDist": true }, "release": { "publishToClawHub": true, diff --git a/package.json b/package.json index 7966d1c6a85b..04a8afae4661 100644 --- a/package.json +++ b/package.json @@ -307,7 +307,6 @@ "!dist/extensions/nostr/**", "!dist/extensions/novita/**", "!dist/extensions/opencode/**", - "!dist/extensions/opencode-go/**", "!dist/extensions/parallel/**", "!dist/extensions/perplexity/**", "!dist/extensions/qianfan/**", diff --git a/scripts/generate-plugin-inventory-doc.mjs b/scripts/generate-plugin-inventory-doc.mjs index 3253bd572546..bd85f848b279 100644 --- a/scripts/generate-plugin-inventory-doc.mjs +++ b/scripts/generate-plugin-inventory-doc.mjs @@ -334,6 +334,10 @@ function resolveInstallRoute(packageJson, status) { return "source checkout only"; } if (status === "core") { + // Explicit bundle ownership describes the current install surface; release flags may stage future publication. + if (packageJson.openclaw?.build?.bundledDist === true) { + return "included in OpenClaw"; + } const release = packageJson.openclaw?.release; if (release?.publishToClawHub === true || release?.publishToNpm === true) { return `included in OpenClaw; ${resolveInstallRoute(packageJson, "external")}`; diff --git a/scripts/lib/plugin-clawhub-release.ts b/scripts/lib/plugin-clawhub-release.ts index b075fa4d470e..ba0e9fd3b7a9 100644 --- a/scripts/lib/plugin-clawhub-release.ts +++ b/scripts/lib/plugin-clawhub-release.ts @@ -13,6 +13,7 @@ import { collectChangedExtensionIdsFromPaths, collectPublishablePluginPackageErrors, collectRequiredLatestDependencies, + isPluginExternalPublicationDeferred, assertPluginReleaseVersionFloors, parsePluginReleaseArgs, resolvePublishablePluginVersion, @@ -45,6 +46,7 @@ type PluginPackageJson = { minGatewayVersion?: string; }; build?: { + bundledDist?: boolean; openclawVersion?: string; pluginSdkVersion?: string; }; @@ -283,6 +285,9 @@ export function collectClawHubPublishablePluginPackages( if (hasSelectedPackageNames && !selectedPackageNames.has(packageName)) { continue; } + if (isPluginExternalPublicationDeferred(packageJson)) { + continue; + } if (packageJson.openclaw?.release?.publishToClawHub !== true) { continue; } diff --git a/scripts/lib/plugin-npm-release.ts b/scripts/lib/plugin-npm-release.ts index 3c1ec3fb9793..5c1c5dadaae4 100644 --- a/scripts/lib/plugin-npm-release.ts +++ b/scripts/lib/plugin-npm-release.ts @@ -34,6 +34,7 @@ type PluginPackageJson = { minGatewayVersion?: string; }; build?: { + bundledDist?: boolean; openclawVersion?: string; pluginSdkVersion?: string; }; @@ -45,6 +46,13 @@ type PluginPackageJson = { }; }; +/** Explicit core ownership defers staged external publication until the plugin is externalized. */ +export function isPluginExternalPublicationDeferred(packageJson: { + openclaw?: { build?: { bundledDist?: unknown } }; +}): boolean { + return packageJson.openclaw?.build?.bundledDist === true; +} + export type RequiredLatestDependency = { packageName: string; version: string; @@ -430,6 +438,9 @@ export function collectPublishablePluginPackages( if (hasSelectedPackageNames && !selectedPackageNames.has(packageName)) { continue; } + if (isPluginExternalPublicationDeferred(packageJson)) { + continue; + } if (packageJson.openclaw?.release?.publishToNpm !== true) { continue; } diff --git a/src/commands/doctor/shared/missing-configured-plugin-install.load-path.test.ts b/src/commands/doctor/shared/missing-configured-plugin-install.load-path.test.ts index 8ac847e35c65..4ca79aeb0639 100644 --- a/src/commands/doctor/shared/missing-configured-plugin-install.load-path.test.ts +++ b/src/commands/doctor/shared/missing-configured-plugin-install.load-path.test.ts @@ -45,6 +45,41 @@ function writeProviderPlugin(rootDir: string): void { ); } +function writeBundledOpenCodeGoPlugin(bundledPluginsDir: string): void { + const pluginDir = path.join(bundledPluginsDir, "opencode-go"); + fs.mkdirSync(pluginDir, { recursive: true }); + fs.writeFileSync(path.join(pluginDir, "index.js"), "export default {};\n", "utf8"); + fs.writeFileSync( + path.join(pluginDir, "package.json"), + JSON.stringify({ + name: "@openclaw/opencode-go-provider", + version: "2026.8.1", + openclaw: { + extensions: ["./index.js"], + install: { + clawhubSpec: "clawhub:@openclaw/opencode-go-provider", + npmSpec: "@openclaw/opencode-go-provider", + defaultChoice: "npm", + }, + build: { openclawVersion: "2026.8.1" }, + release: { publishToClawHub: true, publishToNpm: true }, + }, + }), + "utf8", + ); + fs.writeFileSync( + path.join(pluginDir, "openclaw.plugin.json"), + JSON.stringify({ + id: "opencode-go", + activation: { onStartup: false }, + enabledByDefault: true, + providers: ["opencode-go"], + configSchema: { type: "object", additionalProperties: false, properties: {} }, + }), + "utf8", + ); +} + describe("configured plugin install health for explicit load paths", () => { it("does not install a provider plugin already present at a configured load path", async () => { const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-load-path-provider-")); @@ -80,4 +115,53 @@ describe("configured plugin install health for explicit load paths", () => { warnings: [], }); }); + + it("discovers packaged OpenCode Go before configured-plugin repair", async () => { + const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-bundled-opencode-go-")); + tempDirs.push(rootDir); + const homeDir = path.join(rootDir, "home"); + const stateDir = path.join(rootDir, "state"); + const configPath = path.join(stateDir, "openclaw.json"); + const bundledPluginsDir = path.join(rootDir, "dist", "extensions"); + fs.mkdirSync(homeDir, { recursive: true }); + fs.mkdirSync(stateDir, { recursive: true }); + writeBundledOpenCodeGoPlugin(bundledPluginsDir); + + const cfg = { + auth: { + profiles: { + "opencode-go:default": { provider: "opencode-go", mode: "api_key" as const }, + }, + }, + }; + fs.writeFileSync(configPath, `${JSON.stringify(cfg)}\n`, "utf8"); + const env = { + HOME: homeDir, + USERPROFILE: homeDir, + OPENCLAW_HOME: homeDir, + OPENCLAW_STATE_DIR: stateDir, + OPENCLAW_CONFIG_PATH: configPath, + OPENCLAW_BUNDLED_PLUGINS_DIR: bundledPluginsDir, + OPENCLAW_DISABLE_BUNDLED_SOURCE_OVERLAYS: "1", + OPENCLAW_TEST_TRUST_BUNDLED_PLUGINS_DIR: "1", + NPM_CONFIG_REGISTRY: "http://127.0.0.1:9", + npm_config_registry: "http://127.0.0.1:9", + XDG_CONFIG_HOME: path.join(rootDir, "xdg-config"), + VITEST: "true", + }; + const snapshot = loadManifestMetadataSnapshot({ config: cfg, env }); + expect(snapshot.plugins).toEqual( + expect.arrayContaining([expect.objectContaining({ id: "opencode-go", origin: "bundled" })]), + ); + + const issues = await detectConfiguredPluginInstallHealthIssues({ cfg, env }); + expect(issues).toStrictEqual([]); + + const repair = await repairMissingConfiguredPluginInstalls({ cfg, env }); + expect(repair).toStrictEqual({ + changes: [], + records: {}, + warnings: [], + }); + }); }); diff --git a/test/plugin-npm-release.test.ts b/test/plugin-npm-release.test.ts index 97ff3591ab4f..d4606e0038f4 100644 --- a/test/plugin-npm-release.test.ts +++ b/test/plugin-npm-release.test.ts @@ -540,6 +540,28 @@ describe("collectPluginReleasePlan", () => { }); describe("collectPublishablePluginPackages", () => { + it("defers explicitly bundled plugins from npm and ClawHub release plans", () => { + const repoDir = makeTempRepoRoot(tempDirs, "openclaw-plugin-npm-release-"); + mkdirSync(join(repoDir, "extensions", "demo-plugin"), { recursive: true }); + writePluginReadme(repoDir, "demo-plugin"); + writeJsonFile(join(repoDir, "extensions", "demo-plugin", "package.json"), { + name: "@openclaw/demo-plugin", + version: "2026.4.10", + type: "module", + repository: { type: "git", url: OPENCLAW_PLUGIN_NPM_REPOSITORY_URL }, + openclaw: { + extensions: ["./index.ts"], + ...externalPluginContract("2026.4.10"), + build: { openclawVersion: "2026.4.10", bundledDist: true }, + install: { npmSpec: "@openclaw/demo-plugin" }, + release: { publishToClawHub: true, publishToNpm: true }, + }, + }); + + expect(collectPublishablePluginPackages(repoDir)).toStrictEqual([]); + expect(collectClawHubPublishablePluginPackages(repoDir)).toStrictEqual([]); + }); + it("keeps publishable plugin dist trees out of the core npm package unless bundled", () => { const corePackageRuntimePluginIds = new Set(["discord"]); const rootPackage = JSON.parse(readFileSync("package.json", "utf8")) as { diff --git a/test/scripts/bundled-plugin-build-entries.test.ts b/test/scripts/bundled-plugin-build-entries.test.ts index 01b885b12d2d..4f2fe6c0b574 100644 --- a/test/scripts/bundled-plugin-build-entries.test.ts +++ b/test/scripts/bundled-plugin-build-entries.test.ts @@ -356,7 +356,6 @@ describe("bundled plugin build entries", () => { "mistral", "novita", "opencode", - "opencode-go", "xiaomi", ]) { expect(artifacts).not.toContain(`dist/extensions/${pluginId}/index.js`); @@ -365,6 +364,18 @@ describe("bundled plugin build entries", () => { } }); + it("keeps OpenCode Go bundled until its companion artifact is available", () => { + const artifacts = listBundledPluginPackArtifacts(); + + expect(artifacts).toEqual( + expect.arrayContaining([ + "dist/extensions/opencode-go/index.js", + "dist/extensions/opencode-go/openclaw.plugin.json", + "dist/extensions/opencode-go/package.json", + ]), + ); + }); + it("excludes the externalized Vydra provider from bundled artifacts", () => { const artifacts = listBundledPluginPackArtifacts();