feat: add pi lab ai management integration
This commit is contained in:
@@ -415,6 +415,35 @@ function getProcessNameByPid(pid) {
|
||||
}
|
||||
}
|
||||
|
||||
function getProcessCommandByPid(pid) {
|
||||
if (process.platform === "win32") {
|
||||
try {
|
||||
const out = execSync(`wmic process where ProcessId=${pid} get CommandLine /value`, {
|
||||
encoding: "utf8",
|
||||
});
|
||||
return out.replace(/^CommandLine=/m, "").trim();
|
||||
} catch {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
return execSync(`ps -p ${pid} -o args=`, { encoding: "utf8" }).trim();
|
||||
} catch {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
function getProcessGroupByPid(pid) {
|
||||
if (process.platform === "win32") return pid;
|
||||
try {
|
||||
const pgid = Number(execSync(`ps -p ${pid} -o pgid=`, { encoding: "utf8" }).trim());
|
||||
return Number.isFinite(pgid) && pgid > 0 ? pgid : pid;
|
||||
} catch {
|
||||
return pid;
|
||||
}
|
||||
}
|
||||
|
||||
function terminatePid(pid) {
|
||||
if (process.platform === "win32") {
|
||||
execSync(`taskkill /PID ${pid} /T /F`, { stdio: "ignore" });
|
||||
@@ -441,6 +470,84 @@ function forceKillPid(pid) {
|
||||
}
|
||||
}
|
||||
|
||||
function terminateProcessGroup(pgid) {
|
||||
if (process.platform === "win32") {
|
||||
terminatePid(pgid);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
process.kill(-pgid, "SIGTERM");
|
||||
} catch {
|
||||
terminatePid(pgid);
|
||||
}
|
||||
}
|
||||
|
||||
function forceKillProcessGroup(pgid) {
|
||||
if (process.platform === "win32") {
|
||||
forceKillPid(pgid);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
process.kill(-pgid, "SIGKILL");
|
||||
} catch {
|
||||
forceKillPid(pgid);
|
||||
}
|
||||
}
|
||||
|
||||
function collectStaleMnoteWebCargoPids() {
|
||||
if (process.platform === "win32") return [];
|
||||
const currentPgid = getProcessGroupByPid(process.pid);
|
||||
try {
|
||||
const out = execSync("pgrep -f 'cargo (watch|run).*mnote-web|cargo-watch watch.*mnote-web'", {
|
||||
encoding: "utf8",
|
||||
});
|
||||
return out
|
||||
.split(/\r?\n/)
|
||||
.map((line) => Number(line.trim()))
|
||||
.filter((pid) => Number.isFinite(pid) && pid > 0 && pid !== process.pid)
|
||||
.filter((pid) => getProcessGroupByPid(pid) !== currentPgid)
|
||||
.filter((pid) => {
|
||||
const command = getProcessCommandByPid(pid);
|
||||
return !command.includes("pgrep -f");
|
||||
});
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async function stopStaleMnoteWebCargoProcesses() {
|
||||
const pids = collectStaleMnoteWebCargoPids();
|
||||
if (pids.length === 0) return true;
|
||||
|
||||
const safePids = pids.filter((pid) => {
|
||||
const command = getProcessCommandByPid(pid);
|
||||
return (
|
||||
command.includes("mnote-web") &&
|
||||
command.includes("cargo") &&
|
||||
(command.includes("cargo watch") ||
|
||||
command.includes("cargo-watch watch") ||
|
||||
command.includes("cargo run -p mnote-web"))
|
||||
);
|
||||
});
|
||||
if (safePids.length === 0) return true;
|
||||
|
||||
const pgids = [...new Set(safePids.map(getProcessGroupByPid).filter((pgid) => pgid > 0))];
|
||||
logPrefix("mnote-web", `检测到陈旧 cargo-watch/cargo run,先清理进程组:${pgids.join(", ")}`);
|
||||
pgids.forEach(terminateProcessGroup);
|
||||
|
||||
for (let i = 0; i < 20; i += 1) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await new Promise((r) => setTimeout(r, 150));
|
||||
const remaining = collectStaleMnoteWebCargoPids().filter((pid) =>
|
||||
safePids.includes(pid) || pgids.includes(getProcessGroupByPid(pid)),
|
||||
);
|
||||
if (remaining.length === 0) return true;
|
||||
}
|
||||
|
||||
pgids.forEach(forceKillProcessGroup);
|
||||
return true;
|
||||
}
|
||||
|
||||
async function ensurePortFree(port, nameForLog) {
|
||||
const free = await isPortFree("127.0.0.1", port);
|
||||
if (free) return true;
|
||||
@@ -665,6 +772,7 @@ async function main() {
|
||||
// 如果检测到 3000 被占用,则自动结束旧进程后重启,以保证始终跑在 3000。
|
||||
const desiredFrontendPort = runtimePlan.publicPort;
|
||||
const frontendOwnerName = "mnote-web";
|
||||
await stopStaleMnoteWebCargoProcesses();
|
||||
const frontendPortOk = await ensurePortFree(desiredFrontendPort, frontendOwnerName);
|
||||
if (!frontendPortOk) {
|
||||
console.error(`前端端口 ${desiredFrontendPort} 无法释放,已中止启动。`);
|
||||
@@ -762,8 +870,11 @@ module.exports = {
|
||||
buildDefaultOpencodeCommand,
|
||||
buildDefaultOpenHubCommand,
|
||||
checkOpenHubHealth,
|
||||
collectStaleMnoteWebCargoPids,
|
||||
ensurePortFree,
|
||||
getListeningPidsByPort,
|
||||
getProcessCommandByPid,
|
||||
getProcessGroupByPid,
|
||||
getProcessNameByPid,
|
||||
isHttpHealthy,
|
||||
isPortFree,
|
||||
@@ -772,5 +883,6 @@ module.exports = {
|
||||
resolveBackendExecutable,
|
||||
shouldStartBackend,
|
||||
shouldStartOpenHub,
|
||||
stopStaleMnoteWebCargoProcesses,
|
||||
terminatePid,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user