@roxy-agent/agents 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.
Files changed (49) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +306 -0
  3. package/dist/approvals.js +143 -0
  4. package/dist/approvals.js.map +1 -0
  5. package/dist/classifier.js +436 -0
  6. package/dist/classifier.js.map +1 -0
  7. package/dist/dashboard/client.js +2057 -0
  8. package/dist/dashboard/client.js.map +1 -0
  9. package/dist/dashboard/html.js +57 -0
  10. package/dist/dashboard/html.js.map +1 -0
  11. package/dist/dashboard/icons.js +18 -0
  12. package/dist/dashboard/icons.js.map +1 -0
  13. package/dist/dashboard/server.js +423 -0
  14. package/dist/dashboard/server.js.map +1 -0
  15. package/dist/dashboard/styles.js +1685 -0
  16. package/dist/dashboard/styles.js.map +1 -0
  17. package/dist/dashboard.js +2 -0
  18. package/dist/dashboard.js.map +1 -0
  19. package/dist/db.js +526 -0
  20. package/dist/db.js.map +1 -0
  21. package/dist/index.js +94 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/license.js +257 -0
  24. package/dist/license.js.map +1 -0
  25. package/dist/logger.js +44 -0
  26. package/dist/logger.js.map +1 -0
  27. package/dist/ml/bash-classifier.js +121 -0
  28. package/dist/ml/bash-classifier.js.map +1 -0
  29. package/dist/ml/embedder.js +79 -0
  30. package/dist/ml/embedder.js.map +1 -0
  31. package/dist/ml/prototypes.js +707 -0
  32. package/dist/ml/prototypes.js.map +1 -0
  33. package/dist/policies.js +289 -0
  34. package/dist/policies.js.map +1 -0
  35. package/dist/slack.js +149 -0
  36. package/dist/slack.js.map +1 -0
  37. package/dist/tools/bash.js +134 -0
  38. package/dist/tools/bash.js.map +1 -0
  39. package/dist/tools/conversation.js +36 -0
  40. package/dist/tools/conversation.js.map +1 -0
  41. package/dist/tools/filesystem.js +243 -0
  42. package/dist/tools/filesystem.js.map +1 -0
  43. package/dist/tools/introspect.js +187 -0
  44. package/dist/tools/introspect.js.map +1 -0
  45. package/dist/tools/network.js +152 -0
  46. package/dist/tools/network.js.map +1 -0
  47. package/dist/tools/policies.js +107 -0
  48. package/dist/tools/policies.js.map +1 -0
  49. package/package.json +61 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styles.js","sourceRoot":"","sources":["../../src/dashboard/styles.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,qDAAqD;AACrD,MAAM,CAAC,MAAM,aAAa,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAipDtC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { startDashboard } from "./dashboard/server.js";
