mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 16:50:43 +00:00
fix(plugins): update external plugins in recorded root
This commit is contained in:
@@ -746,6 +746,7 @@ export async function installPluginFromClawHub(
|
||||
token?: string;
|
||||
logger?: PluginInstallLogger;
|
||||
mode?: "install" | "update";
|
||||
extensionsDir?: string;
|
||||
dryRun?: boolean;
|
||||
expectedPluginId?: string;
|
||||
},
|
||||
@@ -862,6 +863,7 @@ export async function installPluginFromClawHub(
|
||||
dangerouslyForceUnsafeInstall: params.dangerouslyForceUnsafeInstall,
|
||||
logger: params.logger,
|
||||
mode: params.mode,
|
||||
extensionsDir: params.extensionsDir,
|
||||
dryRun: params.dryRun,
|
||||
expectedPluginId: params.expectedPluginId,
|
||||
});
|
||||
|
||||
@@ -1109,6 +1109,7 @@ export async function installPluginFromMarketplace(
|
||||
logger?: MarketplaceLogger;
|
||||
timeoutMs?: number;
|
||||
mode?: "install" | "update";
|
||||
extensionsDir?: string;
|
||||
dryRun?: boolean;
|
||||
expectedPluginId?: string;
|
||||
},
|
||||
@@ -1154,6 +1155,7 @@ export async function installPluginFromMarketplace(
|
||||
path: resolved.path,
|
||||
logger: params.logger,
|
||||
mode: params.mode,
|
||||
extensionsDir: params.extensionsDir,
|
||||
dryRun: params.dryRun,
|
||||
expectedPluginId: params.expectedPluginId,
|
||||
});
|
||||
|
||||
@@ -21,7 +21,8 @@ const tempDirs: string[] = [];
|
||||
|
||||
vi.mock("./install.js", () => ({
|
||||
installPluginFromNpmSpec: (...args: unknown[]) => installPluginFromNpmSpecMock(...args),
|
||||
resolvePluginInstallDir: (pluginId: string) => `/tmp/${pluginId}`,
|
||||
resolvePluginInstallDir: (pluginId: string, extensionsDir = "/tmp") =>
|
||||
`${extensionsDir}/${pluginId}`,
|
||||
PLUGIN_INSTALL_ERROR_CODE: {
|
||||
NPM_PACKAGE_NOT_FOUND: "npm_package_not_found",
|
||||
},
|
||||
@@ -920,6 +921,82 @@ describe("updateNpmInstalledPlugins", () => {
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("reuses the recorded managed extensions root when updating external plugins", async () => {
|
||||
const installPath = "/var/openclaw/extensions/demo";
|
||||
const extensionsDir = "/var/openclaw/extensions";
|
||||
installPluginFromNpmSpecMock.mockResolvedValue(
|
||||
createSuccessfulNpmUpdateResult({
|
||||
pluginId: "demo",
|
||||
targetDir: installPath,
|
||||
version: "1.2.0",
|
||||
}),
|
||||
);
|
||||
installPluginFromClawHubMock.mockResolvedValue({
|
||||
ok: true,
|
||||
pluginId: "demo",
|
||||
targetDir: installPath,
|
||||
version: "1.2.0",
|
||||
extensions: ["index.ts"],
|
||||
clawhub: {
|
||||
source: "clawhub",
|
||||
clawhubUrl: "https://clawhub.ai",
|
||||
clawhubPackage: "demo",
|
||||
clawhubFamily: "code-plugin",
|
||||
clawhubChannel: "official",
|
||||
integrity: "sha256-next",
|
||||
resolvedAt: "2026-03-22T00:00:00.000Z",
|
||||
},
|
||||
});
|
||||
installPluginFromMarketplaceMock.mockResolvedValue({
|
||||
ok: true,
|
||||
pluginId: "demo",
|
||||
targetDir: installPath,
|
||||
version: "1.2.0",
|
||||
extensions: ["index.ts"],
|
||||
marketplaceSource: "acme/plugins",
|
||||
marketplacePlugin: "demo",
|
||||
});
|
||||
|
||||
await updateNpmInstalledPlugins({
|
||||
config: createNpmInstallConfig({
|
||||
pluginId: "demo",
|
||||
spec: "@acme/demo",
|
||||
installPath,
|
||||
}),
|
||||
pluginIds: ["demo"],
|
||||
});
|
||||
await updateNpmInstalledPlugins({
|
||||
config: createClawHubInstallConfig({
|
||||
pluginId: "demo",
|
||||
installPath,
|
||||
clawhubUrl: "https://clawhub.ai",
|
||||
clawhubPackage: "demo",
|
||||
clawhubFamily: "code-plugin",
|
||||
clawhubChannel: "official",
|
||||
}),
|
||||
pluginIds: ["demo"],
|
||||
});
|
||||
await updateNpmInstalledPlugins({
|
||||
config: createMarketplaceInstallConfig({
|
||||
pluginId: "demo",
|
||||
installPath,
|
||||
marketplaceSource: "acme/plugins",
|
||||
marketplacePlugin: "demo",
|
||||
}),
|
||||
pluginIds: ["demo"],
|
||||
});
|
||||
|
||||
expect(installPluginFromNpmSpecMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ extensionsDir }),
|
||||
);
|
||||
expect(installPluginFromClawHubMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ extensionsDir }),
|
||||
);
|
||||
expect(installPluginFromMarketplaceMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ extensionsDir }),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("syncPluginsForUpdateChannel", () => {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import path from "node:path";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import type { PluginInstallRecord } from "../config/types.plugins.js";
|
||||
import type { NpmSpecResolution } from "../infra/install-source-utils.js";
|
||||
@@ -155,6 +156,19 @@ function pathsEqual(
|
||||
return resolveUserPath(left, env) === resolveUserPath(right, env);
|
||||
}
|
||||
|
||||
function resolveRecordedExtensionsDir(params: {
|
||||
pluginId: string;
|
||||
installPath: string;
|
||||
}): string | undefined {
|
||||
const parentDir = path.dirname(params.installPath);
|
||||
try {
|
||||
const canonicalInstallPath = resolvePluginInstallDir(params.pluginId, parentDir);
|
||||
return canonicalInstallPath === params.installPath ? parentDir : undefined;
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function buildLoadPathHelpers(existing: string[], env: NodeJS.ProcessEnv = process.env) {
|
||||
let paths = [...existing];
|
||||
const resolveSet = () => new Set(paths.map((entry) => resolveUserPath(entry, env)));
|
||||
@@ -535,6 +549,10 @@ export async function updateNpmInstalledPlugins(params: {
|
||||
continue;
|
||||
}
|
||||
const currentVersion = await readInstalledPackageVersion(installPath);
|
||||
const extensionsDir = resolveRecordedExtensionsDir({
|
||||
pluginId,
|
||||
installPath,
|
||||
});
|
||||
|
||||
if (!params.dryRun && record.source === "npm" && currentVersion) {
|
||||
const metadataResult = await resolveNpmSpecMetadata({ spec: effectiveSpec! });
|
||||
@@ -573,6 +591,7 @@ export async function updateNpmInstalledPlugins(params: {
|
||||
? await installPluginFromNpmSpec({
|
||||
spec: effectiveSpec!,
|
||||
mode: "update",
|
||||
extensionsDir,
|
||||
dryRun: true,
|
||||
dangerouslyForceUnsafeInstall: params.dangerouslyForceUnsafeInstall,
|
||||
expectedPluginId: pluginId,
|
||||
@@ -590,6 +609,7 @@ export async function updateNpmInstalledPlugins(params: {
|
||||
spec: effectiveSpec ?? `clawhub:${record.clawhubPackage!}`,
|
||||
baseUrl: record.clawhubUrl,
|
||||
mode: "update",
|
||||
extensionsDir,
|
||||
dryRun: true,
|
||||
dangerouslyForceUnsafeInstall: params.dangerouslyForceUnsafeInstall,
|
||||
expectedPluginId: pluginId,
|
||||
@@ -599,6 +619,7 @@ export async function updateNpmInstalledPlugins(params: {
|
||||
marketplace: record.marketplaceSource!,
|
||||
plugin: record.marketplacePlugin!,
|
||||
mode: "update",
|
||||
extensionsDir,
|
||||
dryRun: true,
|
||||
dangerouslyForceUnsafeInstall: params.dangerouslyForceUnsafeInstall,
|
||||
expectedPluginId: pluginId,
|
||||
@@ -674,6 +695,7 @@ export async function updateNpmInstalledPlugins(params: {
|
||||
? await installPluginFromNpmSpec({
|
||||
spec: effectiveSpec!,
|
||||
mode: "update",
|
||||
extensionsDir,
|
||||
dangerouslyForceUnsafeInstall: params.dangerouslyForceUnsafeInstall,
|
||||
expectedPluginId: pluginId,
|
||||
expectedIntegrity,
|
||||
@@ -690,6 +712,7 @@ export async function updateNpmInstalledPlugins(params: {
|
||||
spec: effectiveSpec ?? `clawhub:${record.clawhubPackage!}`,
|
||||
baseUrl: record.clawhubUrl,
|
||||
mode: "update",
|
||||
extensionsDir,
|
||||
dangerouslyForceUnsafeInstall: params.dangerouslyForceUnsafeInstall,
|
||||
expectedPluginId: pluginId,
|
||||
logger,
|
||||
@@ -698,6 +721,7 @@ export async function updateNpmInstalledPlugins(params: {
|
||||
marketplace: record.marketplaceSource!,
|
||||
plugin: record.marketplacePlugin!,
|
||||
mode: "update",
|
||||
extensionsDir,
|
||||
dangerouslyForceUnsafeInstall: params.dangerouslyForceUnsafeInstall,
|
||||
expectedPluginId: pluginId,
|
||||
logger,
|
||||
|
||||
Reference in New Issue
Block a user