mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 05:50:43 +00:00
fix(catalog): pin prerelease channel npm specs
This commit is contained in:
@@ -22,6 +22,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Plugins/externalization: keep official ACPX, Google Chat, and LINE install specs on production package names, leaving beta-tag probing to the explicit OpenClaw beta update channel. Thanks @vincentkoc.
|
||||
- CLI/doctor: keep missing-plugin repair from overriding official catalog metadata with runtime fallbacks, so ACPX repairs preserve the official 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.
|
||||
- Plugins/catalog: pin bare npm specs from prerelease external channel catalog entries to the catalog entry version, so beta catalogs do not silently install the latest stable package.
|
||||
- Plugins/update: treat catalog-matched official npm updates and OpenClaw-authored externalized-bundled npm bridges as trusted official installs so launch-code plugins can update or migrate out of the bundled tree without scanner false positives. Thanks @vincentkoc.
|
||||
- Plugins/onboarding: fall back from ClawHub to npm only for missing package/version errors, keeping integrity and verification failures fail-closed during storepack rollout. 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.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { MANIFEST_KEY } from "../../compat/legacy-names.js";
|
||||
import { isPrereleaseSemverVersion, parseRegistryNpmSpec } from "../../infra/npm-registry-spec.js";
|
||||
import { resolveOpenClawPackageRootSync } from "../../infra/openclaw-root.js";
|
||||
import { listChannelCatalogEntries } from "../../plugins/channel-catalog-registry.js";
|
||||
import {
|
||||
@@ -240,12 +241,26 @@ function toChannelMeta(params: {
|
||||
function resolveInstallInfo(params: {
|
||||
install?: PluginPackageInstall;
|
||||
packageName?: string;
|
||||
packageVersion?: string;
|
||||
packageDir?: string;
|
||||
workspaceDir?: string;
|
||||
}): ChannelPluginCatalogEntry["install"] | null {
|
||||
const clawhubSpec = normalizeOptionalString(params.install?.clawhubSpec);
|
||||
const npmSpec =
|
||||
let npmSpec =
|
||||
normalizeOptionalString(params.install?.npmSpec) ?? normalizeOptionalString(params.packageName);
|
||||
const packageVersion = normalizeOptionalString(params.packageVersion);
|
||||
const parsedNpmSpec = npmSpec ? parseRegistryNpmSpec(npmSpec) : null;
|
||||
const expectedPackageName = normalizeOptionalString(params.packageName);
|
||||
const parsedPackageName = expectedPackageName ? parseRegistryNpmSpec(expectedPackageName) : null;
|
||||
if (
|
||||
npmSpec &&
|
||||
packageVersion &&
|
||||
isPrereleaseSemverVersion(packageVersion) &&
|
||||
parsedNpmSpec?.selectorKind === "none" &&
|
||||
(!parsedPackageName || parsedNpmSpec.name === parsedPackageName.name)
|
||||
) {
|
||||
npmSpec = `${parsedNpmSpec.name}@${packageVersion}`;
|
||||
}
|
||||
if (!clawhubSpec && !npmSpec) {
|
||||
return null;
|
||||
}
|
||||
@@ -296,6 +311,7 @@ function resolveInstallInfo(params: {
|
||||
function buildCatalogEntryFromManifest(params: {
|
||||
pluginId?: string;
|
||||
packageName?: string;
|
||||
packageVersion?: string;
|
||||
packageDir?: string;
|
||||
origin?: PluginOrigin;
|
||||
trustedSourceLinkedOfficialInstall?: boolean;
|
||||
@@ -317,6 +333,7 @@ function buildCatalogEntryFromManifest(params: {
|
||||
const install = resolveInstallInfo({
|
||||
install: params.install,
|
||||
packageName: params.packageName,
|
||||
packageVersion: params.packageVersion,
|
||||
packageDir: params.packageDir,
|
||||
workspaceDir: params.workspaceDir,
|
||||
});
|
||||
@@ -349,6 +366,7 @@ function buildExternalCatalogEntry(
|
||||
return buildCatalogEntryFromManifest({
|
||||
pluginId: manifest?.plugin?.id,
|
||||
packageName: entry.name,
|
||||
packageVersion: entry.version,
|
||||
trustedSourceLinkedOfficialInstall: options?.trustedSourceLinkedOfficialInstall,
|
||||
channel: manifest?.channel,
|
||||
install: manifest?.install,
|
||||
|
||||
@@ -287,6 +287,40 @@ export function describeChannelPluginCatalogEntriesContract() {
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "pins bare external prerelease package specs to the entry version",
|
||||
setup: () => {
|
||||
const dir = fs.mkdtempSync(
|
||||
path.join(resolvePreferredOpenClawTmpDir(), "openclaw-catalog-prerelease-"),
|
||||
);
|
||||
const catalogPath = path.join(dir, "catalog.json");
|
||||
writeCatalogFile(catalogPath, {
|
||||
...createCatalogEntry({
|
||||
packageName: "@openclaw/prerelease-demo-channel",
|
||||
channelId: "prerelease-demo",
|
||||
label: "Prerelease Demo",
|
||||
blurb: "Prerelease package pinning fixture",
|
||||
}),
|
||||
version: "2026.5.3-beta.1",
|
||||
});
|
||||
return {
|
||||
channelId: "prerelease-demo",
|
||||
catalogPaths: [catalogPath],
|
||||
expected: {
|
||||
install: { npmSpec: "@openclaw/prerelease-demo-channel@2026.5.3-beta.1" },
|
||||
installSource: {
|
||||
npm: {
|
||||
spec: "@openclaw/prerelease-demo-channel@2026.5.3-beta.1",
|
||||
packageName: "@openclaw/prerelease-demo-channel",
|
||||
selector: "2026.5.3-beta.1",
|
||||
selectorKind: "exact-version",
|
||||
exactVersion: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "accepts external manifest entries with ClawHub-only install metadata",
|
||||
setup: () => {
|
||||
|
||||
Reference in New Issue
Block a user