





















@@ -370,6 +370,7 @@ describe("ensureBundledPluginRuntimeDeps", () => {
370370371371const calls: Array<{
372372installRoot: string;
373+installExecutionRoot?: string;
373374missingSpecs: string[];
374375installSpecs?: string[];
375376}> = [];
@@ -391,6 +392,7 @@ describe("ensureBundledPluginRuntimeDeps", () => {
391392expect(calls).toEqual([
392393{
393394installRoot: pluginRoot,
395+installExecutionRoot: path.join(pluginRoot, ".openclaw-install-stage"),
394396missingSpecs: ["missing@2.0.0"],
395397installSpecs: ["already-present@1.0.0", "missing@2.0.0", "previous@3.0.0"],
396398},
@@ -430,12 +432,60 @@ describe("ensureBundledPluginRuntimeDeps", () => {
430432expect(calls).toEqual([
431433{
432434installRoot: pluginRoot,
435+installExecutionRoot: path.join(pluginRoot, ".openclaw-install-stage"),
433436missingSpecs: ["external-runtime@^1.2.3"],
434437installSpecs: ["external-runtime@^1.2.3"],
435438},
436439]);
437440});
438441442+it("stages plugin-root install when the plugin's own package.json declares workspace:* deps", () => {
443+// Regression guard for packaged/Docker bundled plugins whose `package.json`
444+// still lists `"@openclaw/plugin-sdk": "workspace:*"` (and similar) alongside
445+// concrete runtime deps. Without a distinct execution root, `npm install`
446+// would resolve the plugin's own cwd manifest and fail with
447+// EUNSUPPORTEDPROTOCOL on the `workspace:` protocol.
448+const packageRoot = makeTempDir();
449+const extensionsRoot = path.join(packageRoot, "dist", "extensions");
450+const pluginRoot = path.join(extensionsRoot, "anthropic");
451+fs.mkdirSync(pluginRoot, { recursive: true });
452+fs.writeFileSync(
453+path.join(pluginRoot, "package.json"),
454+JSON.stringify({
455+dependencies: {
456+"@openclaw/plugin-sdk": "workspace:*",
457+"@anthropic-ai/sdk": "^0.50.0",
458+},
459+}),
460+);
461+462+const calls: BundledRuntimeDepsInstallParams[] = [];
463+const result = ensureBundledPluginRuntimeDeps({
464+env: {},
465+installDeps: (params) => {
466+calls.push(params);
467+},
468+pluginId: "anthropic",
469+ pluginRoot,
470+});
471+472+expect(result).toEqual({
473+installedSpecs: ["@anthropic-ai/sdk@^0.50.0"],
474+retainSpecs: ["@anthropic-ai/sdk@^0.50.0"],
475+});
476+expect(calls).toEqual([
477+{
478+installRoot: pluginRoot,
479+installExecutionRoot: path.join(pluginRoot, ".openclaw-install-stage"),
480+missingSpecs: ["@anthropic-ai/sdk@^0.50.0"],
481+installSpecs: ["@anthropic-ai/sdk@^0.50.0"],
482+},
483+]);
484+// The stage dir must be distinct from the plugin root so npm does not read
485+// the plugin's cwd manifest during install.
486+expect(path.resolve(calls[0]!.installExecutionRoot!)).not.toEqual(path.resolve(pluginRoot));
487+});
488+439489it("installs runtime deps into an external stage dir and exposes loader aliases", () => {
440490const packageRoot = makeTempDir();
441491const stageDir = makeTempDir();
@@ -932,6 +982,7 @@ describe("ensureBundledPluginRuntimeDeps", () => {
932982expect(calls).toEqual([
933983{
934984installRoot: pluginRoot,
985+installExecutionRoot: path.join(pluginRoot, ".openclaw-install-stage"),
935986missingSpecs: ["@mariozechner/pi-ai@0.68.1"],
936987installSpecs: ["@mariozechner/pi-ai@0.68.1"],
937988},
@@ -974,6 +1025,7 @@ describe("ensureBundledPluginRuntimeDeps", () => {
9741025expect(calls).toEqual([
9751026{
9761027installRoot: pluginRoot,
1028+installExecutionRoot: path.join(pluginRoot, ".openclaw-install-stage"),
9771029missingSpecs: ["ws@^8.20.0", "zod@^4.3.6"],
9781030installSpecs: ["ws@^8.20.0", "zod@^4.3.6"],
9791031},
@@ -1018,6 +1070,7 @@ describe("ensureBundledPluginRuntimeDeps", () => {
10181070expect(calls).toEqual([
10191071{
10201072installRoot: pluginRoot,
1073+installExecutionRoot: path.join(pluginRoot, ".openclaw-install-stage"),
10211074missingSpecs: ["zod@^4.3.6"],
10221075installSpecs: ["zod@^4.3.6"],
10231076},
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。