refactor(installer): rename to install.mjs, remove record/restore flow, add --no-reply-port and wire config port
This commit is contained in:
247
scripts/install.mjs
Executable file
247
scripts/install.mjs
Executable file
@@ -0,0 +1,247 @@
|
||||
#!/usr/bin/env node
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import os from "node:os";
|
||||
import { execFileSync, spawnSync } from "node:child_process";
|
||||
|
||||
const VALID_MODES = new Set(["--install", "--uninstall", "--update"]);
|
||||
let modeArg = null;
|
||||
let argOpenClawDir = null;
|
||||
let argNoReplyPort = 8787;
|
||||
|
||||
for (let i = 2; i < process.argv.length; i++) {
|
||||
const arg = process.argv[i];
|
||||
if (VALID_MODES.has(arg)) {
|
||||
modeArg = arg;
|
||||
} else if (arg === "--openclaw-profile-path" && i + 1 < process.argv.length) {
|
||||
argOpenClawDir = process.argv[++i];
|
||||
} else if (arg.startsWith("--openclaw-profile-path=")) {
|
||||
argOpenClawDir = arg.split("=").slice(1).join("=");
|
||||
} else if (arg === "--no-reply-port" && i + 1 < process.argv.length) {
|
||||
argNoReplyPort = Number(process.argv[++i]);
|
||||
} else if (arg.startsWith("--no-reply-port=")) {
|
||||
argNoReplyPort = Number(arg.split("=").slice(1).join("="));
|
||||
}
|
||||
}
|
||||
|
||||
if (!modeArg) {
|
||||
console.error("Usage: node scripts/install.mjs --install|--uninstall|--update [--openclaw-profile-path <path>] [--no-reply-port <port>]");
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
if (!Number.isFinite(argNoReplyPort) || argNoReplyPort < 1 || argNoReplyPort > 65535) {
|
||||
console.error("[dirigent] invalid --no-reply-port (1-65535)");
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
const mode = modeArg.slice(2);
|
||||
|
||||
function step(msg) { console.log(`→ ${msg}`); }
|
||||
function ok(msg) { console.log(`✓ ${msg}`); }
|
||||
function warn(msg) { console.log(`! ${msg}`); }
|
||||
|
||||
function resolveOpenClawDir() {
|
||||
if (argOpenClawDir) {
|
||||
const dir = argOpenClawDir.replace(/^~(?=$|\/)/, os.homedir());
|
||||
if (!fs.existsSync(dir)) throw new Error(`--openclaw-profile-path not found: ${dir}`);
|
||||
return dir;
|
||||
}
|
||||
if (process.env.OPENCLAW_DIR) {
|
||||
const dir = process.env.OPENCLAW_DIR.replace(/^~(?=$|\/)/, os.homedir());
|
||||
if (fs.existsSync(dir)) return dir;
|
||||
warn(`OPENCLAW_DIR not found: ${dir}, fallback to ~/.openclaw`);
|
||||
}
|
||||
const fallback = path.join(os.homedir(), ".openclaw");
|
||||
if (!fs.existsSync(fallback)) throw new Error("cannot resolve OpenClaw dir");
|
||||
return fallback;
|
||||
}
|
||||
|
||||
const OPENCLAW_DIR = resolveOpenClawDir();
|
||||
const OPENCLAW_CONFIG_PATH = process.env.OPENCLAW_CONFIG_PATH || path.join(OPENCLAW_DIR, "openclaw.json");
|
||||
if (!fs.existsSync(OPENCLAW_CONFIG_PATH)) {
|
||||
console.error(`[dirigent] config not found: ${OPENCLAW_CONFIG_PATH}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const __dirname = path.dirname(new URL(import.meta.url).pathname);
|
||||
const REPO_ROOT = path.resolve(__dirname, "..");
|
||||
const PLUGINS_DIR = path.join(OPENCLAW_DIR, "plugins");
|
||||
const PLUGIN_INSTALL_DIR = path.join(PLUGINS_DIR, "dirigent");
|
||||
const NO_REPLY_INSTALL_DIR = path.join(PLUGINS_DIR, "no-reply-api");
|
||||
|
||||
const NO_REPLY_PROVIDER_ID = process.env.NO_REPLY_PROVIDER_ID || "dirigentway";
|
||||
const NO_REPLY_MODEL_ID = process.env.NO_REPLY_MODEL_ID || "no-reply";
|
||||
const NO_REPLY_PORT = Number(process.env.NO_REPLY_PORT || argNoReplyPort);
|
||||
const NO_REPLY_BASE_URL = process.env.NO_REPLY_BASE_URL || `http://127.0.0.1:${NO_REPLY_PORT}/v1`;
|
||||
const NO_REPLY_API_KEY = process.env.NO_REPLY_API_KEY || "wg-local-test-token";
|
||||
const LIST_MODE = process.env.LIST_MODE || "human-list";
|
||||
const HUMAN_LIST_JSON = process.env.HUMAN_LIST_JSON || "[]";
|
||||
const AGENT_LIST_JSON = process.env.AGENT_LIST_JSON || "[]";
|
||||
const CHANNEL_POLICIES_FILE = process.env.CHANNEL_POLICIES_FILE || path.join(OPENCLAW_DIR, "dirigent-channel-policies.json");
|
||||
const CHANNEL_POLICIES_JSON = process.env.CHANNEL_POLICIES_JSON || "{}";
|
||||
const END_SYMBOLS_JSON = process.env.END_SYMBOLS_JSON || '["🔚"]';
|
||||
const SCHEDULING_IDENTIFIER = process.env.SCHEDULING_IDENTIFIER || "➡️";
|
||||
|
||||
function runOpenclaw(args, allowFail = false) {
|
||||
try {
|
||||
return execFileSync("openclaw", args, { encoding: "utf8" }).trim();
|
||||
} catch (e) {
|
||||
if (allowFail) return null;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
function getJson(pathKey) {
|
||||
const out = runOpenclaw(["config", "get", pathKey, "--json"], true);
|
||||
if (!out || out === "undefined") return undefined;
|
||||
try { return JSON.parse(out); } catch { return undefined; }
|
||||
}
|
||||
|
||||
function setJson(pathKey, value) {
|
||||
runOpenclaw(["config", "set", pathKey, JSON.stringify(value), "--json"]);
|
||||
}
|
||||
|
||||
function unsetPath(pathKey) {
|
||||
runOpenclaw(["config", "unset", pathKey], true);
|
||||
}
|
||||
|
||||
function syncDirRecursive(src, dest) {
|
||||
fs.mkdirSync(dest, { recursive: true });
|
||||
fs.cpSync(src, dest, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
function isRegistered() {
|
||||
const entry = getJson("plugins.entries.dirigent");
|
||||
return !!(entry && typeof entry === "object");
|
||||
}
|
||||
|
||||
if (mode === "update") {
|
||||
const branch = process.env.DIRIGENT_GIT_BRANCH || "latest";
|
||||
step(`update source branch=${branch}`);
|
||||
execFileSync("git", ["fetch", "origin", branch], { cwd: REPO_ROOT, stdio: "inherit" });
|
||||
execFileSync("git", ["checkout", branch], { cwd: REPO_ROOT, stdio: "inherit" });
|
||||
execFileSync("git", ["pull", "origin", branch], { cwd: REPO_ROOT, stdio: "inherit" });
|
||||
ok("source updated");
|
||||
|
||||
const script = path.join(REPO_ROOT, "scripts", "install.mjs");
|
||||
const args = [script, "--install", "--openclaw-profile-path", OPENCLAW_DIR, "--no-reply-port", String(NO_REPLY_PORT)];
|
||||
const ret = spawnSync(process.execPath, args, { cwd: REPO_ROOT, stdio: "inherit", env: process.env });
|
||||
process.exit(ret.status ?? 1);
|
||||
}
|
||||
|
||||
if (mode === "install") {
|
||||
step(`OpenClaw dir: ${OPENCLAW_DIR}`);
|
||||
|
||||
if (isRegistered()) {
|
||||
warn("plugins.entries.dirigent exists; reinstalling in-place");
|
||||
}
|
||||
|
||||
step("build dist assets");
|
||||
const pluginSrc = path.resolve(REPO_ROOT, "plugin");
|
||||
const noReplySrc = path.resolve(REPO_ROOT, "no-reply-api");
|
||||
const distPlugin = path.resolve(REPO_ROOT, "dist", "dirigent");
|
||||
const distNoReply = path.resolve(REPO_ROOT, "dist", "no-reply-api");
|
||||
syncDirRecursive(pluginSrc, distPlugin);
|
||||
syncDirRecursive(noReplySrc, distNoReply);
|
||||
|
||||
step(`install plugin -> ${PLUGIN_INSTALL_DIR}`);
|
||||
fs.mkdirSync(PLUGINS_DIR, { recursive: true });
|
||||
syncDirRecursive(distPlugin, PLUGIN_INSTALL_DIR);
|
||||
syncDirRecursive(distNoReply, NO_REPLY_INSTALL_DIR);
|
||||
|
||||
if (!fs.existsSync(CHANNEL_POLICIES_FILE)) {
|
||||
fs.mkdirSync(path.dirname(CHANNEL_POLICIES_FILE), { recursive: true });
|
||||
fs.writeFileSync(CHANNEL_POLICIES_FILE, `${CHANNEL_POLICIES_JSON}\n`);
|
||||
ok(`init channel policies file: ${CHANNEL_POLICIES_FILE}`);
|
||||
}
|
||||
|
||||
const plugins = getJson("plugins") || {};
|
||||
const loadPaths = Array.isArray(plugins?.load?.paths) ? plugins.load.paths : [];
|
||||
if (!loadPaths.includes(PLUGIN_INSTALL_DIR)) loadPaths.push(PLUGIN_INSTALL_DIR);
|
||||
plugins.load = plugins.load || {};
|
||||
plugins.load.paths = loadPaths;
|
||||
|
||||
plugins.entries = plugins.entries || {};
|
||||
plugins.entries.dirigent = {
|
||||
enabled: true,
|
||||
config: {
|
||||
enabled: true,
|
||||
discordOnly: true,
|
||||
listMode: LIST_MODE,
|
||||
humanList: JSON.parse(HUMAN_LIST_JSON),
|
||||
agentList: JSON.parse(AGENT_LIST_JSON),
|
||||
channelPoliciesFile: CHANNEL_POLICIES_FILE,
|
||||
endSymbols: JSON.parse(END_SYMBOLS_JSON),
|
||||
schedulingIdentifier: SCHEDULING_IDENTIFIER,
|
||||
noReplyProvider: NO_REPLY_PROVIDER_ID,
|
||||
noReplyModel: NO_REPLY_MODEL_ID,
|
||||
noReplyPort: NO_REPLY_PORT,
|
||||
},
|
||||
};
|
||||
setJson("plugins", plugins);
|
||||
|
||||
const providers = getJson("models.providers") || {};
|
||||
providers[NO_REPLY_PROVIDER_ID] = {
|
||||
baseUrl: NO_REPLY_BASE_URL,
|
||||
apiKey: NO_REPLY_API_KEY,
|
||||
api: "openai-completions",
|
||||
models: [
|
||||
{
|
||||
id: NO_REPLY_MODEL_ID,
|
||||
name: `${NO_REPLY_MODEL_ID} (Custom Provider)`,
|
||||
reasoning: false,
|
||||
input: ["text"],
|
||||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
||||
contextWindow: 200000,
|
||||
maxTokens: 8192,
|
||||
},
|
||||
],
|
||||
};
|
||||
setJson("models.providers", providers);
|
||||
|
||||
const allow = getJson("plugins.allow") || [];
|
||||
if (!allow.includes("dirigent")) {
|
||||
allow.push("dirigent");
|
||||
setJson("plugins.allow", allow);
|
||||
}
|
||||
|
||||
ok(`installed (no-reply port: ${NO_REPLY_PORT})`);
|
||||
console.log("↻ restart gateway: openclaw gateway restart");
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (mode === "uninstall") {
|
||||
step(`OpenClaw dir: ${OPENCLAW_DIR}`);
|
||||
|
||||
const allow = getJson("plugins.allow") || [];
|
||||
const idx = allow.indexOf("dirigent");
|
||||
if (idx >= 0) {
|
||||
allow.splice(idx, 1);
|
||||
setJson("plugins.allow", allow);
|
||||
ok("removed from plugins.allow");
|
||||
}
|
||||
|
||||
unsetPath("plugins.entries.dirigent");
|
||||
ok("removed plugins.entries.dirigent");
|
||||
|
||||
const plugins = getJson("plugins") || {};
|
||||
const paths = Array.isArray(plugins?.load?.paths) ? plugins.load.paths : [];
|
||||
plugins.load = plugins.load || {};
|
||||
plugins.load.paths = paths.filter((p) => p !== PLUGIN_INSTALL_DIR);
|
||||
setJson("plugins", plugins);
|
||||
ok("removed plugin path from plugins.load.paths");
|
||||
|
||||
const providers = getJson("models.providers") || {};
|
||||
delete providers[NO_REPLY_PROVIDER_ID];
|
||||
setJson("models.providers", providers);
|
||||
ok(`removed provider ${NO_REPLY_PROVIDER_ID}`);
|
||||
|
||||
if (fs.existsSync(PLUGIN_INSTALL_DIR)) fs.rmSync(PLUGIN_INSTALL_DIR, { recursive: true, force: true });
|
||||
if (fs.existsSync(NO_REPLY_INSTALL_DIR)) fs.rmSync(NO_REPLY_INSTALL_DIR, { recursive: true, force: true });
|
||||
const legacyNoReply = path.join(PLUGINS_DIR, "dirigent-no-reply-api");
|
||||
if (fs.existsSync(legacyNoReply)) fs.rmSync(legacyNoReply, { recursive: true, force: true });
|
||||
ok("removed installed files");
|
||||
|
||||
console.log("↻ restart gateway: openclaw gateway restart");
|
||||
process.exit(0);
|
||||
}
|
||||
Reference in New Issue
Block a user