mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-01 12:21:25 +00:00
fix: harden ACP plugin tools bridge (#56867) (thanks @joe2643)
This commit is contained in:
@@ -6,37 +6,57 @@
|
||||
import { execSync } from "node:child_process";
|
||||
import { existsSync } from "node:fs";
|
||||
import { dirname, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { fileURLToPath, pathToFileURL } from "node:url";
|
||||
|
||||
const isGlobal = process.env.npm_config_global === "true";
|
||||
if (!isGlobal) {
|
||||
process.exit(0);
|
||||
}
|
||||
export const BUNDLED_PLUGIN_INSTALL_TARGETS = [
|
||||
{
|
||||
pluginId: "acpx",
|
||||
sentinelPath: join("node_modules", "acpx", "package.json"),
|
||||
},
|
||||
];
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const extensionsDir = join(__dirname, "..", "dist", "extensions");
|
||||
const DEFAULT_EXTENSIONS_DIR = join(__dirname, "..", "dist", "extensions");
|
||||
|
||||
// Extensions whose runtime deps include platform-specific binaries and therefore
|
||||
// cannot be pre-bundled. Add entries here if new extensions share this pattern.
|
||||
const NEEDS_INSTALL = ["acpx"];
|
||||
export function createNestedNpmInstallEnv(env = process.env) {
|
||||
const nextEnv = { ...env };
|
||||
delete nextEnv.npm_config_global;
|
||||
delete nextEnv.npm_config_prefix;
|
||||
return nextEnv;
|
||||
}
|
||||
|
||||
for (const ext of NEEDS_INSTALL) {
|
||||
const extDir = join(extensionsDir, ext);
|
||||
if (!existsSync(join(extDir, "package.json"))) {
|
||||
continue;
|
||||
export function runBundledPluginPostinstall(params = {}) {
|
||||
const env = params.env ?? process.env;
|
||||
if (env.npm_config_global !== "true") {
|
||||
return;
|
||||
}
|
||||
// Skip if already installed (node_modules/.bin present).
|
||||
if (existsSync(join(extDir, "node_modules", ".bin"))) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
execSync("npm install --omit=dev --no-save --package-lock=false", {
|
||||
cwd: extDir,
|
||||
stdio: "pipe",
|
||||
});
|
||||
console.log(`[postinstall] installed bundled plugin deps: ${ext}`);
|
||||
} catch (e) {
|
||||
// Non-fatal: gateway will surface the missing dep via doctor.
|
||||
console.warn(`[postinstall] could not install deps for ${ext}: ${String(e)}`);
|
||||
const extensionsDir = params.extensionsDir ?? DEFAULT_EXTENSIONS_DIR;
|
||||
const exec = params.execSync ?? execSync;
|
||||
const pathExists = params.existsSync ?? existsSync;
|
||||
const log = params.log ?? console;
|
||||
|
||||
for (const target of BUNDLED_PLUGIN_INSTALL_TARGETS) {
|
||||
const extDir = join(extensionsDir, target.pluginId);
|
||||
if (!pathExists(join(extDir, "package.json"))) {
|
||||
continue;
|
||||
}
|
||||
if (pathExists(join(extDir, target.sentinelPath))) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
exec("npm install --omit=dev --no-save --package-lock=false", {
|
||||
cwd: extDir,
|
||||
env: createNestedNpmInstallEnv(env),
|
||||
stdio: "pipe",
|
||||
});
|
||||
log.log(`[postinstall] installed bundled plugin deps: ${target.pluginId}`);
|
||||
} catch (e) {
|
||||
// Non-fatal: gateway will surface the missing dep via doctor.
|
||||
log.warn(`[postinstall] could not install deps for ${target.pluginId}: ${String(e)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) {
|
||||
runBundledPluginPostinstall();
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
||||
*
|
||||
* Each entry: { src: repo-root-relative source, dest: dist-relative dest }
|
||||
*/
|
||||
const STATIC_EXTENSION_ASSETS = [
|
||||
export const STATIC_EXTENSION_ASSETS = [
|
||||
// acpx MCP proxy — co-deployed alongside the acpx index bundle so that
|
||||
// `path.resolve(dirname(import.meta.url), "mcp-proxy.mjs")` resolves correctly
|
||||
// at runtime (see extensions/acpx/src/runtime-internals/mcp-agent-command.ts).
|
||||
@@ -25,15 +25,19 @@ const STATIC_EXTENSION_ASSETS = [
|
||||
},
|
||||
];
|
||||
|
||||
function copyStaticExtensionAssets() {
|
||||
for (const { src, dest } of STATIC_EXTENSION_ASSETS) {
|
||||
const srcPath = path.join(ROOT, src);
|
||||
const destPath = path.join(ROOT, dest);
|
||||
if (fs.existsSync(srcPath)) {
|
||||
fs.mkdirSync(path.dirname(destPath), { recursive: true });
|
||||
fs.copyFileSync(srcPath, destPath);
|
||||
export function copyStaticExtensionAssets(params = {}) {
|
||||
const rootDir = params.rootDir ?? ROOT;
|
||||
const assets = params.assets ?? STATIC_EXTENSION_ASSETS;
|
||||
const fsImpl = params.fs ?? fs;
|
||||
const warn = params.warn ?? console.warn;
|
||||
for (const { src, dest } of assets) {
|
||||
const srcPath = path.join(rootDir, src);
|
||||
const destPath = path.join(rootDir, dest);
|
||||
if (fsImpl.existsSync(srcPath)) {
|
||||
fsImpl.mkdirSync(path.dirname(destPath), { recursive: true });
|
||||
fsImpl.copyFileSync(srcPath, destPath);
|
||||
} else {
|
||||
console.warn(`[runtime-postbuild] static asset not found, skipping: ${src}`);
|
||||
warn(`[runtime-postbuild] static asset not found, skipping: ${src}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user