fix(plugins): fallback bundled channel specs when npm install returns 404 (#12849)
* plugins: add bundled source resolver * plugins: add bundled source resolver tests * cli: fallback npm 404 plugin installs to bundled sources * plugins: use bundled source resolver during updates * protocol: regenerate macos gateway swift models * protocol: regenerate shared swift models * Revert "protocol: regenerate shared swift models" This reverts commit 6a2b08c47d2636610efbf16fc210d4114b05b4b4. * Revert "protocol: regenerate macos gateway swift models" This reverts commit 27c03010c6b9da07b404c93cdb0a1c2a3db671f5.
This commit is contained in:
@@ -6,6 +6,7 @@ import type { OpenClawConfig } from "../config/config.js";
|
||||
import { loadConfig, writeConfigFile } from "../config/config.js";
|
||||
import { resolveStateDir } from "../config/paths.js";
|
||||
import { resolveArchiveKind } from "../infra/archive.js";
|
||||
import { findBundledPluginByNpmSpec } from "../plugins/bundled-sources.js";
|
||||
import { enablePluginInConfig } from "../plugins/enable.js";
|
||||
import { installPluginFromNpmSpec, installPluginFromPath } from "../plugins/install.js";
|
||||
import { recordPluginInstall } from "../plugins/installs.js";
|
||||
@@ -147,6 +148,16 @@ function logSlotWarnings(warnings: string[]) {
|
||||
}
|
||||
}
|
||||
|
||||
function isPackageNotFoundInstallError(message: string): boolean {
|
||||
const lower = message.toLowerCase();
|
||||
return (
|
||||
lower.includes("npm pack failed:") &&
|
||||
(lower.includes("e404") ||
|
||||
lower.includes("404 not found") ||
|
||||
lower.includes("could not be found"))
|
||||
);
|
||||
}
|
||||
|
||||
export function registerPluginsCli(program: Command) {
|
||||
const plugins = program
|
||||
.command("plugins")
|
||||
@@ -614,8 +625,52 @@ export function registerPluginsCli(program: Command) {
|
||||
logger: createPluginInstallLogger(),
|
||||
});
|
||||
if (!result.ok) {
|
||||
defaultRuntime.error(result.error);
|
||||
process.exit(1);
|
||||
const bundledFallback = isPackageNotFoundInstallError(result.error)
|
||||
? findBundledPluginByNpmSpec({ spec: raw })
|
||||
: undefined;
|
||||
if (!bundledFallback) {
|
||||
defaultRuntime.error(result.error);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const existing = cfg.plugins?.load?.paths ?? [];
|
||||
const mergedPaths = Array.from(new Set([...existing, bundledFallback.localPath]));
|
||||
let next: OpenClawConfig = {
|
||||
...cfg,
|
||||
plugins: {
|
||||
...cfg.plugins,
|
||||
load: {
|
||||
...cfg.plugins?.load,
|
||||
paths: mergedPaths,
|
||||
},
|
||||
entries: {
|
||||
...cfg.plugins?.entries,
|
||||
[bundledFallback.pluginId]: {
|
||||
...(cfg.plugins?.entries?.[bundledFallback.pluginId] as object | undefined),
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
next = recordPluginInstall(next, {
|
||||
pluginId: bundledFallback.pluginId,
|
||||
source: "path",
|
||||
spec: raw,
|
||||
sourcePath: bundledFallback.localPath,
|
||||
installPath: bundledFallback.localPath,
|
||||
});
|
||||
const slotResult = applySlotSelectionForPlugin(next, bundledFallback.pluginId);
|
||||
next = slotResult.config;
|
||||
await writeConfigFile(next);
|
||||
logSlotWarnings(slotResult.warnings);
|
||||
defaultRuntime.log(
|
||||
theme.warn(
|
||||
`npm package unavailable for ${raw}; using bundled plugin at ${shortenHomePath(bundledFallback.localPath)}.`,
|
||||
),
|
||||
);
|
||||
defaultRuntime.log(`Installed plugin: ${bundledFallback.pluginId}`);
|
||||
defaultRuntime.log(`Restart the gateway to load plugins.`);
|
||||
return;
|
||||
}
|
||||
// Ensure config validation sees newly installed plugin(s) even if the cache was warmed at startup.
|
||||
clearPluginManifestRegistryCache();
|
||||
|
||||
Reference in New Issue
Block a user