mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 06:50:43 +00:00
fix: skip workspace plugin runtime deps
This commit is contained in:
@@ -30,7 +30,7 @@ Docs: https://docs.openclaw.ai
|
||||
- OpenAI/Responses: resolve `/think` levels against each GPT model's supported reasoning efforts so `/think off` no longer becomes high reasoning or sends unsupported `reasoning.effort: "none"` payloads.
|
||||
- Lobster/TaskFlow: allow managed approval resumes to use `approvalId` without a resume token, and persist that id in approval wait state. (#69559) Thanks @kirkluokun.
|
||||
- Plugins/startup: install bundled runtime dependencies into each plugin's own runtime directory, reuse source-checkout repair caches after rebuilds, and log only packages that were actually installed so repeated Gateway starts stay quiet once deps are present.
|
||||
- Plugins/startup: ignore pnpm's `npm_execpath` when repairing bundled plugin runtime dependencies so npm-only install flags are not passed to pnpm-launched gateways.
|
||||
- Plugins/startup: ignore pnpm's `npm_execpath` when repairing bundled plugin runtime dependencies and skip workspace-only package specs so npm-only install flags or local workspace links do not break packaged plugin startup.
|
||||
- Setup/TUI: relaunch the setup hatch TUI in a fresh process while preserving the configured gateway target and auth source, so onboarding recovers terminal state cleanly without exposing gateway secrets on command-line args. (#69524) Thanks @shakkernerd.
|
||||
- Codex: avoid re-exposing the image-generation tool on native vision turns with inbound images, and keep bare image-model overrides on the configured image provider. (#65061) Thanks @zhulijin1991.
|
||||
- Sessions/reset: clear auto-sourced model, provider, and auth-profile overrides on `/new` and `/reset` while preserving explicit user selections, so channel sessions stop staying pinned to runtime fallback choices. (#69419) Thanks @sk7n4k3d.
|
||||
|
||||
@@ -43,8 +43,10 @@ describe("doctor bundled plugin runtime deps", () => {
|
||||
|
||||
writeJson(path.join(root, "dist", "extensions", "alpha", "package.json"), {
|
||||
dependencies: {
|
||||
"@openclaw/plugin-sdk": "workspace:*",
|
||||
"dep-one": "1.0.0",
|
||||
"@scope/dep-two": "2.0.0",
|
||||
openclaw: "workspace:*",
|
||||
},
|
||||
optionalDependencies: {
|
||||
"dep-opt": "3.0.0",
|
||||
|
||||
@@ -262,6 +262,72 @@ describe("ensureBundledPluginRuntimeDeps", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("skips workspace-only runtime deps before npm install", () => {
|
||||
const packageRoot = makeTempDir();
|
||||
const extensionsRoot = path.join(packageRoot, "dist", "extensions");
|
||||
const pluginRoot = path.join(extensionsRoot, "qa-channel");
|
||||
fs.mkdirSync(pluginRoot, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(pluginRoot, "package.json"),
|
||||
JSON.stringify({
|
||||
dependencies: {
|
||||
"@openclaw/plugin-sdk": "workspace:*",
|
||||
"external-runtime": "^1.2.3",
|
||||
openclaw: "workspace:*",
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
const calls: BundledRuntimeDepsInstallParams[] = [];
|
||||
const result = ensureBundledPluginRuntimeDeps({
|
||||
env: {},
|
||||
installDeps: (params) => {
|
||||
calls.push(params);
|
||||
},
|
||||
pluginId: "qa-channel",
|
||||
pluginRoot,
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
installedSpecs: ["external-runtime@^1.2.3"],
|
||||
retainSpecs: ["external-runtime@^1.2.3"],
|
||||
});
|
||||
expect(calls).toEqual([
|
||||
{
|
||||
installRoot: pluginRoot,
|
||||
missingSpecs: ["external-runtime@^1.2.3"],
|
||||
installSpecs: ["external-runtime@^1.2.3"],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("does not install when runtime deps are only workspace links", () => {
|
||||
const packageRoot = makeTempDir();
|
||||
const extensionsRoot = path.join(packageRoot, "dist", "extensions");
|
||||
const pluginRoot = path.join(extensionsRoot, "qa-channel");
|
||||
fs.mkdirSync(pluginRoot, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(pluginRoot, "package.json"),
|
||||
JSON.stringify({
|
||||
dependencies: {
|
||||
"@openclaw/plugin-sdk": "workspace:*",
|
||||
openclaw: "workspace:*",
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
const result = ensureBundledPluginRuntimeDeps({
|
||||
env: {},
|
||||
installDeps: () => {
|
||||
throw new Error("workspace-only runtime deps should not install");
|
||||
},
|
||||
pluginId: "qa-channel",
|
||||
pluginRoot,
|
||||
});
|
||||
|
||||
expect(result).toEqual({ installedSpecs: [], retainSpecs: [] });
|
||||
});
|
||||
|
||||
it("skips install when staged plugin-local runtime deps are present", () => {
|
||||
const packageRoot = makeTempDir();
|
||||
const extensionsRoot = path.join(packageRoot, "dist", "extensions");
|
||||
|
||||
@@ -62,6 +62,17 @@ function collectRuntimeDeps(packageJson: JsonObject): Record<string, unknown> {
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeInstallableRuntimeDepVersion(rawVersion: unknown): string | null {
|
||||
if (typeof rawVersion !== "string") {
|
||||
return null;
|
||||
}
|
||||
const version = rawVersion.trim();
|
||||
if (version === "" || version.toLowerCase().startsWith("workspace:")) {
|
||||
return null;
|
||||
}
|
||||
return version;
|
||||
}
|
||||
|
||||
function isSourceCheckoutRoot(packageRoot: string): boolean {
|
||||
return (
|
||||
fs.existsSync(path.join(packageRoot, ".git")) &&
|
||||
@@ -402,10 +413,10 @@ function collectBundledPluginRuntimeDeps(params: {
|
||||
continue;
|
||||
}
|
||||
for (const [name, rawVersion] of Object.entries(collectRuntimeDeps(packageJson))) {
|
||||
if (typeof rawVersion !== "string" || rawVersion.trim() === "") {
|
||||
const version = normalizeInstallableRuntimeDepVersion(rawVersion);
|
||||
if (!version) {
|
||||
continue;
|
||||
}
|
||||
const version = rawVersion.trim();
|
||||
const byVersion = versionMap.get(name) ?? new Map<string, Set<string>>();
|
||||
const pluginIds = byVersion.get(version) ?? new Set<string>();
|
||||
pluginIds.add(pluginId);
|
||||
@@ -549,11 +560,10 @@ export function ensureBundledPluginRuntimeDeps(params: {
|
||||
return { installedSpecs: [], retainSpecs: [] };
|
||||
}
|
||||
const deps = Object.entries(collectRuntimeDeps(packageJson))
|
||||
.map(([name, rawVersion]) =>
|
||||
typeof rawVersion === "string" && rawVersion.trim() !== ""
|
||||
? { name, version: rawVersion.trim() }
|
||||
: null,
|
||||
)
|
||||
.map(([name, rawVersion]) => {
|
||||
const version = normalizeInstallableRuntimeDepVersion(rawVersion);
|
||||
return version ? { name, version } : null;
|
||||
})
|
||||
.filter((entry): entry is { name: string; version: string } => Boolean(entry));
|
||||
if (deps.length === 0) {
|
||||
return { installedSpecs: [], retainSpecs: [] };
|
||||
|
||||
Reference in New Issue
Block a user