mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 09:50:42 +00:00
fix: repair packaged plugin runtime mirrors
This commit is contained in:
@@ -1072,7 +1072,7 @@ describe("scanBundledPluginRuntimeDeps config policy", () => {
|
||||
JSON.stringify({
|
||||
name: "openclaw",
|
||||
version: "2026.4.25",
|
||||
dependencies: { tslog: "^4.10.2" },
|
||||
dependencies: { semver: "7.7.4", tslog: "^4.10.2" },
|
||||
}),
|
||||
);
|
||||
writeBundledPluginPackage({
|
||||
@@ -1090,10 +1090,12 @@ describe("scanBundledPluginRuntimeDeps config policy", () => {
|
||||
|
||||
expect(result.deps.map((dep) => `${dep.name}@${dep.version}`)).toEqual([
|
||||
"discord-runtime@1.0.0",
|
||||
"semver@7.7.4",
|
||||
"tslog@^4.10.2",
|
||||
]);
|
||||
expect(result.missing.map((dep) => `${dep.name}@${dep.version}`)).toEqual([
|
||||
"discord-runtime@1.0.0",
|
||||
"semver@7.7.4",
|
||||
"tslog@^4.10.2",
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -69,7 +69,7 @@ const BUNDLED_RUNTIME_DEPS_OWNERLESS_LOCK_STALE_MS = 30_000;
|
||||
const BUNDLED_RUNTIME_DEPS_INSTALL_PROGRESS_INTERVAL_MS = 5_000;
|
||||
const BUNDLED_RUNTIME_MIRROR_MATERIALIZED_EXTENSIONS = new Set([".cjs", ".js", ".mjs"]);
|
||||
const BUNDLED_EXTENSION_DIST_DIR = "extensions";
|
||||
const MIRRORED_CORE_RUNTIME_DEP_NAMES = ["tslog"] as const;
|
||||
const MIRRORED_CORE_RUNTIME_DEP_NAMES = ["semver", "tslog"] as const;
|
||||
const MIRRORED_PACKAGE_RUNTIME_DEP_PLUGIN_ID = "openclaw-core";
|
||||
const BUNDLED_RUNTIME_MIRROR_PLUGIN_REGION_RE = /(?:^|\n)\/\/#region extensions\/[^/\s]+(?:\/|$)/u;
|
||||
const BUNDLED_RUNTIME_MIRROR_IMPORT_SPECIFIER_RE =
|
||||
|
||||
@@ -180,6 +180,86 @@ describe("prepareBundledPluginRuntimeRoot", () => {
|
||||
expect(fs.readFileSync(distChunk, "utf8")).toContain("same-root");
|
||||
});
|
||||
|
||||
it("mirrors canonical dist chunks when loading from dist-runtime", () => {
|
||||
const packageRoot = makeTempRoot();
|
||||
const stageDir = makeTempRoot();
|
||||
const canonicalPluginRoot = path.join(packageRoot, "dist", "extensions", "qqbot");
|
||||
const runtimePluginRoot = path.join(packageRoot, "dist-runtime", "extensions", "qqbot");
|
||||
const env = { ...process.env, OPENCLAW_PLUGIN_STAGE_DIR: stageDir };
|
||||
fs.mkdirSync(canonicalPluginRoot, { recursive: true });
|
||||
fs.mkdirSync(runtimePluginRoot, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(packageRoot, "package.json"),
|
||||
JSON.stringify({ name: "openclaw", version: "2026.4.27", type: "module" }),
|
||||
"utf8",
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(packageRoot, "dist", "onboard-abc123.js"),
|
||||
"export const setup = 'canonical-setup';\n",
|
||||
"utf8",
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(canonicalPluginRoot, "index.js"),
|
||||
`import { setup } from "../../onboard-abc123.js"; export default { id: "qqbot", setup };\n`,
|
||||
"utf8",
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(canonicalPluginRoot, "package.json"),
|
||||
JSON.stringify(
|
||||
{
|
||||
name: "@openclaw/qqbot",
|
||||
version: "1.0.0",
|
||||
type: "module",
|
||||
dependencies: { "qqbot-runtime": "1.0.0" },
|
||||
openclaw: { extensions: ["./index.js"] },
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
"utf8",
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(runtimePluginRoot, "index.js"),
|
||||
`export { default } from "../../../dist/extensions/qqbot/index.js";\n`,
|
||||
"utf8",
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(runtimePluginRoot, "package.json"),
|
||||
JSON.stringify(
|
||||
{
|
||||
name: "@openclaw/qqbot",
|
||||
version: "1.0.0",
|
||||
type: "module",
|
||||
dependencies: { "qqbot-runtime": "1.0.0" },
|
||||
openclaw: { extensions: ["./index.js"] },
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
"utf8",
|
||||
);
|
||||
const installRoot = resolveBundledRuntimeDependencyInstallRoot(runtimePluginRoot, { env });
|
||||
fs.mkdirSync(path.join(installRoot, "node_modules", "qqbot-runtime"), { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(installRoot, "node_modules", "qqbot-runtime", "package.json"),
|
||||
JSON.stringify({ name: "qqbot-runtime", version: "1.0.0", type: "module" }),
|
||||
"utf8",
|
||||
);
|
||||
|
||||
const prepared = prepareBundledPluginRuntimeRoot({
|
||||
pluginId: "qqbot",
|
||||
pluginRoot: runtimePluginRoot,
|
||||
modulePath: path.join(runtimePluginRoot, "index.js"),
|
||||
env,
|
||||
});
|
||||
|
||||
expect(prepared.pluginRoot).toBe(path.join(installRoot, "dist-runtime", "extensions", "qqbot"));
|
||||
expect(fs.existsSync(path.join(installRoot, "dist", "onboard-abc123.js"))).toBe(true);
|
||||
expect(
|
||||
fs.readFileSync(path.join(installRoot, "dist", "extensions", "qqbot", "index.js"), "utf8"),
|
||||
).toContain("onboard-abc123");
|
||||
});
|
||||
|
||||
it("reuses unchanged external runtime mirrors from the original plugin root", async () => {
|
||||
const packageRoot = makeTempRoot();
|
||||
const stageDir = makeTempRoot();
|
||||
|
||||
@@ -138,16 +138,52 @@ function prepareBundledPluginRuntimeDistMirror(params: {
|
||||
}): string {
|
||||
const sourceExtensionsRoot = path.dirname(params.pluginRoot);
|
||||
const sourceDistRoot = path.dirname(sourceExtensionsRoot);
|
||||
const mirrorDistRoot = path.join(params.installRoot, "dist");
|
||||
const sourceDistRootName = path.basename(sourceDistRoot);
|
||||
const mirrorDistRoot = path.join(params.installRoot, sourceDistRootName);
|
||||
const mirrorExtensionsRoot = path.join(mirrorDistRoot, "extensions");
|
||||
ensureBundledRuntimeMirrorDirectory(mirrorDistRoot);
|
||||
fs.mkdirSync(mirrorExtensionsRoot, { recursive: true, mode: 0o755 });
|
||||
ensureBundledRuntimeDistPackageJson(mirrorDistRoot);
|
||||
for (const entry of fs.readdirSync(sourceDistRoot, { withFileTypes: true })) {
|
||||
mirrorBundledRuntimeDistRootEntries({
|
||||
sourceDistRoot,
|
||||
mirrorDistRoot,
|
||||
});
|
||||
if (sourceDistRootName === "dist-runtime") {
|
||||
mirrorCanonicalBundledRuntimeDistRoot({
|
||||
installRoot: params.installRoot,
|
||||
pluginRoot: params.pluginRoot,
|
||||
sourceRuntimeDistRoot: sourceDistRoot,
|
||||
});
|
||||
}
|
||||
ensureOpenClawPluginSdkAlias(mirrorDistRoot);
|
||||
return mirrorExtensionsRoot;
|
||||
}
|
||||
|
||||
function ensureBundledRuntimeMirrorDirectory(targetRoot: string): void {
|
||||
try {
|
||||
const stat = fs.lstatSync(targetRoot);
|
||||
if (stat.isDirectory() && !stat.isSymbolicLink()) {
|
||||
return;
|
||||
}
|
||||
fs.rmSync(targetRoot, { recursive: true, force: true });
|
||||
} catch (error) {
|
||||
if ((error as NodeJS.ErrnoException).code !== "ENOENT") {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
fs.mkdirSync(targetRoot, { recursive: true, mode: 0o755 });
|
||||
}
|
||||
|
||||
function mirrorBundledRuntimeDistRootEntries(params: {
|
||||
sourceDistRoot: string;
|
||||
mirrorDistRoot: string;
|
||||
}): void {
|
||||
for (const entry of fs.readdirSync(params.sourceDistRoot, { withFileTypes: true })) {
|
||||
if (entry.name === "extensions") {
|
||||
continue;
|
||||
}
|
||||
const sourcePath = path.join(sourceDistRoot, entry.name);
|
||||
const targetPath = path.join(mirrorDistRoot, entry.name);
|
||||
const sourcePath = path.join(params.sourceDistRoot, entry.name);
|
||||
const targetPath = path.join(params.mirrorDistRoot, entry.name);
|
||||
if (path.resolve(sourcePath) === path.resolve(targetPath)) {
|
||||
continue;
|
||||
}
|
||||
@@ -171,8 +207,39 @@ function prepareBundledPluginRuntimeDistMirror(params: {
|
||||
}
|
||||
}
|
||||
}
|
||||
ensureOpenClawPluginSdkAlias(mirrorDistRoot);
|
||||
return mirrorExtensionsRoot;
|
||||
}
|
||||
|
||||
function mirrorCanonicalBundledRuntimeDistRoot(params: {
|
||||
installRoot: string;
|
||||
pluginRoot: string;
|
||||
sourceRuntimeDistRoot: string;
|
||||
}): void {
|
||||
const sourceCanonicalDistRoot = path.join(path.dirname(params.sourceRuntimeDistRoot), "dist");
|
||||
if (!fs.existsSync(sourceCanonicalDistRoot)) {
|
||||
return;
|
||||
}
|
||||
const targetCanonicalDistRoot = path.join(params.installRoot, "dist");
|
||||
ensureBundledRuntimeMirrorDirectory(targetCanonicalDistRoot);
|
||||
fs.mkdirSync(path.join(targetCanonicalDistRoot, "extensions"), { recursive: true, mode: 0o755 });
|
||||
ensureBundledRuntimeDistPackageJson(targetCanonicalDistRoot);
|
||||
mirrorBundledRuntimeDistRootEntries({
|
||||
sourceDistRoot: sourceCanonicalDistRoot,
|
||||
mirrorDistRoot: targetCanonicalDistRoot,
|
||||
});
|
||||
ensureOpenClawPluginSdkAlias(targetCanonicalDistRoot);
|
||||
|
||||
const pluginId = path.basename(params.pluginRoot);
|
||||
const sourceCanonicalPluginRoot = path.join(sourceCanonicalDistRoot, "extensions", pluginId);
|
||||
if (!fs.existsSync(sourceCanonicalPluginRoot)) {
|
||||
return;
|
||||
}
|
||||
const targetCanonicalPluginRoot = path.join(targetCanonicalDistRoot, "extensions", pluginId);
|
||||
refreshBundledPluginRuntimeMirrorRoot({
|
||||
pluginId,
|
||||
sourceRoot: sourceCanonicalPluginRoot,
|
||||
targetRoot: targetCanonicalPluginRoot,
|
||||
tempDirParent: path.dirname(targetCanonicalPluginRoot),
|
||||
});
|
||||
}
|
||||
|
||||
function ensureBundledRuntimeDistPackageJson(mirrorDistRoot: string): void {
|
||||
|
||||
Reference in New Issue
Block a user