mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 07:00:43 +00:00
fix(plugins): keep mirrored runtime deps on staged root
This commit is contained in:
@@ -62,6 +62,9 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
- Providers/Google: transcode Gemini TTS PCM to Opus for voice-note targets so
|
||||
WhatsApp and other native voice-note replies can play as voice messages.
|
||||
- Plugins/runtime deps: reuse existing external bundled-plugin stage roots when
|
||||
mirrored plugin roots are inspected again, avoiding second-generation
|
||||
`openclaw-unknown-*` stages and repeated first-turn restaging. Fixes #71599.
|
||||
- iOS/macOS Talk Mode: allow `talk.speechLocale` to set the speech
|
||||
recognition locale for non-English voice conversations. Fixes #44688.
|
||||
- Plugins/providers: honor explicit plugin candidate lists instead of reading a
|
||||
|
||||
@@ -1107,6 +1107,14 @@ find_external_dep_package() {
|
||||
find "$(stage_root)" -maxdepth 12 -path "*/node_modules/$dep_path/package.json" -type f -print -quit 2>/dev/null || true
|
||||
}
|
||||
|
||||
assert_no_unknown_stage_roots() {
|
||||
if find "$(stage_root)" -maxdepth 1 -type d -name 'openclaw-unknown-*' -print -quit 2>/dev/null | grep -q .; then
|
||||
echo "runtime deps created second-generation unknown stage roots" >&2
|
||||
find "$(stage_root)" -maxdepth 1 -type d -name 'openclaw-*' -print | sort >&2 || true
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
package_tgz="${OPENCLAW_CURRENT_PACKAGE_TGZ:?missing OPENCLAW_CURRENT_PACKAGE_TGZ}"
|
||||
update_target="file:$package_tgz"
|
||||
candidate_version="$(node - <<'NODE' "$package_tgz"
|
||||
@@ -1391,6 +1399,7 @@ if should_run_update_target telegram; then
|
||||
cat /tmp/openclaw-update-telegram.json
|
||||
assert_update_ok /tmp/openclaw-update-telegram.json "$candidate_version"
|
||||
assert_dep_available telegram grammy
|
||||
assert_no_unknown_stage_roots
|
||||
|
||||
echo "Mutating installed package: remove Telegram deps, then update-mode doctor repairs them..."
|
||||
remove_runtime_dep telegram grammy
|
||||
@@ -1401,6 +1410,7 @@ if should_run_update_target telegram; then
|
||||
exit 1
|
||||
fi
|
||||
assert_dep_available telegram grammy
|
||||
assert_no_unknown_stage_roots
|
||||
fi
|
||||
|
||||
if should_run_update_target discord; then
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { createHash } from "node:crypto";
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
@@ -726,6 +727,56 @@ describe("ensureBundledPluginRuntimeDeps", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("does not derive a second-generation stage root from external runtime mirrors", () => {
|
||||
const packageRoot = makeTempDir();
|
||||
const stageDir = makeTempDir();
|
||||
fs.writeFileSync(
|
||||
path.join(packageRoot, "package.json"),
|
||||
JSON.stringify({ name: "openclaw", version: "2026.4.25" }),
|
||||
);
|
||||
const pluginRoot = path.join(packageRoot, "dist", "extensions", "telegram");
|
||||
fs.mkdirSync(pluginRoot, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(pluginRoot, "package.json"),
|
||||
JSON.stringify({ dependencies: { grammy: "^1.42.0" } }),
|
||||
);
|
||||
const env = { OPENCLAW_PLUGIN_STAGE_DIR: stageDir };
|
||||
const installRoot = resolveBundledRuntimeDependencyInstallRoot(pluginRoot, { env });
|
||||
const mirroredPluginRoot = path.join(installRoot, "dist", "extensions", "telegram");
|
||||
fs.mkdirSync(mirroredPluginRoot, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(mirroredPluginRoot, "package.json"),
|
||||
JSON.stringify({ dependencies: { grammy: "^1.42.0" } }),
|
||||
);
|
||||
fs.mkdirSync(path.join(installRoot, "node_modules", "grammy"), { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(installRoot, "node_modules", "grammy", "package.json"),
|
||||
JSON.stringify({ name: "grammy", version: "1.42.0" }),
|
||||
);
|
||||
|
||||
const nestedUnknownRoot = path.join(
|
||||
stageDir,
|
||||
`openclaw-unknown-${createHash("sha256").update(path.resolve(installRoot)).digest("hex").slice(0, 12)}`,
|
||||
);
|
||||
|
||||
expect(resolveBundledRuntimeDependencyInstallRoot(mirroredPluginRoot, { env })).toBe(
|
||||
installRoot,
|
||||
);
|
||||
expect(resolveBundledRuntimeDependencyInstallRoot(mirroredPluginRoot, { env })).not.toBe(
|
||||
nestedUnknownRoot,
|
||||
);
|
||||
expect(
|
||||
ensureBundledPluginRuntimeDeps({
|
||||
env,
|
||||
installDeps: () => {
|
||||
throw new Error("mirrored staged deps should not reinstall into a nested stage root");
|
||||
},
|
||||
pluginId: "telegram",
|
||||
pluginRoot: mirroredPluginRoot,
|
||||
}),
|
||||
).toEqual({ installedSpecs: [], retainSpecs: [] });
|
||||
});
|
||||
|
||||
it("retains existing staged deps without a retained manifest before shared installs", () => {
|
||||
const packageRoot = makeTempDir();
|
||||
const stageDir = makeTempDir();
|
||||
|
||||
@@ -607,11 +607,36 @@ function resolveExternalBundledRuntimeDepsInstallRoot(params: {
|
||||
env: NodeJS.ProcessEnv;
|
||||
}): string {
|
||||
const packageRoot = resolveBundledPluginPackageRoot(params.pluginRoot) ?? params.pluginRoot;
|
||||
const existingExternalRoot = resolveExistingExternalBundledRuntimeDepsRoot({
|
||||
packageRoot,
|
||||
env: params.env,
|
||||
});
|
||||
if (existingExternalRoot) {
|
||||
return existingExternalRoot;
|
||||
}
|
||||
const version = sanitizePathSegment(readPackageVersion(packageRoot));
|
||||
const packageKey = `openclaw-${version}-${createPathHash(packageRoot)}`;
|
||||
return path.join(resolveBundledRuntimeDepsExternalBaseDir(params.env), packageKey);
|
||||
}
|
||||
|
||||
function resolveExistingExternalBundledRuntimeDepsRoot(params: {
|
||||
packageRoot: string;
|
||||
env: NodeJS.ProcessEnv;
|
||||
}): string | null {
|
||||
const externalBaseDir = path.resolve(resolveBundledRuntimeDepsExternalBaseDir(params.env));
|
||||
const packageRoot = path.resolve(params.packageRoot);
|
||||
const relative = path.relative(externalBaseDir, packageRoot);
|
||||
if (
|
||||
relative === "" ||
|
||||
relative.startsWith("..") ||
|
||||
path.isAbsolute(relative) ||
|
||||
relative.includes(path.sep)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
return path.basename(packageRoot).startsWith("openclaw-") ? packageRoot : null;
|
||||
}
|
||||
|
||||
function resolveSourceCheckoutRuntimeDepsCacheDir(params: {
|
||||
pluginId: string;
|
||||
pluginRoot: string;
|
||||
|
||||
Reference in New Issue
Block a user