




















@@ -1,3 +1,4 @@
1+import { spawnSync } from "node:child_process";
12import fs from "node:fs";
23import path from "node:path";
34import {
@@ -12,8 +13,8 @@ export const NON_PACKAGED_BUNDLED_PLUGIN_DIRS = new Set(["qa-channel", "qa-lab",
1213const EXCLUDED_CORE_BUNDLED_PLUGIN_DIRS = new Set(["qqbot", "whatsapp"]);
1314const toPosixPath = (value) => value.replaceAll("\\", "/");
141515-function readBundledPluginPackageJson(packageJsonPath) {
16-if (!fs.existsSync(packageJsonPath)) {
16+function readBundledPluginPackageJson(packageJsonPath, options = {}) {
17+if (!(options.hasPackageJson ?? fs.existsSync(packageJsonPath))) {
1718return null;
1819}
1920try {
@@ -86,45 +87,124 @@ export function collectTopLevelPublicSurfaceEntries(pluginDir) {
8687.toSorted((left, right) => left.localeCompare(right));
8788}
888990+function collectTopLevelPublicSurfaceEntriesFromFiles(relativeFiles) {
91+return relativeFiles
92+.flatMap((relativeFile) => {
93+if (relativeFile.includes("/")) {
94+return [];
95+}
96+97+const ext = path.extname(relativeFile);
98+if (!TOP_LEVEL_PUBLIC_SURFACE_EXTENSIONS.has(ext)) {
99+return [];
100+}
101+102+const normalizedName = relativeFile.toLowerCase();
103+if (
104+normalizedName.endsWith(".d.ts") ||
105+/^config-api\.(?:[cm]?[jt]s)$/u.test(normalizedName) ||
106+normalizedName.includes(".test.") ||
107+normalizedName.includes(".spec.") ||
108+normalizedName.includes(".fixture.") ||
109+normalizedName.includes(".snap")
110+) {
111+return [];
112+}
113+114+return [`./${relativeFile}`];
115+})
116+.toSorted((left, right) => left.localeCompare(right));
117+}
118+119+function collectTrackedBundledPluginFiles(cwd) {
120+const result = spawnSync("git", ["ls-files", "--", BUNDLED_PLUGIN_ROOT_DIR], {
121+ cwd,
122+encoding: "utf8",
123+stdio: ["ignore", "pipe", "ignore"],
124+});
125+if (result.status !== 0) {
126+return null;
127+}
128+129+const filesByPlugin = new Map();
130+for (const rawLine of result.stdout.split("\n")) {
131+const line = toPosixPath(rawLine.trim());
132+const match = new RegExp(`^${BUNDLED_PLUGIN_ROOT_DIR}/([^/]+)/(.+)$`).exec(line);
133+if (!match) {
134+continue;
135+}
136+const [, dirName, relativeFile] = match;
137+const files = filesByPlugin.get(dirName) ?? [];
138+files.push(relativeFile);
139+filesByPlugin.set(dirName, files);
140+}
141+142+return filesByPlugin;
143+}
144+145+function collectBundledPluginCandidates(cwd, extensionsRoot) {
146+const trackedFiles = collectTrackedBundledPluginFiles(cwd);
147+if (trackedFiles) {
148+return [...trackedFiles.entries()]
149+.map(([dirName, relativeFiles]) => ({
150+ dirName,
151+pluginDir: path.join(extensionsRoot, dirName),
152+ relativeFiles,
153+topLevelPublicSurfaceEntries: collectTopLevelPublicSurfaceEntriesFromFiles(relativeFiles),
154+}))
155+.toSorted((left, right) => left.dirName.localeCompare(right.dirName));
156+}
157+158+return fs
159+.readdirSync(extensionsRoot, { withFileTypes: true })
160+.filter((dirent) => dirent.isDirectory())
161+.map((dirent) => {
162+const pluginDir = path.join(extensionsRoot, dirent.name);
163+return {
164+dirName: dirent.name,
165+ pluginDir,
166+relativeFiles: null,
167+topLevelPublicSurfaceEntries: collectTopLevelPublicSurfaceEntries(pluginDir),
168+};
169+});
170+}
171+89172export function collectBundledPluginBuildEntries(params = {}) {
90173const cwd = params.cwd ?? process.cwd();
91174const env = params.env ?? process.env;
92175const extensionsRoot = path.join(cwd, BUNDLED_PLUGIN_ROOT_DIR);
93176const entries = [];
9417795-for (const dirent of fs.readdirSync(extensionsRoot, { withFileTypes: true })) {
96-if (!dirent.isDirectory()) {
97-continue;
98-}
99-100-const pluginDir = path.join(extensionsRoot, dirent.name);
178+for (const candidate of collectBundledPluginCandidates(cwd, extensionsRoot)) {
179+const { dirName, pluginDir, relativeFiles, topLevelPublicSurfaceEntries } = candidate;
101180const manifestPath = path.join(pluginDir, "openclaw.plugin.json");
102-const hasManifest = fs.existsSync(manifestPath);
181+const hasManifest = relativeFiles?.includes("openclaw.plugin.json") ?? fs.existsSync(manifestPath);
103182const packageJsonPath = path.join(pluginDir, "package.json");
104-const packageJson = readBundledPluginPackageJson(packageJsonPath);
105-const topLevelPublicSurfaceEntries = collectTopLevelPublicSurfaceEntries(pluginDir);
183+const packageJson = readBundledPluginPackageJson(packageJsonPath, {
184+hasPackageJson: relativeFiles?.includes("package.json"),
185+});
106186if (
107187!hasManifest &&
108188!isManifestlessBundledRuntimeSupportPackage({
109-dirName: dirent.name,
189+ dirName,
110190 packageJson,
111191 topLevelPublicSurfaceEntries,
112192})
113193) {
114194continue;
115195}
116-if (!shouldBuildBundledCluster(dirent.name, env, { packageJson })) {
196+if (!shouldBuildBundledCluster(dirName, env, { packageJson })) {
117197continue;
118198}
119199if (!shouldBuildBundledDistEntry(packageJson)) {
120200continue;
121201}
122-if (EXCLUDED_CORE_BUNDLED_PLUGIN_DIRS.has(dirent.name)) {
202+if (EXCLUDED_CORE_BUNDLED_PLUGIN_DIRS.has(dirName)) {
123203continue;
124204}
125205126206entries.push({
127-id: dirent.name,
207+id: dirName,
128208 hasManifest,
129209hasPackageJson: packageJson !== null,
130210 packageJson,
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。