refactor: restructure PrismFacet per OpenClaw plugin spec
- plugin/ directory structure: hooks/, tools/, core/
- export default { id, name, register } entry format
- globalThis state management with lifecycle protection
- WeakSet dedup on before_prompt_build hook
- Tool uses inputSchema + execute (not parameters + handler)
- additionalProperties: false in config schema
- Core logic in plugin/core/ (no plugin-sdk dependency)
- Install/uninstall script (scripts/install.mjs)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
143
scripts/install.mjs
Normal file
143
scripts/install.mjs
Normal file
@@ -0,0 +1,143 @@
|
||||
#!/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;
|
||||
}
|
||||
Reference in New Issue
Block a user