mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 08:00:42 +00:00
feat(plugins): prefer clawhub for channel setup installs
This commit is contained in:
@@ -7,6 +7,7 @@ Docs: https://docs.openclaw.ai
|
||||
### Changes
|
||||
|
||||
- Gateway/startup: skip plugin-backed auth-profile overlays during startup secrets preflight, reducing gateway readiness latency while keeping reload and OAuth recovery paths overlay-capable. (#68327) Thanks @JIRBOY.
|
||||
- Plugins/onboarding: carry ClawHub install metadata through channel setup catalogs so missing channel plugins can install from ClawHub before npm/local fallback. Thanks @vincentkoc.
|
||||
|
||||
### Fixes
|
||||
|
||||
|
||||
@@ -32,14 +32,15 @@ export type ChannelUiCatalog = {
|
||||
byId: Record<string, ChannelUiMetaEntry>;
|
||||
};
|
||||
|
||||
export type ChannelPluginCatalogInstall = PluginPackageInstall &
|
||||
({ clawhubSpec: string } | { npmSpec: string });
|
||||
|
||||
export type ChannelPluginCatalogEntry = {
|
||||
id: string;
|
||||
pluginId?: string;
|
||||
origin?: PluginOrigin;
|
||||
meta: ChannelMeta;
|
||||
install: PluginPackageInstall & {
|
||||
npmSpec: string;
|
||||
};
|
||||
install: ChannelPluginCatalogInstall;
|
||||
installSource?: PluginInstallSourceInfo;
|
||||
};
|
||||
|
||||
@@ -210,19 +211,34 @@ function resolveInstallInfo(params: {
|
||||
packageDir?: string;
|
||||
workspaceDir?: string;
|
||||
}): ChannelPluginCatalogEntry["install"] | null {
|
||||
const npmSpec = params.install?.npmSpec?.trim() ?? params.packageName?.trim();
|
||||
if (!npmSpec) {
|
||||
const clawhubSpec = normalizeOptionalString(params.install?.clawhubSpec);
|
||||
const npmSpec =
|
||||
normalizeOptionalString(params.install?.npmSpec) ?? normalizeOptionalString(params.packageName);
|
||||
if (!clawhubSpec && !npmSpec) {
|
||||
return null;
|
||||
}
|
||||
let localPath = normalizeOptionalString(params.install?.localPath);
|
||||
if (!localPath && params.workspaceDir && params.packageDir) {
|
||||
localPath = path.relative(params.workspaceDir, params.packageDir) || undefined;
|
||||
}
|
||||
const defaultChoice = params.install?.defaultChoice ?? (localPath ? "local" : "npm");
|
||||
const requestedDefaultChoice = params.install?.defaultChoice;
|
||||
const defaultChoice =
|
||||
requestedDefaultChoice === "clawhub" && clawhubSpec
|
||||
? "clawhub"
|
||||
: requestedDefaultChoice === "npm" && npmSpec
|
||||
? "npm"
|
||||
: requestedDefaultChoice === "local" && localPath
|
||||
? "local"
|
||||
: clawhubSpec
|
||||
? "clawhub"
|
||||
: localPath
|
||||
? "local"
|
||||
: "npm";
|
||||
return {
|
||||
npmSpec,
|
||||
...(clawhubSpec ? { clawhubSpec } : {}),
|
||||
...(npmSpec ? { npmSpec } : {}),
|
||||
...(localPath ? { localPath } : {}),
|
||||
...(defaultChoice ? { defaultChoice } : {}),
|
||||
defaultChoice,
|
||||
...(params.install?.minHostVersion ? { minHostVersion: params.install.minHostVersion } : {}),
|
||||
...(params.install?.expectedIntegrity
|
||||
? { expectedIntegrity: params.install.expectedIntegrity }
|
||||
|
||||
@@ -287,6 +287,78 @@ export function describeChannelPluginCatalogEntriesContract() {
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "accepts external manifest entries with ClawHub-only install metadata",
|
||||
setup: () => {
|
||||
const dir = fs.mkdtempSync(
|
||||
path.join(resolvePreferredOpenClawTmpDir(), "openclaw-catalog-clawhub-"),
|
||||
);
|
||||
const catalogPath = path.join(dir, "catalog.json");
|
||||
fs.writeFileSync(
|
||||
catalogPath,
|
||||
JSON.stringify({
|
||||
$schema: "./manifest.schema.json",
|
||||
schemaVersion: 1,
|
||||
description:
|
||||
"Extension manifest. Declares plugin packages that OpenClaw can discover during onboarding and install on demand via `openclaw plugins install`.",
|
||||
entries: [
|
||||
{
|
||||
source: "external",
|
||||
kind: "channel",
|
||||
openclaw: {
|
||||
channel: {
|
||||
id: "clawhub-chat",
|
||||
label: "ClawHub Chat",
|
||||
selectionLabel: "ClawHub Chat",
|
||||
detailLabel: "ClawHub",
|
||||
docsPath: "/channels/clawhub-chat",
|
||||
docsLabel: "clawhub chat",
|
||||
blurb: "ClawHub-backed chat channel.",
|
||||
aliases: ["chchat"],
|
||||
order: 47,
|
||||
},
|
||||
install: {
|
||||
clawhubSpec: "clawhub:openclaw/clawhub-chat@2026.5.2",
|
||||
defaultChoice: "clawhub",
|
||||
minHostVersion: ">=2026.5.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
return {
|
||||
channelId: "clawhub-chat",
|
||||
catalogPaths: [catalogPath],
|
||||
expected: {
|
||||
id: "clawhub-chat",
|
||||
meta: {
|
||||
label: "ClawHub Chat",
|
||||
selectionLabel: "ClawHub Chat",
|
||||
detailLabel: "ClawHub",
|
||||
docsPath: "/channels/clawhub-chat",
|
||||
docsLabel: "clawhub chat",
|
||||
blurb: "ClawHub-backed chat channel.",
|
||||
},
|
||||
install: {
|
||||
clawhubSpec: "clawhub:openclaw/clawhub-chat@2026.5.2",
|
||||
defaultChoice: "clawhub",
|
||||
minHostVersion: ">=2026.5.1",
|
||||
},
|
||||
installSource: {
|
||||
defaultChoice: "clawhub",
|
||||
clawhub: {
|
||||
spec: "clawhub:openclaw/clawhub-chat@2026.5.2",
|
||||
packageName: "openclaw/clawhub-chat",
|
||||
version: "2026.5.2",
|
||||
exactVersion: true,
|
||||
},
|
||||
warnings: [],
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "accepts rich external manifest entries for yuanbao with pinned npm metadata",
|
||||
setup: () => {
|
||||
|
||||
@@ -528,6 +528,50 @@ describe("ensureChannelSetupPluginInstalled", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("offers ClawHub as the first-class install source for channel catalog entries", async () => {
|
||||
const runtime = makeRuntime();
|
||||
const { prompter, select } = makeSkipInstallPrompter();
|
||||
const cfg: OpenClawConfig = { update: { channel: "beta" } };
|
||||
vi.mocked(fs.existsSync).mockReturnValue(false);
|
||||
resolveBundledPluginSources.mockReturnValue(new Map());
|
||||
|
||||
await ensureChannelSetupPluginInstalled({
|
||||
cfg,
|
||||
entry: {
|
||||
id: "clawhub-chat",
|
||||
pluginId: "clawhub-chat",
|
||||
meta: {
|
||||
id: "clawhub-chat",
|
||||
label: "ClawHub Chat",
|
||||
selectionLabel: "ClawHub Chat",
|
||||
docsPath: "/channels/clawhub-chat",
|
||||
blurb: "Test",
|
||||
},
|
||||
install: {
|
||||
clawhubSpec: "clawhub:openclaw/clawhub-chat@2026.5.2",
|
||||
defaultChoice: "clawhub",
|
||||
},
|
||||
},
|
||||
prompter,
|
||||
runtime,
|
||||
});
|
||||
|
||||
expect(select).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
initialValue: "clawhub",
|
||||
options: [
|
||||
expect.objectContaining({
|
||||
value: "clawhub",
|
||||
label: "Download from ClawHub (clawhub:openclaw/clawhub-chat@2026.5.2)",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
value: "skip",
|
||||
}),
|
||||
],
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("falls back to local path after npm install failure", async () => {
|
||||
const runtime = makeRuntime();
|
||||
const note = vi.fn(async () => {});
|
||||
|
||||
Reference in New Issue
Block a user