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 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 security issue, caught before you ship. <br />
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
- That's it. Brakit detects your framework, adds itself as a devDependency, and creates the instrumentation file. Start your app normally:
62
+ ### Python (FastAPI / Flask)
54
63
 
55
64
  ```bash
56
- npm run dev
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
- > **Requirements:** Node.js >= 18 and a project with `package.json`.
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: string;
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
- interface Lifecycle {
385
- start(): void;
386
- stop(): void;
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
- interface TelemetryStoreInterface<T extends TelemetryEntry> {
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
- interface RequestStoreInterface {
395
- capture(input: CaptureInput): TracedRequest;
396
- add(entry: TracedRequest): void;
397
- getAll(): readonly TracedRequest[];
398
- clear(): void;
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
- interface MetricsStoreInterface extends Lifecycle {
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
- interface IssueStoreInterface extends Lifecycle {
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 ServiceMap {
425
- "event-bus": EventBus;
426
- "request-store": RequestStoreInterface;
427
- "query-store": TelemetryStoreInterface<TracedQuery>;
428
- "fetch-store": TelemetryStoreInterface<TracedFetch>;
429
- "log-store": TelemetryStoreInterface<TracedLog>;
430
- "error-store": TelemetryStoreInterface<TracedError>;
431
- "metrics-store": MetricsStoreInterface;
432
- "issue-store": IssueStoreInterface;
433
- "analysis-engine": AnalysisEngineInterface;
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 registry;
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(registry: ServiceRegistry, debounceMs?: number);
489
+ constructor(services: Services, debounceMs?: number);
451
490
  start(): void;
452
491
  stop(): void;
453
492
  getInsights(): readonly Insight[];