@agentcairn/mcp-server 0.0.0-stage → 0.1.1
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/README.md +23 -2
- package/dist/authn.d.ts +38 -0
- package/dist/authn.js +86 -0
- package/dist/authn.js.map +1 -0
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +25 -0
- package/dist/bin.js.map +1 -0
- package/dist/context.d.ts +49 -0
- package/dist/context.js +79 -0
- package/dist/context.js.map +1 -0
- package/dist/dashboard.d.ts +29 -0
- package/dist/dashboard.js +365 -0
- package/dist/dashboard.js.map +1 -0
- package/dist/events.d.ts +120 -0
- package/dist/events.js +161 -0
- package/dist/events.js.map +1 -0
- package/dist/http.d.ts +35 -0
- package/dist/http.js +348 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/security.d.ts +49 -0
- package/dist/security.js +143 -0
- package/dist/security.js.map +1 -0
- package/dist/serve.d.ts +2 -0
- package/dist/serve.js +60 -0
- package/dist/serve.js.map +1 -0
- package/dist/server.d.ts +10 -0
- package/dist/server.js +14 -0
- package/dist/server.js.map +1 -0
- package/dist/tools.d.ts +8 -0
- package/dist/tools.js +199 -0
- package/dist/tools.js.map +1 -0
- package/dist/util.d.ts +13 -0
- package/dist/util.js +49 -0
- package/dist/util.js.map +1 -0
- package/package.json +55 -4
- package/ui/css/app.css +361 -0
- package/ui/favicon.svg +1 -0
- package/ui/index.html +17 -0
- package/ui/js/api.js +37 -0
- package/ui/js/app.js +190 -0
- package/ui/js/charts.js +133 -0
- package/ui/js/dom.js +247 -0
- package/ui/js/drawer.js +39 -0
- package/ui/js/json.js +37 -0
- package/ui/js/store.js +77 -0
- package/ui/js/views/access.js +129 -0
- package/ui/js/views/auth.js +102 -0
- package/ui/js/views/eventDrawer.js +106 -0
- package/ui/js/views/graph.js +344 -0
- package/ui/js/views/live.js +203 -0
- package/ui/js/views/memoryDrawer.js +37 -0
- package/ui/js/views/project.js +94 -0
- package/ui/js/views/projects.js +77 -0
- package/ui/js/views/query.js +107 -0
- package/ui/js/views/system.js +73 -0
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { extname, join, normalize, sep } from 'node:path';
|
|
4
|
+
import { AdminError, MEMORY_KINDS, QueryRejectedError, changeAdminPassword, checkConnection, countActiveTokens, countAdmins, createApiToken, createFirstAdmin, ensureProject, getGraph, getMemory, getMemoryChain, getProjectDetail, ensureSchema, getSystemInfo, hasDefaultCredentials, isValidProjectId, listApiTokens, listProjects, loadConfig, recall, revokeApiToken, runReadOnlyCypher, schemaVersion, tidySystemNodes, verifyAdmin, } from '@agentcairn/core';
|
|
5
|
+
import { RateLimiter, SESSION_COOKIE, clearedSessionCookie, parseCookies, sameOrigin, sessionCookie, setupBanner, } from './security.js';
|
|
6
|
+
import { HttpError, asRecord, clientIp, readJson, send } from './util.js';
|
|
7
|
+
const MIME = {
|
|
8
|
+
'.html': 'text/html; charset=utf-8',
|
|
9
|
+
'.js': 'text/javascript; charset=utf-8',
|
|
10
|
+
'.mjs': 'text/javascript; charset=utf-8',
|
|
11
|
+
'.css': 'text/css; charset=utf-8',
|
|
12
|
+
'.svg': 'image/svg+xml',
|
|
13
|
+
'.png': 'image/png',
|
|
14
|
+
'.ico': 'image/x-icon',
|
|
15
|
+
'.json': 'application/json',
|
|
16
|
+
};
|
|
17
|
+
const CSP = "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; frame-ancestors 'none'; base-uri 'none'; form-action 'self'";
|
|
18
|
+
const SECURITY_HEADERS = {
|
|
19
|
+
'content-security-policy': CSP,
|
|
20
|
+
'x-content-type-options': 'nosniff',
|
|
21
|
+
'referrer-policy': 'no-referrer',
|
|
22
|
+
'x-frame-options': 'DENY',
|
|
23
|
+
};
|
|
24
|
+
function vendorFiles() {
|
|
25
|
+
try {
|
|
26
|
+
const require = createRequire(import.meta.url);
|
|
27
|
+
return { '/ui/vendor/cytoscape.js': require.resolve('cytoscape/dist/cytoscape.min.js') };
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return {};
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The web dashboard: static UI under /ui/, JSON under /admin/api/. Returns a
|
|
35
|
+
* handler that answers true when it took the request.
|
|
36
|
+
*/
|
|
37
|
+
export function createDashboard(deps) {
|
|
38
|
+
const { brain, events, auth, sessions, setup } = deps;
|
|
39
|
+
const vendor = vendorFiles();
|
|
40
|
+
const loginByIp = new RateLimiter(10, 10 * 60_000);
|
|
41
|
+
const loginByUser = new RateLimiter(5, 10 * 60_000);
|
|
42
|
+
const setupGuesses = new RateLimiter(5, 10 * 60_000);
|
|
43
|
+
let adminCache;
|
|
44
|
+
const adminCount = async () => {
|
|
45
|
+
if (adminCache && adminCache.until > Date.now())
|
|
46
|
+
return adminCache.value;
|
|
47
|
+
const value = await countAdmins(brain);
|
|
48
|
+
adminCache = { value, until: Date.now() + 2000 };
|
|
49
|
+
return value;
|
|
50
|
+
};
|
|
51
|
+
/** While no admin exists the one-time code is always open (and printed once per opening). */
|
|
52
|
+
async function ensureSetupGate() {
|
|
53
|
+
if ((await adminCount()) === 0 && !setup.isOpen)
|
|
54
|
+
deps.log(`\n${setupBanner(setup.open(), deps.port)}\n`);
|
|
55
|
+
}
|
|
56
|
+
const cookieFor = (id, maxAge) => sessionCookie(id, maxAge, deps.secureCookies);
|
|
57
|
+
function sessionOf(req) {
|
|
58
|
+
return sessions.get(parseCookies(req)[SESSION_COOKIE]);
|
|
59
|
+
}
|
|
60
|
+
async function serveStatic(res, path) {
|
|
61
|
+
const vendored = vendor[path];
|
|
62
|
+
let file;
|
|
63
|
+
if (vendored)
|
|
64
|
+
file = vendored;
|
|
65
|
+
else {
|
|
66
|
+
const rel = normalize(path.replace(/^\/ui\/?/, '') || 'index.html');
|
|
67
|
+
file = join(deps.uiDir, rel);
|
|
68
|
+
if (rel.startsWith('..') || !file.startsWith(deps.uiDir + sep))
|
|
69
|
+
throw new HttpError(404, 'not_found', 'Not found.');
|
|
70
|
+
}
|
|
71
|
+
let body;
|
|
72
|
+
try {
|
|
73
|
+
body = await readFile(file);
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
throw new HttpError(404, 'not_found', 'Not found.');
|
|
77
|
+
}
|
|
78
|
+
res.writeHead(200, {
|
|
79
|
+
'content-type': MIME[extname(file)] ?? 'application/octet-stream',
|
|
80
|
+
'content-length': body.length,
|
|
81
|
+
'cache-control': 'no-cache',
|
|
82
|
+
...SECURITY_HEADERS,
|
|
83
|
+
});
|
|
84
|
+
res.end(body);
|
|
85
|
+
}
|
|
86
|
+
async function handleAdmin(req, res, path) {
|
|
87
|
+
const method = req.method ?? 'GET';
|
|
88
|
+
const ip = clientIp(req, deps.trustProxy) ?? 'unknown';
|
|
89
|
+
const url = new URL(req.url ?? '/', 'http://localhost');
|
|
90
|
+
const q = url.searchParams;
|
|
91
|
+
const route = path.replace(/^\/admin\/api\//, '');
|
|
92
|
+
if (method !== 'GET' && method !== 'HEAD' && !sameOrigin(req)) {
|
|
93
|
+
throw new HttpError(403, 'cross_origin', 'Cross-origin request refused.');
|
|
94
|
+
}
|
|
95
|
+
// ---- public ----------------------------------------------------------
|
|
96
|
+
if (route === 'state' && method === 'GET') {
|
|
97
|
+
await ensureSetupGate();
|
|
98
|
+
const session = sessionOf(req);
|
|
99
|
+
return send(res, 200, {
|
|
100
|
+
version: deps.version,
|
|
101
|
+
setupRequired: (await adminCount()) === 0,
|
|
102
|
+
authenticated: Boolean(session),
|
|
103
|
+
username: session?.username ?? null,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
if (route === 'setup' && method === 'POST') {
|
|
107
|
+
const body = asRecord(await readJson(req, deps.maxBodyBytes));
|
|
108
|
+
if ((await adminCount()) > 0)
|
|
109
|
+
throw new HttpError(409, 'setup_closed', 'Setup is already complete.');
|
|
110
|
+
await ensureSetupGate();
|
|
111
|
+
if (!setupGuesses.attempt(ip)) {
|
|
112
|
+
throw new HttpError(429, 'rate_limited', `Too many attempts. Try again in ${setupGuesses.retryAfterSeconds(ip)}s.`);
|
|
113
|
+
}
|
|
114
|
+
if (!setup.check(String(body.code ?? '')))
|
|
115
|
+
throw new HttpError(403, 'bad_code', 'That setup code is not right.');
|
|
116
|
+
try {
|
|
117
|
+
const admin = await createFirstAdmin(brain, String(body.username ?? ''), String(body.password ?? ''));
|
|
118
|
+
setup.close();
|
|
119
|
+
adminCache = undefined;
|
|
120
|
+
setupGuesses.reset(ip);
|
|
121
|
+
const s = sessions.create(admin.username);
|
|
122
|
+
deps.log(`dashboard: admin "${admin.username}" created`);
|
|
123
|
+
// the first-setup lock has done its job; remove it once any racing attempts are finished with it
|
|
124
|
+
setTimeout(() => void tidySystemNodes(brain).catch(() => undefined), 3000).unref();
|
|
125
|
+
return send(res, 200, { username: admin.username }, { 'set-cookie': cookieFor(s.id, s.maxAgeSeconds) });
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
if (error instanceof AdminError) {
|
|
129
|
+
throw new HttpError(error.code === 'setup_closed' ? 409 : 400, error.code === 'setup_closed' ? 'setup_closed' : 'invalid', error.message);
|
|
130
|
+
}
|
|
131
|
+
throw error;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (route === 'login' && method === 'POST') {
|
|
135
|
+
const body = asRecord(await readJson(req, deps.maxBodyBytes));
|
|
136
|
+
const username = String(body.username ?? '').toLowerCase();
|
|
137
|
+
if (!loginByIp.attempt(ip) || !loginByUser.attempt(username)) {
|
|
138
|
+
throw new HttpError(429, 'rate_limited', `Too many attempts. Try again in ${Math.max(loginByIp.retryAfterSeconds(ip), loginByUser.retryAfterSeconds(username))}s.`);
|
|
139
|
+
}
|
|
140
|
+
const admin = await verifyAdmin(brain, username, String(body.password ?? ''));
|
|
141
|
+
if (!admin)
|
|
142
|
+
throw new HttpError(401, 'bad_credentials', 'Wrong username or password.');
|
|
143
|
+
loginByUser.reset(username);
|
|
144
|
+
const s = sessions.create(admin.username);
|
|
145
|
+
return send(res, 200, { username: admin.username }, { 'set-cookie': cookieFor(s.id, s.maxAgeSeconds) });
|
|
146
|
+
}
|
|
147
|
+
if (route === 'logout' && method === 'POST') {
|
|
148
|
+
sessions.destroy(parseCookies(req)[SESSION_COOKIE]);
|
|
149
|
+
return send(res, 200, { ok: true }, { 'set-cookie': clearedSessionCookie(deps.secureCookies) });
|
|
150
|
+
}
|
|
151
|
+
// ---- everything below needs a session --------------------------------
|
|
152
|
+
const session = sessionOf(req);
|
|
153
|
+
if (!session)
|
|
154
|
+
throw new HttpError(401, 'login_required', 'Sign in first.');
|
|
155
|
+
if (route === 'password' && method === 'POST') {
|
|
156
|
+
const body = asRecord(await readJson(req, deps.maxBodyBytes));
|
|
157
|
+
if (!loginByUser.attempt(`pw:${session.username}`))
|
|
158
|
+
throw new HttpError(429, 'rate_limited', 'Too many attempts.');
|
|
159
|
+
try {
|
|
160
|
+
const ok = await changeAdminPassword(brain, session.username, String(body.current ?? ''), String(body.next ?? ''));
|
|
161
|
+
if (!ok)
|
|
162
|
+
throw new HttpError(403, 'bad_credentials', 'The current password is wrong.');
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
if (error instanceof AdminError)
|
|
166
|
+
throw new HttpError(400, 'invalid', error.message);
|
|
167
|
+
throw error;
|
|
168
|
+
}
|
|
169
|
+
sessions.clear();
|
|
170
|
+
const s = sessions.create(session.username);
|
|
171
|
+
return send(res, 200, { ok: true }, { 'set-cookie': cookieFor(s.id, s.maxAgeSeconds) });
|
|
172
|
+
}
|
|
173
|
+
if (route === 'overview' && method === 'GET') {
|
|
174
|
+
const started = Date.now();
|
|
175
|
+
const info = await checkConnection(brain);
|
|
176
|
+
const pingMs = Date.now() - started;
|
|
177
|
+
const projects = await listProjects(brain);
|
|
178
|
+
return send(res, 200, {
|
|
179
|
+
stats: events.stats(),
|
|
180
|
+
db: {
|
|
181
|
+
projects: projects.length,
|
|
182
|
+
memories: projects.reduce((a, p) => a + p.active, 0),
|
|
183
|
+
superseded: projects.reduce((a, p) => a + p.superseded, 0),
|
|
184
|
+
tokens: await countActiveTokens(brain),
|
|
185
|
+
},
|
|
186
|
+
neo4j: { agent: info.agent, address: info.address, pingMs },
|
|
187
|
+
server: { version: deps.version },
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
if (route === 'events' && method === 'GET') {
|
|
191
|
+
const ok = q.get('ok');
|
|
192
|
+
return send(res, 200, {
|
|
193
|
+
events: events.recent({
|
|
194
|
+
tool: q.get('tool') ?? undefined,
|
|
195
|
+
project: q.get('project') ?? undefined,
|
|
196
|
+
client: q.get('client') ?? undefined,
|
|
197
|
+
source: q.get('source') ?? undefined,
|
|
198
|
+
ok: ok === null ? undefined : ok === 'true',
|
|
199
|
+
before: q.get('before') ? Number(q.get('before')) : undefined,
|
|
200
|
+
limit: q.get('limit') ? Number(q.get('limit')) : undefined,
|
|
201
|
+
}),
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
const eventMatch = /^events\/(\d+)$/.exec(route);
|
|
205
|
+
if (eventMatch && method === 'GET') {
|
|
206
|
+
const event = events.get(Number(eventMatch[1]));
|
|
207
|
+
if (!event)
|
|
208
|
+
throw new HttpError(404, 'not_found', 'That event has aged out of the buffer.');
|
|
209
|
+
return send(res, 200, event);
|
|
210
|
+
}
|
|
211
|
+
if (route === 'stream' && method === 'GET')
|
|
212
|
+
return stream(req, res);
|
|
213
|
+
if (route === 'projects' && method === 'GET')
|
|
214
|
+
return send(res, 200, { projects: await listProjects(brain) });
|
|
215
|
+
if (route === 'projects' && method === 'POST') {
|
|
216
|
+
const id = String(asRecord(await readJson(req, deps.maxBodyBytes)).id ?? '').trim();
|
|
217
|
+
if (!isValidProjectId(id))
|
|
218
|
+
throw new HttpError(400, 'invalid', 'Project id: letters, digits, ".", "_" or "-", up to 64 characters.');
|
|
219
|
+
await ensureSchema(brain);
|
|
220
|
+
return send(res, 200, { id, ...(await ensureProject(brain, id)) });
|
|
221
|
+
}
|
|
222
|
+
const projectMatch = /^projects\/([^/]+)$/.exec(route);
|
|
223
|
+
if (projectMatch && method === 'GET') {
|
|
224
|
+
const detail = await getProjectDetail(brain, decodeURIComponent(projectMatch[1]));
|
|
225
|
+
if (!detail)
|
|
226
|
+
throw new HttpError(404, 'not_found', 'No such project.');
|
|
227
|
+
return send(res, 200, detail);
|
|
228
|
+
}
|
|
229
|
+
if (route === 'memories' && method === 'GET') {
|
|
230
|
+
const project = q.get('project');
|
|
231
|
+
if (!project)
|
|
232
|
+
throw new HttpError(400, 'invalid', 'project is required.');
|
|
233
|
+
const kind = q.get('kind');
|
|
234
|
+
if (kind && !MEMORY_KINDS.includes(kind))
|
|
235
|
+
throw new HttpError(400, 'invalid', 'Unknown kind.');
|
|
236
|
+
const status = q.get('status') ?? 'active';
|
|
237
|
+
const found = await recall(brain, project, {
|
|
238
|
+
query: q.get('q') ?? undefined,
|
|
239
|
+
kind: kind ?? undefined,
|
|
240
|
+
area: q.get('area') ?? undefined,
|
|
241
|
+
limit: q.get('limit') ? Number(q.get('limit')) : 50,
|
|
242
|
+
includeSuperseded: status !== 'active',
|
|
243
|
+
});
|
|
244
|
+
return send(res, 200, { memories: status === 'superseded' ? found.filter(m => m.status === 'superseded') : found });
|
|
245
|
+
}
|
|
246
|
+
const memoryMatch = /^memories\/([^/]+)$/.exec(route);
|
|
247
|
+
if (memoryMatch && method === 'GET') {
|
|
248
|
+
const project = q.get('project');
|
|
249
|
+
if (!project)
|
|
250
|
+
throw new HttpError(400, 'invalid', 'project is required.');
|
|
251
|
+
const memory = await getMemory(brain, project, decodeURIComponent(memoryMatch[1]));
|
|
252
|
+
if (!memory)
|
|
253
|
+
throw new HttpError(404, 'not_found', 'No such memory.');
|
|
254
|
+
return send(res, 200, { memory, chain: await getMemoryChain(brain, project, memory.id) });
|
|
255
|
+
}
|
|
256
|
+
if (route === 'graph' && method === 'GET') {
|
|
257
|
+
return send(res, 200, await getGraph(brain, { projectId: q.get('project') ?? undefined, limit: q.get('limit') ? Number(q.get('limit')) : undefined }));
|
|
258
|
+
}
|
|
259
|
+
if (route === 'cypher' && method === 'POST') {
|
|
260
|
+
const body = asRecord(await readJson(req, deps.maxBodyBytes));
|
|
261
|
+
try {
|
|
262
|
+
return send(res, 200, await runReadOnlyCypher(brain, String(body.query ?? '')));
|
|
263
|
+
}
|
|
264
|
+
catch (error) {
|
|
265
|
+
if (error instanceof QueryRejectedError)
|
|
266
|
+
throw new HttpError(400, 'query_rejected', error.message);
|
|
267
|
+
const e = error;
|
|
268
|
+
if (e.code?.startsWith('Neo.ClientError'))
|
|
269
|
+
throw new HttpError(400, 'query_failed', e.message ?? 'Query failed.');
|
|
270
|
+
throw error;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
if (route === 'tokens' && method === 'GET')
|
|
274
|
+
return send(res, 200, { tokens: await listApiTokens(brain) });
|
|
275
|
+
if (route === 'tokens' && method === 'POST') {
|
|
276
|
+
const body = asRecord(await readJson(req, deps.maxBodyBytes));
|
|
277
|
+
try {
|
|
278
|
+
const created = await createApiToken(brain, String(body.name ?? ''), session.username);
|
|
279
|
+
auth.invalidate();
|
|
280
|
+
return send(res, 200, created);
|
|
281
|
+
}
|
|
282
|
+
catch (error) {
|
|
283
|
+
if (error instanceof AdminError)
|
|
284
|
+
throw new HttpError(400, 'invalid', error.message);
|
|
285
|
+
throw error;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
const tokenMatch = /^tokens\/([0-9a-f]+)$/.exec(route);
|
|
289
|
+
if (tokenMatch && method === 'DELETE') {
|
|
290
|
+
if (!(await revokeApiToken(brain, tokenMatch[1])))
|
|
291
|
+
throw new HttpError(404, 'not_found', 'No such active token.');
|
|
292
|
+
auth.invalidate();
|
|
293
|
+
return send(res, 200, { ok: true });
|
|
294
|
+
}
|
|
295
|
+
if (route === 'system' && method === 'GET') {
|
|
296
|
+
const config = loadConfig();
|
|
297
|
+
const started = Date.now();
|
|
298
|
+
const info = await checkConnection(brain);
|
|
299
|
+
const mem = process.memoryUsage();
|
|
300
|
+
return send(res, 200, {
|
|
301
|
+
server: {
|
|
302
|
+
version: deps.version,
|
|
303
|
+
node: process.version,
|
|
304
|
+
platform: `${process.platform}/${process.arch}`,
|
|
305
|
+
pid: process.pid,
|
|
306
|
+
uptimeSeconds: Math.round(process.uptime()),
|
|
307
|
+
rssMb: Math.round(mem.rss / 1048576),
|
|
308
|
+
heapMb: Math.round(mem.heapUsed / 1048576),
|
|
309
|
+
},
|
|
310
|
+
neo4j: {
|
|
311
|
+
agent: info.agent,
|
|
312
|
+
address: info.address,
|
|
313
|
+
uri: config.uri,
|
|
314
|
+
user: config.user,
|
|
315
|
+
defaultCredentials: hasDefaultCredentials(config),
|
|
316
|
+
schemaVersion: await schemaVersion(brain),
|
|
317
|
+
pingMs: Date.now() - started,
|
|
318
|
+
},
|
|
319
|
+
graph: await getSystemInfo(brain),
|
|
320
|
+
auth: { ...deps.authMode, managedTokens: await countActiveTokens(brain) },
|
|
321
|
+
capture: { mode: deps.capture, capacity: events.capacity },
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
throw new HttpError(404, 'not_found', `No such endpoint: ${method} ${path}`);
|
|
325
|
+
}
|
|
326
|
+
function stream(req, res) {
|
|
327
|
+
res.writeHead(200, {
|
|
328
|
+
'content-type': 'text/event-stream',
|
|
329
|
+
'cache-control': 'no-cache, no-transform',
|
|
330
|
+
connection: 'keep-alive',
|
|
331
|
+
'x-accel-buffering': 'no',
|
|
332
|
+
});
|
|
333
|
+
const emit = (name, data) => res.write(`event: ${name}\ndata: ${JSON.stringify(data)}\n\n`);
|
|
334
|
+
res.write('retry: 3000\n\n');
|
|
335
|
+
events.viewers++;
|
|
336
|
+
emit('stats', events.stats());
|
|
337
|
+
const unsubscribe = events.subscribe(e => emit('event', e));
|
|
338
|
+
const stats = setInterval(() => emit('stats', events.stats()), 2000);
|
|
339
|
+
const ping = setInterval(() => res.write(': ping\n\n'), 15_000);
|
|
340
|
+
const cleanup = () => {
|
|
341
|
+
clearInterval(stats);
|
|
342
|
+
clearInterval(ping);
|
|
343
|
+
unsubscribe();
|
|
344
|
+
events.viewers = Math.max(0, events.viewers - 1);
|
|
345
|
+
};
|
|
346
|
+
req.on('close', cleanup);
|
|
347
|
+
}
|
|
348
|
+
return async (req, res, path) => {
|
|
349
|
+
if (path === '/' || path === '/ui') {
|
|
350
|
+
res.writeHead(302, { location: '/ui/' });
|
|
351
|
+
res.end();
|
|
352
|
+
return true;
|
|
353
|
+
}
|
|
354
|
+
if (path.startsWith('/ui/')) {
|
|
355
|
+
await serveStatic(res, path);
|
|
356
|
+
return true;
|
|
357
|
+
}
|
|
358
|
+
if (path.startsWith('/admin/api/')) {
|
|
359
|
+
await handleAdmin(req, res, path);
|
|
360
|
+
return true;
|
|
361
|
+
}
|
|
362
|
+
return false;
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
//# sourceMappingURL=dashboard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../src/dashboard.ts"],"names":[],"mappings":"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"}
|
package/dist/events.d.ts
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory flight recorder for everything agents send the server. Nothing
|
|
3
|
+
* here is persisted: it holds the last `capacity` requests plus rolling
|
|
4
|
+
* aggregates, which is what the dashboard's live views are drawn from.
|
|
5
|
+
*/
|
|
6
|
+
export interface EventClient {
|
|
7
|
+
/** Name of the token that was used (or `env` / `open`). */
|
|
8
|
+
token?: string;
|
|
9
|
+
ip?: string;
|
|
10
|
+
/** First word of the User-Agent, e.g. `claude-code`. */
|
|
11
|
+
agent?: string;
|
|
12
|
+
userAgent?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface BrainEvent {
|
|
15
|
+
id: number;
|
|
16
|
+
ts: string;
|
|
17
|
+
/** `mcp` and `api` are authorized calls; `auth` is a rejected one. */
|
|
18
|
+
source: 'mcp' | 'api' | 'auth';
|
|
19
|
+
/** JSON-RPC method (`tools/call`) or `GET /api/project`. */
|
|
20
|
+
method: string;
|
|
21
|
+
tool?: string;
|
|
22
|
+
/** What the client said its project was: an id, or `remote:<host/owner/repo>`. */
|
|
23
|
+
project?: string;
|
|
24
|
+
client: EventClient;
|
|
25
|
+
durationMs: number;
|
|
26
|
+
ok: boolean;
|
|
27
|
+
httpStatus: number;
|
|
28
|
+
bytesIn: number;
|
|
29
|
+
bytesOut: number;
|
|
30
|
+
/** Estimated tokens in the text sent back to the agent. */
|
|
31
|
+
tokensOut: number;
|
|
32
|
+
/** Short ids (#abc12345) mentioned in the response: the memories this call touched. */
|
|
33
|
+
memoryRefs: string[];
|
|
34
|
+
error?: string;
|
|
35
|
+
request?: unknown;
|
|
36
|
+
response?: unknown;
|
|
37
|
+
}
|
|
38
|
+
export type EventSummary = Omit<BrainEvent, 'request' | 'response'> & {
|
|
39
|
+
hasPayload: boolean;
|
|
40
|
+
};
|
|
41
|
+
export interface ToolStat {
|
|
42
|
+
name: string;
|
|
43
|
+
count: number;
|
|
44
|
+
errors: number;
|
|
45
|
+
p50Ms: number;
|
|
46
|
+
p95Ms: number;
|
|
47
|
+
avgTokensOut: number;
|
|
48
|
+
}
|
|
49
|
+
export interface ClientStat {
|
|
50
|
+
key: string;
|
|
51
|
+
token?: string;
|
|
52
|
+
ip?: string;
|
|
53
|
+
agent?: string;
|
|
54
|
+
firstSeen: string;
|
|
55
|
+
lastSeen: string;
|
|
56
|
+
requests: number;
|
|
57
|
+
errors: number;
|
|
58
|
+
lastTool?: string;
|
|
59
|
+
lastProject?: string;
|
|
60
|
+
active: boolean;
|
|
61
|
+
}
|
|
62
|
+
export interface ProjectStat {
|
|
63
|
+
project: string;
|
|
64
|
+
requests: number;
|
|
65
|
+
lastSeen: string;
|
|
66
|
+
}
|
|
67
|
+
export interface MinuteBucket {
|
|
68
|
+
t: string;
|
|
69
|
+
requests: number;
|
|
70
|
+
errors: number;
|
|
71
|
+
tokensOut: number;
|
|
72
|
+
}
|
|
73
|
+
export interface Stats {
|
|
74
|
+
startedAt: string;
|
|
75
|
+
uptimeSeconds: number;
|
|
76
|
+
totals: {
|
|
77
|
+
requests: number;
|
|
78
|
+
errors: number;
|
|
79
|
+
authFailures: number;
|
|
80
|
+
tokensOut: number;
|
|
81
|
+
bytesIn: number;
|
|
82
|
+
bytesOut: number;
|
|
83
|
+
};
|
|
84
|
+
perMinute: MinuteBucket[];
|
|
85
|
+
tools: ToolStat[];
|
|
86
|
+
clients: ClientStat[];
|
|
87
|
+
projects: ProjectStat[];
|
|
88
|
+
viewers: number;
|
|
89
|
+
}
|
|
90
|
+
export interface EventFilter {
|
|
91
|
+
tool?: string;
|
|
92
|
+
project?: string;
|
|
93
|
+
client?: string;
|
|
94
|
+
ok?: boolean;
|
|
95
|
+
source?: string;
|
|
96
|
+
/** Only events with an id below this (for paging back). */
|
|
97
|
+
before?: number;
|
|
98
|
+
limit?: number;
|
|
99
|
+
}
|
|
100
|
+
export declare class EventLog {
|
|
101
|
+
readonly capacity: number;
|
|
102
|
+
private readonly startedAt;
|
|
103
|
+
private readonly clock;
|
|
104
|
+
private ring;
|
|
105
|
+
private nextId;
|
|
106
|
+
private readonly subscribers;
|
|
107
|
+
private readonly clients;
|
|
108
|
+
private readonly projects;
|
|
109
|
+
private readonly minutes;
|
|
110
|
+
private readonly totals;
|
|
111
|
+
viewers: number;
|
|
112
|
+
constructor(capacity?: number, startedAt?: Date, clock?: () => number);
|
|
113
|
+
add(input: Omit<BrainEvent, 'id' | 'ts'>): BrainEvent;
|
|
114
|
+
private track;
|
|
115
|
+
subscribe(fn: (event: EventSummary) => void): () => void;
|
|
116
|
+
get(id: number): BrainEvent | undefined;
|
|
117
|
+
recent(filter?: EventFilter): EventSummary[];
|
|
118
|
+
stats(): Stats;
|
|
119
|
+
}
|
|
120
|
+
export declare function summarize(e: BrainEvent): EventSummary;
|