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.
This commit is contained in:
2026-07-29 17:07:45 +07:00
commit 57dc91585d
783 changed files with 221743 additions and 0 deletions
+213
View File
@@ -0,0 +1,213 @@
/**
* @file Express router for remote data sources — other machines whose Claude
* Code history this dashboard pulls in over SSH (see server/lib/remote-sync.js).
*
* GET /api/remote-sources — list configured sources + status
* POST /api/remote-sources — add a source
* PATCH /api/remote-sources/:id — edit a source (partial)
* DELETE /api/remote-sources/:id — remove a source (config + staging);
* ?purge=true also deletes its imported
* sessions (destructive, opt-in)
* POST /api/remote-sources/:id/test — probe SSH connectivity
* POST /api/remote-sources/:id/sync — sync now
*
* No secrets are stored or accepted here — authentication defers entirely to the
* host's SSH stack. All inputs are validated in remote-sync.validateSourceInput
* before touching the DB or any command.
*
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
*/
const { Router } = require("express");
const crypto = require("crypto");
const fs = require("fs");
const { stmts, db } = require("../db");
const { broadcast } = require("../websocket");
const {
validateSourceInput,
ValidationError,
testConnection,
syncSource,
syncAllEnabled,
stagingDir,
} = require("../lib/remote-sync");
const router = Router();
/**
* Shape a DB row for the API: bool `enabled`, parsed counts, and the live number
* of sessions currently attributed to this source (`session_count`) so the UI
* can show how much data each machine has contributed.
*/
function serialize(row, sessionCount = 0) {
let lastCounts = null;
try {
lastCounts = row.last_sync_counts ? JSON.parse(row.last_sync_counts) : null;
} catch {
lastCounts = null;
}
return {
id: row.id,
label: row.label,
host: row.host,
ssh_port: row.ssh_port,
identity_file: row.identity_file,
remote_home: row.remote_home,
enabled: !!row.enabled,
status: row.status,
last_error: row.last_error,
last_sync_at: row.last_sync_at,
last_sync_counts: lastCounts,
session_count: sessionCount,
created_at: row.created_at,
updated_at: row.updated_at,
};
}
/** Map of source id -> current session count, in one grouped query. */
function sessionCountsBySource() {
const rows = db.prepare("SELECT source, COUNT(*) AS c FROM sessions GROUP BY source").all();
const map = new Map();
for (const r of rows) map.set(r.source, r.c);
return map;
}
function handleValidation(res, err) {
if (err instanceof ValidationError) {
res.status(400).json({ error: { code: err.code, message: err.message } });
return true;
}
return false;
}
// GET / — list all sources, each with its live session count.
router.get("/", (_req, res) => {
const rows = stmts.listRemoteSources.all();
const counts = sessionCountsBySource();
res.json({ sources: rows.map((r) => serialize(r, counts.get(r.id) || 0)) });
});
// POST / — create a source.
router.post("/", (req, res) => {
let v;
try {
v = validateSourceInput(req.body || {}, false);
} catch (err) {
if (handleValidation(res, err)) return;
throw err;
}
const id = `src_${crypto.randomBytes(6).toString("hex")}`;
const enabled = v.enabled === undefined ? 1 : v.enabled;
stmts.insertRemoteSource.run(
id,
v.label,
v.host,
v.sshPort ?? null,
v.identityFile ?? null,
v.remoteHome ?? null,
enabled
);
const row = stmts.getRemoteSource.get(id);
broadcast("remote_source.status", { id, status: row.status });
res.status(201).json({ source: serialize(row) });
if (enabled) {
syncSource(require("../db"), row, { broadcast }).catch(() => {});
}
});
// POST /sync-all — sync every enabled source now (sequential; per-source
// failures are isolated). Defined before the /:id routes; "sync-all" is a
// single path segment so it never collides with "/:id/sync".
router.post("/sync-all", async (_req, res) => {
const results = await syncAllEnabled(require("../db"), { broadcast });
res.json({ ok: true, synced: results.length, results });
});
// PATCH /:id — partial update.
router.patch("/:id", (req, res) => {
const existing = stmts.getRemoteSource.get(req.params.id);
if (!existing) {
return res.status(404).json({ error: { code: "NOT_FOUND", message: "Source not found" } });
}
let v;
try {
v = validateSourceInput(req.body || {}, true);
} catch (err) {
if (handleValidation(res, err)) return;
throw err;
}
// COALESCE-based stmt keeps unspecified fields; port/identity/home are written
// verbatim (nullable), so a PATCH that omits them leaves them unchanged only
// when we pass the existing value through.
stmts.updateRemoteSource.run(
v.label ?? null,
v.host ?? null,
v.sshPort !== undefined ? v.sshPort : existing.ssh_port,
v.identityFile !== undefined ? v.identityFile : existing.identity_file,
v.remoteHome !== undefined ? v.remoteHome : existing.remote_home,
v.enabled === undefined ? null : v.enabled,
req.params.id
);
const row = stmts.getRemoteSource.get(req.params.id);
broadcast("remote_source.status", { id: row.id, status: row.status });
res.json({ source: serialize(row) });
if (v.enabled === 1 && !existing.enabled) {
syncSource(require("../db"), row, { broadcast }).catch(() => {});
}
});
// DELETE /:id — remove config + staging dir. ?purge=true also deletes the
// sessions this source imported (destructive, opt-in).
router.delete("/:id", (req, res) => {
const existing = stmts.getRemoteSource.get(req.params.id);
if (!existing) {
return res.status(404).json({ error: { code: "NOT_FOUND", message: "Source not found" } });
}
const purge = req.query.purge === "true" || req.query.purge === "1";
let purged = 0;
if (purge) {
// FK ON DELETE CASCADE removes the sessions' agents/events/token_usage too.
const info = db.prepare("DELETE FROM sessions WHERE source = ?").run(req.params.id);
purged = info.changes || 0;
} else {
// Keep the imported rows but detach them from the (now gone) source id so
// they fall back to the local view instead of a dangling filter value.
db.prepare("UPDATE sessions SET source = 'local' WHERE source = ?").run(req.params.id);
}
stmts.deleteRemoteSource.run(req.params.id);
// Reclaim the mirrored staging dir.
try {
fs.rmSync(stagingDir(req.params.id), { recursive: true, force: true });
} catch {
/* non-fatal */
}
broadcast("remote_source.status", { id: req.params.id, status: "deleted" });
res.json({ ok: true, purged });
});
// POST /:id/test — probe connectivity (does not import).
router.post("/:id/test", async (req, res) => {
const row = stmts.getRemoteSource.get(req.params.id);
if (!row) {
return res.status(404).json({ error: { code: "NOT_FOUND", message: "Source not found" } });
}
const result = await testConnection(row);
res.json(result);
});
// POST /:id/sync — sync now.
router.post("/:id/sync", async (req, res) => {
const row = stmts.getRemoteSource.get(req.params.id);
if (!row) {
return res.status(404).json({ error: { code: "NOT_FOUND", message: "Source not found" } });
}
try {
const result = await syncSource(require("../db"), row, { broadcast });
res.json({ ok: true, ...result });
} catch (err) {
res.status(500).json({ error: { code: "SYNC_FAILED", message: err.message } });
}
});
module.exports = router;