mirror of https://github.com/openclaw/openclaw.git
152 lines
4.1 KiB
JavaScript
152 lines
4.1 KiB
JavaScript
#!/usr/bin/env node
|
|
import { spawn } from "node:child_process";
|
|
import path from "node:path";
|
|
import process from "node:process";
|
|
import { pathToFileURL } from "node:url";
|
|
import chokidar from "chokidar";
|
|
import { isRestartRelevantRunNodePath, runNodeWatchedPaths } from "./run-node.mjs";
|
|
|
|
const WATCH_NODE_RUNNER = "scripts/run-node.mjs";
|
|
const WATCH_RESTART_SIGNAL = "SIGTERM";
|
|
|
|
const buildRunnerArgs = (args) => [WATCH_NODE_RUNNER, ...args];
|
|
|
|
const normalizePath = (filePath) =>
|
|
String(filePath ?? "")
|
|
.replaceAll("\\", "/")
|
|
.replace(/^\.\/+/, "");
|
|
|
|
const resolveRepoPath = (filePath, cwd) => {
|
|
const rawPath = String(filePath ?? "");
|
|
if (path.isAbsolute(rawPath)) {
|
|
return normalizePath(path.relative(cwd, rawPath));
|
|
}
|
|
return normalizePath(rawPath);
|
|
};
|
|
|
|
const isIgnoredWatchPath = (filePath, cwd) =>
|
|
!isRestartRelevantRunNodePath(resolveRepoPath(filePath, cwd));
|
|
|
|
export async function runWatchMain(params = {}) {
|
|
const deps = {
|
|
spawn: params.spawn ?? spawn,
|
|
process: params.process ?? process,
|
|
cwd: params.cwd ?? process.cwd(),
|
|
args: params.args ?? process.argv.slice(2),
|
|
env: params.env ? { ...params.env } : { ...process.env },
|
|
now: params.now ?? Date.now,
|
|
createWatcher:
|
|
params.createWatcher ?? ((watchPaths, options) => chokidar.watch(watchPaths, options)),
|
|
watchPaths: params.watchPaths ?? runNodeWatchedPaths,
|
|
};
|
|
|
|
const childEnv = { ...deps.env };
|
|
const watchSession = `${deps.now()}-${deps.process.pid}`;
|
|
childEnv.OPENCLAW_WATCH_MODE = "1";
|
|
childEnv.OPENCLAW_WATCH_SESSION = watchSession;
|
|
if (deps.args.length > 0) {
|
|
childEnv.OPENCLAW_WATCH_COMMAND = deps.args.join(" ");
|
|
}
|
|
|
|
return await new Promise((resolve) => {
|
|
let settled = false;
|
|
let shuttingDown = false;
|
|
let restartRequested = false;
|
|
let watchProcess = null;
|
|
let onSigInt;
|
|
let onSigTerm;
|
|
|
|
const watcher = deps.createWatcher(deps.watchPaths, {
|
|
ignoreInitial: true,
|
|
ignored: (watchPath) => isIgnoredWatchPath(watchPath, deps.cwd),
|
|
});
|
|
|
|
const settle = (code) => {
|
|
if (settled) {
|
|
return;
|
|
}
|
|
settled = true;
|
|
if (onSigInt) {
|
|
deps.process.off("SIGINT", onSigInt);
|
|
}
|
|
if (onSigTerm) {
|
|
deps.process.off("SIGTERM", onSigTerm);
|
|
}
|
|
watcher.close?.().catch?.(() => {});
|
|
resolve(code);
|
|
};
|
|
|
|
const startRunner = () => {
|
|
watchProcess = deps.spawn(deps.process.execPath, buildRunnerArgs(deps.args), {
|
|
cwd: deps.cwd,
|
|
env: childEnv,
|
|
stdio: "inherit",
|
|
});
|
|
watchProcess.on("exit", () => {
|
|
watchProcess = null;
|
|
if (shuttingDown) {
|
|
return;
|
|
}
|
|
if (restartRequested) {
|
|
restartRequested = false;
|
|
startRunner();
|
|
}
|
|
});
|
|
};
|
|
|
|
const requestRestart = (changedPath) => {
|
|
if (shuttingDown || isIgnoredWatchPath(changedPath, deps.cwd)) {
|
|
return;
|
|
}
|
|
if (!watchProcess) {
|
|
startRunner();
|
|
return;
|
|
}
|
|
restartRequested = true;
|
|
if (typeof watchProcess.kill === "function") {
|
|
watchProcess.kill(WATCH_RESTART_SIGNAL);
|
|
}
|
|
};
|
|
|
|
watcher.on("add", requestRestart);
|
|
watcher.on("change", requestRestart);
|
|
watcher.on("unlink", requestRestart);
|
|
watcher.on("error", () => {
|
|
shuttingDown = true;
|
|
if (watchProcess && typeof watchProcess.kill === "function") {
|
|
watchProcess.kill(WATCH_RESTART_SIGNAL);
|
|
}
|
|
settle(1);
|
|
});
|
|
|
|
startRunner();
|
|
|
|
onSigInt = () => {
|
|
shuttingDown = true;
|
|
if (watchProcess && typeof watchProcess.kill === "function") {
|
|
watchProcess.kill(WATCH_RESTART_SIGNAL);
|
|
}
|
|
settle(130);
|
|
};
|
|
onSigTerm = () => {
|
|
shuttingDown = true;
|
|
if (watchProcess && typeof watchProcess.kill === "function") {
|
|
watchProcess.kill(WATCH_RESTART_SIGNAL);
|
|
}
|
|
settle(143);
|
|
};
|
|
|
|
deps.process.on("SIGINT", onSigInt);
|
|
deps.process.on("SIGTERM", onSigTerm);
|
|
});
|
|
}
|
|
|
|
if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) {
|
|
void runWatchMain()
|
|
.then((code) => process.exit(code))
|
|
.catch((err) => {
|
|
console.error(err);
|
|
process.exit(1);
|
|
});
|
|
}
|