mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-19 21:21:10 +00:00
fix: ship bundled runtime support packages
This commit is contained in:
@@ -26,6 +26,45 @@ export function rewritePackageExtensions(entries) {
|
||||
});
|
||||
}
|
||||
|
||||
function collectTopLevelPublicSurfaceEntries(pluginDir) {
|
||||
if (!fs.existsSync(pluginDir)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return fs
|
||||
.readdirSync(pluginDir, { withFileTypes: true })
|
||||
.flatMap((dirent) => {
|
||||
if (!dirent.isFile()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!/\.(?:[cm]?[jt]s)$/u.test(dirent.name) || dirent.name.endsWith(".d.ts")) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const normalizedName = dirent.name.toLowerCase();
|
||||
if (
|
||||
normalizedName.includes(".test.") ||
|
||||
normalizedName.includes(".spec.") ||
|
||||
normalizedName.includes(".fixture.") ||
|
||||
normalizedName.includes(".snap")
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [dirent.name];
|
||||
})
|
||||
.toSorted((left, right) => left.localeCompare(right));
|
||||
}
|
||||
|
||||
function isManifestlessBundledRuntimeSupportPackage(params) {
|
||||
const packageName = typeof params.packageJson?.name === "string" ? params.packageJson.name : "";
|
||||
if (packageName !== `@openclaw/${params.dirName}`) {
|
||||
return false;
|
||||
}
|
||||
return params.topLevelPublicSurfaceEntries.length > 0;
|
||||
}
|
||||
|
||||
function rewritePackageEntry(entry) {
|
||||
if (typeof entry !== "string" || entry.trim().length === 0) {
|
||||
return undefined;
|
||||
@@ -193,35 +232,48 @@ export function copyBundledPluginMetadata(params = {}) {
|
||||
const packageJson = fs.existsSync(packageJsonPath)
|
||||
? JSON.parse(fs.readFileSync(packageJsonPath, "utf8"))
|
||||
: undefined;
|
||||
const topLevelPublicSurfaceEntries = collectTopLevelPublicSurfaceEntries(pluginDir);
|
||||
if (!shouldBuildBundledCluster(dirent.name, env, { packageJson })) {
|
||||
removePathIfExists(distPluginDir);
|
||||
continue;
|
||||
}
|
||||
|
||||
const isManifestlessSupportPackage =
|
||||
!fs.existsSync(manifestPath) &&
|
||||
isManifestlessBundledRuntimeSupportPackage({
|
||||
dirName: dirent.name,
|
||||
packageJson,
|
||||
topLevelPublicSurfaceEntries,
|
||||
});
|
||||
|
||||
sourcePluginDirs.add(dirent.name);
|
||||
|
||||
const distManifestPath = path.join(distPluginDir, "openclaw.plugin.json");
|
||||
const distPackageJsonPath = path.join(distPluginDir, "package.json");
|
||||
if (!fs.existsSync(manifestPath)) {
|
||||
if (!fs.existsSync(manifestPath) && !isManifestlessSupportPackage) {
|
||||
removePathIfExists(distPluginDir);
|
||||
continue;
|
||||
}
|
||||
|
||||
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
||||
// Generated skill assets live under a dedicated dist-owned directory. Also
|
||||
// remove the older bad node_modules tree so release packs cannot pick it up.
|
||||
removePathIfExists(path.join(distPluginDir, GENERATED_BUNDLED_SKILLS_DIR));
|
||||
removePathIfExists(path.join(distPluginDir, "node_modules"));
|
||||
const copiedSkills = copyDeclaredPluginSkillPaths({
|
||||
manifest,
|
||||
pluginDir,
|
||||
distPluginDir,
|
||||
repoRoot,
|
||||
});
|
||||
const bundledManifest = Array.isArray(manifest.skills)
|
||||
? { ...manifest, skills: copiedSkills }
|
||||
: manifest;
|
||||
writeTextFileIfChanged(distManifestPath, `${JSON.stringify(bundledManifest, null, 2)}\n`);
|
||||
if (fs.existsSync(manifestPath)) {
|
||||
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
||||
// Generated skill assets live under a dedicated dist-owned directory. Also
|
||||
// remove the older bad node_modules tree so release packs cannot pick it up.
|
||||
removePathIfExists(path.join(distPluginDir, GENERATED_BUNDLED_SKILLS_DIR));
|
||||
removePathIfExists(path.join(distPluginDir, "node_modules"));
|
||||
const copiedSkills = copyDeclaredPluginSkillPaths({
|
||||
manifest,
|
||||
pluginDir,
|
||||
distPluginDir,
|
||||
repoRoot,
|
||||
});
|
||||
const bundledManifest = Array.isArray(manifest.skills)
|
||||
? { ...manifest, skills: copiedSkills }
|
||||
: manifest;
|
||||
writeTextFileIfChanged(distManifestPath, `${JSON.stringify(bundledManifest, null, 2)}\n`);
|
||||
} else {
|
||||
removeFileIfExists(distManifestPath);
|
||||
}
|
||||
|
||||
if (!fs.existsSync(packageJsonPath)) {
|
||||
removeFileIfExists(distPackageJsonPath);
|
||||
|
||||
@@ -20,6 +20,14 @@ function readBundledPluginPackageJson(packageJsonPath) {
|
||||
}
|
||||
}
|
||||
|
||||
function isManifestlessBundledRuntimeSupportPackage(params) {
|
||||
const packageName = typeof params.packageJson?.name === "string" ? params.packageJson.name : "";
|
||||
if (packageName !== `@openclaw/${params.dirName}`) {
|
||||
return false;
|
||||
}
|
||||
return params.topLevelPublicSurfaceEntries.length > 0;
|
||||
}
|
||||
|
||||
function collectPluginSourceEntries(packageJson) {
|
||||
let packageEntries = Array.isArray(packageJson?.openclaw?.extensions)
|
||||
? packageJson.openclaw.extensions.filter(
|
||||
@@ -83,24 +91,33 @@ export function collectBundledPluginBuildEntries(params = {}) {
|
||||
|
||||
const pluginDir = path.join(extensionsRoot, dirent.name);
|
||||
const manifestPath = path.join(pluginDir, "openclaw.plugin.json");
|
||||
if (!fs.existsSync(manifestPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const hasManifest = fs.existsSync(manifestPath);
|
||||
const packageJsonPath = path.join(pluginDir, "package.json");
|
||||
const packageJson = readBundledPluginPackageJson(packageJsonPath);
|
||||
const topLevelPublicSurfaceEntries = collectTopLevelPublicSurfaceEntries(pluginDir);
|
||||
if (
|
||||
!hasManifest &&
|
||||
!isManifestlessBundledRuntimeSupportPackage({
|
||||
dirName: dirent.name,
|
||||
packageJson,
|
||||
topLevelPublicSurfaceEntries,
|
||||
})
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
if (!shouldBuildBundledCluster(dirent.name, env, { packageJson })) {
|
||||
continue;
|
||||
}
|
||||
|
||||
entries.push({
|
||||
id: dirent.name,
|
||||
hasManifest,
|
||||
hasPackageJson: packageJson !== null,
|
||||
packageJson,
|
||||
sourceEntries: Array.from(
|
||||
new Set([
|
||||
...collectPluginSourceEntries(packageJson),
|
||||
...collectTopLevelPublicSurfaceEntries(pluginDir),
|
||||
...(hasManifest ? collectPluginSourceEntries(packageJson) : []),
|
||||
...topLevelPublicSurfaceEntries,
|
||||
]),
|
||||
),
|
||||
});
|
||||
@@ -125,8 +142,10 @@ export function listBundledPluginPackArtifacts(params = {}) {
|
||||
const entries = collectBundledPluginBuildEntries(params);
|
||||
const artifacts = new Set();
|
||||
|
||||
for (const { id, hasPackageJson, sourceEntries } of entries) {
|
||||
artifacts.add(bundledDistPluginFile(id, "openclaw.plugin.json"));
|
||||
for (const { id, hasManifest, hasPackageJson, sourceEntries } of entries) {
|
||||
if (hasManifest) {
|
||||
artifacts.add(bundledDistPluginFile(id, "openclaw.plugin.json"));
|
||||
}
|
||||
if (hasPackageJson) {
|
||||
artifacts.add(bundledDistPluginFile(id, "package.json"));
|
||||
}
|
||||
|
||||
@@ -350,4 +350,52 @@ describe("copyBundledPluginMetadata", () => {
|
||||
|
||||
expect(fs.existsSync(path.join(repoRoot, "dist", "extensions", pluginId))).toBe(expectedExists);
|
||||
});
|
||||
|
||||
it("preserves manifest-less runtime support package outputs and copies package metadata", () => {
|
||||
const repoRoot = makeRepoRoot("openclaw-bundled-runtime-support-");
|
||||
const pluginDir = path.join(repoRoot, "extensions", "image-generation-core");
|
||||
fs.mkdirSync(pluginDir, { recursive: true });
|
||||
writeJson(path.join(pluginDir, "package.json"), {
|
||||
name: "@openclaw/image-generation-core",
|
||||
version: "0.0.1",
|
||||
private: true,
|
||||
type: "module",
|
||||
});
|
||||
fs.writeFileSync(path.join(pluginDir, "runtime-api.ts"), "export {};\n", "utf8");
|
||||
fs.mkdirSync(path.join(repoRoot, "dist", "extensions", "image-generation-core"), {
|
||||
recursive: true,
|
||||
});
|
||||
fs.writeFileSync(
|
||||
path.join(repoRoot, "dist", "extensions", "image-generation-core", "runtime-api.js"),
|
||||
"export {};\n",
|
||||
"utf8",
|
||||
);
|
||||
|
||||
copyBundledPluginMetadata({ repoRoot });
|
||||
|
||||
expect(fs.existsSync(path.join(repoRoot, "dist", "extensions", "image-generation-core"))).toBe(
|
||||
true,
|
||||
);
|
||||
expect(
|
||||
fs.existsSync(
|
||||
path.join(repoRoot, "dist", "extensions", "image-generation-core", "runtime-api.js"),
|
||||
),
|
||||
).toBe(true);
|
||||
expect(
|
||||
fs.existsSync(
|
||||
path.join(repoRoot, "dist", "extensions", "image-generation-core", "openclaw.plugin.json"),
|
||||
),
|
||||
).toBe(false);
|
||||
expect(
|
||||
JSON.parse(
|
||||
fs.readFileSync(
|
||||
path.join(repoRoot, "dist", "extensions", "image-generation-core", "package.json"),
|
||||
"utf8",
|
||||
),
|
||||
),
|
||||
).toMatchObject({
|
||||
name: "@openclaw/image-generation-core",
|
||||
type: "module",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
35
test/scripts/bundled-plugin-build-entries.test.ts
Normal file
35
test/scripts/bundled-plugin-build-entries.test.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
listBundledPluginBuildEntries,
|
||||
listBundledPluginPackArtifacts,
|
||||
} from "../../scripts/lib/bundled-plugin-build-entries.mjs";
|
||||
|
||||
describe("bundled plugin build entries", () => {
|
||||
it("includes manifest-less runtime core support packages in dist build entries", () => {
|
||||
const entries = listBundledPluginBuildEntries();
|
||||
|
||||
expect(entries).toMatchObject({
|
||||
"extensions/image-generation-core/api": "extensions/image-generation-core/api.ts",
|
||||
"extensions/image-generation-core/runtime-api":
|
||||
"extensions/image-generation-core/runtime-api.ts",
|
||||
"extensions/media-understanding-core/runtime-api":
|
||||
"extensions/media-understanding-core/runtime-api.ts",
|
||||
"extensions/speech-core/api": "extensions/speech-core/api.ts",
|
||||
"extensions/speech-core/runtime-api": "extensions/speech-core/runtime-api.ts",
|
||||
});
|
||||
});
|
||||
|
||||
it("packs runtime core support packages without requiring plugin manifests", () => {
|
||||
const artifacts = listBundledPluginPackArtifacts();
|
||||
|
||||
expect(artifacts).toContain("dist/extensions/image-generation-core/package.json");
|
||||
expect(artifacts).toContain("dist/extensions/image-generation-core/runtime-api.js");
|
||||
expect(artifacts).not.toContain("dist/extensions/image-generation-core/openclaw.plugin.json");
|
||||
expect(artifacts).toContain("dist/extensions/media-understanding-core/runtime-api.js");
|
||||
expect(artifacts).not.toContain(
|
||||
"dist/extensions/media-understanding-core/openclaw.plugin.json",
|
||||
);
|
||||
expect(artifacts).toContain("dist/extensions/speech-core/runtime-api.js");
|
||||
expect(artifacts).not.toContain("dist/extensions/speech-core/openclaw.plugin.json");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user