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:
+634
@@ -0,0 +1,634 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Seeds the database with sample data for development and demo purposes.
|
||||
*
|
||||
* Default behavior is ADDITIVE and IDEMPOTENT:
|
||||
* node scripts/seed.js Insert the three stable test fixtures
|
||||
* (single-agent, deeply-nested, and a
|
||||
* waiting-on-input session that exercises
|
||||
* the Waiting badge / reason chip /
|
||||
* banner UI). Re-runs are no-ops if
|
||||
* fixtures already exist.
|
||||
*
|
||||
* node scripts/seed.js --full Also insert the random/demo sessions
|
||||
* (old behavior; produces unbounded data
|
||||
* on repeat runs — use intentionally).
|
||||
*
|
||||
* node scripts/seed.js --reset Remove existing fixture rows before
|
||||
* re-inserting them (e.g. to refresh
|
||||
* timestamps). Only deletes fixture
|
||||
* sessions, never user data.
|
||||
*
|
||||
* This script NEVER deletes non-fixture data. To wipe the DB, use
|
||||
* scripts/clear-data.js (which now requires --yes).
|
||||
*
|
||||
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
|
||||
*/
|
||||
|
||||
const { v4: uuidv4 } = require("uuid");
|
||||
const { db, stmts } = require("../server/db");
|
||||
|
||||
// ── Stable fixture IDs ─────────────────────────────────────────────────────
|
||||
// These IDs are intentionally non-UUID-shaped strings prefixed with `demo-`
|
||||
// so they are easy to recognize, never collide with real Claude Code session
|
||||
// UUIDs, and stay stable across seed runs.
|
||||
const FIXTURES = {
|
||||
solo: {
|
||||
sessionId: "demo-solo-0001-0001-0001-000000000001",
|
||||
mainAgentId: "demo-solo-0001-main",
|
||||
},
|
||||
waiting: {
|
||||
sessionId: "demo-waiting-0001-0001-0001-000000000001",
|
||||
mainAgentId: "demo-waiting-0001-main",
|
||||
},
|
||||
nested: {
|
||||
sessionId: "demo-nested-0001-0001-0001-000000000001",
|
||||
mainAgentId: "demo-nested-0001-main",
|
||||
agents: {
|
||||
l1Explorer: "demo-nested-0001-l1-explorer",
|
||||
l2Researcher: "demo-nested-0001-l2-researcher",
|
||||
l3TestWriter: "demo-nested-0001-l3-testwriter",
|
||||
l4Debugger: "demo-nested-0001-l4-debugger",
|
||||
l2Reviewer: "demo-nested-0001-l2-reviewer",
|
||||
l1Architect: "demo-nested-0001-l1-architect",
|
||||
l1DocWriter: "demo-nested-0001-l1-docwriter",
|
||||
l2ExampleGen: "demo-nested-0001-l2-examplegen",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const FIXTURE_SESSION_IDS = [
|
||||
FIXTURES.solo.sessionId,
|
||||
FIXTURES.waiting.sessionId,
|
||||
FIXTURES.nested.sessionId,
|
||||
];
|
||||
|
||||
const args = new Set(process.argv.slice(2));
|
||||
const FULL = args.has("--full");
|
||||
const RESET = args.has("--reset");
|
||||
|
||||
function randomItem(arr) {
|
||||
return arr[Math.floor(Math.random() * arr.length)];
|
||||
}
|
||||
|
||||
function minutesAgo(minutes) {
|
||||
return new Date(Date.now() - minutes * 60000).toISOString();
|
||||
}
|
||||
|
||||
const AGENT_NAMES = [
|
||||
"Main Agent",
|
||||
"Code Explorer",
|
||||
"Test Runner",
|
||||
"Code Reviewer",
|
||||
"Security Auditor",
|
||||
"Doc Writer",
|
||||
"Debugger",
|
||||
"Knowledge Base",
|
||||
"TDD Assistant",
|
||||
"UI Engineer",
|
||||
];
|
||||
|
||||
const SUBAGENT_TYPES = [
|
||||
"Explore",
|
||||
"general-purpose",
|
||||
"Plan",
|
||||
"code-reviewer",
|
||||
"tdd-assistant",
|
||||
"debugger",
|
||||
"security-auditor",
|
||||
"doc-writer",
|
||||
"knowledge-base",
|
||||
"ui-engineer",
|
||||
];
|
||||
|
||||
const TOOL_NAMES = [
|
||||
"Read",
|
||||
"Write",
|
||||
"Edit",
|
||||
"Bash",
|
||||
"Grep",
|
||||
"Glob",
|
||||
"Agent",
|
||||
"WebSearch",
|
||||
"WebFetch",
|
||||
];
|
||||
|
||||
const TASKS = [
|
||||
"Searching for authentication middleware patterns",
|
||||
"Running test suite for user service",
|
||||
"Reviewing PR #42 for security vulnerabilities",
|
||||
"Analyzing database schema for optimization",
|
||||
"Exploring component structure in src/components",
|
||||
"Writing unit tests for payment processor",
|
||||
"Debugging failing integration test",
|
||||
"Documenting API endpoints",
|
||||
"Scanning for OWASP Top 10 vulnerabilities",
|
||||
"Refactoring utility functions",
|
||||
];
|
||||
|
||||
function sessionExists(id) {
|
||||
return !!db.prepare("SELECT 1 FROM sessions WHERE id = ?").get(id);
|
||||
}
|
||||
|
||||
function deleteFixtureRows() {
|
||||
const placeholders = FIXTURE_SESSION_IDS.map(() => "?").join(",");
|
||||
const tx = db.transaction(() => {
|
||||
db.prepare(`DELETE FROM events WHERE session_id IN (${placeholders})`).run(
|
||||
...FIXTURE_SESSION_IDS
|
||||
);
|
||||
db.prepare(`DELETE FROM agents WHERE session_id IN (${placeholders})`).run(
|
||||
...FIXTURE_SESSION_IDS
|
||||
);
|
||||
db.prepare(`DELETE FROM token_usage WHERE session_id IN (${placeholders})`).run(
|
||||
...FIXTURE_SESSION_IDS
|
||||
);
|
||||
db.prepare(`DELETE FROM sessions WHERE id IN (${placeholders})`).run(...FIXTURE_SESSION_IDS);
|
||||
});
|
||||
tx();
|
||||
}
|
||||
|
||||
// ── Stable fixtures (AgentCard click behavior + the Waiting overlay demo) ──
|
||||
function seedFixtures() {
|
||||
const result = { inserted: [], skipped: [] };
|
||||
|
||||
const tx = db.transaction(() => {
|
||||
// 1. Single-agent session (no subagents — leaf-only; click should NAVIGATE)
|
||||
if (sessionExists(FIXTURES.solo.sessionId)) {
|
||||
result.skipped.push("Single Agent: Quick Hotfix");
|
||||
} else {
|
||||
stmts.insertSession.run(
|
||||
FIXTURES.solo.sessionId,
|
||||
"Single Agent: Quick Hotfix",
|
||||
"active",
|
||||
"/home/dev/hotfix",
|
||||
"claude-sonnet-4-6",
|
||||
null
|
||||
);
|
||||
stmts.insertAgent.run(
|
||||
FIXTURES.solo.mainAgentId,
|
||||
FIXTURES.solo.sessionId,
|
||||
"Main Agent",
|
||||
"main",
|
||||
null,
|
||||
"working",
|
||||
"Patching null-pointer in checkout handler",
|
||||
null,
|
||||
null
|
||||
);
|
||||
db.prepare("UPDATE agents SET current_tool = ? WHERE id = ?").run(
|
||||
"Edit",
|
||||
FIXTURES.solo.mainAgentId
|
||||
);
|
||||
result.inserted.push("Single Agent: Quick Hotfix");
|
||||
}
|
||||
|
||||
// 2. Waiting-on-input session — exercises the yellow Waiting overlay end
|
||||
// to end: awaiting_input_since + awaiting_reason drive the Waiting
|
||||
// badge, the reason chip/tooltip (urgent "notification" → amber), the
|
||||
// Kanban Waiting column, and SessionDetail's waiting-for-input banner.
|
||||
if (sessionExists(FIXTURES.waiting.sessionId)) {
|
||||
result.skipped.push("Waiting Demo: Permission Prompt");
|
||||
} else {
|
||||
stmts.insertSession.run(
|
||||
FIXTURES.waiting.sessionId,
|
||||
"Waiting Demo: Permission Prompt",
|
||||
"active",
|
||||
"/home/dev/waiting-demo",
|
||||
"claude-opus-4-6",
|
||||
null
|
||||
);
|
||||
stmts.insertAgent.run(
|
||||
FIXTURES.waiting.mainAgentId,
|
||||
FIXTURES.waiting.sessionId,
|
||||
"Main Agent",
|
||||
"main",
|
||||
null,
|
||||
"waiting",
|
||||
"Blocked on a permission prompt (Bash: npm publish)",
|
||||
null,
|
||||
null
|
||||
);
|
||||
// Stamp the awaiting overlay a few minutes in the past so the banner's
|
||||
// "how long" readout shows something real on first render.
|
||||
const awaitingTs = new Date(Date.now() - 4 * 60 * 1000).toISOString();
|
||||
stmts.setSessionAwaitingInput.run(awaitingTs, "notification", FIXTURES.waiting.sessionId);
|
||||
stmts.setAgentAwaitingInput.run(awaitingTs, "notification", FIXTURES.waiting.mainAgentId);
|
||||
result.inserted.push("Waiting Demo: Permission Prompt");
|
||||
}
|
||||
|
||||
// 3. Deeply-nested session (depth 4, branching — click PARENT toggles, LEAF navigates)
|
||||
if (sessionExists(FIXTURES.nested.sessionId)) {
|
||||
result.skipped.push("Deep Nesting: Multi-Agent Research Pipeline");
|
||||
} else {
|
||||
const ids = FIXTURES.nested.agents;
|
||||
stmts.insertSession.run(
|
||||
FIXTURES.nested.sessionId,
|
||||
"Deep Nesting: Multi-Agent Research Pipeline",
|
||||
"active",
|
||||
"/home/dev/research-pipeline",
|
||||
"claude-opus-4-6",
|
||||
null
|
||||
);
|
||||
stmts.insertAgent.run(
|
||||
FIXTURES.nested.mainAgentId,
|
||||
FIXTURES.nested.sessionId,
|
||||
"Main Agent",
|
||||
"main",
|
||||
null,
|
||||
"waiting",
|
||||
"Orchestrating multi-agent research pipeline",
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
// Depth 1: Main → Codebase Explorer (working)
|
||||
stmts.insertAgent.run(
|
||||
ids.l1Explorer,
|
||||
FIXTURES.nested.sessionId,
|
||||
"Codebase Explorer",
|
||||
"subagent",
|
||||
"Explore",
|
||||
"working",
|
||||
"Mapping authentication module dependencies",
|
||||
FIXTURES.nested.mainAgentId,
|
||||
null
|
||||
);
|
||||
db.prepare("UPDATE agents SET current_tool = ? WHERE id = ?").run("Glob", ids.l1Explorer);
|
||||
|
||||
// Depth 2: Explorer → Security Researcher (working)
|
||||
stmts.insertAgent.run(
|
||||
ids.l2Researcher,
|
||||
FIXTURES.nested.sessionId,
|
||||
"Security Researcher",
|
||||
"subagent",
|
||||
"general-purpose",
|
||||
"working",
|
||||
"Analyzing OAuth2 token validation patterns",
|
||||
ids.l1Explorer,
|
||||
null
|
||||
);
|
||||
db.prepare("UPDATE agents SET current_tool = ? WHERE id = ?").run(
|
||||
"WebSearch",
|
||||
ids.l2Researcher
|
||||
);
|
||||
|
||||
// Depth 3: Researcher → Test Engineer (working)
|
||||
stmts.insertAgent.run(
|
||||
ids.l3TestWriter,
|
||||
FIXTURES.nested.sessionId,
|
||||
"Test Engineer",
|
||||
"subagent",
|
||||
"test-engineer",
|
||||
"working",
|
||||
"Writing integration tests for token refresh flow",
|
||||
ids.l2Researcher,
|
||||
null
|
||||
);
|
||||
db.prepare("UPDATE agents SET current_tool = ? WHERE id = ?").run("Write", ids.l3TestWriter);
|
||||
|
||||
// Depth 4: Test Engineer → Test Debugger (deepest leaf)
|
||||
stmts.insertAgent.run(
|
||||
ids.l4Debugger,
|
||||
FIXTURES.nested.sessionId,
|
||||
"Test Debugger",
|
||||
"subagent",
|
||||
"debugger",
|
||||
"working",
|
||||
"Investigating flaky assertion in token expiry test",
|
||||
ids.l3TestWriter,
|
||||
null
|
||||
);
|
||||
db.prepare("UPDATE agents SET current_tool = ? WHERE id = ?").run("Bash", ids.l4Debugger);
|
||||
|
||||
// Depth 2 branch (sibling of Researcher): Code Reviewer (completed leaf)
|
||||
stmts.insertAgent.run(
|
||||
ids.l2Reviewer,
|
||||
FIXTURES.nested.sessionId,
|
||||
"Code Reviewer",
|
||||
"subagent",
|
||||
"code-reviewer",
|
||||
"completed",
|
||||
"Reviewed middleware chain for injection risks",
|
||||
ids.l1Explorer,
|
||||
null
|
||||
);
|
||||
db.prepare("UPDATE agents SET ended_at = ? WHERE id = ?").run(minutesAgo(5), ids.l2Reviewer);
|
||||
|
||||
// Depth 1 sibling: Architecture Planner (completed leaf)
|
||||
stmts.insertAgent.run(
|
||||
ids.l1Architect,
|
||||
FIXTURES.nested.sessionId,
|
||||
"Architecture Planner",
|
||||
"subagent",
|
||||
"Plan",
|
||||
"completed",
|
||||
"Designed auth service boundary and API contracts",
|
||||
FIXTURES.nested.mainAgentId,
|
||||
null
|
||||
);
|
||||
db.prepare("UPDATE agents SET ended_at = ? WHERE id = ?").run(
|
||||
minutesAgo(12),
|
||||
ids.l1Architect
|
||||
);
|
||||
|
||||
// Depth 1 sibling: Documentation Writer (working — has its own child)
|
||||
stmts.insertAgent.run(
|
||||
ids.l1DocWriter,
|
||||
FIXTURES.nested.sessionId,
|
||||
"Documentation Writer",
|
||||
"subagent",
|
||||
"doc-writer",
|
||||
"working",
|
||||
"Writing API docs for /auth/* endpoints",
|
||||
FIXTURES.nested.mainAgentId,
|
||||
null
|
||||
);
|
||||
db.prepare("UPDATE agents SET current_tool = ? WHERE id = ?").run("Edit", ids.l1DocWriter);
|
||||
|
||||
// Depth 2: Doc Writer → Example Generator (connected leaf)
|
||||
stmts.insertAgent.run(
|
||||
ids.l2ExampleGen,
|
||||
FIXTURES.nested.sessionId,
|
||||
"Example Generator",
|
||||
"subagent",
|
||||
"general-purpose",
|
||||
"working",
|
||||
"Generating cURL examples for auth endpoints",
|
||||
ids.l1DocWriter,
|
||||
null
|
||||
);
|
||||
|
||||
result.inserted.push("Deep Nesting: Multi-Agent Research Pipeline (9 agents, depth 4)");
|
||||
}
|
||||
|
||||
// Sprinkle a few events on freshly inserted fixtures only
|
||||
for (const sid of FIXTURE_SESSION_IDS) {
|
||||
const hasEvents =
|
||||
db.prepare("SELECT 1 FROM events WHERE session_id = ? LIMIT 1").get(sid) !== undefined;
|
||||
if (hasEvents) continue;
|
||||
const agents = stmts.listAgentsBySession.all(sid);
|
||||
const eventCount = Math.floor(Math.random() * 8) + 3;
|
||||
for (let i = 0; i < eventCount; i++) {
|
||||
const agent = randomItem(agents);
|
||||
const eventType = randomItem(["PreToolUse", "PostToolUse", "Notification"]);
|
||||
const tool = randomItem(TOOL_NAMES);
|
||||
stmts.insertEvent.run(
|
||||
sid,
|
||||
agent?.id ?? null,
|
||||
eventType,
|
||||
eventType.includes("Tool") ? tool : null,
|
||||
eventType === "PreToolUse"
|
||||
? `Using tool: ${tool}`
|
||||
: eventType === "PostToolUse"
|
||||
? `Tool completed: ${tool}`
|
||||
: `Agent ${agent?.name || "unknown"} notification`,
|
||||
JSON.stringify({ tool_name: tool })
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
tx();
|
||||
return result;
|
||||
}
|
||||
|
||||
// ── Random demo data (old behavior — opt-in with --full) ───────────────────
|
||||
function seedFullDemo() {
|
||||
console.log("⚠️ --full mode: inserting random demo sessions on top of existing data.");
|
||||
console.log(" These get fresh UUIDs each run, so re-runs accumulate. Use intentionally.\n");
|
||||
|
||||
const tx = db.transaction(() => {
|
||||
const sessions = [];
|
||||
|
||||
const activeSessionId = uuidv4();
|
||||
stmts.insertSession.run(
|
||||
activeSessionId,
|
||||
"Feature: User Authentication",
|
||||
"active",
|
||||
"/home/dev/my-app",
|
||||
"claude-opus-4-6",
|
||||
null
|
||||
);
|
||||
sessions.push(activeSessionId);
|
||||
|
||||
const activeSessionId2 = uuidv4();
|
||||
stmts.insertSession.run(
|
||||
activeSessionId2,
|
||||
"Bug Fix: Payment Processing",
|
||||
"active",
|
||||
"/home/dev/payment-service",
|
||||
"claude-sonnet-4-6",
|
||||
null
|
||||
);
|
||||
sessions.push(activeSessionId2);
|
||||
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const id = uuidv4();
|
||||
stmts.insertSession.run(
|
||||
id,
|
||||
randomItem([
|
||||
"Refactor: Database Layer",
|
||||
"Feature: Email Notifications",
|
||||
"Fix: Memory Leak in Worker",
|
||||
"Test: API Integration Suite",
|
||||
"Docs: README Update",
|
||||
]),
|
||||
"completed",
|
||||
randomItem(["/home/dev/api", "/home/dev/frontend", "/home/dev/worker"]),
|
||||
randomItem(["claude-opus-4-6", "claude-sonnet-4-6"]),
|
||||
null
|
||||
);
|
||||
db.prepare("UPDATE sessions SET ended_at = ? WHERE id = ?").run(
|
||||
minutesAgo(Math.floor(Math.random() * 120)),
|
||||
id
|
||||
);
|
||||
sessions.push(id);
|
||||
}
|
||||
|
||||
const errSessionId = uuidv4();
|
||||
stmts.insertSession.run(
|
||||
errSessionId,
|
||||
"Deploy: Production Release",
|
||||
"error",
|
||||
"/home/dev/infra",
|
||||
"claude-opus-4-6",
|
||||
null
|
||||
);
|
||||
db.prepare("UPDATE sessions SET ended_at = ? WHERE id = ?").run(minutesAgo(45), errSessionId);
|
||||
sessions.push(errSessionId);
|
||||
|
||||
const mainAgent1 = `${activeSessionId}-main`;
|
||||
stmts.insertAgent.run(
|
||||
mainAgent1,
|
||||
activeSessionId,
|
||||
"Main Agent",
|
||||
"main",
|
||||
null,
|
||||
"working",
|
||||
"Implementing JWT authentication middleware",
|
||||
null,
|
||||
null
|
||||
);
|
||||
db.prepare("UPDATE agents SET current_tool = ? WHERE id = ?").run("Edit", mainAgent1);
|
||||
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const subId = uuidv4();
|
||||
const status = randomItem(["working", "working", "working"]);
|
||||
stmts.insertAgent.run(
|
||||
subId,
|
||||
activeSessionId,
|
||||
AGENT_NAMES[i + 1],
|
||||
"subagent",
|
||||
SUBAGENT_TYPES[i + 1],
|
||||
status,
|
||||
TASKS[i],
|
||||
mainAgent1,
|
||||
null
|
||||
);
|
||||
if (status === "working") {
|
||||
db.prepare("UPDATE agents SET current_tool = ? WHERE id = ?").run(
|
||||
randomItem(TOOL_NAMES),
|
||||
subId
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mainAgent2 = `${activeSessionId2}-main`;
|
||||
stmts.insertAgent.run(
|
||||
mainAgent2,
|
||||
activeSessionId2,
|
||||
"Main Agent",
|
||||
"main",
|
||||
null,
|
||||
"working",
|
||||
"Investigating payment webhook failures",
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
const sub2 = uuidv4();
|
||||
stmts.insertAgent.run(
|
||||
sub2,
|
||||
activeSessionId2,
|
||||
"Debugger",
|
||||
"subagent",
|
||||
"debugger",
|
||||
"working",
|
||||
"Tracing webhook request flow",
|
||||
mainAgent2,
|
||||
null
|
||||
);
|
||||
db.prepare("UPDATE agents SET current_tool = ? WHERE id = ?").run("Grep", sub2);
|
||||
|
||||
for (const sid of sessions.slice(2)) {
|
||||
const mainId = `${sid}-main`;
|
||||
stmts.insertAgent.run(mainId, sid, "Main Agent", "main", null, "completed", null, null, null);
|
||||
db.prepare("UPDATE agents SET ended_at = ? WHERE id = ?").run(
|
||||
minutesAgo(Math.floor(Math.random() * 60)),
|
||||
mainId
|
||||
);
|
||||
const subCount = Math.floor(Math.random() * 3) + 1;
|
||||
for (let i = 0; i < subCount; i++) {
|
||||
const subId = uuidv4();
|
||||
const name = randomItem(AGENT_NAMES.slice(1));
|
||||
stmts.insertAgent.run(
|
||||
subId,
|
||||
sid,
|
||||
name,
|
||||
"subagent",
|
||||
randomItem(SUBAGENT_TYPES.slice(1)),
|
||||
sid === sessions[sessions.length - 1] ? "error" : "completed",
|
||||
randomItem(TASKS),
|
||||
mainId,
|
||||
null
|
||||
);
|
||||
db.prepare("UPDATE agents SET ended_at = ? WHERE id = ?").run(
|
||||
minutesAgo(Math.floor(Math.random() * 60)),
|
||||
subId
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (const sid of sessions) {
|
||||
const eventCount = Math.floor(Math.random() * 15) + 5;
|
||||
const agents = stmts.listAgentsBySession.all(sid);
|
||||
for (let i = 0; i < eventCount; i++) {
|
||||
const agent = randomItem(agents);
|
||||
const eventType = randomItem([
|
||||
"PreToolUse",
|
||||
"PostToolUse",
|
||||
"PreToolUse",
|
||||
"PostToolUse",
|
||||
"Notification",
|
||||
]);
|
||||
const tool = randomItem(TOOL_NAMES);
|
||||
const summary =
|
||||
eventType === "PreToolUse"
|
||||
? `Using tool: ${tool}`
|
||||
: eventType === "PostToolUse"
|
||||
? `Tool completed: ${tool}`
|
||||
: `Agent ${agent?.name || "unknown"} notification`;
|
||||
stmts.insertEvent.run(
|
||||
sid,
|
||||
agent?.id ?? null,
|
||||
eventType,
|
||||
eventType.includes("Tool") ? tool : null,
|
||||
summary,
|
||||
JSON.stringify({ tool_name: tool })
|
||||
);
|
||||
}
|
||||
const session = stmts.getSession.get(sid);
|
||||
if (session && session.status !== "active") {
|
||||
stmts.insertEvent.run(
|
||||
sid,
|
||||
null,
|
||||
"Stop",
|
||||
null,
|
||||
`Session ended: ${session.status}`,
|
||||
JSON.stringify({ stop_reason: session.status })
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
tx();
|
||||
}
|
||||
|
||||
function main() {
|
||||
console.log("Seeding database (additive — existing data is preserved)...\n");
|
||||
|
||||
if (RESET) {
|
||||
console.log("--reset: removing existing fixture rows before re-inserting.");
|
||||
deleteFixtureRows();
|
||||
}
|
||||
|
||||
const fixtureResult = seedFixtures();
|
||||
|
||||
if (fixtureResult.inserted.length > 0) {
|
||||
console.log("Inserted fixtures:");
|
||||
for (const name of fixtureResult.inserted) console.log(` + ${name}`);
|
||||
}
|
||||
if (fixtureResult.skipped.length > 0) {
|
||||
console.log("Skipped (already present — pass --reset to recreate):");
|
||||
for (const name of fixtureResult.skipped) console.log(` · ${name}`);
|
||||
}
|
||||
|
||||
if (FULL) {
|
||||
console.log("");
|
||||
seedFullDemo();
|
||||
}
|
||||
|
||||
const stats = stmts.stats.get();
|
||||
console.log("");
|
||||
console.log(
|
||||
`Total in DB: ${stats.total_sessions} sessions, ${stats.total_agents} agents, ${stats.total_events} events.`
|
||||
);
|
||||
console.log("");
|
||||
console.log("Test URLs:");
|
||||
console.log(` Single-agent: /sessions/${FIXTURES.solo.sessionId}`);
|
||||
console.log(` Waiting (reason): /sessions/${FIXTURES.waiting.sessionId}`);
|
||||
console.log(` Nested (depth 4): /sessions/${FIXTURES.nested.sessionId}`);
|
||||
}
|
||||
|
||||
main();
|
||||
Reference in New Issue
Block a user