brakit 0.8.6 → 0.9.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/README.md +22 -4
- package/dist/api.d.ts +87 -48
- package/dist/api.js +820 -788
- package/dist/bin/brakit.js +327 -419
- package/dist/dashboard-client.global.js +901 -0
- package/dist/dashboard.html +1215 -2215
- package/dist/mcp/server.js +7 -15
- package/dist/runtime/index.js +3364 -5700
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
<p align="center">
|
|
4
4
|
<b>AI writes your code. Brakit watches what it does.</b> <br />
|
|
5
|
-
Every request, query, and
|
|
5
|
+
Every request, query, and API call mapped to the action that triggered it. See your entire backend at a glance. <br />
|
|
6
6
|
<b>Open source · Local first · Zero config · AI-native via MCP</b>
|
|
7
7
|
</p>
|
|
8
8
|
|
|
@@ -32,6 +32,12 @@
|
|
|
32
32
|
|
|
33
33
|
---
|
|
34
34
|
|
|
35
|
+
<p align="center">
|
|
36
|
+
<img width="700" src="docs/images/actions.png" alt="Brakit Actions — endpoints grouped by user action" />
|
|
37
|
+
<br />
|
|
38
|
+
<sub>Every endpoint grouped by the action that triggered it — see "Sign Up" and "Load Dashboard", not 47 raw requests</sub>
|
|
39
|
+
</p>
|
|
40
|
+
|
|
35
41
|
<p align="center">
|
|
36
42
|
<img width="700" src="docs/images/dashboard.png" alt="Brakit Dashboard — issues surfaced automatically" />
|
|
37
43
|
<br />
|
|
@@ -46,19 +52,31 @@
|
|
|
46
52
|
|
|
47
53
|
## Quick Start
|
|
48
54
|
|
|
55
|
+
### Node.js
|
|
56
|
+
|
|
49
57
|
```bash
|
|
50
58
|
npx brakit install
|
|
59
|
+
npm run dev
|
|
51
60
|
```
|
|
52
61
|
|
|
53
|
-
|
|
62
|
+
### Python (FastAPI / Flask)
|
|
54
63
|
|
|
55
64
|
```bash
|
|
56
|
-
|
|
65
|
+
pip install brakit
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
import brakit # must be before framework import
|
|
70
|
+
from fastapi import FastAPI
|
|
71
|
+
|
|
72
|
+
app = FastAPI()
|
|
57
73
|
```
|
|
58
74
|
|
|
59
75
|
Dashboard at `http://localhost:<port>/__brakit`. Issues in the terminal.
|
|
60
76
|
|
|
61
|
-
> **
|
|
77
|
+
> **Full setup guide:** [brakit.ai/docs/introduction](https://brakit.ai/docs/introduction)
|
|
78
|
+
|
|
79
|
+
> **Requirements:** Node.js >= 18. Python SDK requires Python >= 3.9.
|
|
62
80
|
|
|
63
81
|
---
|
|
64
82
|
|
package/dist/api.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ interface TracedRequest {
|
|
|
16
16
|
durationMs: number;
|
|
17
17
|
responseSize: number;
|
|
18
18
|
isStatic: boolean;
|
|
19
|
+
isHealthCheck: boolean;
|
|
19
20
|
}
|
|
20
21
|
type RequestListener = (req: TracedRequest) => void;
|
|
21
22
|
|
|
@@ -35,7 +36,7 @@ interface BrakitConfig {
|
|
|
35
36
|
customCommand?: string;
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
type RequestCategory = "auth-handshake" | "auth-check" | "middleware" | "server-action" | "api-call" | "data-fetch" | "page-load" | "navigation" | "polling" | "static" | "unknown";
|
|
39
|
+
type RequestCategory = "auth-handshake" | "auth-check" | "health-check" | "middleware" | "server-action" | "api-call" | "data-fetch" | "page-load" | "navigation" | "polling" | "static" | "unknown";
|
|
39
40
|
interface LabeledRequest extends TracedRequest {
|
|
40
41
|
label: string;
|
|
41
42
|
category: RequestCategory;
|
|
@@ -63,6 +64,7 @@ interface TelemetryEntry {
|
|
|
63
64
|
timestamp: number;
|
|
64
65
|
}
|
|
65
66
|
interface TracedFetch extends TelemetryEntry {
|
|
67
|
+
fetchId?: string;
|
|
66
68
|
url: string;
|
|
67
69
|
method: string;
|
|
68
70
|
statusCode: number;
|
|
@@ -75,11 +77,11 @@ interface TracedLog extends TelemetryEntry {
|
|
|
75
77
|
interface TracedError extends TelemetryEntry {
|
|
76
78
|
name: string;
|
|
77
79
|
message: string;
|
|
78
|
-
stack
|
|
80
|
+
stack?: string;
|
|
79
81
|
}
|
|
80
82
|
type NormalizedOp = "SELECT" | "INSERT" | "UPDATE" | "DELETE" | "OTHER";
|
|
81
83
|
interface TracedQuery extends TelemetryEntry {
|
|
82
|
-
driver: "pg" | "mysql2" | "prisma" | "sdk";
|
|
84
|
+
driver: "pg" | "mysql2" | "prisma" | "asyncpg" | "sqlalchemy" | "sdk";
|
|
83
85
|
sql?: string;
|
|
84
86
|
model?: string;
|
|
85
87
|
operation?: string;
|
|
@@ -88,6 +90,7 @@ interface TracedQuery extends TelemetryEntry {
|
|
|
88
90
|
normalizedOp?: NormalizedOp;
|
|
89
91
|
table?: string;
|
|
90
92
|
source?: string;
|
|
93
|
+
parentFetchId?: string;
|
|
91
94
|
}
|
|
92
95
|
type TelemetryEvent = {
|
|
93
96
|
type: "fetch";
|
|
@@ -119,6 +122,10 @@ interface EndpointMetrics {
|
|
|
119
122
|
sessions: SessionMetric[];
|
|
120
123
|
dataPoints?: LiveRequestPoint[];
|
|
121
124
|
}
|
|
125
|
+
interface MetricsData {
|
|
126
|
+
version: 1;
|
|
127
|
+
endpoints: EndpointMetrics[];
|
|
128
|
+
}
|
|
122
129
|
interface LiveRequestPoint {
|
|
123
130
|
timestamp: number;
|
|
124
131
|
durationMs: number;
|
|
@@ -129,6 +136,7 @@ interface LiveRequestPoint {
|
|
|
129
136
|
}
|
|
130
137
|
interface LiveEndpointSummary {
|
|
131
138
|
p95Ms: number;
|
|
139
|
+
medianMs: number;
|
|
132
140
|
errorRate: number;
|
|
133
141
|
avgQueryCount: number;
|
|
134
142
|
totalRequests: number;
|
|
@@ -140,6 +148,8 @@ interface LiveEndpointData {
|
|
|
140
148
|
endpoint: string;
|
|
141
149
|
requests: LiveRequestPoint[];
|
|
142
150
|
summary: LiveEndpointSummary;
|
|
151
|
+
sessions?: SessionMetric[];
|
|
152
|
+
baselineP95Ms: number | null;
|
|
143
153
|
}
|
|
144
154
|
interface RequestMetrics {
|
|
145
155
|
queryCount: number;
|
|
@@ -156,6 +166,7 @@ interface SecurityFinding {
|
|
|
156
166
|
title: string;
|
|
157
167
|
desc: string;
|
|
158
168
|
hint: string;
|
|
169
|
+
detail?: string;
|
|
159
170
|
endpoint: string;
|
|
160
171
|
count: number;
|
|
161
172
|
}
|
|
@@ -380,74 +391,102 @@ interface CaptureInput {
|
|
|
380
391
|
endTime?: number;
|
|
381
392
|
config: Pick<BrakitConfig, "maxBodyCapture">;
|
|
382
393
|
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
394
|
+
declare class RequestStore {
|
|
395
|
+
private maxEntries;
|
|
396
|
+
private requests;
|
|
397
|
+
private listeners;
|
|
398
|
+
constructor(maxEntries?: number);
|
|
399
|
+
capture(input: CaptureInput): TracedRequest;
|
|
400
|
+
add(entry: TracedRequest): void;
|
|
401
|
+
getAll(): readonly TracedRequest[];
|
|
402
|
+
clear(): void;
|
|
403
|
+
onRequest(fn: RequestListener): void;
|
|
404
|
+
offRequest(fn: RequestListener): void;
|
|
387
405
|
}
|
|
388
|
-
|
|
406
|
+
|
|
407
|
+
type TelemetryListener<T> = (entry: T) => void;
|
|
408
|
+
/** Read-only view of a TelemetryStore — used by API handlers that only query data. */
|
|
409
|
+
interface ReadonlyTelemetryStore {
|
|
410
|
+
getAll(): readonly TelemetryEntry[];
|
|
411
|
+
getByRequest(requestId: string): TelemetryEntry[];
|
|
412
|
+
}
|
|
413
|
+
declare class TelemetryStore<T extends TelemetryEntry> implements ReadonlyTelemetryStore {
|
|
414
|
+
private maxEntries;
|
|
415
|
+
private entries;
|
|
416
|
+
private listeners;
|
|
417
|
+
constructor(maxEntries?: number);
|
|
389
418
|
add(data: Omit<T, "id">): T;
|
|
390
419
|
getAll(): readonly T[];
|
|
391
420
|
getByRequest(requestId: string): T[];
|
|
392
421
|
clear(): void;
|
|
422
|
+
onEntry(fn: TelemetryListener<T>): void;
|
|
423
|
+
offEntry(fn: TelemetryListener<T>): void;
|
|
393
424
|
}
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
425
|
+
|
|
426
|
+
interface MetricsPersistence {
|
|
427
|
+
load(): MetricsData;
|
|
428
|
+
loadAsync(): Promise<MetricsData>;
|
|
429
|
+
save(data: MetricsData): void;
|
|
430
|
+
saveSync(data: MetricsData): void;
|
|
431
|
+
remove(): void;
|
|
399
432
|
}
|
|
400
|
-
|
|
433
|
+
|
|
434
|
+
declare class MetricsStore {
|
|
435
|
+
private persistence;
|
|
436
|
+
private data;
|
|
437
|
+
private endpointIndex;
|
|
438
|
+
private sessionId;
|
|
439
|
+
private sessionStart;
|
|
440
|
+
private flushTimer;
|
|
441
|
+
private dirty;
|
|
442
|
+
private accumulators;
|
|
443
|
+
private pendingPoints;
|
|
444
|
+
constructor(persistence: MetricsPersistence);
|
|
445
|
+
start(): void;
|
|
446
|
+
stop(): void;
|
|
401
447
|
recordRequest(req: TracedRequest, metrics: RequestMetrics): void;
|
|
402
448
|
getAll(): readonly EndpointMetrics[];
|
|
403
449
|
getEndpoint(endpoint: string): EndpointMetrics | undefined;
|
|
450
|
+
/**
|
|
451
|
+
* Compute the adaptive performance baseline for an endpoint.
|
|
452
|
+
* Returns the median p95 across historical sessions, or null when
|
|
453
|
+
* there isn't enough data to establish a meaningful baseline.
|
|
454
|
+
*/
|
|
455
|
+
/**
|
|
456
|
+
* Cached baselines — invalidated on flush (when sessions change) and
|
|
457
|
+
* on new request recordings (when pending points grow). Avoids recomputing
|
|
458
|
+
* on every getLiveEndpoints() API call.
|
|
459
|
+
*/
|
|
460
|
+
private baselineCache;
|
|
461
|
+
getEndpointBaseline(endpoint: string): number | null;
|
|
462
|
+
private computeBaseline;
|
|
404
463
|
getLiveEndpoints(): LiveEndpointData[];
|
|
405
464
|
reset(): void;
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
upsert(issue: Issue, source: IssueSource): StatefulIssue;
|
|
409
|
-
transition(issueId: string, state: IssueState): boolean;
|
|
410
|
-
reportFix(issueId: string, status: AiFixStatus, notes: string): boolean;
|
|
411
|
-
reconcile(currentIssueIds: Set<string>, activeEndpoints: Set<string>): void;
|
|
412
|
-
getAll(): readonly StatefulIssue[];
|
|
413
|
-
getByState(state: IssueState): readonly StatefulIssue[];
|
|
414
|
-
getByCategory(category: IssueCategory): readonly StatefulIssue[];
|
|
415
|
-
get(issueId: string): StatefulIssue | undefined;
|
|
416
|
-
clear(): void;
|
|
417
|
-
}
|
|
418
|
-
interface AnalysisEngineInterface extends Lifecycle {
|
|
419
|
-
recompute(): void;
|
|
420
|
-
getInsights(): readonly Insight[];
|
|
421
|
-
getFindings(): readonly SecurityFinding[];
|
|
465
|
+
flush(sync?: boolean): void;
|
|
466
|
+
private getOrCreateEndpoint;
|
|
422
467
|
}
|
|
423
468
|
|
|
424
|
-
interface
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
}
|
|
435
|
-
declare class ServiceRegistry {
|
|
436
|
-
private services;
|
|
437
|
-
register<K extends keyof ServiceMap>(name: K, service: ServiceMap[K]): void;
|
|
438
|
-
get<K extends keyof ServiceMap>(name: K): ServiceMap[K];
|
|
439
|
-
has<K extends keyof ServiceMap>(name: K): boolean;
|
|
469
|
+
interface Services {
|
|
470
|
+
bus: EventBus;
|
|
471
|
+
requestStore: RequestStore;
|
|
472
|
+
queryStore: TelemetryStore<TracedQuery>;
|
|
473
|
+
fetchStore: TelemetryStore<TracedFetch>;
|
|
474
|
+
logStore: TelemetryStore<TracedLog>;
|
|
475
|
+
errorStore: TelemetryStore<TracedError>;
|
|
476
|
+
metricsStore: MetricsStore;
|
|
477
|
+
issueStore: IssueStore;
|
|
478
|
+
analysisEngine: AnalysisEngine;
|
|
440
479
|
}
|
|
441
480
|
|
|
442
481
|
declare class AnalysisEngine {
|
|
443
|
-
private
|
|
482
|
+
private services;
|
|
444
483
|
private debounceMs;
|
|
445
484
|
private scanner;
|
|
446
485
|
private cachedInsights;
|
|
447
486
|
private cachedFindings;
|
|
448
487
|
private debounceTimer;
|
|
449
488
|
private subs;
|
|
450
|
-
constructor(
|
|
489
|
+
constructor(services: Services, debounceMs?: number);
|
|
451
490
|
start(): void;
|
|
452
491
|
stop(): void;
|
|
453
492
|
getInsights(): readonly Insight[];
|