




















@@ -1,53 +1,93 @@
1+import { spawnSync } from "node:child_process";
12import fs from "node:fs";
23import path from "node:path";
3445const readJson = (file) => JSON.parse(fs.readFileSync(file, "utf8"));
566-function loadManifestEntries() {
7-const explicit = (process.env.OPENCLAW_BUNDLED_PLUGIN_SWEEP_IDS || "")
8-.split(/[,\s]+/u)
9-.map((entry) => entry.trim())
10-.filter(Boolean);
11-const extensionRoot = path.join(process.cwd(), "dist", "extensions");
12-const manifestEntries = fs
13-.readdirSync(extensionRoot, { withFileTypes: true })
14-.filter((entry) => entry.isDirectory())
15-.map((entry) => {
16-const manifestPath = path.join(extensionRoot, entry.name, "openclaw.plugin.json");
17-if (!fs.existsSync(manifestPath)) {
7+function resolveOpenClawEntry() {
8+if (process.env.OPENCLAW_ENTRY) {
9+return process.env.OPENCLAW_ENTRY;
10+}
11+for (const entry of ["dist/index.mjs", "dist/index.js"]) {
12+if (fs.existsSync(entry)) {
13+return entry;
14+}
15+}
16+throw new Error("Missing OPENCLAW_ENTRY and dist/index.(m)js");
17+}
18+19+function readPluginsList() {
20+const entry = resolveOpenClawEntry();
21+const result = spawnSync(process.execPath, [entry, "plugins", "list", "--json"], {
22+cwd: process.cwd(),
23+encoding: "utf8",
24+env: process.env,
25+});
26+if (result.status !== 0) {
27+throw new Error(
28+`Unable to list packaged bundled plugins: ${result.stderr || result.stdout || `exit ${result.status}`}`,
29+);
30+}
31+const payload = JSON.parse(result.stdout);
32+return Array.isArray(payload.plugins) ? payload.plugins : [];
33+}
34+35+function pluginRequiresConfig(pluginDir) {
36+const manifestPath = path.join(pluginDir, "openclaw.plugin.json");
37+if (!fs.existsSync(manifestPath)) {
38+throw new Error(`missing bundled plugin manifest: ${manifestPath}`);
39+}
40+const manifest = readJson(manifestPath);
41+const required = manifest.configSchema?.required;
42+return Array.isArray(required) && required.some((value) => typeof value === "string");
43+}
44+45+async function loadPackagedBundledEntries() {
46+return readPluginsList()
47+.filter((plugin) => plugin?.origin === "bundled")
48+.map((plugin) => {
49+const id = typeof plugin.id === "string" ? plugin.id.trim() : "";
50+const rootDir = typeof plugin.rootDir === "string" ? plugin.rootDir.trim() : "";
51+const source = typeof plugin.source === "string" ? plugin.source.trim() : "";
52+const pluginDir = rootDir || (source ? path.dirname(source) : "");
53+if (!id || !pluginDir) {
1854return null;
1955}
20-const manifest = readJson(manifestPath);
21-const id = typeof manifest.id === "string" ? manifest.id.trim() : "";
22-if (!id) {
23-throw new Error(`Bundled plugin manifest is missing id: ${manifestPath}`);
24-}
25-const required = manifest.configSchema?.required;
2656return {
2757 id,
28-dir: entry.name,
29-requiresConfig:
30- Array.isArray(required) && required.some((value) => typeof value === "string"),
58+dir: path.basename(pluginDir),
59+rootDir: pluginDir,
60+requiresConfig: pluginRequiresConfig(pluginDir),
3161};
3262})
3363.filter(Boolean)
3464.toSorted((a, b) => a.id.localeCompare(b.id));
65+}
66+67+async function loadManifestEntries() {
68+const explicit = (process.env.OPENCLAW_BUNDLED_PLUGIN_SWEEP_IDS || "")
69+.split(/[,\s]+/u)
70+.map((entry) => entry.trim())
71+.filter(Boolean);
72+const manifestEntries = await loadPackagedBundledEntries();
35733674if (explicit.length === 0) {
3775return manifestEntries;
3876}
39-return explicit.map(
40-(lookup) =>
41-manifestEntries.find((entry) => entry.id === lookup || entry.dir === lookup) || {
42-id: lookup,
43-dir: lookup,
44-requiresConfig: false,
45-},
46-);
77+const available = manifestEntries.map((entry) => entry.id).join(", ");
78+return explicit.map((lookup) => {
79+const found = manifestEntries.find((entry) => entry.id === lookup || entry.dir === lookup);
80+if (!found) {
81+throw new Error(
82+`OPENCLAW_BUNDLED_PLUGIN_SWEEP_IDS entry is not an installable bundled plugin in this package: ${lookup}. Available: ${available}`,
83+);
84+}
85+return found;
86+});
4787}
488849-function selectedManifestEntries() {
50-const allEntries = loadManifestEntries();
89+async function selectedManifestEntries() {
90+const allEntries = await loadManifestEntries();
5191const total = Number.parseInt(process.env.OPENCLAW_BUNDLED_PLUGIN_SWEEP_TOTAL || "1", 10);
5292const index = Number.parseInt(process.env.OPENCLAW_BUNDLED_PLUGIN_SWEEP_INDEX || "0", 10);
5393if (!Number.isInteger(total) || total < 1) {
@@ -85,15 +125,23 @@ function assertInstalled(pluginId, pluginDir, requiresConfig) {
85125}
86126if (
87127typeof record.sourcePath !== "string" ||
88-!record.sourcePath.includes(`/dist/extensions/${pluginDir}`)
128+![`/dist/extensions/${pluginDir}`, `/dist-runtime/extensions/${pluginDir}`].some((fragment) =>
129+record.sourcePath.includes(fragment),
130+)
89131) {
90132throw new Error(`unexpected bundled source path for ${pluginId}: ${record.sourcePath}`);
91133}
92134if (record.installPath !== record.sourcePath) {
93135throw new Error(`bundled install path should equal source path for ${pluginId}`);
94136}
95137const paths = config.plugins?.load?.paths || [];
96-if (paths.some((entry) => String(entry).includes(`/dist/extensions/${pluginDir}`))) {
138+if (
139+paths.some((entry) =>
140+[`/dist/extensions/${pluginDir}`, `/dist-runtime/extensions/${pluginDir}`].some(
141+(fragment) => String(entry).includes(fragment),
142+),
143+)
144+) {
97145throw new Error(`config load paths should not include bundled install path for ${pluginId}`);
98146}
99147if (requiresConfig && config.plugins?.entries?.[pluginId]?.enabled === true) {
@@ -123,7 +171,13 @@ function assertUninstalled(pluginId, pluginDir) {
123171throw new Error(`install record still present after uninstall for ${pluginId}`);
124172}
125173const paths = config.plugins?.load?.paths || [];
126-if (paths.some((entry) => String(entry).includes(`/dist/extensions/${pluginDir}`))) {
174+if (
175+paths.some((entry) =>
176+[`/dist/extensions/${pluginDir}`, `/dist-runtime/extensions/${pluginDir}`].some(
177+(fragment) => String(entry).includes(fragment),
178+),
179+)
180+) {
127181throw new Error(`load path still present after uninstall for ${pluginId}`);
128182}
129183if (config.plugins?.entries?.[pluginId]) {
@@ -145,8 +199,8 @@ function assertUninstalled(pluginId, pluginDir) {
145199146200const [command, pluginId, pluginDir, requiresConfig] = process.argv.slice(2);
147201if (command === "select") {
148-for (const entry of selectedManifestEntries()) {
149-console.log(`${entry.id}\t${entry.dir}\t${entry.requiresConfig ? "1" : "0"}`);
202+for (const entry of await selectedManifestEntries()) {
203+console.log(`${entry.id}\t${entry.dir}\t${entry.requiresConfig ? "1" : "0"}\t${entry.rootDir}`);
150204}
151205} else if (command === "assert-installed") {
152206assertInstalled(pluginId, pluginDir, requiresConfig === "1");
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。