feat: lifecycle management - no-reply API + moderator bot follow gateway start/stop
- Added gateway_start hook: spawns no-reply API as child process, then starts moderator bot - Added gateway_stop hook: kills no-reply API process, stops moderator bot - No-reply API server.mjs is located relative to plugin dir via import.meta.url - Moderator presence moved from register() to gateway_start for proper lifecycle
This commit is contained in:
@@ -1,10 +1,50 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { spawn, type ChildProcess } from "node:child_process";
|
||||
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
||||
import { evaluateDecision, resolvePolicy, type ChannelPolicy, type Decision, type DirigentConfig } from "./rules.js";
|
||||
import { checkTurn, advanceTurn, resetTurn, onNewMessage, onSpeakerDone, initTurnOrder, getTurnDebugInfo } from "./turn-manager.js";
|
||||
import { startModeratorPresence, stopModeratorPresence } from "./moderator-presence.js";
|
||||
|
||||
// ── No-Reply API child process lifecycle ──────────────────────────────
|
||||
let noReplyProcess: ChildProcess | null = null;
|
||||
|
||||
function startNoReplyApi(logger: { info: (m: string) => void; warn: (m: string) => void }, pluginDir: string, port = 8787): void {
|
||||
if (noReplyProcess) {
|
||||
logger.info("dirigent: no-reply API already running, skipping");
|
||||
return;
|
||||
}
|
||||
|
||||
const serverPath = path.resolve(pluginDir, "..", "no-reply-api", "server.mjs");
|
||||
if (!fs.existsSync(serverPath)) {
|
||||
logger.warn(`dirigent: no-reply API server not found at ${serverPath}, skipping`);
|
||||
return;
|
||||
}
|
||||
|
||||
noReplyProcess = spawn(process.execPath, [serverPath], {
|
||||
env: { ...process.env, PORT: String(port) },
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
detached: false,
|
||||
});
|
||||
|
||||
noReplyProcess.stdout?.on("data", (d: Buffer) => logger.info(`dirigent: no-reply-api: ${d.toString().trim()}`));
|
||||
noReplyProcess.stderr?.on("data", (d: Buffer) => logger.warn(`dirigent: no-reply-api: ${d.toString().trim()}`));
|
||||
|
||||
noReplyProcess.on("exit", (code, signal) => {
|
||||
logger.info(`dirigent: no-reply API exited (code=${code}, signal=${signal})`);
|
||||
noReplyProcess = null;
|
||||
});
|
||||
|
||||
logger.info(`dirigent: no-reply API started (pid=${noReplyProcess.pid}, port=${port})`);
|
||||
}
|
||||
|
||||
function stopNoReplyApi(logger: { info: (m: string) => void }): void {
|
||||
if (!noReplyProcess) return;
|
||||
logger.info("dirigent: stopping no-reply API");
|
||||
noReplyProcess.kill("SIGTERM");
|
||||
noReplyProcess = null;
|
||||
}
|
||||
|
||||
type DiscordControlAction = "channel-private-create" | "channel-private-update" | "member-list";
|
||||
|
||||
type DecisionRecord = {
|
||||
@@ -467,11 +507,25 @@ export default {
|
||||
const liveAtRegister = getLivePluginConfig(api, baseConfig as DirigentConfig);
|
||||
ensurePolicyStateLoaded(api, liveAtRegister);
|
||||
|
||||
// Start moderator bot presence (keep it "online" on Discord)
|
||||
if (liveAtRegister.moderatorBotToken) {
|
||||
startModeratorPresence(liveAtRegister.moderatorBotToken, api.logger);
|
||||
// Resolve plugin directory for locating sibling modules (no-reply-api/)
|
||||
const pluginDir = path.dirname(new URL(import.meta.url).pathname);
|
||||
|
||||
// Gateway lifecycle: start/stop no-reply API and moderator bot with the gateway
|
||||
api.on("gateway_start", () => {
|
||||
startNoReplyApi(api.logger, pluginDir);
|
||||
|
||||
const live = getLivePluginConfig(api, baseConfig as DirigentConfig);
|
||||
if (live.moderatorBotToken) {
|
||||
startModeratorPresence(live.moderatorBotToken, api.logger);
|
||||
api.logger.info("dirigent: moderator bot presence starting");
|
||||
}
|
||||
});
|
||||
|
||||
api.on("gateway_stop", () => {
|
||||
stopNoReplyApi(api.logger);
|
||||
stopModeratorPresence();
|
||||
api.logger.info("dirigent: gateway stopping, services shut down");
|
||||
});
|
||||
|
||||
api.registerTool(
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user