fix(build): harden bundled plugin runtime staging

Copy bundled plugin skill trees into dist-runtime, broaden Windows symlink-copy fallbacks, and harden runtime-deps fingerprinting.
This commit is contained in:
Vincent Koc
2026-04-25 04:27:17 -07:00
committed by GitHub
parent f408bba9de
commit 443b837bd5
6 changed files with 169 additions and 35 deletions

View File

@@ -457,16 +457,17 @@ function appendDirectoryFingerprint(hash, rootDir, currentDir = rootDir) {
for (const entry of entries) {
const fullPath = path.join(currentDir, entry.name);
const relativePath = path.relative(rootDir, fullPath).replace(/\\/g, "/");
if (entry.isSymbolicLink()) {
const stats = fs.lstatSync(fullPath);
if (stats.isSymbolicLink()) {
hash.update(`symlink:${relativePath}->${fs.readlinkSync(fullPath).replace(/\\/g, "/")}\n`);
continue;
}
if (entry.isDirectory()) {
if (stats.isDirectory()) {
hash.update(`dir:${relativePath}\n`);
appendDirectoryFingerprint(hash, rootDir, fullPath);
continue;
}
if (!entry.isFile()) {
if (!stats.isFile()) {
continue;
}
const stat = fs.statSync(fullPath);

View File

@@ -15,7 +15,11 @@ function relativeSymlinkTarget(sourcePath, targetPath) {
function shouldFallbackToCopy(error) {
return (
process.platform === "win32" &&
(error?.code === "EPERM" || error?.code === "EINVAL" || error?.code === "UNKNOWN")
(error?.code === "EACCES" ||
error?.code === "EINVAL" ||
error?.code === "ENOSYS" ||
error?.code === "EPERM" ||
error?.code === "UNKNOWN")
);
}
@@ -128,15 +132,23 @@ function shouldWrapRuntimeJsFile(sourcePath) {
return path.extname(sourcePath) === ".js";
}
function shouldCopyRuntimeFile(sourcePath) {
const relativePath = sourcePath.replace(/\\/g, "/");
function isBundledSkillRuntimePath(relativePath) {
return relativePath === "skills" || relativePath.startsWith("skills/");
}
function isPathOrNestedPath(relativePath, nestedPath) {
return relativePath === nestedPath || relativePath.endsWith(`/${nestedPath}`);
}
function shouldCopyRuntimeFile(relativePath) {
return (
relativePath.endsWith("/package.json") ||
relativePath.endsWith("/openclaw.plugin.json") ||
relativePath.endsWith("/.codex-plugin/plugin.json") ||
relativePath.endsWith("/.claude-plugin/plugin.json") ||
relativePath.endsWith("/.cursor-plugin/plugin.json") ||
relativePath.endsWith("/SKILL.md")
isBundledSkillRuntimePath(relativePath) ||
isPathOrNestedPath(relativePath, "package.json") ||
isPathOrNestedPath(relativePath, "openclaw.plugin.json") ||
isPathOrNestedPath(relativePath, ".codex-plugin/plugin.json") ||
isPathOrNestedPath(relativePath, ".claude-plugin/plugin.json") ||
isPathOrNestedPath(relativePath, ".cursor-plugin/plugin.json") ||
isPathOrNestedPath(relativePath, "SKILL.md")
);
}
@@ -175,7 +187,7 @@ function writeRuntimeModuleWrapper(sourcePath, targetPath) {
);
}
function stagePluginRuntimeOverlay(sourceDir, targetDir) {
function stagePluginRuntimeOverlay(sourceDir, targetDir, relativeDir = "") {
fs.mkdirSync(targetDir, { recursive: true });
for (const dirent of fs.readdirSync(sourceDir, { withFileTypes: true })) {
@@ -185,13 +197,18 @@ function stagePluginRuntimeOverlay(sourceDir, targetDir) {
const sourcePath = path.join(sourceDir, dirent.name);
const targetPath = path.join(targetDir, dirent.name);
const relativePath = path.join(relativeDir, dirent.name).replace(/\\/g, "/");
if (dirent.isDirectory()) {
stagePluginRuntimeOverlay(sourcePath, targetPath);
stagePluginRuntimeOverlay(sourcePath, targetPath, relativePath);
continue;
}
if (dirent.isSymbolicLink()) {
if (isBundledSkillRuntimePath(relativePath)) {
copyPathFallback(sourcePath, targetPath);
continue;
}
ensureSymlink(fs.readlinkSync(sourcePath), targetPath, undefined, sourcePath);
continue;
}
@@ -205,7 +222,7 @@ function stagePluginRuntimeOverlay(sourceDir, targetDir) {
continue;
}
if (shouldCopyRuntimeFile(sourcePath)) {
if (shouldCopyRuntimeFile(relativePath)) {
fs.copyFileSync(sourcePath, targetPath);
continue;
}