2
+ //# sourceMappingURL=dashboard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../src/dashboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC"}
package/dist/db.js ADDED
@@ -0,0 +1,526 @@
1
+ import Database from "better-sqlite3";
2
+ import path from "node:path";
3
+ import fs from "node:fs";
4
+ import os from "node:os";
5
+ import { fileURLToPath } from "node:url";
6
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
+ // Resolve the data directory. Order of precedence:
8
+ // 1. AGENT_PROXY_DATA_DIR env override
9
+ // 2. ~/.agent-proxy/ (default — works whether the server was npm-installed,
10
+ // run via `npx`, or cloned from source)
11
+ // 3. <repo>/data/ if we detect we're running from a source checkout and
12
+ // the repo-local data dir already exists (back-compat for existing dev
13
+ // installs).
14
+ function resolveDataDir() {
15
+ const env = process.env.AGENT_PROXY_DATA_DIR;
16
+ if (env && env.trim())
17
+ return path.resolve(env.trim());
18
+ const home = os.homedir();
19
+ const userDir = path.join(home, ".agent-proxy");
20
+ // Back-compat: if a developer is running from a source checkout that
21
+ // already has <repo>/data/audit.db, keep using it so we don't orphan
22
+ // their existing audit history.
23
+ const repoLocal = path.resolve(__dirname, "..", "data");
24
+ const repoLocalDb = path.join(repoLocal, "audit.db");
25
+ if (fs.existsSync(repoLocalDb) &&
26
+ !fs.existsSync(path.join(userDir, "audit.db"))) {
27
+ return repoLocal;
28
+ }
29
+ return userDir;
30
+ }
31
+ const DATA_DIR = resolveDataDir();
32
+ const DB_PATH = path.join(DATA_DIR, "audit.db");
33
+ if (!fs.existsSync(DATA_DIR))
34
+ fs.mkdirSync(DATA_DIR, { recursive: true });
35
+ export const db = new Database(DB_PATH);
36
+ // WAL mode keeps the dashboard responsive while the MCP server writes events.
37
+ db.pragma("journal_mode = WAL");
38
+ db.pragma("synchronous = NORMAL");
39
+ export function initDb() {
40
+ db.exec(`
41
+ CREATE TABLE IF NOT EXISTS events (
42
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
43
+ timestamp TEXT NOT NULL,
44
+ session_id TEXT NOT NULL,
45
+ tool TEXT NOT NULL,
46
+ action_type TEXT NOT NULL,
47
+ payload TEXT NOT NULL,
48
+ risk_level TEXT NOT NULL CHECK(risk_level IN ('low', 'medium', 'high')),
49
+ decision TEXT NOT NULL CHECK(decision IN ('allowed', 'denied', 'flagged', 'pending_approval')),
50
+ result TEXT,
51
+ duration_ms INTEGER,
52
+ error TEXT,
53
+ reason TEXT
54
+ );
55
+
56
+ CREATE INDEX IF NOT EXISTS idx_timestamp ON events(timestamp);
57
+ CREATE INDEX IF NOT EXISTS idx_session ON events(session_id);
58
+ CREATE INDEX IF NOT EXISTS idx_risk ON events(risk_level);
59
+ CREATE INDEX IF NOT EXISTS idx_decision ON events(decision);
60
+
61
+ CREATE TABLE IF NOT EXISTS usage (
62
+ period TEXT PRIMARY KEY,
63
+ used INTEGER NOT NULL DEFAULT 0,
64
+ last_event TEXT,
65
+ synced_at TEXT
66
+ );
67
+
68
+ CREATE TABLE IF NOT EXISTS builtin_rule_overrides (
69
+ id TEXT PRIMARY KEY,
70
+ enabled INTEGER NOT NULL DEFAULT 1,
71
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
72
+ );
73
+
74
+ CREATE TABLE IF NOT EXISTS approvals (
75
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
76
+ event_id INTEGER,
77
+ tool TEXT NOT NULL,
78
+ summary TEXT NOT NULL,
79
+ reason TEXT NOT NULL,
80
+ status TEXT NOT NULL DEFAULT 'pending',
81
+ requested_at TEXT NOT NULL DEFAULT (datetime('now')),
82
+ decided_at TEXT,
83
+ decided_by TEXT,
84
+ slack_ts TEXT,
85
+ payload TEXT,
86
+ session_id TEXT
87
+ );
88
+ CREATE INDEX IF NOT EXISTS idx_approvals_status ON approvals(status);
89
+
90
+ CREATE TABLE IF NOT EXISTS app_settings (
91
+ key TEXT PRIMARY KEY,
92
+ value TEXT NOT NULL,
93
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
94
+ );
95
+ `);
96
+ // Soft-migration: relax the events.decision CHECK constraint so existing
97
+ // databases accept the new 'pending_approval' value. SQLite stores CHECK
98
+ // constraints inline, so we patch the schema string in sqlite_master. This
99
+ // requires temporarily disabling better-sqlite3's "defensive" mode.
100
+ const evRow = db
101
+ .prepare(`SELECT sql FROM sqlite_master WHERE type='table' AND name='events'`)
102
+ .get();
103
+ if (evRow && !evRow.sql.includes("pending_approval")) {
104
+ try {
105
+ db.unsafeMode(true);
106
+ db.pragma("writable_schema = ON");
107
+ db.prepare(`UPDATE sqlite_master SET sql = ? WHERE type='table' AND name='events'`).run(evRow.sql.replace("decision IN ('allowed', 'denied', 'flagged')", "decision IN ('allowed', 'denied', 'flagged', 'pending_approval')"));
108
+ // Force every connection to re-read the schema by bumping the cookie.
109
+ const v = db.pragma("schema_version", { simple: true });
110
+ db.pragma(`schema_version = ${v + 1}`);
111
+ db.pragma("writable_schema = OFF");
112
+ }
113
+ finally {
114
+ db.unsafeMode(false);
115
+ }
116
+ }
117
+ // Lightweight migration: older databases may not yet have the `reason`
118
+ // column, so add it on the fly.
119
+ const cols = db.prepare(`PRAGMA table_info(events)`).all();
120
+ if (!cols.some((c) => c.name === "reason")) {
121
+ db.exec(`ALTER TABLE events ADD COLUMN reason TEXT`);
122
+ }
123
+ }
124
+ // ---------------------------------------------------------------------------
125
+ // Usage / billing
126
+ // ---------------------------------------------------------------------------
127
+ export function getUsage(period) {
128
+ const row = db
129
+ .prepare(`SELECT used FROM usage WHERE period = ?`)
130
+ .get(period);
131
+ return row?.used ?? 0;
132
+ }
133
+ export function incrementUsage(period) {
134
+ db.prepare(`INSERT INTO usage (period, used, last_event)
135
+ VALUES (?, 1, datetime('now'))
136
+ ON CONFLICT(period) DO UPDATE SET
137
+ used = used + 1,
138
+ last_event = datetime('now')`).run(period);
139
+ return getUsage(period);
140
+ }
141
+ export function setUsageSyncedAt(period, ts) {
142
+ db.prepare(`INSERT INTO usage (period, used, synced_at)
143
+ VALUES (?, 0, ?)
144
+ ON CONFLICT(period) DO UPDATE SET synced_at = excluded.synced_at`).run(period, ts);
145
+ }
146
+ export function getRecentUsage(limit = 6) {
147
+ return db
148
+ .prepare(`SELECT * FROM usage ORDER BY period DESC LIMIT ?`)
149
+ .all(limit);
150
+ }
151
+ export function getBuiltinRuleOverrides() {
152
+ const rows = db
153
+ .prepare(`SELECT id, enabled FROM builtin_rule_overrides`)
154
+ .all();
155
+ const out = new Map();
156
+ for (const r of rows)
157
+ out.set(r.id, !!r.enabled);
158
+ return out;
159
+ }
160
+ export function setBuiltinRuleOverride(id, enabled) {
161
+ db.prepare(`INSERT INTO builtin_rule_overrides (id, enabled, updated_at)
162
+ VALUES (?, ?, datetime('now'))
163
+ ON CONFLICT(id) DO UPDATE SET
164
+ enabled = excluded.enabled,
165
+ updated_at = datetime('now')`).run(id, enabled ? 1 : 0);
166
+ }
167
+ export function createApprovalRow(input) {
168
+ const r = db
169
+ .prepare(`INSERT INTO approvals (event_id, tool, summary, reason, payload, session_id)
170
+ VALUES (?, ?, ?, ?, ?, ?)`)
171
+ .run(input.event_id, input.tool, input.summary.slice(0, 500), input.reason, input.payload === undefined ? null : JSON.stringify(input.payload), input.session_id ?? null);
172
+ return Number(r.lastInsertRowid);
173
+ }
174
+ export function setApprovalStatus(id, status, by) {
175
+ const r = db
176
+ .prepare(`UPDATE approvals SET status = ?, decided_at = datetime('now'), decided_by = ?
177
+ WHERE id = ? AND status = 'pending'`)
178
+ .run(status, by, id);
179
+ return r.changes > 0;
180
+ }
181
+ export function setApprovalSlackTs(id, ts) {
182
+ db.prepare(`UPDATE approvals SET slack_ts = ? WHERE id = ?`).run(ts, id);
183
+ }
184
+ export function getApproval(id) {
185
+ return db.prepare(`SELECT * FROM approvals WHERE id = ?`).get(id);
186
+ }
187
+ export function listApprovals(opts) {
188
+ const limit = opts?.limit ?? 100;
189
+ if (opts?.status) {
190
+ return db
191
+ .prepare(`SELECT * FROM approvals WHERE status = ? ORDER BY id DESC LIMIT ?`)
192
+ .all(opts.status, limit);
193
+ }
194
+ return db
195
+ .prepare(`SELECT * FROM approvals ORDER BY id DESC LIMIT ?`)
196
+ .all(limit);
197
+ }
198
+ export function countPendingApprovals() {
199
+ const row = db
200
+ .prepare(`SELECT COUNT(*) AS n FROM approvals WHERE status = 'pending'`)
201
+ .get();
202
+ return row.n;
203
+ }
204
+ // Mark any approvals left over from a previous process run as 'timeout' since
205
+ // the awaiting promise can't be rehydrated across restarts.
206
+ export function expireOrphanApprovals() {
207
+ const r = db
208
+ .prepare(`UPDATE approvals SET status = 'timeout', decided_at = datetime('now'),
209
+ decided_by = COALESCE(decided_by, 'process restart')
210
+ WHERE status = 'pending'`)
211
+ .run();
212
+ return r.changes;
213
+ }
214
+ // ---------------------------------------------------------------------------
215
+ // Update an event row's decision/reason after the fact (used when an HITL
216
+ // approval resolves: pending_approval → allowed/denied so aggregation queries
217
+ // stay consistent).
218
+ // ---------------------------------------------------------------------------
219
+ export function updateEventDecision(id, decision, reason) {
220
+ db.prepare(`UPDATE events SET decision = ?, reason = COALESCE(?, reason) WHERE id = ?`).run(decision, reason, id);
221
+ }
222
+ // ---------------------------------------------------------------------------
223
+ // App settings (small key/value store for things like Slack webhook URL).
224
+ // ---------------------------------------------------------------------------
225
+ export function getAppSetting(key) {
226
+ const row = db
227
+ .prepare(`SELECT value FROM app_settings WHERE key = ?`)
228
+ .get(key);
229
+ return row?.value ?? null;
230
+ }
231
+ export function setAppSetting(key, value) {
232
+ if (value == null || value === "") {
233
+ db.prepare(`DELETE FROM app_settings WHERE key = ?`).run(key);
234
+ return;
235
+ }
236
+ db.prepare(`INSERT INTO app_settings (key, value, updated_at)
237
+ VALUES (?, ?, datetime('now'))
238
+ ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = datetime('now')`).run(key, value);
239
+ }
240
+ export function getAllAppSettings() {
241
+ const rows = db
242
+ .prepare(`SELECT key, value FROM app_settings`)
243
+ .all();
244
+ const out = {};
245
+ for (const r of rows)
246
+ out[r.key] = r.value;
247
+ return out;
248
+ }
249
+ let _insert = null;
250
+ let _updateResult = null;
251
+ function insertStmt() {
252
+ if (_insert)
253
+ return _insert;
254
+ _insert = db.prepare(`
255
+ INSERT INTO events
256
+ (timestamp, session_id, tool, action_type, payload, risk_level, decision, result, duration_ms, error, reason)
257
+ VALUES
258
+ (datetime('now'), @session_id, @tool, @action_type, @payload, @risk_level, @decision, @result, @duration_ms, @error, @reason)
259
+ `);
260
+ return _insert;
261
+ }
262
+ function updateResultStmt() {
263
+ if (_updateResult)
264
+ return _updateResult;
265
+ _updateResult = db.prepare(`
266
+ UPDATE events
267
+ SET result = ?, duration_ms = ?, error = ?
268
+ WHERE id = ?
269
+ `);
270
+ return _updateResult;
271
+ }
272
+ export function logEvent(event) {
273
+ const result = insertStmt().run({
274
+ session_id: event.session_id,
275
+ tool: event.tool,
276
+ action_type: event.action_type,
277
+ payload: JSON.stringify(event.payload),
278
+ risk_level: event.risk_level,
279
+ decision: event.decision,
280
+ result: event.result ?? null,
281
+ duration_ms: event.duration_ms ?? null,
282
+ error: event.error ?? null,
283
+ reason: event.reason ?? null,
284
+ });
285
+ return Number(result.lastInsertRowid);
286
+ }
287
+ export function updateEventResult(id, result, duration_ms, error) {
288
+ updateResultStmt().run(truncate(result, 100_000), duration_ms, error ?? null, id);
289
+ }
290
+ function truncate(s, max) {
291
+ if (s.length <= max)
292
+ return s;
293
+ return s.slice(0, max) + `… [truncated, ${s.length - max} more chars]`;
294
+ }
295
+ export function getRecentEvents(limit = 100) {
296
+ return db
297
+ .prepare(`SELECT * FROM events ORDER BY id DESC LIMIT ?`)
298
+ .all(limit);
299
+ }
300
+ export function getEventsSince(sinceId, limit = 500) {
301
+ return db
302
+ .prepare(`SELECT * FROM events WHERE id > ? ORDER BY id ASC LIMIT ?`)
303
+ .all(sinceId, limit);
304
+ }
305
+ export function getEvent(id) {
306
+ return db.prepare(`SELECT * FROM events WHERE id = ?`).get(id);
307
+ }
308
+ export function getSessionEvents(session_id) {
309
+ return db
310
+ .prepare(`SELECT * FROM events WHERE session_id = ? ORDER BY id ASC`)
311
+ .all(session_id);
312
+ }
313
+ export function getStats() {
314
+ return db
315
+ .prepare(`
316
+ SELECT
317
+ COUNT(*) as total,
318
+ COALESCE(SUM(CASE WHEN risk_level = 'high' THEN 1 ELSE 0 END), 0) as high_risk,
319
+ COALESCE(SUM(CASE WHEN risk_level = 'medium' THEN 1 ELSE 0 END), 0) as medium_risk,
320
+ COALESCE(SUM(CASE WHEN risk_level = 'low' THEN 1 ELSE 0 END), 0) as low_risk,
321
+ COALESCE(SUM(CASE WHEN decision = 'denied' THEN 1 ELSE 0 END), 0) as denied,
322
+ COALESCE(SUM(CASE WHEN decision = 'flagged' THEN 1 ELSE 0 END), 0) as flagged,
323
+ COALESCE(SUM(CASE WHEN tool = 'bash' THEN 1 ELSE 0 END), 0) as bash_calls,
324
+ COALESCE(SUM(CASE WHEN tool = 'filesystem' THEN 1 ELSE 0 END), 0) as file_calls,
325
+ COALESCE(SUM(CASE WHEN tool = 'network' THEN 1 ELSE 0 END), 0) as network_calls,
326
+ COALESCE(SUM(CASE WHEN error IS NOT NULL AND error <> '' THEN 1 ELSE 0 END), 0) as errors,
327
+ COUNT(DISTINCT session_id) as sessions
328
+ FROM events
329
+ `)
330
+ .get();
331
+ }
332
+ export function getDbPath() {
333
+ return DB_PATH;
334
+ }
335
+ function strftimeFmt(g) {
336
+ if (g === "minute")
337
+ return "%Y-%m-%d %H:%M";
338
+ if (g === "day")
339
+ return "%Y-%m-%d";
340
+ return "%Y-%m-%d %H:00";
341
+ }
342
+ // Returns one row per bucket in the requested window, zero-filled when a
343
+ // concrete window is given, sorted oldest→newest. Pass `hours = null` for
344
+ // "all time" (no zero-fill — only buckets that actually have data).
345
+ export function getActivityAggregate(hours = 24, granularity = "hour") {
346
+ const fmt = strftimeFmt(granularity);
347
+ const filterClause = hours == null ? "" : "WHERE timestamp >= ?";
348
+ const params = hours == null
349
+ ? []
350
+ : [
351
+ new Date(Date.now() - hours * 60 * 60 * 1000)
352
+ .toISOString()
353
+ .slice(0, 19)
354
+ .replace("T", " "),
355
+ ];
356
+ const rows = db
357
+ .prepare(`
358
+ SELECT
359
+ strftime('${fmt}', timestamp) AS hour,
360
+ COUNT(*) AS total,
361
+ COALESCE(SUM(CASE WHEN decision = 'allowed' THEN 1 ELSE 0 END), 0) AS allowed,
362
+ COALESCE(SUM(CASE WHEN decision = 'flagged' THEN 1 ELSE 0 END), 0) AS flagged,
363
+ COALESCE(SUM(CASE WHEN decision = 'denied' THEN 1 ELSE 0 END), 0) AS denied
364
+ FROM events
365
+ ${filterClause}
366
+ GROUP BY hour
367
+ ORDER BY hour ASC
368
+ `)
369
+ .all(...params);
370
+ // No concrete window → return raw buckets (sparse).
371
+ if (hours == null)
372
+ return rows;
373
+ // Zero-fill so charts have a continuous x-axis.
374
+ const byKey = new Map(rows.map((r) => [r.hour, r]));
375
+ const out = [];
376
+ const now = new Date();
377
+ if (granularity === "day") {
378
+ now.setUTCHours(0, 0, 0, 0);
379
+ const days = Math.max(1, Math.ceil(hours / 24));
380
+ for (let i = days - 1; i >= 0; i--) {
381
+ const d = new Date(now.getTime() - i * 24 * 60 * 60 * 1000);
382
+ const key = d.toISOString().slice(0, 10);
383
+ out.push(byKey.get(key) ?? { hour: key, total: 0, allowed: 0, flagged: 0, denied: 0 });
384
+ }
385
+ }
386
+ else if (granularity === "minute") {
387
+ now.setUTCSeconds(0, 0);
388
+ const mins = Math.max(1, Math.ceil(hours * 60));
389
+ for (let i = mins - 1; i >= 0; i--) {
390
+ const d = new Date(now.getTime() - i * 60 * 1000);
391
+ const key = d.toISOString().slice(0, 16).replace("T", " ");
392
+ out.push(byKey.get(key) ?? { hour: key, total: 0, allowed: 0, flagged: 0, denied: 0 });
393
+ }
394
+ }
395
+ else {
396
+ now.setUTCMinutes(0, 0, 0);
397
+ const buckets = Math.max(1, Math.ceil(hours));
398
+ for (let i = buckets - 1; i >= 0; i--) {
399
+ const d = new Date(now.getTime() - i * 60 * 60 * 1000);
400
+ const key = d.toISOString().slice(0, 13).replace("T", " ") + ":00";
401
+ out.push(byKey.get(key) ?? { hour: key, total: 0, allowed: 0, flagged: 0, denied: 0 });
402
+ }
403
+ }
404
+ return out;
405
+ }
406
+ // Backwards-compat alias used elsewhere.
407
+ export function getHourlyAggregate(hours = 24) {
408
+ return getActivityAggregate(hours, "hour");
409
+ }
410
+ // Frequency table of recent commands/paths/urls grouped by their canonical
411
+ // summary. Used by the Insights "top denied / flagged" cards.
412
+ export function getTopActions(decision = "any", limit = 8, windowHours = 24 * 7) {
413
+ const decisionFilter = decision === "any" ? "1=1" : `decision = '${decision}'`;
414
+ const params = [];
415
+ let timeClause = "";
416
+ if (windowHours != null) {
417
+ timeClause = "timestamp >= ? AND ";
418
+ params.push(new Date(Date.now() - windowHours * 60 * 60 * 1000)
419
+ .toISOString()
420
+ .slice(0, 19)
421
+ .replace("T", " "));
422
+ }
423
+ params.push(limit);
424
+ const rows = db
425
+ .prepare(`
426
+ SELECT
427
+ tool,
428
+ decision,
429
+ risk_level,
430
+ COALESCE(json_extract(payload, '$.command'),
431
+ json_extract(payload, '$.path'),
432
+ json_extract(payload, '$.url'),
433
+ substr(payload, 1, 80)) AS summary,
434
+ COUNT(*) AS count
435
+ FROM events
436
+ WHERE ${timeClause}${decisionFilter}
437
+ GROUP BY summary, tool, decision, risk_level
438
+ ORDER BY count DESC, MAX(id) DESC
439
+ LIMIT ?
440
+ `)
441
+ .all(...params);
442
+ return rows;
443
+ }
444
+ export function getRecentSessions(limit = 10) {
445
+ return db
446
+ .prepare(`
447
+ SELECT
448
+ session_id,
449
+ MAX(timestamp) AS last_event,
450
+ COUNT(*) AS events,
451
+ COALESCE(SUM(CASE WHEN decision = 'flagged' THEN 1 ELSE 0 END), 0) AS flagged,
452
+ COALESCE(SUM(CASE WHEN decision = 'denied' THEN 1 ELSE 0 END), 0) AS denied,
453
+ GROUP_CONCAT(DISTINCT tool) AS tools
454
+ FROM events
455
+ GROUP BY session_id
456
+ ORDER BY MAX(id) DESC
457
+ LIMIT ?
458
+ `)
459
+ .all(limit);
460
+ }
461
+ export function getToolBreakdown(windowHours = 24 * 7) {
462
+ const params = [];
463
+ let where = "";
464
+ if (windowHours != null) {
465
+ where = "WHERE timestamp >= ?";
466
+ params.push(new Date(Date.now() - windowHours * 60 * 60 * 1000)
467
+ .toISOString()
468
+ .slice(0, 19)
469
+ .replace("T", " "));
470
+ }
471
+ return db
472
+ .prepare(`
473
+ SELECT
474
+ tool,
475
+ COUNT(*) AS count,
476
+ COALESCE(SUM(CASE WHEN decision = 'allowed' THEN 1 ELSE 0 END), 0) AS allowed,
477
+ COALESCE(SUM(CASE WHEN decision = 'flagged' THEN 1 ELSE 0 END), 0) AS flagged,
478
+ COALESCE(SUM(CASE WHEN decision = 'denied' THEN 1 ELSE 0 END), 0) AS denied
479
+ FROM events
480
+ ${where}
481
+ GROUP BY tool
482
+ ORDER BY count DESC
483
+ `)
484
+ .all(...params);
485
+ }
486
+ // Stats over an arbitrary trailing window. `hours = null` returns all-time.
487
+ export function getRangedStats(hours = 24) {
488
+ const params = [];
489
+ let where = "";
490
+ if (hours != null) {
491
+ where = "WHERE timestamp >= ?";
492
+ params.push(new Date(Date.now() - hours * 60 * 60 * 1000)
493
+ .toISOString()
494
+ .slice(0, 19)
495
+ .replace("T", " "));
496
+ }
497
+ return db
498
+ .prepare(`
499
+ SELECT
500
+ COUNT(*) AS total,
501
+ COALESCE(SUM(CASE WHEN decision = 'flagged' THEN 1 ELSE 0 END), 0) AS flagged,
502
+ COALESCE(SUM(CASE WHEN decision = 'denied' THEN 1 ELSE 0 END), 0) AS denied,
503
+ COUNT(DISTINCT session_id) AS sessions
504
+ FROM events
505
+ ${where}
506
+ `)
507
+ .get(...params);
508
+ }
509
+ // "Today" stats (since UTC midnight) used by the Overview hero KPIs.
510
+ export function getTodayStats() {
511
+ const since = new Date();
512
+ since.setUTCHours(0, 0, 0, 0);
513
+ const sinceStr = since.toISOString().slice(0, 19).replace("T", " ");
514
+ return db
515
+ .prepare(`
516
+ SELECT
517
+ COUNT(*) AS total,
518
+ COALESCE(SUM(CASE WHEN decision = 'flagged' THEN 1 ELSE 0 END), 0) AS flagged,
519
+ COALESCE(SUM(CASE WHEN decision = 'denied' THEN 1 ELSE 0 END), 0) AS denied,
520
+ COUNT(DISTINCT session_id) AS sessions
521
+ FROM events
522
+ WHERE timestamp >= ?
523
+ `)
524
+ .get(sinceStr);
525
+ }
526
+ //# sourceMappingURL=db.js.map
package/dist/db.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.js","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"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"}
package/dist/index.js ADDED
@@ -0,0 +1,94 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { randomUUID } from "node:crypto";
5
+ import { initDb } from "./db.js";
6
+ import { startDashboard } from "./dashboard.js";
7
+ import { prepareBashML } from "./ml/bash-classifier.js";
8
+ import { initPoliciesTable, ensureEmbeddings } from "./policies.js";
9
+ import { initApprovals } from "./approvals.js";
10
+ import { bashToolDefinition, executeBash } from "./tools/bash.js";
11
+ import { readFileToolDefinition, writeFileToolDefinition, deleteFileToolDefinition, listDirectoryToolDefinition, readFile, writeFile, deleteFile, listDirectory, } from "./tools/filesystem.js";
12
+ import { networkToolDefinition, fetchUrl } from "./tools/network.js";
13
+ import { classifyToolDefinition, classify, recentEventsToolDefinition, recentEvents, proxyStatsToolDefinition, proxyStats, } from "./tools/introspect.js";
14
+ import { addPolicyToolDefinition, addPolicy, listPoliciesToolDefinition, listPoliciesTool, deletePolicyToolDefinition, deletePolicyTool, updatePolicyToolDefinition, updatePolicyTool, } from "./tools/policies.js";
15
+ import { ensureLicense, checkQuota, chargeUsage, syncWithCloud, } from "./license.js";
16
+ // One session ID per server run (one per agent session). The agent or
17
+ // orchestrator can override it via AGENT_PROXY_SESSION_ID for traceability.
18
+ const SESSION_ID = process.env.AGENT_PROXY_SESSION_ID ?? randomUUID();
19
+ const DASHBOARD_PORT = Number(process.env.AGENT_PROXY_PORT) || 4242;
20
+ const DASHBOARD_DISABLED = process.env.AGENT_PROXY_DISABLE_DASHBOARD === "1";
21
+ initDb();
22
+ initPoliciesTable();
23
+ initApprovals();
24
+ const LICENSE = ensureLicense();
25
+ if (!DASHBOARD_DISABLED)
26
+ startDashboard(DASHBOARD_PORT);
27
+ process.stderr.write(`[agent-proxy] license user=${LICENSE.user_id} plan=${LICENSE.plan} quota=${LICENSE.quota}\n`);
28
+ // Background cloud sync — best-effort, non-fatal. Pulls fresh quota from the
29
+ // remote API every 5 min so paid upgrades take effect without a restart.
30
+ if (LICENSE.jwt) {
31
+ syncWithCloud().catch(() => { });
32
+ setInterval(() => {
33
+ syncWithCloud().catch(() => { });
34
+ }, 5 * 60 * 1000);
35
+ }
36
+ process.stderr.write(`[agent-proxy] session=${SESSION_ID} pid=${process.pid}\n`);
37
+ // Kick off ML embedder + prototype embedding in the background. We don't
38
+ // await: bash calls that arrive before the model is ready transparently fall
39
+ // back to the rule-based classifier. Set AGENT_PROXY_ML=0 to skip entirely.
40
+ if (process.env.AGENT_PROXY_ML !== "0") {
41
+ prepareBashML()
42
+ .then(() => ensureEmbeddings())
43
+ .catch((err) => {
44
+ process.stderr.write(`[agent-proxy] ML prep failed: ${err.message}\n`);
45
+ });
46
+ }
47
+ const server = new McpServer({
48
+ name: "agent-proxy",
49
+ version: "0.1.0",
50
+ });
51
+ function asText(value) {
52
+ return {
53
+ content: [{ type: "text", text: JSON.stringify(value, null, 2) }],
54
+ };
55
+ }
56
+ // Quota gate — runs before any billable tool. Returns the agent a structured
57
+ // "blocked" object (mirroring how policy/classifier blocks already look) when
58
+ // the current month's free quota is exhausted, and bumps the counter on
59
+ // successful execution.
60
+ async function billable(toolName, fn, actionHint) {
61
+ const block = checkQuota(toolName, actionHint);
62
+ if (block)
63
+ return block;
64
+ const result = await fn();
65
+ chargeUsage(toolName, actionHint);
66
+ return result;
67
+ }
68
+ server.tool(bashToolDefinition.name, bashToolDefinition.description, bashToolDefinition.schema.shape, async (args) => asText(await billable("bash", () => executeBash(args, SESSION_ID))));
69
+ server.tool(readFileToolDefinition.name, readFileToolDefinition.description, readFileToolDefinition.schema.shape, async (args) => asText(await readFile(args, SESSION_ID)));
70
+ server.tool(writeFileToolDefinition.name, writeFileToolDefinition.description, writeFileToolDefinition.schema.shape, async (args) => asText(await billable("write_file", () => writeFile({ ...args, append: args.append ?? false }, SESSION_ID))));
71
+ server.tool(deleteFileToolDefinition.name, deleteFileToolDefinition.description, deleteFileToolDefinition.schema.shape, async (args) => asText(await billable("delete_file", () => deleteFile(args, SESSION_ID))));
72
+ server.tool(listDirectoryToolDefinition.name, listDirectoryToolDefinition.description, listDirectoryToolDefinition.schema.shape, async (args) => asText(await listDirectory(args, SESSION_ID)));
73
+ server.tool(networkToolDefinition.name, networkToolDefinition.description, networkToolDefinition.schema.shape, async (args) => asText(await billable("network", () => fetchUrl({ ...args, method: args.method ?? "GET" }, SESSION_ID))));
74
+ // Introspection tools (read-only — no audit entry generated).
75
+ server.tool(classifyToolDefinition.name, classifyToolDefinition.description, classifyToolDefinition.schema.shape, async (args) => asText(await classify(args)));
76
+ server.tool(recentEventsToolDefinition.name, recentEventsToolDefinition.description, recentEventsToolDefinition.schema.shape, async (args) => asText(recentEvents(args, SESSION_ID)));
77
+ server.tool(proxyStatsToolDefinition.name, proxyStatsToolDefinition.description, proxyStatsToolDefinition.schema.shape, async () => asText(proxyStats(SESSION_ID, DASHBOARD_PORT)));
78
+ // Policy management tools — persist guardrails across sessions.
79
+ server.tool(addPolicyToolDefinition.name, addPolicyToolDefinition.description, addPolicyToolDefinition.schema.shape, async (args) => asText(await addPolicy(args)));
80
+ server.tool(listPoliciesToolDefinition.name, listPoliciesToolDefinition.description, listPoliciesToolDefinition.schema.shape, async (args) => asText(await listPoliciesTool(args)));
81
+ server.tool(updatePolicyToolDefinition.name, updatePolicyToolDefinition.description, updatePolicyToolDefinition.schema.shape, async (args) => asText(await updatePolicyTool(args)));
82
+ server.tool(deletePolicyToolDefinition.name, deletePolicyToolDefinition.description, deletePolicyToolDefinition.schema.shape, async (args) => asText(deletePolicyTool(args)));
83
+ const transport = new StdioServerTransport();
84
+ process.on("SIGINT", () => {
85
+ process.stderr.write("[agent-proxy] SIGINT — shutting down\n");
86
+ process.exit(0);
87
+ });
88
+ process.on("SIGTERM", () => {
89
+ process.stderr.write("[agent-proxy] SIGTERM — shutting down\n");
90
+ process.exit(0);
91
+ });
92
+ await server.connect(transport);
93
+ process.stderr.write("[agent-proxy] MCP server ready on stdio\n");
94
+ //# sourceMappingURL=index.js.map