fix(catalog): preserve channel ClawHub specs

This commit is contained in:
Vincent Koc
2026-05-03 02:59:49 -07:00
parent a4c1c28a17
commit 961575ddf7
3 changed files with 42 additions and 3 deletions

View File

@@ -21,6 +21,7 @@ Docs: https://docs.openclaw.ai
- Plugins/externalization: pin beta-only official launch packages for ACPX, Google Chat, and LINE to explicit npm beta specs so catalog-driven installs do not trip the prerelease safety guard while npm `latest` still points at beta. Thanks @vincentkoc.
- CLI/doctor: keep missing-plugin repair from overriding official catalog metadata with runtime fallbacks, so ACPX repairs preserve the beta npm spec during the externalization rollout. Thanks @vincentkoc.
- Plugins/catalog: preserve ClawHub install specs when generating the packaged channel catalog so future storepack-first channel plugins keep their remote source instead of becoming npm-only. Thanks @vincentkoc.
- Control UI/Talk: fix Talk (OpenAI Realtime WebRTC) CORS failure by stripping server-side-only attribution headers (`originator`, `version`, `User-Agent`) from browser offer headers; `api.openai.com/v1/realtime/calls` only allows `authorization` and `content-type` in its CORS preflight, so forwarding these headers caused the browser SDP exchange to fail. Fixes #76435. Thanks @hclsys.
- CLI/logs: auto-reconnect `openclaw logs --follow` on transient gateway disconnects (WebSocket close, timeout, connection drop) with bounded exponential backoff (up to 8 retries, capped at 30 s) and stderr retry warnings, while still exiting immediately on non-recoverable auth or configuration errors. Fixes #74782. (#75059) Thanks @shashank-poola.
- Plugins/onboarding: trust optional official plugin and web-search installs selected from the official catalog so npm security scanning treats them like other source-linked official install paths. Thanks @vincentkoc.

View File

@@ -9,16 +9,20 @@ export const OFFICIAL_CHANNEL_CATALOG_RELATIVE_PATH = "dist/channel-catalog.json
function toCatalogInstall(value, packageName) {
const install = isRecord(value) ? value : {};
const clawhubSpec = trimString(install.clawhubSpec);
const npmSpec = trimString(install.npmSpec) || packageName;
if (!npmSpec) {
if (!clawhubSpec && !npmSpec) {
return null;
}
const defaultChoice = trimString(install.defaultChoice);
const minHostVersion = trimString(install.minHostVersion);
const expectedIntegrity = trimString(install.expectedIntegrity);
return {
npmSpec,
...(defaultChoice === "npm" || defaultChoice === "local" ? { defaultChoice } : {}),
...(clawhubSpec ? { clawhubSpec } : {}),
...(npmSpec ? { npmSpec } : {}),
...(defaultChoice === "clawhub" || defaultChoice === "npm" || defaultChoice === "local"
? { defaultChoice }
: {}),
...(minHostVersion ? { minHostVersion } : {}),
...(expectedIntegrity ? { expectedIntegrity } : {}),
...(install.allowInvalidConfigRecovery === true ? { allowInvalidConfigRecovery: true } : {}),

View File

@@ -168,6 +168,40 @@ describe("buildOfficialChannelCatalog", () => {
expect(installSource.warnings).toEqual(["npm-spec-floating", "npm-spec-missing-integrity"]);
});
it("preserves ClawHub specs when generating publishable channel catalog entries", () => {
const repoRoot = makeRepoRoot("openclaw-official-channel-catalog-clawhub-");
writeJson(path.join(repoRoot, "extensions", "storepack-chat", "package.json"), {
name: "@openclaw/storepack-chat",
openclaw: {
channel: {
id: "storepack-chat",
label: "Storepack Chat",
selectionLabel: "Storepack Chat",
docsPath: "/channels/storepack-chat",
blurb: "storepack-first channel",
},
install: {
clawhubSpec: "clawhub:@openclaw/storepack-chat",
npmSpec: "@openclaw/storepack-chat",
defaultChoice: "clawhub",
},
release: {
publishToNpm: true,
},
},
});
const entry = buildOfficialChannelCatalog({ repoRoot }).entries.find(
(candidate) => candidate.openclaw?.channel?.id === "storepack-chat",
);
expect(entry?.openclaw?.install).toEqual({
clawhubSpec: "clawhub:@openclaw/storepack-chat",
npmSpec: "@openclaw/storepack-chat",
defaultChoice: "clawhub",
});
});
it("writes the official catalog under dist", () => {
const repoRoot = makeRepoRoot("openclaw-official-channel-catalog-write-");
writeJson(path.join(repoRoot, "extensions", "whatsapp", "package.json"), {