#!/usr/bin/env node import fs from "node:fs"; import path from "node:path"; import { execSync } from "node:child_process"; import { fileURLToPath } from "node:url"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const projRoot = path.resolve(__dirname, ".."); const pluginSrcDir = path.join(projRoot, "plugin"); const routersSrcDir = path.join(projRoot, "routers"); const PLUGIN_ID = "prism-facet"; const ocDir = path.join(process.env.HOME || "~", ".openclaw"); const pluginsDir = path.join(ocDir, "plugins"); const installDir = path.join(pluginsDir, PLUGIN_ID); const configPath = path.join(ocDir, "openclaw.json"); const args = process.argv.slice(2); const action = args[0]; if (!action || !["--install", "--uninstall", "--update"].includes(action)) { console.log("Usage: node scripts/install.mjs --install | --uninstall | --update"); process.exit(1); } function readConfig() { return JSON.parse(fs.readFileSync(configPath, "utf8")); } function writeConfig(cfg) { fs.writeFileSync(configPath, JSON.stringify(cfg, null, 2) + "\n", "utf8"); } function setIfMissing(obj, key, value) { if (obj[key] === undefined || obj[key] === null) { obj[key] = value; } } function buildPlugin() { console.log("Building plugin..."); // Compile TypeScript from plugin/ to plugin/ (in-place, JS output) execSync("npx tsc --project tsconfig.plugin.json", { cwd: projRoot, stdio: "inherit" }); } function install() { // 1. Build buildPlugin(); // 2. Clean and copy plugin to install dir if (fs.existsSync(installDir)) { fs.rmSync(installDir, { recursive: true }); } fs.mkdirSync(installDir, { recursive: true }); // Copy compiled plugin files copyDirRecursive(pluginSrcDir, installDir, [".ts"]); // Copy routers alongside plugin const routersInstallDir = path.join(installDir, "routers"); if (fs.existsSync(routersSrcDir)) { fs.mkdirSync(routersInstallDir, { recursive: true }); copyDirRecursive(routersSrcDir, routersInstallDir); } // Create empty rules.json if not exists const rulesPath = path.join(installDir, "rules.json"); if (!fs.existsSync(rulesPath)) { fs.writeFileSync(rulesPath, "{}\n", "utf8"); } // 3. Update openclaw.json const cfg = readConfig(); const plugins = cfg.plugins ??= {}; const allow = plugins.allow ??= []; const loadPaths = (plugins.load ??= {}).paths ??= []; const entries = plugins.entries ??= {}; if (!allow.includes(PLUGIN_ID)) allow.push(PLUGIN_ID); if (!loadPaths.includes(installDir)) loadPaths.push(installDir); const entry = entries[PLUGIN_ID] ??= {}; setIfMissing(entry, "enabled", true); const hooks = entry.hooks ??= {}; setIfMissing(hooks, "allowPromptInjection", true); writeConfig(cfg); console.log(`Installed ${PLUGIN_ID} to ${installDir}`); console.log("Restart gateway: openclaw gateway restart"); } function uninstall() { const cfg = readConfig(); const plugins = cfg.plugins ?? {}; // Remove from allow if (Array.isArray(plugins.allow)) { plugins.allow = plugins.allow.filter((id) => id !== PLUGIN_ID); } // Remove entry if (plugins.entries) delete plugins.entries[PLUGIN_ID]; // Remove load path if (plugins.load?.paths) { plugins.load.paths = plugins.load.paths.filter((p) => !p.includes(PLUGIN_ID)); } writeConfig(cfg); // Remove install dir if (fs.existsSync(installDir)) { fs.rmSync(installDir, { recursive: true }); console.log(`Removed ${installDir}`); } console.log(`Uninstalled ${PLUGIN_ID}`); } function copyDirRecursive(src, dest, excludeExts = []) { if (!fs.existsSync(dest)) fs.mkdirSync(dest, { recursive: true }); for (const entry of fs.readdirSync(src, { withFileTypes: true })) { const srcPath = path.join(src, entry.name); const destPath = path.join(dest, entry.name); if (entry.isDirectory()) { if (entry.name === "node_modules") continue; copyDirRecursive(srcPath, destPath, excludeExts); } else { if (excludeExts.some((ext) => entry.name.endsWith(ext))) continue; fs.copyFileSync(srcPath, destPath); } } } switch (action) { case "--install": case "--update": install(); break; case "--uninstall": uninstall(); break; }