chore(telemetry): fix agent loops, name mangling, dev server leaks, CWD alignment, and add daily session auditor
This commit is contained in:
@@ -803,20 +803,57 @@ export async function startDevServer(
|
||||
|
||||
// 2. Stop ALL tracked rows for this project on the target port.
|
||||
// Previous runs may have crashed or exited without being marked
|
||||
// stopped, causing stale rows to accumulate (15+ rows seen in
|
||||
// prod). We reap them unconditionally before starting anything
|
||||
// new — the AI's intent is "I want THIS command on THIS port",
|
||||
// so most-recent-write-wins.
|
||||
// stopped, causing stale rows to accumulate. We reap them
|
||||
// unconditionally before starting anything new — the AI's intent
|
||||
// is "I want THIS command on THIS port", so most-recent-write-wins.
|
||||
const existingRows = await query<{ id: string; pid: number | null }>(
|
||||
`SELECT id, pid FROM fs_dev_servers
|
||||
WHERE project_id = $1 AND port = $2 AND state IN ('starting','running','failed')`,
|
||||
[opts.projectId, opts.port],
|
||||
);
|
||||
|
||||
const killPortNodeCmd =
|
||||
`node -e '` +
|
||||
`const fs = require("fs"); ` +
|
||||
`const port = ${opts.port}; ` +
|
||||
`try { ` +
|
||||
`const hexPort = port.toString(16).toUpperCase().padStart(4, "0"); ` +
|
||||
`const tcp = fs.readFileSync("/proc/net/tcp", "utf8"); ` +
|
||||
`const inodes = []; ` +
|
||||
`tcp.split("\\n").forEach(line => { ` +
|
||||
`const parts = line.trim().split(/\\s+/); ` +
|
||||
`if (parts.length > 9) { ` +
|
||||
`const local = parts[1]; ` +
|
||||
`if (local.endsWith(":" + hexPort)) { inodes.push(parts[9]); } ` +
|
||||
`} ` +
|
||||
`}); ` +
|
||||
`if (inodes.length > 0) { ` +
|
||||
`fs.readdirSync("/proc").forEach(file => { ` +
|
||||
`if (/^\\d+$/.test(file)) { ` +
|
||||
`try { ` +
|
||||
`const fds = fs.readdirSync("/proc/" + file + "/fd"); ` +
|
||||
`for (const fd of fds) { ` +
|
||||
`const link = fs.readlinkSync("/proc/" + file + "/fd/" + fd); ` +
|
||||
`for (const inode of inodes) { ` +
|
||||
`if (link.includes("socket:[" + inode + "]")) { ` +
|
||||
`process.kill(parseInt(file, 10), 9); ` +
|
||||
`break; ` +
|
||||
`} ` +
|
||||
`} ` +
|
||||
`} ` +
|
||||
`} catch (e) {} ` +
|
||||
`} ` +
|
||||
`}); ` +
|
||||
`} ` +
|
||||
`} catch (e) { ` +
|
||||
`try { require("child_process").execSync("fuser -k -9 ${opts.port}/tcp 2>/dev/null || true"); } catch (err) {} ` +
|
||||
`}'`;
|
||||
|
||||
for (const row of existingRows) {
|
||||
if (row.pid) {
|
||||
await execInDevContainer({
|
||||
projectId: opts.projectId,
|
||||
command: `kill ${row.pid} 2>/dev/null || true`,
|
||||
command: `kill -9 ${row.pid} 2>/dev/null || true`,
|
||||
timeoutMs: 3_000,
|
||||
}).catch(() => {});
|
||||
}
|
||||
@@ -826,44 +863,13 @@ export async function startDevServer(
|
||||
);
|
||||
}
|
||||
|
||||
// 3. Detect ANY listener on the requested port (including untracked
|
||||
// processes from earlier manual runs). We use ss (ships in
|
||||
// iproute2, default in Ubuntu base) because lsof isn't installed.
|
||||
const portCheck = await execInDevContainer({
|
||||
// 3. Force-kill ANY process currently listening on the port inside the container
|
||||
// (including untracked orphans or processes from other runs).
|
||||
await execInDevContainer({
|
||||
projectId: opts.projectId,
|
||||
command:
|
||||
`ss -tlnp 2>/dev/null | grep ':${opts.port}\b' | head -1; ` +
|
||||
`lsof -iTCP:${opts.port} -sTCP:LISTEN -n -P 2>/dev/null | tail -n +2 | head -1 || true`,
|
||||
command: killPortNodeCmd,
|
||||
timeoutMs: 5_000,
|
||||
});
|
||||
const listenerLine = portCheck.stdout.trim();
|
||||
if (listenerLine) {
|
||||
const pidMatch =
|
||||
listenerLine.match(/pid=(\d+)/) || listenerLine.match(/^\S+\s+(\d+)/);
|
||||
const listenerPid = pidMatch ? parseInt(pidMatch[1], 10) : null;
|
||||
// Force-kill whatever is squatting on the port — we already
|
||||
// reaped our tracked rows above, so this is an orphan.
|
||||
if (listenerPid) {
|
||||
await execInDevContainer({
|
||||
projectId: opts.projectId,
|
||||
command: `kill ${listenerPid} 2>/dev/null || true; sleep 0.5`,
|
||||
timeoutMs: 5_000,
|
||||
}).catch(() => {});
|
||||
}
|
||||
// Double-check the port is actually free now
|
||||
const recheck = await execInDevContainer({
|
||||
projectId: opts.projectId,
|
||||
command: `ss -tlnp 2>/dev/null | grep ':${opts.port}\b' | head -1`,
|
||||
timeoutMs: 3_000,
|
||||
});
|
||||
if (recheck.stdout.trim()) {
|
||||
throw new PortBusyError(
|
||||
opts.port,
|
||||
listenerPid,
|
||||
listenerLine.slice(0, 200),
|
||||
);
|
||||
}
|
||||
}
|
||||
}).catch(() => {});
|
||||
|
||||
// 3. Launch.
|
||||
const id = `ds_${randomToken(6)}`;
|
||||
@@ -876,7 +882,7 @@ export async function startDevServer(
|
||||
|
||||
const launch =
|
||||
`mkdir -p /var/log/vibn-dev && ` +
|
||||
`cd /workspace && ` +
|
||||
`cd /workspace/${opts.projectSlug} && ` +
|
||||
`nohup env PORT=${opts.port} VIBN_DEV_SERVER_ID=${id} ` +
|
||||
`bash -lc ${shellEscape(listenSafeCommand)} > ${logFile} 2>&1 & ` +
|
||||
`echo $!`;
|
||||
|
||||
Reference in New Issue
Block a user