Files
Claude-Code-Monitor/monitoring/scripts/lib.js
T
nntrivi2001 57dc91585d feat: Claude Code Monitor — lanes, pipelines and a merged workspace
Internal SmartGift build of a Claude Code monitoring dashboard.

Lanes: a durable unit of parallel agent work, one per working directory,
tracked across session restarts. Managed lanes are git worktrees the
dashboard provisions and can reset or remove behind a three-check destroy
guard and a counted preflight; adopted lanes are directories you already
own and are never destroyable.

Pipelines: a lane moves through pipeline stages. A stage the agent declares
with evidence renders green; a stage inferred from the tool-event stream
renders dashed amber and never counts as done. Detection is forward-only
within a 30-minute window, and never writes the declared stage.

Workspace: one page at /run with a lane grid, the selected lane's pipeline,
and a full Claude console behind a disclosure.
2026-07-30 14:39:03 +07:00

159 lines
3.6 KiB
JavaScript

/**
* @file Shared helpers for starting and stopping the npm-managed monitoring stack.
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
*/
const fs = require("node:fs");
const path = require("node:path");
const { spawn, spawnSync } = require("node:child_process");
const {
DATA_ROOT,
RUNTIME_PROVISIONING,
GRAFANA_DASHBOARDS,
GRAFANA_DATASOURCE_TEMPLATE,
PROMETHEUS_PID,
GRAFANA_PID,
PROMETHEUS_LOG,
GRAFANA_LOG,
isWindows,
toGrafanaPath,
} = require("./paths");
function readPid(file) {
try {
const raw = fs.readFileSync(file, "utf8").trim();
const pid = Number(raw);
return Number.isInteger(pid) && pid > 0 ? pid : null;
} catch {
return null;
}
}
function isRunning(pid) {
if (!pid) return false;
try {
process.kill(pid, 0);
return true;
} catch {
return false;
}
}
function writePid(file, pid) {
fs.mkdirSync(path.dirname(file), { recursive: true });
fs.writeFileSync(file, String(pid));
}
function removePid(file) {
try {
fs.unlinkSync(file);
} catch {
/* already gone */
}
}
function killProcess(pid) {
if (!pid) return;
if (isWindows()) {
spawnSync("taskkill", ["/PID", String(pid), "/T", "/F"], { stdio: "ignore" });
return;
}
process.kill(pid, "SIGTERM");
}
function stopPid(file, label) {
const pid = readPid(file);
if (!pid) return false;
if (!isRunning(pid)) {
removePid(file);
return false;
}
try {
killProcess(pid);
console.log(`Stopped ${label} (pid ${pid})`);
} catch (err) {
console.warn(`Could not stop ${label} (pid ${pid}): ${err.message}`);
}
removePid(file);
return true;
}
function writeGrafanaProvisioning() {
const dashboardsDir = path.join(RUNTIME_PROVISIONING, "dashboards");
const datasourcesDir = path.join(RUNTIME_PROVISIONING, "datasources");
fs.mkdirSync(dashboardsDir, { recursive: true });
fs.mkdirSync(datasourcesDir, { recursive: true });
fs.copyFileSync(GRAFANA_DATASOURCE_TEMPLATE, path.join(datasourcesDir, "datasource.yml"));
const provider = [
"apiVersion: 1",
"",
"providers:",
" - name: CCAM",
" orgId: 1",
" type: file",
" disableDeletion: false",
" updateIntervalSeconds: 30",
" allowUiUpdates: true",
" options:",
` path: ${toGrafanaPath(GRAFANA_DASHBOARDS)}`,
" foldersFromFilesStructure: false",
"",
].join("\n");
fs.writeFileSync(path.join(dashboardsDir, "provider.yml"), provider);
}
function spawnDetached(binary, args, env, logFile) {
fs.mkdirSync(path.dirname(logFile), { recursive: true });
const logFd = fs.openSync(logFile, "a");
const child = spawn(binary, args, {
detached: !isWindows(),
stdio: ["ignore", logFd, logFd],
env: { ...process.env, ...env },
windowsHide: true,
});
if (!isWindows()) child.unref();
fs.closeSync(logFd);
return child.pid;
}
function spawnForeground(binary, args, env) {
return spawn(binary, args, {
stdio: "inherit",
env: { ...process.env, ...env },
windowsHide: true,
});
}
async function waitForHttp(url, attempts = 30, intervalMs = 500) {
for (let i = 0; i < attempts; i += 1) {
try {
const res = await fetch(url);
if (res.ok) return true;
} catch {
/* retry */
}
await new Promise((r) => setTimeout(r, intervalMs));
}
return false;
}
module.exports = {
readPid,
isRunning,
writePid,
removePid,
killProcess,
stopPid,
writeGrafanaProvisioning,
spawnDetached,
spawnForeground,
waitForHttp,
PROMETHEUS_PID,
GRAFANA_PID,
PROMETHEUS_LOG,
GRAFANA_LOG,
DATA_ROOT,
RUNTIME_PROVISIONING,
};