diff --git a/CHANGELOG.md b/CHANGELOG.md index afe8a0d8797..9df5de71f85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ Docs: https://docs.openclaw.ai ### Changes - Plugins/source checkout: load bundled plugins from the `extensions/*` pnpm workspace tree in source checkouts, so plugin-local dependencies and edits are used directly while packaged installs keep using the built runtime tree. Thanks @vincentkoc. +- Plugins/beta: prepare Brave, Codex, Feishu, Synology Chat, Tlon, and Twitch for `2026.5.1-beta.1` npm and ClawHub publishing. Thanks @vincentkoc. - Providers/xAI: add Grok 4.3 to the bundled catalog and make it the default xAI chat model. - Plugins/ClawHub: prefer versioned ClawPack artifacts when ClawHub publishes digest metadata, verifying the ClawPack response header and downloaded bytes before installing. Thanks @vincentkoc. - Plugins/ClawHub: persist ClawPack digest metadata on ClawHub plugin install and update records so registry refreshes and download verification can reuse stored artifact facts. Thanks @vincentkoc. diff --git a/extensions/brave/package.json b/extensions/brave/package.json index ed34380269a..60fe3ee3d54 100644 --- a/extensions/brave/package.json +++ b/extensions/brave/package.json @@ -1,8 +1,11 @@ { "name": "@openclaw/brave-plugin", - "version": "2026.4.25", - "private": true, + "version": "2026.5.1-beta.1", "description": "OpenClaw Brave plugin", + "repository": { + "type": "git", + "url": "https://github.com/openclaw/openclaw" + }, "type": "module", "devDependencies": { "@openclaw/plugin-sdk": "workspace:*" @@ -10,6 +13,21 @@ "openclaw": { "extensions": [ "./index.ts" - ] + ], + "install": { + "npmSpec": "@openclaw/brave-plugin", + "defaultChoice": "npm", + "minHostVersion": ">=2026.4.10" + }, + "compat": { + "pluginApi": ">=2026.4.10" + }, + "build": { + "openclawVersion": "2026.5.1-beta.1" + }, + "release": { + "publishToClawHub": true, + "publishToNpm": true + } } } diff --git a/extensions/codex/package.json b/extensions/codex/package.json index e7321ee5323..a0fcf93e5b3 100644 --- a/extensions/codex/package.json +++ b/extensions/codex/package.json @@ -1,7 +1,11 @@ { "name": "@openclaw/codex", - "version": "2026.4.25", + "version": "2026.5.1-beta.1", "description": "OpenClaw Codex harness and model provider plugin", + "repository": { + "type": "git", + "url": "https://github.com/openclaw/openclaw" + }, "type": "module", "dependencies": { "@mariozechner/pi-coding-agent": "0.71.1", @@ -16,6 +20,21 @@ "openclaw": { "extensions": [ "./index.ts" - ] + ], + "install": { + "npmSpec": "@openclaw/codex", + "defaultChoice": "npm", + "minHostVersion": ">=2026.5.1-beta.1" + }, + "compat": { + "pluginApi": ">=2026.5.1-beta.1" + }, + "build": { + "openclawVersion": "2026.5.1-beta.1" + }, + "release": { + "publishToClawHub": true, + "publishToNpm": true + } } } diff --git a/extensions/feishu/package.json b/extensions/feishu/package.json index 5cfd65292f5..37fe0accded 100644 --- a/extensions/feishu/package.json +++ b/extensions/feishu/package.json @@ -1,7 +1,11 @@ { "name": "@openclaw/feishu", - "version": "2026.4.25", + "version": "2026.5.1-beta.1", "description": "OpenClaw Feishu/Lark channel plugin (community maintained by @m1heng)", + "repository": { + "type": "git", + "url": "https://github.com/openclaw/openclaw" + }, "type": "module", "dependencies": { "@larksuiteoapi/node-sdk": "^1.62.1", @@ -46,7 +50,7 @@ "pluginApi": ">=2026.4.25" }, "build": { - "openclawVersion": "2026.4.25" + "openclawVersion": "2026.5.1-beta.1" }, "release": { "publishToClawHub": true, diff --git a/extensions/synology-chat/package.json b/extensions/synology-chat/package.json index a25f44c9ca4..2d1d29686e9 100644 --- a/extensions/synology-chat/package.json +++ b/extensions/synology-chat/package.json @@ -1,7 +1,11 @@ { "name": "@openclaw/synology-chat", - "version": "2026.4.25", + "version": "2026.5.1-beta.1", "description": "Synology Chat channel plugin for OpenClaw", + "repository": { + "type": "git", + "url": "https://github.com/openclaw/openclaw" + }, "type": "module", "dependencies": { "zod": "^4.4.1" @@ -27,6 +31,16 @@ "npmSpec": "@openclaw/synology-chat", "defaultChoice": "npm", "minHostVersion": ">=2026.4.10" + }, + "compat": { + "pluginApi": ">=2026.4.10" + }, + "build": { + "openclawVersion": "2026.5.1-beta.1" + }, + "release": { + "publishToClawHub": true, + "publishToNpm": true } } } diff --git a/extensions/tlon/package.json b/extensions/tlon/package.json index 8864ef01737..9eb4aab57a3 100644 --- a/extensions/tlon/package.json +++ b/extensions/tlon/package.json @@ -1,7 +1,11 @@ { "name": "@openclaw/tlon", - "version": "2026.4.25", + "version": "2026.5.1-beta.1", "description": "OpenClaw Tlon/Urbit channel plugin", + "repository": { + "type": "git", + "url": "https://github.com/openclaw/openclaw" + }, "type": "module", "dependencies": { "@aws-sdk/client-s3": "3.1041.0", @@ -66,6 +70,16 @@ "npmSpec": "@openclaw/tlon", "defaultChoice": "npm", "minHostVersion": ">=2026.4.10" + }, + "compat": { + "pluginApi": ">=2026.4.10" + }, + "build": { + "openclawVersion": "2026.5.1-beta.1" + }, + "release": { + "publishToClawHub": true, + "publishToNpm": true } } } diff --git a/extensions/twitch/package.json b/extensions/twitch/package.json index 7e04162ad4b..b994cd44ab4 100644 --- a/extensions/twitch/package.json +++ b/extensions/twitch/package.json @@ -25,6 +25,12 @@ "defaultChoice": "npm", "minHostVersion": ">=2026.4.10" }, + "compat": { + "pluginApi": ">=2026.4.10" + }, + "build": { + "openclawVersion": "2026.5.1-beta.1" + }, "channel": { "id": "twitch", "label": "Twitch", @@ -36,6 +42,7 @@ ] }, "release": { + "publishToClawHub": true, "publishToNpm": true } } diff --git a/scripts/lib/plugin-clawhub-release.ts b/scripts/lib/plugin-clawhub-release.ts index 9f0369cfd5c..fe840e45f93 100644 --- a/scripts/lib/plugin-clawhub-release.ts +++ b/scripts/lib/plugin-clawhub-release.ts @@ -73,6 +73,11 @@ export type PluginReleasePlan = { skippedPublished: PluginReleasePlanItem[]; }; +export type ClawHubPublishablePluginPackageFilters = { + extensionIds?: readonly string[]; + packageNames?: readonly string[]; +}; + const CLAWHUB_DEFAULT_REGISTRY = "https://clawhub.ai"; const SAFE_EXTENSION_ID_RE = /^[a-z0-9][a-z0-9._-]*$/; const CLAWHUB_SHARED_RELEASE_INPUT_PATHS = [ @@ -101,12 +106,24 @@ function getRegistryBaseUrl(explicit?: string) { export function collectClawHubPublishablePluginPackages( rootDir = resolve("."), + filters: ClawHubPublishablePluginPackageFilters = {}, ): PublishablePluginPackage[] { const publishable: PublishablePluginPackage[] = []; const validationErrors: string[] = []; + const selectedExtensionIds = new Set(filters.extensionIds ?? []); + const selectedPackageNames = new Set(filters.packageNames ?? []); + const hasSelectedExtensionIds = Array.isArray(filters.extensionIds); + const hasSelectedPackageNames = Array.isArray(filters.packageNames); for (const candidate of collectExtensionPackageJsonCandidates(rootDir)) { const { extensionId, packageDir, packageJson } = candidate; + if (hasSelectedExtensionIds && !selectedExtensionIds.has(extensionId)) { + continue; + } + const packageName = packageJson.name?.trim() ?? ""; + if (hasSelectedPackageNames && !selectedPackageNames.has(packageName)) { + continue; + } if (packageJson.openclaw?.release?.publishToClawHub !== true) { continue; } @@ -147,7 +164,7 @@ export function collectClawHubPublishablePluginPackages( publishable.push({ extensionId, packageDir, - packageName: packageJson.name!.trim(), + packageName, version, channel: parsedVersion.channel, publishTag: parsedVersion.channel === "beta" ? "beta" : "latest", @@ -342,13 +359,29 @@ export async function collectPluginClawHubReleasePlan(params?: { registryBaseUrl?: string; fetchImpl?: typeof fetch; }): Promise { - const allPublishable = collectClawHubPublishablePluginPackages(params?.rootDir); + const rootDir = params?.rootDir; + const selection = params?.selection ?? []; + const changedPaths = params?.gitRange + ? collectPluginClawHubRelevantPathsFromGitRange({ + rootDir, + gitRange: params.gitRange, + }) + : []; + const sharedInputChanged = hasSharedClawHubReleaseInputChanges(changedPaths); + const extensionIds = + params?.selectionMode === "all-publishable" || !params?.gitRange || sharedInputChanged + ? undefined + : collectChangedExtensionIdsFromPaths(changedPaths); + const allPublishable = collectClawHubPublishablePluginPackages(rootDir, { + extensionIds, + packageNames: selection.length > 0 ? selection : undefined, + }); const selectedPublishable = resolveSelectedClawHubPublishablePluginPackages({ plugins: allPublishable, - selection: params?.selection, + selection, selectionMode: params?.selectionMode, gitRange: params?.gitRange, - rootDir: params?.rootDir, + rootDir, }); const all = await Promise.all( diff --git a/scripts/plugin-clawhub-release-check.ts b/scripts/plugin-clawhub-release-check.ts index 6602c2f97d0..a510b5cfbdc 100644 --- a/scripts/plugin-clawhub-release-check.ts +++ b/scripts/plugin-clawhub-release-check.ts @@ -10,7 +10,10 @@ import { export async function runPluginClawHubReleaseCheck(argv: string[]) { const { selection, selectionMode, baseRef, headRef } = parsePluginReleaseArgs(argv); - const publishable = collectClawHubPublishablePluginPackages(); + const publishable = collectClawHubPublishablePluginPackages(".", { + packageNames: + selectionMode === "all-publishable" || selection.length === 0 ? undefined : selection, + }); const gitRange = baseRef && headRef ? { baseRef, headRef } : undefined; const selected = resolveSelectedClawHubPublishablePluginPackages({ plugins: publishable, diff --git a/test/plugin-clawhub-release.test.ts b/test/plugin-clawhub-release.test.ts index 716cb615e5a..c5990b8e885 100644 --- a/test/plugin-clawhub-release.test.ts +++ b/test/plugin-clawhub-release.test.ts @@ -69,6 +69,35 @@ describe("collectClawHubPublishablePluginPackages", () => { "Demo Plugin: extension directory name must match", ); }); + + it("validates only selected package names when filters are provided", () => { + const repoDir = createTempPluginRepo({ + extraExtensionIds: ["broken-plugin"], + }); + writeFileSync( + join(repoDir, "extensions", "broken-plugin", "package.json"), + JSON.stringify( + { + name: "@openclaw/broken-plugin", + version: "2026.4.1", + openclaw: { + extensions: ["./index.ts"], + release: { + publishToClawHub: true, + }, + }, + }, + null, + 2, + ), + ); + + expect( + collectClawHubPublishablePluginPackages(repoDir, { + packageNames: ["@openclaw/demo-plugin"], + }).map((plugin) => plugin.packageName), + ).toEqual(["@openclaw/demo-plugin"]); + }); }); describe("collectClawHubVersionGateErrors", () => { @@ -238,6 +267,38 @@ describe("collectPluginClawHubReleasePlan", () => { version: "2026.4.1", }); }); + + it("plans selected packages without validating unrelated publishable packages", async () => { + const repoDir = createTempPluginRepo({ + extraExtensionIds: ["broken-plugin"], + }); + writeFileSync( + join(repoDir, "extensions", "broken-plugin", "package.json"), + JSON.stringify( + { + name: "@openclaw/broken-plugin", + version: "2026.4.1", + openclaw: { + extensions: ["./index.ts"], + release: { + publishToClawHub: true, + }, + }, + }, + null, + 2, + ), + ); + + const plan = await collectPluginClawHubReleasePlan({ + rootDir: repoDir, + selection: ["@openclaw/demo-plugin"], + fetchImpl: async () => new Response("{}", { status: 404 }), + registryBaseUrl: "https://clawhub.ai", + }); + + expect(plan.candidates.map((plugin) => plugin.packageName)).toEqual(["@openclaw/demo-plugin"]); + }); }); describe("collectPluginClawHubReleasePathsFromGitRange", () => {