@claudecam/server 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/db/index.js +68 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/queries.js +658 -0
- package/dist/db/queries.js.map +1 -0
- package/dist/db/schema.sql +259 -0
- package/dist/index.js +128 -0
- package/dist/index.js.map +1 -0
- package/dist/routes/agents.js +68 -0
- package/dist/routes/agents.js.map +1 -0
- package/dist/routes/correlation-audit.js +31 -0
- package/dist/routes/correlation-audit.js.map +1 -0
- package/dist/routes/events.js +81 -0
- package/dist/routes/events.js.map +1 -0
- package/dist/routes/files.js +24 -0
- package/dist/routes/files.js.map +1 -0
- package/dist/routes/parse-prd.js +38 -0
- package/dist/routes/parse-prd.js.map +1 -0
- package/dist/routes/projects.js +96 -0
- package/dist/routes/projects.js.map +1 -0
- package/dist/routes/registry.js +88 -0
- package/dist/routes/registry.js.map +1 -0
- package/dist/routes/session-groups.js +182 -0
- package/dist/routes/session-groups.js.map +1 -0
- package/dist/routes/sessions.js +109 -0
- package/dist/routes/sessions.js.map +1 -0
- package/dist/routes/sprints.js +58 -0
- package/dist/routes/sprints.js.map +1 -0
- package/dist/routes/stats.js +63 -0
- package/dist/routes/stats.js.map +1 -0
- package/dist/routes/stream.js +21 -0
- package/dist/routes/stream.js.map +1 -0
- package/dist/routes/tasks.js +198 -0
- package/dist/routes/tasks.js.map +1 -0
- package/dist/services/correlation-engine.js +577 -0
- package/dist/services/correlation-engine.js.map +1 -0
- package/dist/services/event-processor.js +857 -0
- package/dist/services/event-processor.js.map +1 -0
- package/dist/services/prd-parser.js +142 -0
- package/dist/services/prd-parser.js.map +1 -0
- package/dist/services/project-manager.js +351 -0
- package/dist/services/project-manager.js.map +1 -0
- package/dist/services/project-router.js +56 -0
- package/dist/services/project-router.js.map +1 -0
- package/dist/services/session-manager.js +76 -0
- package/dist/services/session-manager.js.map +1 -0
- package/dist/services/sse-manager.js +115 -0
- package/dist/services/sse-manager.js.map +1 -0
- package/dist/services/string-similarity.js +256 -0
- package/dist/services/string-similarity.js.map +1 -0
- package/dist/services/task-completion.js +251 -0
- package/dist/services/task-completion.js.map +1 -0
- package/package.json +59 -0
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
-- Claude Agent Monitor - Database Schema
|
|
2
|
+
-- SQLite tables for both Pilar 1 (Agent Monitor) and Pilar 2 (PRD Tracker)
|
|
3
|
+
|
|
4
|
+
-- === Pilar 1: Agent Monitor ===
|
|
5
|
+
|
|
6
|
+
CREATE TABLE IF NOT EXISTS sessions (
|
|
7
|
+
id TEXT PRIMARY KEY,
|
|
8
|
+
started_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
9
|
+
ended_at TEXT,
|
|
10
|
+
working_directory TEXT NOT NULL DEFAULT '',
|
|
11
|
+
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'completed', 'error')),
|
|
12
|
+
agent_count INTEGER NOT NULL DEFAULT 0,
|
|
13
|
+
event_count INTEGER NOT NULL DEFAULT 0,
|
|
14
|
+
metadata TEXT
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
CREATE TABLE IF NOT EXISTS agents (
|
|
18
|
+
id TEXT NOT NULL,
|
|
19
|
+
session_id TEXT NOT NULL,
|
|
20
|
+
name TEXT NOT NULL,
|
|
21
|
+
type TEXT NOT NULL DEFAULT 'general-purpose',
|
|
22
|
+
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'idle', 'error', 'completed', 'shutdown')),
|
|
23
|
+
first_seen_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
24
|
+
last_activity_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
25
|
+
current_task TEXT,
|
|
26
|
+
tool_call_count INTEGER NOT NULL DEFAULT 0,
|
|
27
|
+
error_count INTEGER NOT NULL DEFAULT 0,
|
|
28
|
+
PRIMARY KEY (id, session_id),
|
|
29
|
+
FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
CREATE TABLE IF NOT EXISTS events (
|
|
33
|
+
id TEXT PRIMARY KEY,
|
|
34
|
+
session_id TEXT NOT NULL,
|
|
35
|
+
agent_id TEXT NOT NULL,
|
|
36
|
+
timestamp TEXT NOT NULL,
|
|
37
|
+
hook_type TEXT NOT NULL,
|
|
38
|
+
category TEXT NOT NULL,
|
|
39
|
+
tool TEXT,
|
|
40
|
+
file_path TEXT,
|
|
41
|
+
input TEXT,
|
|
42
|
+
output TEXT,
|
|
43
|
+
error TEXT,
|
|
44
|
+
duration REAL,
|
|
45
|
+
metadata TEXT,
|
|
46
|
+
FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
CREATE INDEX IF NOT EXISTS idx_events_session ON events(session_id);
|
|
50
|
+
CREATE INDEX IF NOT EXISTS idx_events_agent ON events(session_id, agent_id);
|
|
51
|
+
CREATE INDEX IF NOT EXISTS idx_events_category ON events(session_id, category);
|
|
52
|
+
CREATE INDEX IF NOT EXISTS idx_events_timestamp ON events(timestamp);
|
|
53
|
+
CREATE INDEX IF NOT EXISTS idx_events_tool ON events(tool);
|
|
54
|
+
|
|
55
|
+
CREATE TABLE IF NOT EXISTS file_changes (
|
|
56
|
+
file_path TEXT NOT NULL,
|
|
57
|
+
session_id TEXT NOT NULL,
|
|
58
|
+
agent_id TEXT NOT NULL,
|
|
59
|
+
change_type TEXT NOT NULL CHECK (change_type IN ('created', 'modified', 'read')),
|
|
60
|
+
first_touched_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
61
|
+
last_touched_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
62
|
+
touch_count INTEGER NOT NULL DEFAULT 1,
|
|
63
|
+
PRIMARY KEY (file_path, session_id, agent_id),
|
|
64
|
+
FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
CREATE TABLE IF NOT EXISTS task_items (
|
|
68
|
+
id TEXT PRIMARY KEY,
|
|
69
|
+
session_id TEXT NOT NULL,
|
|
70
|
+
subject TEXT NOT NULL,
|
|
71
|
+
status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'in_progress', 'completed')),
|
|
72
|
+
owner TEXT,
|
|
73
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
74
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
75
|
+
FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
-- === Session Groups (DEPRECATED Sprint 8: replaced by project_registry) ===
|
|
79
|
+
-- Tables kept for backward compatibility with existing databases.
|
|
80
|
+
|
|
81
|
+
CREATE TABLE IF NOT EXISTS session_groups (
|
|
82
|
+
id TEXT PRIMARY KEY,
|
|
83
|
+
name TEXT,
|
|
84
|
+
main_session_id TEXT NOT NULL,
|
|
85
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
86
|
+
FOREIGN KEY (main_session_id) REFERENCES sessions(id) ON DELETE CASCADE
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
CREATE TABLE IF NOT EXISTS session_group_members (
|
|
90
|
+
group_id TEXT NOT NULL,
|
|
91
|
+
session_id TEXT NOT NULL,
|
|
92
|
+
agent_name TEXT,
|
|
93
|
+
agent_type TEXT,
|
|
94
|
+
joined_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
95
|
+
PRIMARY KEY (group_id, session_id),
|
|
96
|
+
FOREIGN KEY (group_id) REFERENCES session_groups(id) ON DELETE CASCADE,
|
|
97
|
+
FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
CREATE INDEX IF NOT EXISTS idx_session_group_members_session ON session_group_members(session_id);
|
|
101
|
+
|
|
102
|
+
-- === Pilar 2: PRD Tracker ===
|
|
103
|
+
|
|
104
|
+
CREATE TABLE IF NOT EXISTS projects (
|
|
105
|
+
id TEXT PRIMARY KEY,
|
|
106
|
+
name TEXT NOT NULL,
|
|
107
|
+
description TEXT,
|
|
108
|
+
prd_source TEXT NOT NULL DEFAULT '',
|
|
109
|
+
prd_content TEXT NOT NULL DEFAULT '',
|
|
110
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
111
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
112
|
+
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'completed', 'archived')),
|
|
113
|
+
total_tasks INTEGER NOT NULL DEFAULT 0,
|
|
114
|
+
completed_tasks INTEGER NOT NULL DEFAULT 0,
|
|
115
|
+
current_sprint_id TEXT,
|
|
116
|
+
metadata TEXT
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
CREATE TABLE IF NOT EXISTS sprints (
|
|
120
|
+
id TEXT PRIMARY KEY,
|
|
121
|
+
project_id TEXT NOT NULL,
|
|
122
|
+
name TEXT NOT NULL,
|
|
123
|
+
description TEXT,
|
|
124
|
+
"order" INTEGER NOT NULL DEFAULT 1,
|
|
125
|
+
status TEXT NOT NULL DEFAULT 'planned' CHECK (status IN ('planned', 'active', 'completed')),
|
|
126
|
+
started_at TEXT,
|
|
127
|
+
completed_at TEXT,
|
|
128
|
+
total_tasks INTEGER NOT NULL DEFAULT 0,
|
|
129
|
+
completed_tasks INTEGER NOT NULL DEFAULT 0,
|
|
130
|
+
metadata TEXT,
|
|
131
|
+
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
CREATE INDEX IF NOT EXISTS idx_sprints_project ON sprints(project_id);
|
|
135
|
+
|
|
136
|
+
CREATE TABLE IF NOT EXISTS prd_tasks (
|
|
137
|
+
id TEXT PRIMARY KEY,
|
|
138
|
+
project_id TEXT NOT NULL,
|
|
139
|
+
sprint_id TEXT,
|
|
140
|
+
external_id TEXT,
|
|
141
|
+
title TEXT NOT NULL,
|
|
142
|
+
description TEXT NOT NULL DEFAULT '',
|
|
143
|
+
acceptance_criteria TEXT,
|
|
144
|
+
status TEXT NOT NULL DEFAULT 'backlog' CHECK (status IN ('backlog', 'planned', 'pending', 'in_progress', 'in_review', 'completed', 'blocked', 'deferred')),
|
|
145
|
+
priority TEXT NOT NULL DEFAULT 'medium' CHECK (priority IN ('critical', 'high', 'medium', 'low')),
|
|
146
|
+
complexity INTEGER,
|
|
147
|
+
tags TEXT,
|
|
148
|
+
depends_on TEXT NOT NULL DEFAULT '[]',
|
|
149
|
+
blocked_by TEXT NOT NULL DEFAULT '[]',
|
|
150
|
+
assigned_agent TEXT,
|
|
151
|
+
started_at TEXT,
|
|
152
|
+
completed_at TEXT,
|
|
153
|
+
session_id TEXT,
|
|
154
|
+
prd_section TEXT,
|
|
155
|
+
prd_subsection TEXT,
|
|
156
|
+
prd_line_start INTEGER,
|
|
157
|
+
prd_line_end INTEGER,
|
|
158
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
159
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
160
|
+
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
|
|
161
|
+
FOREIGN KEY (sprint_id) REFERENCES sprints(id) ON DELETE SET NULL
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
CREATE INDEX IF NOT EXISTS idx_prd_tasks_project ON prd_tasks(project_id);
|
|
165
|
+
CREATE INDEX IF NOT EXISTS idx_prd_tasks_sprint ON prd_tasks(sprint_id);
|
|
166
|
+
CREATE INDEX IF NOT EXISTS idx_prd_tasks_status ON prd_tasks(status);
|
|
167
|
+
|
|
168
|
+
CREATE TABLE IF NOT EXISTS task_activities (
|
|
169
|
+
id TEXT PRIMARY KEY,
|
|
170
|
+
prd_task_id TEXT NOT NULL,
|
|
171
|
+
event_id TEXT NOT NULL,
|
|
172
|
+
session_id TEXT NOT NULL,
|
|
173
|
+
agent_id TEXT NOT NULL,
|
|
174
|
+
activity_type TEXT NOT NULL,
|
|
175
|
+
timestamp TEXT NOT NULL DEFAULT (datetime('now')),
|
|
176
|
+
details TEXT,
|
|
177
|
+
FOREIGN KEY (prd_task_id) REFERENCES prd_tasks(id) ON DELETE CASCADE,
|
|
178
|
+
FOREIGN KEY (event_id) REFERENCES events(id) ON DELETE CASCADE
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
CREATE INDEX IF NOT EXISTS idx_task_activities_task ON task_activities(prd_task_id);
|
|
182
|
+
|
|
183
|
+
CREATE TABLE IF NOT EXISTS prd_documents (
|
|
184
|
+
id TEXT PRIMARY KEY,
|
|
185
|
+
project_id TEXT NOT NULL,
|
|
186
|
+
version INTEGER NOT NULL DEFAULT 1,
|
|
187
|
+
raw_content TEXT NOT NULL,
|
|
188
|
+
sections TEXT NOT NULL DEFAULT '[]',
|
|
189
|
+
parsed_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
190
|
+
parse_method TEXT NOT NULL DEFAULT 'structured' CHECK (parse_method IN ('structured', 'ai_assisted', 'manual')),
|
|
191
|
+
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
CREATE INDEX IF NOT EXISTS idx_prd_documents_project ON prd_documents(project_id);
|
|
195
|
+
|
|
196
|
+
-- === Correlation Audit Log (Sprint 7) ===
|
|
197
|
+
|
|
198
|
+
CREATE TABLE IF NOT EXISTS correlation_audit_log (
|
|
199
|
+
id TEXT PRIMARY KEY,
|
|
200
|
+
event_id TEXT NOT NULL,
|
|
201
|
+
prd_task_id TEXT,
|
|
202
|
+
session_id TEXT NOT NULL,
|
|
203
|
+
agent_id TEXT NOT NULL,
|
|
204
|
+
layer TEXT NOT NULL,
|
|
205
|
+
score REAL NOT NULL DEFAULT 0,
|
|
206
|
+
matched INTEGER NOT NULL DEFAULT 0,
|
|
207
|
+
reason TEXT,
|
|
208
|
+
timestamp TEXT NOT NULL DEFAULT (datetime('now')),
|
|
209
|
+
FOREIGN KEY (event_id) REFERENCES events(id) ON DELETE CASCADE,
|
|
210
|
+
FOREIGN KEY (prd_task_id) REFERENCES prd_tasks(id) ON DELETE SET NULL
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
CREATE INDEX IF NOT EXISTS idx_correlation_audit_event ON correlation_audit_log(event_id);
|
|
214
|
+
CREATE INDEX IF NOT EXISTS idx_correlation_audit_task ON correlation_audit_log(prd_task_id);
|
|
215
|
+
CREATE INDEX IF NOT EXISTS idx_correlation_audit_timestamp ON correlation_audit_log(timestamp);
|
|
216
|
+
|
|
217
|
+
-- === Session-Project Bindings (Sprint 7) ===
|
|
218
|
+
|
|
219
|
+
CREATE TABLE IF NOT EXISTS session_project_bindings (
|
|
220
|
+
session_id TEXT PRIMARY KEY,
|
|
221
|
+
project_id TEXT NOT NULL,
|
|
222
|
+
bound_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
223
|
+
FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE,
|
|
224
|
+
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
-- === Project Registry (Sprint 8) ===
|
|
228
|
+
|
|
229
|
+
CREATE TABLE IF NOT EXISTS project_registry (
|
|
230
|
+
working_directory TEXT PRIMARY KEY,
|
|
231
|
+
project_id TEXT NOT NULL,
|
|
232
|
+
registered_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
233
|
+
prd_path TEXT,
|
|
234
|
+
hooks_installed INTEGER NOT NULL DEFAULT 0,
|
|
235
|
+
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
CREATE INDEX IF NOT EXISTS idx_project_registry_project ON project_registry(project_id);
|
|
239
|
+
|
|
240
|
+
-- === Agent-Task Bindings (Sprint 7) ===
|
|
241
|
+
|
|
242
|
+
CREATE TABLE IF NOT EXISTS agent_task_bindings (
|
|
243
|
+
id TEXT PRIMARY KEY,
|
|
244
|
+
agent_id TEXT NOT NULL,
|
|
245
|
+
session_id TEXT NOT NULL,
|
|
246
|
+
prd_task_id TEXT NOT NULL,
|
|
247
|
+
confidence REAL NOT NULL DEFAULT 0.5,
|
|
248
|
+
bound_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
249
|
+
expired_at TEXT,
|
|
250
|
+
FOREIGN KEY (prd_task_id) REFERENCES prd_tasks(id) ON DELETE CASCADE
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
CREATE INDEX IF NOT EXISTS idx_agent_task_bindings_agent ON agent_task_bindings(agent_id, session_id);
|
|
254
|
+
CREATE INDEX IF NOT EXISTS idx_agent_task_bindings_task ON agent_task_bindings(prd_task_id);
|
|
255
|
+
CREATE INDEX IF NOT EXISTS idx_agent_task_bindings_active ON agent_task_bindings(agent_id, session_id, expired_at);
|
|
256
|
+
|
|
257
|
+
-- For existing databases, run these ALTER TABLE statements manually:
|
|
258
|
+
-- ALTER TABLE events ADD COLUMN correlation_id TEXT;
|
|
259
|
+
-- ALTER TABLE events ADD COLUMN causation_id TEXT;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import cors from "cors";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { DEFAULT_SERVER_PORT, DEFAULT_BIND_HOST, AGENT_IDLE_TIMEOUT_MS, } from "@claudecam/shared";
|
|
6
|
+
import { initDb, closeDb } from "./db/index.js";
|
|
7
|
+
import { sseManager } from "./services/sse-manager.js";
|
|
8
|
+
import { agentQueries } from "./db/queries.js";
|
|
9
|
+
// Stale session cleanup REMOVED: sessions only end via SessionEnd hook
|
|
10
|
+
// Routes
|
|
11
|
+
import { eventsRouter } from "./routes/events.js";
|
|
12
|
+
import { sessionsRouter } from "./routes/sessions.js";
|
|
13
|
+
import { agentsRouter } from "./routes/agents.js";
|
|
14
|
+
import { filesRouter } from "./routes/files.js";
|
|
15
|
+
import { statsRouter } from "./routes/stats.js";
|
|
16
|
+
import { streamRouter } from "./routes/stream.js";
|
|
17
|
+
import { projectsRouter } from "./routes/projects.js";
|
|
18
|
+
import { sprintsRouter } from "./routes/sprints.js";
|
|
19
|
+
import { tasksRouter } from "./routes/tasks.js";
|
|
20
|
+
import { parsePrdRouter } from "./routes/parse-prd.js";
|
|
21
|
+
import { correlationAuditRouter } from "./routes/correlation-audit.js";
|
|
22
|
+
import { registryRouter } from "./routes/registry.js";
|
|
23
|
+
import { getSessionsForProject } from "./services/project-router.js";
|
|
24
|
+
export function createApp() {
|
|
25
|
+
const app = express();
|
|
26
|
+
// Middleware
|
|
27
|
+
app.use(cors());
|
|
28
|
+
app.use(express.json({ limit: "1mb" }));
|
|
29
|
+
// Health check
|
|
30
|
+
app.get("/api/health", (_req, res) => {
|
|
31
|
+
res.json({
|
|
32
|
+
status: "ok",
|
|
33
|
+
timestamp: new Date().toISOString(),
|
|
34
|
+
connections: sseManager.getConnectionCount(),
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
// Pilar 1 routes
|
|
38
|
+
app.use("/api/events", eventsRouter);
|
|
39
|
+
app.use("/api/sessions", sessionsRouter);
|
|
40
|
+
app.use("/api/sessions", agentsRouter); // /api/sessions/:id/agents
|
|
41
|
+
app.use("/api/sessions", filesRouter); // /api/sessions/:id/files
|
|
42
|
+
app.use("/api/sessions", statsRouter); // /api/sessions/:id/stats
|
|
43
|
+
app.use("/api/sessions", eventsRouter); // /api/sessions/:id/events (mounted on eventsRouter)
|
|
44
|
+
app.use("/api/stream", streamRouter);
|
|
45
|
+
// Correlation audit log
|
|
46
|
+
app.use("/api/correlation-audit", correlationAuditRouter);
|
|
47
|
+
// Project registry (Sprint 8 - Project-First Architecture)
|
|
48
|
+
app.use("/api/registry", registryRouter);
|
|
49
|
+
// Pilar 2 routes
|
|
50
|
+
app.use("/api/projects", projectsRouter);
|
|
51
|
+
app.use("/api/projects", sprintsRouter); // /api/projects/:id/sprints
|
|
52
|
+
app.use("/api/projects", tasksRouter); // /api/projects/:id/tasks
|
|
53
|
+
app.use("/api/parse-prd", parsePrdRouter);
|
|
54
|
+
// Serve dashboard static files in production mode
|
|
55
|
+
const dashboardPath = process.env["CAM_DASHBOARD_PATH"];
|
|
56
|
+
if (dashboardPath && existsSync(dashboardPath)) {
|
|
57
|
+
app.use(express.static(dashboardPath));
|
|
58
|
+
// SPA fallback: serve index.html for non-API routes
|
|
59
|
+
app.get("*", (_req, res) => {
|
|
60
|
+
res.sendFile(join(dashboardPath, "index.html"));
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
return app;
|
|
64
|
+
}
|
|
65
|
+
export function startServer(options) {
|
|
66
|
+
const port = options?.port ?? DEFAULT_SERVER_PORT;
|
|
67
|
+
// Initialize database
|
|
68
|
+
initDb(options?.dbPath);
|
|
69
|
+
const app = createApp();
|
|
70
|
+
// Connect SSE Manager to Project Router for project-level filtering
|
|
71
|
+
sseManager.setProjectSessionResolver(getSessionsForProject);
|
|
72
|
+
const bindHost = process.env["CAM_BIND_HOST"] || DEFAULT_BIND_HOST;
|
|
73
|
+
const server = app.listen(port, bindHost, () => {
|
|
74
|
+
console.log(`Claude Agent Monitor server running at http://${bindHost}:${port}`);
|
|
75
|
+
console.log(` API: http://localhost:${port}/api/health`);
|
|
76
|
+
console.log(` SSE: http://localhost:${port}/api/stream`);
|
|
77
|
+
console.log(` Events: POST http://localhost:${port}/api/events`);
|
|
78
|
+
});
|
|
79
|
+
// Idle detection: periodically check for agents that went stale
|
|
80
|
+
const idleCheckInterval = setInterval(() => {
|
|
81
|
+
try {
|
|
82
|
+
const cutoff = new Date(Date.now() - AGENT_IDLE_TIMEOUT_MS).toISOString();
|
|
83
|
+
const staleAgents = agentQueries.getActiveStale().all(cutoff);
|
|
84
|
+
for (const agent of staleAgents) {
|
|
85
|
+
const agentId = agent["id"];
|
|
86
|
+
const sessionId = agent["session_id"];
|
|
87
|
+
const now = new Date().toISOString();
|
|
88
|
+
agentQueries.updateStatus().run("idle", now, agentId, sessionId);
|
|
89
|
+
sseManager.broadcast("agent_status", {
|
|
90
|
+
agent: agentId,
|
|
91
|
+
sessionId,
|
|
92
|
+
status: "idle",
|
|
93
|
+
previousStatus: "active",
|
|
94
|
+
}, sessionId);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
// DB not ready yet or query error, skip silently
|
|
99
|
+
}
|
|
100
|
+
}, AGENT_IDLE_TIMEOUT_MS);
|
|
101
|
+
// Graceful shutdown
|
|
102
|
+
const shutdown = () => {
|
|
103
|
+
console.log("\nShutting down...");
|
|
104
|
+
clearInterval(idleCheckInterval);
|
|
105
|
+
sseManager.shutdown();
|
|
106
|
+
server.close(() => {
|
|
107
|
+
closeDb();
|
|
108
|
+
process.exit(0);
|
|
109
|
+
});
|
|
110
|
+
// Force exit after 5 seconds
|
|
111
|
+
setTimeout(() => {
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}, 5000);
|
|
114
|
+
};
|
|
115
|
+
process.on("SIGINT", shutdown);
|
|
116
|
+
process.on("SIGTERM", shutdown);
|
|
117
|
+
return server;
|
|
118
|
+
}
|
|
119
|
+
// Start server when run directly
|
|
120
|
+
const isMain = process.argv[1] &&
|
|
121
|
+
(process.argv[1].endsWith("index.ts") ||
|
|
122
|
+
process.argv[1].endsWith("index.js"));
|
|
123
|
+
if (isMain) {
|
|
124
|
+
const port = parseInt(process.env["CAM_PORT"] || "") || DEFAULT_SERVER_PORT;
|
|
125
|
+
const dbPath = process.env["CAM_DB_PATH"] || undefined;
|
|
126
|
+
startServer({ port, dbPath });
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import { agentQueries, eventQueries } from '../db/queries.js';
|
|
3
|
+
import { DEFAULT_EVENT_LIMIT } from '@claudecam/shared';
|
|
4
|
+
export const agentsRouter = Router();
|
|
5
|
+
function mapAgent(row) {
|
|
6
|
+
return {
|
|
7
|
+
id: row.id,
|
|
8
|
+
sessionId: row.session_id,
|
|
9
|
+
name: row.name,
|
|
10
|
+
type: row.type,
|
|
11
|
+
status: row.status,
|
|
12
|
+
firstSeenAt: row.first_seen_at,
|
|
13
|
+
lastActivityAt: row.last_activity_at,
|
|
14
|
+
currentTask: row.current_task ?? undefined,
|
|
15
|
+
toolCallCount: row.tool_call_count,
|
|
16
|
+
errorCount: row.error_count,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
// GET /api/sessions/:id/agents
|
|
20
|
+
agentsRouter.get('/:id/agents', (req, res) => {
|
|
21
|
+
try {
|
|
22
|
+
const sessionId = req.params['id'];
|
|
23
|
+
// Return all agents for the session - frontend handles display limits
|
|
24
|
+
const rows = agentQueries.getBySession().all(sessionId);
|
|
25
|
+
const agents = rows.map(mapAgent);
|
|
26
|
+
res.json({ agents });
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
// GET /api/sessions/:id/agents/:agentId/events
|
|
33
|
+
agentsRouter.get('/:id/agents/:agentId/events', (req, res) => {
|
|
34
|
+
try {
|
|
35
|
+
const sessionId = req.params['id'];
|
|
36
|
+
const agentId = req.params['agentId'];
|
|
37
|
+
const category = req.query['category'];
|
|
38
|
+
const limit = parseInt(req.query['limit']) || DEFAULT_EVENT_LIMIT;
|
|
39
|
+
const offset = parseInt(req.query['offset']) || 0;
|
|
40
|
+
let rows;
|
|
41
|
+
if (category) {
|
|
42
|
+
rows = eventQueries.getByAgentAndCategory().all(sessionId, agentId, category, limit, offset);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
rows = eventQueries.getBySessionAndAgent().all(sessionId, agentId, limit, offset);
|
|
46
|
+
}
|
|
47
|
+
const events = rows.map(row => ({
|
|
48
|
+
id: row['id'],
|
|
49
|
+
sessionId: row['session_id'],
|
|
50
|
+
agentId: row['agent_id'],
|
|
51
|
+
timestamp: row['timestamp'],
|
|
52
|
+
hookType: row['hook_type'],
|
|
53
|
+
category: row['category'],
|
|
54
|
+
tool: row['tool'] ?? undefined,
|
|
55
|
+
filePath: row['file_path'] ?? undefined,
|
|
56
|
+
input: row['input'] ?? undefined,
|
|
57
|
+
output: row['output'] ?? undefined,
|
|
58
|
+
error: row['error'] ?? undefined,
|
|
59
|
+
duration: row['duration'] ?? undefined,
|
|
60
|
+
metadata: row['metadata'] ? JSON.parse(row['metadata']) : undefined,
|
|
61
|
+
}));
|
|
62
|
+
res.json({ events });
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
//# sourceMappingURL=agents.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agents.js","sourceRoot":"","sources":["../../src/routes/agents.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Router } from "express";
|
|
2
|
+
import { correlationAuditQueries } from "../db/queries.js";
|
|
3
|
+
export const correlationAuditRouter = Router();
|
|
4
|
+
// GET /api/correlation-audit
|
|
5
|
+
correlationAuditRouter.get("/", (req, res) => {
|
|
6
|
+
try {
|
|
7
|
+
const limit = parseInt(req.query["limit"]) || 50;
|
|
8
|
+
const offset = parseInt(req.query["offset"]) || 0;
|
|
9
|
+
const rows = correlationAuditQueries
|
|
10
|
+
.getRecent()
|
|
11
|
+
.all(limit, offset);
|
|
12
|
+
res.json({ entries: rows, limit, offset });
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
res.status(500).json({ error: "Internal server error" });
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
// GET /api/correlation-audit/:eventId
|
|
19
|
+
correlationAuditRouter.get("/:eventId", (req, res) => {
|
|
20
|
+
try {
|
|
21
|
+
const eventId = req.params["eventId"];
|
|
22
|
+
const rows = correlationAuditQueries
|
|
23
|
+
.getByEvent()
|
|
24
|
+
.all(eventId);
|
|
25
|
+
res.json({ entries: rows });
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
res.status(500).json({ error: "Internal server error" });
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
//# sourceMappingURL=correlation-audit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"correlation-audit.js","sourceRoot":"","sources":["../../src/routes/correlation-audit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,OAAO,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAE3D,MAAM,CAAC,MAAM,sBAAsB,GAAG,MAAM,EAAE,CAAC;AAgB/C,6BAA6B;AAC7B,sBAAsB,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAY,EAAE,GAAa,EAAE,EAAE;IAC9D,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAW,CAAC,IAAI,EAAE,CAAC;QAC3D,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAW,CAAC,IAAI,CAAC,CAAC;QAE5D,MAAM,IAAI,GAAG,uBAAuB;aACjC,SAAS,EAAE;aACX,GAAG,CAAC,KAAK,EAAE,MAAM,CAAe,CAAC;QAEpC,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAC;IAC3D,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,sCAAsC;AACtC,sBAAsB,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,GAAY,EAAE,GAAa,EAAE,EAAE;IACtE,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,SAAS,CAAE,CAAC;QAEvC,MAAM,IAAI,GAAG,uBAAuB;aACjC,UAAU,EAAE;aACZ,GAAG,CAAC,OAAO,CAAe,CAAC;QAE9B,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAC;IAC3D,CAAC;AACH,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import { incomingEventSchema } from '@claudecam/shared';
|
|
3
|
+
import { processEvent } from '../services/event-processor.js';
|
|
4
|
+
import { correlateEvent } from '../services/correlation-engine.js';
|
|
5
|
+
import { eventQueries } from '../db/queries.js';
|
|
6
|
+
import { DEFAULT_EVENT_LIMIT } from '@claudecam/shared';
|
|
7
|
+
export const eventsRouter = Router();
|
|
8
|
+
// POST /api/events - Main event ingestion endpoint
|
|
9
|
+
eventsRouter.post('/', (req, res) => {
|
|
10
|
+
try {
|
|
11
|
+
const parsed = incomingEventSchema.safeParse(req.body);
|
|
12
|
+
if (!parsed.success) {
|
|
13
|
+
res.status(400).json({ ok: false, error: 'Invalid event payload', details: parsed.error.issues });
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const event = processEvent(parsed.data);
|
|
17
|
+
// Run correlation in the background (non-blocking)
|
|
18
|
+
try {
|
|
19
|
+
correlateEvent(event);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
// Correlation errors should not break event ingestion
|
|
23
|
+
}
|
|
24
|
+
res.status(200).json({ ok: true, event_id: event.id });
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
res.status(500).json({ ok: false, error: 'Internal server error' });
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
// GET /api/sessions/:id/events - List events for a session
|
|
31
|
+
// Note: This route is mounted on /api/sessions, so the path here is /:id/events
|
|
32
|
+
eventsRouter.get('/:id/events', (req, res) => {
|
|
33
|
+
try {
|
|
34
|
+
const sessionId = req.params['id'];
|
|
35
|
+
const category = req.query['category'];
|
|
36
|
+
const agentId = req.query['agent_id'];
|
|
37
|
+
const tool = req.query['tool'];
|
|
38
|
+
const since = req.query['since'];
|
|
39
|
+
const limit = parseInt(req.query['limit']) || DEFAULT_EVENT_LIMIT;
|
|
40
|
+
const offset = parseInt(req.query['offset']) || 0;
|
|
41
|
+
let rows;
|
|
42
|
+
if (category && agentId) {
|
|
43
|
+
rows = eventQueries.getByAgentAndCategory().all(sessionId, agentId, category, limit, offset);
|
|
44
|
+
}
|
|
45
|
+
else if (category) {
|
|
46
|
+
rows = eventQueries.getBySessionAndCategory().all(sessionId, category, limit, offset);
|
|
47
|
+
}
|
|
48
|
+
else if (agentId) {
|
|
49
|
+
rows = eventQueries.getBySessionAndAgent().all(sessionId, agentId, limit, offset);
|
|
50
|
+
}
|
|
51
|
+
else if (tool) {
|
|
52
|
+
rows = eventQueries.getBySessionAndTool().all(sessionId, tool, limit, offset);
|
|
53
|
+
}
|
|
54
|
+
else if (since) {
|
|
55
|
+
rows = eventQueries.getBySessionSince().all(sessionId, since, limit, offset);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
rows = eventQueries.getBySession().all(sessionId, limit, offset);
|
|
59
|
+
}
|
|
60
|
+
const events = rows.map(row => ({
|
|
61
|
+
id: row['id'],
|
|
62
|
+
sessionId: row['session_id'],
|
|
63
|
+
agentId: row['agent_id'],
|
|
64
|
+
timestamp: row['timestamp'],
|
|
65
|
+
hookType: row['hook_type'],
|
|
66
|
+
category: row['category'],
|
|
67
|
+
tool: row['tool'] ?? undefined,
|
|
68
|
+
filePath: row['file_path'] ?? undefined,
|
|
69
|
+
input: row['input'] ?? undefined,
|
|
70
|
+
output: row['output'] ?? undefined,
|
|
71
|
+
error: row['error'] ?? undefined,
|
|
72
|
+
duration: row['duration'] ?? undefined,
|
|
73
|
+
metadata: row['metadata'] ? JSON.parse(row['metadata']) : undefined,
|
|
74
|
+
}));
|
|
75
|
+
res.json({ events });
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
//# sourceMappingURL=events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/routes/events.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import { fileChangeQueries } from '../db/queries.js';
|
|
3
|
+
export const filesRouter = Router();
|
|
4
|
+
// GET /api/sessions/:id/files
|
|
5
|
+
filesRouter.get('/:id/files', (req, res) => {
|
|
6
|
+
try {
|
|
7
|
+
const sessionId = req.params['id'];
|
|
8
|
+
const rows = fileChangeQueries.getBySession().all(sessionId);
|
|
9
|
+
const files = rows.map(row => ({
|
|
10
|
+
filePath: row.file_path,
|
|
11
|
+
sessionId: row.session_id,
|
|
12
|
+
agentId: row.agent_id,
|
|
13
|
+
changeType: row.change_type,
|
|
14
|
+
firstTouchedAt: row.first_touched_at,
|
|
15
|
+
lastTouchedAt: row.last_touched_at,
|
|
16
|
+
touchCount: row.touch_count,
|
|
17
|
+
}));
|
|
18
|
+
res.json({ files });
|
|
19
|
+
}
|
|
20
|
+
catch (err) {
|
|
21
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
//# sourceMappingURL=files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"files.js","sourceRoot":"","sources":["../../src/routes/files.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAErD,MAAM,CAAC,MAAM,WAAW,GAAG,MAAM,EAAE,CAAC;AAYpC,8BAA8B;AAC9B,WAAW,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC,GAAY,EAAE,GAAa,EAAE,EAAE;IAC5D,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAE,CAAC;QACpC,MAAM,IAAI,GAAG,iBAAiB,CAAC,YAAY,EAAE,CAAC,GAAG,CAAC,SAAS,CAAoB,CAAC;QAEhF,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAC7B,QAAQ,EAAE,GAAG,CAAC,SAAS;YACvB,SAAS,EAAE,GAAG,CAAC,UAAU;YACzB,OAAO,EAAE,GAAG,CAAC,QAAQ;YACrB,UAAU,EAAE,GAAG,CAAC,WAAW;YAC3B,cAAc,EAAE,GAAG,CAAC,gBAAgB;YACpC,aAAa,EAAE,GAAG,CAAC,eAAe;YAClC,UAAU,EAAE,GAAG,CAAC,WAAW;SAC5B,CAAC,CAAC,CAAC;QAEJ,GAAG,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;IACtB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAC;IAC3D,CAAC;AACH,CAAC,CAAC,CAAC"}
|