brakit 0.10.0 → 0.10.2
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/api.d.ts +47 -19
- package/dist/api.js +127 -31
- package/dist/bin/brakit.js +425 -60
- package/dist/dashboard-client.global.js +524 -465
- package/dist/dashboard.html +623 -513
- package/dist/mcp/server.js +175 -22
- package/dist/runtime/index.js +1758 -1458
- package/package.json +1 -1
package/dist/mcp/server.js
CHANGED
|
@@ -31,14 +31,10 @@ var DASHBOARD_API_GRAPH = `${DASHBOARD_PREFIX}/api/graph`;
|
|
|
31
31
|
var VALID_TABS_TUPLE = [
|
|
32
32
|
"overview",
|
|
33
33
|
"actions",
|
|
34
|
-
"
|
|
35
|
-
"fetches",
|
|
36
|
-
"queries",
|
|
37
|
-
"errors",
|
|
38
|
-
"logs",
|
|
34
|
+
"insights",
|
|
39
35
|
"performance",
|
|
40
|
-
"
|
|
41
|
-
"
|
|
36
|
+
"graph",
|
|
37
|
+
"explorer"
|
|
42
38
|
];
|
|
43
39
|
var VALID_TABS = new Set(VALID_TABS_TUPLE);
|
|
44
40
|
|
|
@@ -53,7 +49,7 @@ var MAX_DISCOVERY_DEPTH = 5;
|
|
|
53
49
|
var MAX_TIMELINE_EVENTS = 20;
|
|
54
50
|
var MAX_RESOLVED_DISPLAY = 5;
|
|
55
51
|
var ENRICHMENT_SEVERITY_FILTER = ["critical", "warning"];
|
|
56
|
-
var MCP_SERVER_VERSION = "0.10.
|
|
52
|
+
var MCP_SERVER_VERSION = "0.10.2";
|
|
57
53
|
var RECOVERY_WINDOW_MS = 5 * 60 * 1e3;
|
|
58
54
|
var PORT_MIN = 1;
|
|
59
55
|
var PORT_MAX = 65535;
|
|
@@ -157,6 +153,83 @@ var VALID_ISSUE_STATES = /* @__PURE__ */ new Set(["open", "fixing", "resolved",
|
|
|
157
153
|
var VALID_AI_FIX_STATUSES = /* @__PURE__ */ new Set(["fixed", "wont_fix"]);
|
|
158
154
|
var VALID_SECURITY_SEVERITIES = /* @__PURE__ */ new Set(["critical", "warning"]);
|
|
159
155
|
|
|
156
|
+
// src/constants/detection.ts
|
|
157
|
+
var KNOWN_DEPENDENCY_NAMES = [
|
|
158
|
+
// -- Frameworks (meta) --
|
|
159
|
+
"next",
|
|
160
|
+
"@remix-run/dev",
|
|
161
|
+
"nuxt",
|
|
162
|
+
"astro",
|
|
163
|
+
// -- Frameworks (backend) --
|
|
164
|
+
"@nestjs/core",
|
|
165
|
+
"@adonisjs/core",
|
|
166
|
+
"sails",
|
|
167
|
+
"express",
|
|
168
|
+
"fastify",
|
|
169
|
+
"hono",
|
|
170
|
+
"koa",
|
|
171
|
+
"@hapi/hapi",
|
|
172
|
+
"elysia",
|
|
173
|
+
"h3",
|
|
174
|
+
"nitro",
|
|
175
|
+
"@trpc/server",
|
|
176
|
+
// -- Bundlers --
|
|
177
|
+
"vite",
|
|
178
|
+
// -- ORM / query builders --
|
|
179
|
+
"prisma",
|
|
180
|
+
"@prisma/client",
|
|
181
|
+
"drizzle-orm",
|
|
182
|
+
"typeorm",
|
|
183
|
+
"sequelize",
|
|
184
|
+
"mongoose",
|
|
185
|
+
"kysely",
|
|
186
|
+
"knex",
|
|
187
|
+
"@mikro-orm/core",
|
|
188
|
+
"objection",
|
|
189
|
+
// -- DB drivers --
|
|
190
|
+
"pg",
|
|
191
|
+
"mysql2",
|
|
192
|
+
"mongodb",
|
|
193
|
+
"better-sqlite3",
|
|
194
|
+
"@libsql/client",
|
|
195
|
+
"@planetscale/database",
|
|
196
|
+
"ioredis",
|
|
197
|
+
"redis",
|
|
198
|
+
// -- Auth --
|
|
199
|
+
"lucia",
|
|
200
|
+
"next-auth",
|
|
201
|
+
"@auth/core",
|
|
202
|
+
"passport",
|
|
203
|
+
// -- Queues / messaging --
|
|
204
|
+
"bullmq",
|
|
205
|
+
"amqplib",
|
|
206
|
+
"kafkajs",
|
|
207
|
+
// -- Validation --
|
|
208
|
+
"zod",
|
|
209
|
+
"joi",
|
|
210
|
+
"yup",
|
|
211
|
+
"arktype",
|
|
212
|
+
"valibot",
|
|
213
|
+
// -- HTTP clients --
|
|
214
|
+
"axios",
|
|
215
|
+
"got",
|
|
216
|
+
"ky",
|
|
217
|
+
"undici",
|
|
218
|
+
// -- Realtime --
|
|
219
|
+
"socket.io",
|
|
220
|
+
"ws",
|
|
221
|
+
// -- CSS / styling --
|
|
222
|
+
"tailwindcss",
|
|
223
|
+
// -- Testing --
|
|
224
|
+
"vitest",
|
|
225
|
+
"jest",
|
|
226
|
+
"mocha",
|
|
227
|
+
// -- Runtime indicators --
|
|
228
|
+
"bun-types",
|
|
229
|
+
"@types/bun"
|
|
230
|
+
];
|
|
231
|
+
var KNOWN_DEPENDENCY_SET = new Set(KNOWN_DEPENDENCY_NAMES);
|
|
232
|
+
|
|
160
233
|
// src/utils/log.ts
|
|
161
234
|
var PREFIX = "[brakit]";
|
|
162
235
|
function brakitDebug(message) {
|
|
@@ -263,10 +336,27 @@ async function enrichFindings(client) {
|
|
|
263
336
|
if (reqData.requests.length > 0) {
|
|
264
337
|
const req = reqData.requests[0];
|
|
265
338
|
if (req.id) {
|
|
266
|
-
const activity = await
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
339
|
+
const [activity, queries, fetches] = await Promise.all([
|
|
340
|
+
client.getActivity(req.id),
|
|
341
|
+
client.getQueries(req.id),
|
|
342
|
+
client.getFetches(req.id)
|
|
343
|
+
]);
|
|
344
|
+
const lines = [`Request took ${req.durationMs}ms.`];
|
|
345
|
+
if (queries.entries.length > 0) {
|
|
346
|
+
lines.push(`DB Queries (${queries.entries.length}):`);
|
|
347
|
+
for (const q of queries.entries.slice(0, 5)) {
|
|
348
|
+
const sql = q.sql ?? `${q.operation ?? ""} ${q.table ?? q.model ?? ""}`;
|
|
349
|
+
lines.push(` [${q.durationMs}ms] ${sql}`);
|
|
350
|
+
}
|
|
351
|
+
if (queries.entries.length > 5) lines.push(` ... and ${queries.entries.length - 5} more`);
|
|
352
|
+
}
|
|
353
|
+
if (fetches.entries.length > 0) {
|
|
354
|
+
lines.push(`Fetches (${fetches.entries.length}):`);
|
|
355
|
+
for (const f of fetches.entries.slice(0, 3)) {
|
|
356
|
+
lines.push(` [${f.durationMs}ms] ${f.method} ${f.url} \u2192 ${f.statusCode}`);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
return lines.join("\n");
|
|
270
360
|
}
|
|
271
361
|
}
|
|
272
362
|
} catch {
|
|
@@ -382,6 +472,9 @@ var getFindings = {
|
|
|
382
472
|
return { content: [{ type: "text", text: `Invalid state "${state}". Use: open, fixing, resolved, stale, regressed.` }], isError: true };
|
|
383
473
|
}
|
|
384
474
|
let findings = await enrichFindings(client);
|
|
475
|
+
if (!state) {
|
|
476
|
+
findings = findings.filter((f) => f.aiStatus !== "wont_fix");
|
|
477
|
+
}
|
|
385
478
|
if (severity) {
|
|
386
479
|
findings = findings.filter((f) => f.severity === severity);
|
|
387
480
|
}
|
|
@@ -393,21 +486,25 @@ var getFindings = {
|
|
|
393
486
|
if (findings.length === 0) {
|
|
394
487
|
return { content: [{ type: "text", text: "No findings detected. The application looks healthy." }] };
|
|
395
488
|
}
|
|
396
|
-
const lines = [
|
|
397
|
-
`
|
|
489
|
+
const lines = [
|
|
490
|
+
`Found ${findings.length} issue(s). Full context included below \u2014 no need to call get_request_detail separately.`,
|
|
491
|
+
`After fixing, call report_fixes ONCE with all results.
|
|
492
|
+
`
|
|
493
|
+
];
|
|
398
494
|
for (const f of findings) {
|
|
399
495
|
lines.push(`[${f.severity.toUpperCase()}] ${f.title}`);
|
|
400
496
|
lines.push(` ID: ${f.findingId}`);
|
|
401
497
|
lines.push(` Endpoint: ${f.endpoint}`);
|
|
402
498
|
lines.push(` Issue: ${f.description}`);
|
|
403
|
-
if (f.context)
|
|
499
|
+
if (f.context) {
|
|
500
|
+
for (const ctxLine of f.context.split("\n")) {
|
|
501
|
+
lines.push(` ${ctxLine}`);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
404
504
|
lines.push(` Fix: ${f.hint}`);
|
|
405
505
|
if (f.aiStatus === "fixed") {
|
|
406
506
|
lines.push(` AI Status: fixed (awaiting verification)`);
|
|
407
507
|
if (f.aiNotes) lines.push(` AI Notes: ${f.aiNotes}`);
|
|
408
|
-
} else if (f.aiStatus === "wont_fix") {
|
|
409
|
-
lines.push(` AI Status: won't fix`);
|
|
410
|
-
if (f.aiNotes) lines.push(` AI Notes: ${f.aiNotes}`);
|
|
411
508
|
}
|
|
412
509
|
lines.push("");
|
|
413
510
|
}
|
|
@@ -752,9 +849,65 @@ var reportFix = {
|
|
|
752
849
|
}
|
|
753
850
|
};
|
|
754
851
|
|
|
852
|
+
// src/mcp/tools/report-fixes.ts
|
|
853
|
+
var reportFixes = {
|
|
854
|
+
name: "report_fixes",
|
|
855
|
+
description: "Report results for multiple findings in a single call. Use this instead of calling report_fix repeatedly \u2014 it's faster and requires only one confirmation. Pass a JSON array string where each item has finding_id, status ('fixed' or 'wont_fix'), and summary.",
|
|
856
|
+
inputSchema: {
|
|
857
|
+
type: "object",
|
|
858
|
+
properties: {
|
|
859
|
+
fixes: {
|
|
860
|
+
type: "string",
|
|
861
|
+
description: 'JSON array of fix reports. Example: [{"finding_id":"abc123","status":"fixed","summary":"Added input validation"}]'
|
|
862
|
+
}
|
|
863
|
+
},
|
|
864
|
+
required: ["fixes"]
|
|
865
|
+
},
|
|
866
|
+
async handler(client, args) {
|
|
867
|
+
let fixes;
|
|
868
|
+
try {
|
|
869
|
+
const raw = typeof args.fixes === "string" ? JSON.parse(args.fixes) : args.fixes;
|
|
870
|
+
if (!Array.isArray(raw) || raw.length === 0) {
|
|
871
|
+
return { content: [{ type: "text", text: "fixes must be a non-empty JSON array." }], isError: true };
|
|
872
|
+
}
|
|
873
|
+
fixes = raw.filter(
|
|
874
|
+
(item) => typeof item === "object" && item !== null && typeof item.finding_id === "string" && typeof item.status === "string" && typeof item.summary === "string"
|
|
875
|
+
);
|
|
876
|
+
if (fixes.length === 0) {
|
|
877
|
+
return { content: [{ type: "text", text: "No valid fix entries found. Each entry needs finding_id, status, and summary." }], isError: true };
|
|
878
|
+
}
|
|
879
|
+
} catch {
|
|
880
|
+
return { content: [{ type: "text", text: "fixes must be valid JSON." }], isError: true };
|
|
881
|
+
}
|
|
882
|
+
const results = [];
|
|
883
|
+
let errors = 0;
|
|
884
|
+
for (const fix of fixes) {
|
|
885
|
+
if (!fix.finding_id || !isValidAiFixStatus(fix.status) || !fix.summary) {
|
|
886
|
+
results.push(`\u2717 Invalid entry: ${fix.finding_id || "missing ID"}`);
|
|
887
|
+
errors++;
|
|
888
|
+
continue;
|
|
889
|
+
}
|
|
890
|
+
const ok = await client.reportFix(fix.finding_id, fix.status, fix.summary);
|
|
891
|
+
if (ok) {
|
|
892
|
+
const label = fix.status === "fixed" ? "fixed" : "won't fix";
|
|
893
|
+
results.push(`\u2713 ${fix.finding_id} \u2192 ${label}`);
|
|
894
|
+
} else {
|
|
895
|
+
results.push(`\u2717 ${fix.finding_id} \u2192 not found`);
|
|
896
|
+
errors++;
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
const errorSuffix = errors > 0 ? ` (${errors} error${errors !== 1 ? "s" : ""})` : "";
|
|
900
|
+
const summary = `Processed ${fixes.length} finding(s)${errorSuffix}. Dashboard updated.`;
|
|
901
|
+
return {
|
|
902
|
+
content: [{ type: "text", text: summary + "\n\n" + results.join("\n") }],
|
|
903
|
+
...errors > 0 ? { isError: true } : {}
|
|
904
|
+
};
|
|
905
|
+
}
|
|
906
|
+
};
|
|
907
|
+
|
|
755
908
|
// src/mcp/tools/index.ts
|
|
756
909
|
var TOOL_MAP = new Map(
|
|
757
|
-
[getFindings, getEndpoints, getRequestDetail, verifyFix, getReport, clearFindings, reportFix].map((t) => [t.name, t])
|
|
910
|
+
[getFindings, getEndpoints, getRequestDetail, verifyFix, getReport, clearFindings, reportFix, reportFixes].map((t) => [t.name, t])
|
|
758
911
|
);
|
|
759
912
|
function getToolDefinitions() {
|
|
760
913
|
return [...TOOL_MAP.values()].map((t) => ({
|
|
@@ -775,7 +928,7 @@ function handleToolCall(client, name, args) {
|
|
|
775
928
|
}
|
|
776
929
|
|
|
777
930
|
// src/mcp/prompts.ts
|
|
778
|
-
var SERVER_INSTRUCTIONS = "Brakit is a dev tool that monitors your running app for security vulnerabilities, performance issues, and query problems. IMPORTANT: After fixing
|
|
931
|
+
var SERVER_INSTRUCTIONS = "Brakit is a dev tool that monitors your running app for security vulnerabilities, performance issues, and query problems. IMPORTANT: After fixing brakit findings, call report_fixes (batch) with all results at once instead of calling report_fix individually. This is faster and requires only one confirmation.";
|
|
779
932
|
var PROMPTS = [
|
|
780
933
|
{
|
|
781
934
|
name: "check-app",
|
|
@@ -787,8 +940,8 @@ var PROMPTS = [
|
|
|
787
940
|
}
|
|
788
941
|
];
|
|
789
942
|
var PROMPT_MESSAGES = {
|
|
790
|
-
"check-app": "Check my running app for security and performance issues using brakit. First get all findings, then get the endpoint summary. For any critical or warning findings, get the request detail to understand the root cause. Give me a clear report of what's wrong and offer to fix each issue. After fixing
|
|
791
|
-
"fix-findings": "Get all open brakit findings. For each finding:\n1. Get the request detail to understand the exact issue\n2. Find the source code responsible and fix it\n3.
|
|
943
|
+
"check-app": "Check my running app for security and performance issues using brakit. First get all findings, then get the endpoint summary. For any critical or warning findings, get the request detail to understand the root cause. Give me a clear report of what's wrong and offer to fix each issue. After fixing issues, call report_fixes once with all results.",
|
|
944
|
+
"fix-findings": "Get all open brakit findings. For each finding:\n1. Get the request detail to understand the exact issue\n2. Find the source code responsible and fix it\n3. Track the finding_id, status ('fixed' or 'wont_fix'), and a brief summary\n\nAfter processing ALL findings, call report_fixes ONCE with the full array of results. Do not call report_fix individually for each finding.\n\nAfter reporting, ask me to re-trigger the endpoints so brakit can verify the fixes."
|
|
792
945
|
};
|
|
793
946
|
|
|
794
947
|
// src/mcp/server.ts
|