@lensmcp/protocol-types 1.24.5 → 1.25.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/index.d.ts +2 -0
- package/index.js +1 -1
- package/lib/bounded-clone.js +1 -1
- package/lib/bounded-map.js +1 -1
- package/lib/bounded-writer.js +1 -1
- package/lib/capture-demand.d.ts +13 -0
- package/lib/capture-demand.js +1 -1
- package/lib/dashboard.d.ts +1973 -0
- package/lib/dashboard.js +1 -1
- package/lib/error-dismissal.js +1 -1
- package/lib/event.d.ts +4 -2
- package/lib/event.js +1 -1
- package/lib/flood-guard.js +1 -1
- package/lib/http-exchange.d.ts +80 -0
- package/lib/http-exchange.js +1 -0
- package/lib/log-query.d.ts +30 -0
- package/lib/log-query.js +1 -0
- package/lib/perf-rules.js +1 -1
- package/lib/pod-memory.js +1 -1
- package/lib/producer-health.js +1 -1
- package/lib/resources.d.ts +11 -11
- package/package.json +1 -1
package/lib/dashboard.d.ts
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* ({@link DashStreamEvent}); the heavier reads (`/api/flow`, `/api/resource`, `/api/logs`) are
|
|
11
11
|
* on-demand GETs.
|
|
12
12
|
*/
|
|
13
|
+
import type { HttpExchange } from './http-exchange.js';
|
|
13
14
|
/** Bumped only on a breaking change to the stream framing itself. */
|
|
14
15
|
export declare const DASHBOARD_PROTOCOL = 1;
|
|
15
16
|
/** The swimlane a flow event belongs to (mirrors `FlowArea` in `@lensmcp/core`'s flow-shape). */
|
|
@@ -214,6 +215,12 @@ export interface DashCluster {
|
|
|
214
215
|
routes: DashRoute[];
|
|
215
216
|
edges: DashEdge[];
|
|
216
217
|
backends: DashBackend[];
|
|
218
|
+
/** Calls to the gateway's own hosts — back in through it, a service → service hop, not a third party. */
|
|
219
|
+
backIn?: Array<{
|
|
220
|
+
host: string;
|
|
221
|
+
ops: number;
|
|
222
|
+
usedBy: string[];
|
|
223
|
+
}>;
|
|
217
224
|
hosts: DashHostCheck[];
|
|
218
225
|
/** Appends the missing /etc/hosts lines — present only when a host does not resolve. */
|
|
219
226
|
hostsFix?: string;
|
|
@@ -350,6 +357,19 @@ export interface DashFlowNode {
|
|
|
350
357
|
jobId?: string;
|
|
351
358
|
sig?: string;
|
|
352
359
|
urlKey?: string;
|
|
360
|
+
method?: string;
|
|
361
|
+
/** The route template, `/companies/:id`: the framework's own when it says, else the path with ids folded. */
|
|
362
|
+
route?: string;
|
|
363
|
+
/** The path as requested, without the query string. */
|
|
364
|
+
rawPath?: string;
|
|
365
|
+
status?: number;
|
|
366
|
+
host?: string;
|
|
367
|
+
/** On an east-west hop through the gateway: the service that made the call. */
|
|
368
|
+
caller?: string;
|
|
369
|
+
/** The response was streamed (an AI provider's SSE). */
|
|
370
|
+
streamed?: boolean;
|
|
371
|
+
/** A call out whose request and response were captured (`/api/exchange?id=`). */
|
|
372
|
+
exchangeId?: string;
|
|
353
373
|
durationMs?: number;
|
|
354
374
|
detail?: string;
|
|
355
375
|
steps?: string[];
|
|
@@ -479,11 +499,19 @@ export interface DashLogLine {
|
|
|
479
499
|
/** The whole record a pod printed across lines — a message and its metadata, or its stack (shown expanded). */
|
|
480
500
|
detail?: string;
|
|
481
501
|
flowId?: string;
|
|
502
|
+
/** The request it happened in (the trace), when the line belongs to one. */
|
|
503
|
+
requestId?: string;
|
|
504
|
+
/** The step in its flow — the same id as the flow's node — so "open this step" lands exactly. */
|
|
505
|
+
nodeId?: string;
|
|
506
|
+
/** How long the thing it records took, when it says (`ms:>500` filters on this). */
|
|
507
|
+
durationMs?: number;
|
|
482
508
|
}
|
|
483
509
|
export interface DashLogsFrame {
|
|
484
510
|
seq: number;
|
|
485
511
|
sources: string[];
|
|
486
512
|
lines: DashLogLine[];
|
|
513
|
+
/** The oldest line the ring still holds — anything before it is gone, not missing. */
|
|
514
|
+
oldestAt?: number;
|
|
487
515
|
}
|
|
488
516
|
export type DashProblemSeverity = 'high' | 'medium' | 'low';
|
|
489
517
|
export type DashProblemKind = 'n-plus-one' | 'db-in-loop' | 'render-storm' | 'slow-path' | 'slow-route' | 'slow-query' | 'missing-index' | 'leak' | 'server-error' | 'browser-error' | 'failed-span' | 'egress-failing' | 'egress-slow' | 'duplicate-requests' | 'build' | 'typecheck' | 'lint' | 'service'
|
|
@@ -510,6 +538,17 @@ export interface DashProblem {
|
|
|
510
538
|
code?: string;
|
|
511
539
|
/** How many times it happened, when the detector counts. */
|
|
512
540
|
count?: number;
|
|
541
|
+
/**
|
|
542
|
+
* The number the detector measured, against its budget when it has one — `812 ms / 500 ms`,
|
|
543
|
+
* `×6 / ×1`, `+421 items`. Only what the detector itself reported; absent otherwise.
|
|
544
|
+
*/
|
|
545
|
+
measure?: {
|
|
546
|
+
value: number;
|
|
547
|
+
budget?: number;
|
|
548
|
+
unit: 'ms' | '×' | 'items';
|
|
549
|
+
};
|
|
550
|
+
/** The latest measurements, oldest first, in `measure.unit` (a slow route's last calls). */
|
|
551
|
+
samples?: number[];
|
|
513
552
|
/** The flow (and node inside it) that shows this happening. */
|
|
514
553
|
evidence?: {
|
|
515
554
|
flowId?: string;
|
|
@@ -558,6 +597,35 @@ export interface DashDiagnostics {
|
|
|
558
597
|
react: {
|
|
559
598
|
hot: DashHotComponent[];
|
|
560
599
|
};
|
|
600
|
+
/** Not broken, worth a look — one entry per source, the third counter in the top bar. Absent from older servers. */
|
|
601
|
+
warnings?: DashWarning[];
|
|
602
|
+
/**
|
|
603
|
+
* The top bar's slow and warning counters, counted here so every client shows the same numbers:
|
|
604
|
+
* `slow` = open problems of a {@link DASH_PERF_KINDS} kind above `low`; `warnings` = the sum of `warnings[].count`.
|
|
605
|
+
* (The error counter is `DashErrors.recentGroups`.)
|
|
606
|
+
*/
|
|
607
|
+
counts?: {
|
|
608
|
+
slow: number;
|
|
609
|
+
warnings: number;
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
/** The problem kinds that are "slow or wasteful" — Performance's findings and the slow counter. */
|
|
613
|
+
export declare const DASH_PERF_KINDS: readonly DashProblemKind[];
|
|
614
|
+
export type DashWarningKind = 'lint' | 'visual' | 'log' | 'bundle' | 'react' | 'memory' | 'service' | 'gateway' | 'db';
|
|
615
|
+
/** Something that is not failing but deserves a look: lint warnings, visual-rule violations, growing memory. */
|
|
616
|
+
export interface DashWarning {
|
|
617
|
+
/** Stable for its source, so a list can be kept and diffed. */
|
|
618
|
+
id: string;
|
|
619
|
+
kind: DashWarningKind;
|
|
620
|
+
title: string;
|
|
621
|
+
/** Where it is, when one place says it (a component, an owner, a file). */
|
|
622
|
+
where?: string;
|
|
623
|
+
count: number;
|
|
624
|
+
lastSeenAt: number;
|
|
625
|
+
/** The resource that holds the detail. */
|
|
626
|
+
resource?: string;
|
|
627
|
+
/** A Logs query that shows it, when it is made of log lines. */
|
|
628
|
+
query?: Array<[string, string]>;
|
|
561
629
|
}
|
|
562
630
|
/**
|
|
563
631
|
* server — a request a service answered with a 5xx / an exception reached its handler
|
|
@@ -684,3 +752,1908 @@ export type DashStreamEvent = {
|
|
|
684
752
|
data: DashErrors;
|
|
685
753
|
};
|
|
686
754
|
export type DashStreamEventType = DashStreamEvent['type'];
|
|
755
|
+
/**
|
|
756
|
+
* What one run of a route touched, by key, with how many times:
|
|
757
|
+
* `svc:<callee>` a service it called · `ext:<host>` a host outside the cluster · `sql:<VERB> <table>`
|
|
758
|
+
* a statement shape · `cache:<op>` a key-value command · `queue:<name>` a job it added ·
|
|
759
|
+
* `err:<what>` something that threw.
|
|
760
|
+
*/
|
|
761
|
+
export type DashTouches = Record<string, number>;
|
|
762
|
+
export interface DashRouteRun {
|
|
763
|
+
at: number;
|
|
764
|
+
flowId?: string;
|
|
765
|
+
requestId?: string;
|
|
766
|
+
status?: number;
|
|
767
|
+
durationMs?: number;
|
|
768
|
+
/** Who called it: another service by name, else `client`. */
|
|
769
|
+
caller?: string;
|
|
770
|
+
touches: DashTouches;
|
|
771
|
+
}
|
|
772
|
+
export type DashRouteChangeKind = 'new-external' | 'new-service' | 'new-query' | 'new-queue' | 'new-cache' | 'new-error' | 'gone' | 'gone-error' | 'count' | 'status' | 'duration';
|
|
773
|
+
export interface DashRouteChange {
|
|
774
|
+
kind: DashRouteChangeKind;
|
|
775
|
+
/** The touch key it is about, when it is about one. */
|
|
776
|
+
key?: string;
|
|
777
|
+
/** One line, plain: "now calls api.openai.com", "now runs select revisions", "select business_facts ×48 → ×96", "404 → 200". */
|
|
778
|
+
text: string;
|
|
779
|
+
}
|
|
780
|
+
export interface DashRouteSummary {
|
|
781
|
+
/** `<service> <METHOD> <route>`. */
|
|
782
|
+
id: string;
|
|
783
|
+
/** Its newest finished route review, when it has one. */
|
|
784
|
+
review?: DashReviewMark;
|
|
785
|
+
service: string;
|
|
786
|
+
method: string;
|
|
787
|
+
/** The route template (`/api/companies/:id`). */
|
|
788
|
+
route: string;
|
|
789
|
+
firstSeenAt: number;
|
|
790
|
+
lastSeenAt: number;
|
|
791
|
+
runs: number;
|
|
792
|
+
errors: number;
|
|
793
|
+
p50?: number;
|
|
794
|
+
p95?: number;
|
|
795
|
+
/** The latest run; `touches` with its counts (`sql:SELECT business_facts` ×96). */
|
|
796
|
+
last: {
|
|
797
|
+
at: number;
|
|
798
|
+
status?: number;
|
|
799
|
+
durationMs?: number;
|
|
800
|
+
flowId?: string;
|
|
801
|
+
touches?: DashTouches;
|
|
802
|
+
};
|
|
803
|
+
/** Who called its kept runs (`client`, or the calling services). */
|
|
804
|
+
callers: string[];
|
|
805
|
+
/** What the latest run touched, most-used first. */
|
|
806
|
+
uses: string[];
|
|
807
|
+
/**
|
|
808
|
+
* The latest run against the route's earlier kept runs: what none of them touched, what all of
|
|
809
|
+
* them touched and it did not, a query count or a time far from the usual one, a status unlike the
|
|
810
|
+
* run before.
|
|
811
|
+
*/
|
|
812
|
+
changes: DashRouteChange[];
|
|
813
|
+
}
|
|
814
|
+
export interface DashRouteUse {
|
|
815
|
+
key: string;
|
|
816
|
+
kind: 'svc' | 'ext' | 'sql' | 'cache' | 'queue' | 'err' | 'other';
|
|
817
|
+
name: string;
|
|
818
|
+
/** For SQL: the statement's verb, so reads and writes can be told apart. */
|
|
819
|
+
verb?: string;
|
|
820
|
+
/** Of the kept runs, how many touched it. */
|
|
821
|
+
inRuns: number;
|
|
822
|
+
/** On average, per run that touched it. */
|
|
823
|
+
perRun: number;
|
|
824
|
+
/** Only the latest run touched it. */
|
|
825
|
+
latestOnly: boolean;
|
|
826
|
+
}
|
|
827
|
+
/**
|
|
828
|
+
* A value's shape. An object's fields are a list, each carrying its name — never property keys: the bus redacts the
|
|
829
|
+
* value under any key named like a credential (`code`, `token`, `session`…), and a field so named would lose its shape.
|
|
830
|
+
*/
|
|
831
|
+
export type DashShape = {
|
|
832
|
+
t: 'null';
|
|
833
|
+
} | {
|
|
834
|
+
t: 'bool';
|
|
835
|
+
} | {
|
|
836
|
+
t: 'num';
|
|
837
|
+
int?: true;
|
|
838
|
+
} | {
|
|
839
|
+
t: 'str';
|
|
840
|
+
f?: DashStringFormat;
|
|
841
|
+
max?: number;
|
|
842
|
+
} | {
|
|
843
|
+
t: 'arr';
|
|
844
|
+
n: [number, number];
|
|
845
|
+
of?: DashShape;
|
|
846
|
+
} | {
|
|
847
|
+
t: 'obj';
|
|
848
|
+
fields: DashShapeField[];
|
|
849
|
+
more?: number;
|
|
850
|
+
} | {
|
|
851
|
+
t: 'map';
|
|
852
|
+
n: [number, number];
|
|
853
|
+
key?: DashStringFormat;
|
|
854
|
+
of?: DashShape;
|
|
855
|
+
} | {
|
|
856
|
+
t: 'any';
|
|
857
|
+
of: DashShape[];
|
|
858
|
+
};
|
|
859
|
+
/** One field of an object's shape. `opt`: some samples lacked it. `cred`: named like a credential — its lengths are not kept. */
|
|
860
|
+
export type DashShapeField = DashShape & {
|
|
861
|
+
name: string;
|
|
862
|
+
opt?: true;
|
|
863
|
+
cred?: true;
|
|
864
|
+
};
|
|
865
|
+
export type DashStringFormat = 'uuid' | 'ulid' | 'date-time' | 'date' | 'email' | 'url' | 'jwt' | 'int' | 'decimal' | 'hex' | 'base64' | 'phone';
|
|
866
|
+
/** Facts about a request's headers: what kind of thing came, never the thing. */
|
|
867
|
+
export interface DashHttpRequestFacts {
|
|
868
|
+
contentType?: string;
|
|
869
|
+
accept?: string;
|
|
870
|
+
/** The credential's scheme (`Bearer`, `Basic`) — never the credential. Not `auth`: the bus redacts what is under a key so named. */
|
|
871
|
+
scheme?: string;
|
|
872
|
+
cookies?: number;
|
|
873
|
+
ifMatch?: true;
|
|
874
|
+
ifNoneMatch?: true;
|
|
875
|
+
idempotencyKey?: true;
|
|
876
|
+
origin?: true;
|
|
877
|
+
bytes?: number;
|
|
878
|
+
/** Its query's and its path's parameter names. */
|
|
879
|
+
query?: string[];
|
|
880
|
+
params?: string[];
|
|
881
|
+
}
|
|
882
|
+
/** Facts about a response's headers. */
|
|
883
|
+
export interface DashHttpResponseFacts {
|
|
884
|
+
contentType?: string;
|
|
885
|
+
bytes?: number;
|
|
886
|
+
cacheControl?: string;
|
|
887
|
+
etag?: 'strong' | 'weak';
|
|
888
|
+
lastModified?: true;
|
|
889
|
+
location?: true;
|
|
890
|
+
vary?: string;
|
|
891
|
+
retryAfter?: true;
|
|
892
|
+
wwwAuthenticate?: string;
|
|
893
|
+
allow?: string;
|
|
894
|
+
/** Each cookie it sets: its name and attributes, never its value. */
|
|
895
|
+
cookies?: Array<{
|
|
896
|
+
name: string;
|
|
897
|
+
secure?: true;
|
|
898
|
+
httpOnly?: true;
|
|
899
|
+
sameSite?: string;
|
|
900
|
+
domain?: true;
|
|
901
|
+
path?: string;
|
|
902
|
+
maxAge?: true;
|
|
903
|
+
expires?: true;
|
|
904
|
+
}>;
|
|
905
|
+
/** `creds`: `Access-Control-Allow-Credentials: true` (not `credentials`, a name the bus redacts). */
|
|
906
|
+
cors?: {
|
|
907
|
+
origin: '*' | 'echoed' | 'fixed';
|
|
908
|
+
creds?: true;
|
|
909
|
+
methods?: string;
|
|
910
|
+
headers?: string;
|
|
911
|
+
};
|
|
912
|
+
hsts?: true;
|
|
913
|
+
nosniff?: true;
|
|
914
|
+
/** `X-Powered-By` / `Server` that name the stack. */
|
|
915
|
+
poweredBy?: true;
|
|
916
|
+
deprecation?: true;
|
|
917
|
+
sunset?: true;
|
|
918
|
+
rateLimit?: true;
|
|
919
|
+
requestId?: true;
|
|
920
|
+
}
|
|
921
|
+
/**
|
|
922
|
+
* What a route's exchanges looked like, merged over the ones recorded (the services record one per route and status
|
|
923
|
+
* class a minute): the requests — media types, credential schemes, preconditions, keys, parameter names, body shape —
|
|
924
|
+
* and, per status, the responses — media type, cache and cookie policy, validators, CORS, body shape. Counts say how
|
|
925
|
+
* many of the recorded exchanges had a fact.
|
|
926
|
+
*/
|
|
927
|
+
export interface DashRouteContract {
|
|
928
|
+
samples: number;
|
|
929
|
+
firstAt: number;
|
|
930
|
+
lastAt: number;
|
|
931
|
+
request: {
|
|
932
|
+
contentTypes: Record<string, number>;
|
|
933
|
+
/** The credential schemes that came (`Bearer` ×3, `none` ×1). */
|
|
934
|
+
schemes: Record<string, number>;
|
|
935
|
+
query: Record<string, number>;
|
|
936
|
+
params: string[];
|
|
937
|
+
ifMatch: number;
|
|
938
|
+
ifNoneMatch: number;
|
|
939
|
+
idempotencyKey: number;
|
|
940
|
+
origin: number;
|
|
941
|
+
cookies: number;
|
|
942
|
+
bytesMax?: number;
|
|
943
|
+
body?: DashShape;
|
|
944
|
+
};
|
|
945
|
+
responses: Record<string, {
|
|
946
|
+
n: number;
|
|
947
|
+
contentTypes: Record<string, number>;
|
|
948
|
+
bytesMax?: number;
|
|
949
|
+
cacheControl: Record<string, number>;
|
|
950
|
+
etag?: 'strong' | 'weak';
|
|
951
|
+
vary?: string;
|
|
952
|
+
wwwAuthenticate?: string;
|
|
953
|
+
allow?: string;
|
|
954
|
+
cookies?: NonNullable<DashHttpResponseFacts['cookies']>;
|
|
955
|
+
cors?: NonNullable<DashHttpResponseFacts['cors']>;
|
|
956
|
+
/** How many had each of: location, lastModified, retryAfter, hsts, nosniff, poweredBy, deprecation, sunset, rateLimit, requestId. */
|
|
957
|
+
has: Record<string, number>;
|
|
958
|
+
body?: DashShape;
|
|
959
|
+
}>;
|
|
960
|
+
}
|
|
961
|
+
export interface DashRouteDetail extends DashRouteSummary {
|
|
962
|
+
/** Newest first, at most 20. */
|
|
963
|
+
recent: DashRouteRun[];
|
|
964
|
+
/** What its exchanges looked like — when its service records them. */
|
|
965
|
+
contract?: DashRouteContract;
|
|
966
|
+
/** Over the kept runs: everything the route depends on. */
|
|
967
|
+
usesDetail: DashRouteUse[];
|
|
968
|
+
/** The latest run against the run before it, alone. */
|
|
969
|
+
changesSincePrev?: DashRouteChange[];
|
|
970
|
+
/** The latest run against the oldest one kept. */
|
|
971
|
+
changesSinceOldest: DashRouteChange[];
|
|
972
|
+
}
|
|
973
|
+
export interface DashRoutesCatalog {
|
|
974
|
+
at: number;
|
|
975
|
+
routes: DashRouteSummary[];
|
|
976
|
+
/** How far back the kept history reaches. */
|
|
977
|
+
since?: number;
|
|
978
|
+
}
|
|
979
|
+
/** What one AI model was asked through an endpoint, and what it cost in tokens. */
|
|
980
|
+
export interface DashAiUse {
|
|
981
|
+
model: string;
|
|
982
|
+
calls: number;
|
|
983
|
+
promptTokens: number;
|
|
984
|
+
completionTokens: number;
|
|
985
|
+
/** Calls that came back without a usage block. */
|
|
986
|
+
unmetered: number;
|
|
987
|
+
}
|
|
988
|
+
/** One call out, as kept: the endpoint's templated path (query strings are never captured). */
|
|
989
|
+
export interface DashExternalCall {
|
|
990
|
+
at: number;
|
|
991
|
+
service: string;
|
|
992
|
+
method: string;
|
|
993
|
+
path: string;
|
|
994
|
+
status?: number;
|
|
995
|
+
/** A transport error (refused, reset, timed out). */
|
|
996
|
+
error?: string;
|
|
997
|
+
durationMs?: number;
|
|
998
|
+
flowId?: string;
|
|
999
|
+
/** The route whose run made the call (a route catalog id), when the request was seen to end. */
|
|
1000
|
+
route?: string;
|
|
1001
|
+
/** Its captured exchange (`/api/exchange?id=`): headers, bodies, a stream's events — when capture was on. */
|
|
1002
|
+
exchangeId?: string;
|
|
1003
|
+
/** The request it was made in — the calls that share it are one request's steps. */
|
|
1004
|
+
requestId?: string;
|
|
1005
|
+
}
|
|
1006
|
+
/** One call opened (Data › External APIs' Request & response panel): the call, its exchange, its request's other steps. */
|
|
1007
|
+
export interface DashExchangeView {
|
|
1008
|
+
host: string;
|
|
1009
|
+
call: DashExternalCall;
|
|
1010
|
+
/** Absent: not captured (capture off, headers rotated away) or withheld (`withheld` says why). */
|
|
1011
|
+
exchange?: HttpExchange;
|
|
1012
|
+
withheld?: string;
|
|
1013
|
+
/** The steps of the same request, in order: the route that made the call, then each outbound call. */
|
|
1014
|
+
chain: Array<{
|
|
1015
|
+
kind: 'route' | 'egress';
|
|
1016
|
+
label: string;
|
|
1017
|
+
status?: number;
|
|
1018
|
+
exchangeId?: string;
|
|
1019
|
+
host?: string;
|
|
1020
|
+
at?: number;
|
|
1021
|
+
current?: boolean;
|
|
1022
|
+
}>;
|
|
1023
|
+
}
|
|
1024
|
+
export interface DashEndpointSummary {
|
|
1025
|
+
/** `<host> <METHOD> <path>`. */
|
|
1026
|
+
id: string;
|
|
1027
|
+
host: string;
|
|
1028
|
+
method: string;
|
|
1029
|
+
/** Ids folded the same way as routes (`/v1/files/:id`). */
|
|
1030
|
+
path: string;
|
|
1031
|
+
services: string[];
|
|
1032
|
+
/** The routes whose runs call it (route catalog ids), most recent first. */
|
|
1033
|
+
routes: string[];
|
|
1034
|
+
calls: number;
|
|
1035
|
+
/** Calls that did not succeed: a transport error, or a status ≥ 400. */
|
|
1036
|
+
failed: number;
|
|
1037
|
+
status4xx: number;
|
|
1038
|
+
status5xx: number;
|
|
1039
|
+
/** Over the kept durations (the last 100). */
|
|
1040
|
+
avgMs?: number;
|
|
1041
|
+
p50?: number;
|
|
1042
|
+
p95?: number;
|
|
1043
|
+
maxMs: number;
|
|
1044
|
+
firstSeenAt: number;
|
|
1045
|
+
lastSeenAt: number;
|
|
1046
|
+
lastStatus?: number;
|
|
1047
|
+
lastError?: string;
|
|
1048
|
+
ai?: DashAiUse[];
|
|
1049
|
+
}
|
|
1050
|
+
export type DashExternalHealth = 'failing' | 'slow' | 'ok';
|
|
1051
|
+
export interface DashExternalHost {
|
|
1052
|
+
host: string;
|
|
1053
|
+
services: string[];
|
|
1054
|
+
/** Failing or slow now, or repeatedly lately — one blip that recovered is retracted. */
|
|
1055
|
+
health: DashExternalHealth;
|
|
1056
|
+
/** "3 of the last 20 calls failed", "2 of the last 20 took ≥ 1 s", "healthy". */
|
|
1057
|
+
verdict: string;
|
|
1058
|
+
calls: number;
|
|
1059
|
+
failed: number;
|
|
1060
|
+
p50?: number;
|
|
1061
|
+
p95?: number;
|
|
1062
|
+
maxMs: number;
|
|
1063
|
+
firstSeenAt: number;
|
|
1064
|
+
lastSeenAt: number;
|
|
1065
|
+
endpoints: number;
|
|
1066
|
+
ai?: DashAiUse[];
|
|
1067
|
+
}
|
|
1068
|
+
export interface DashExternalHostDetail extends DashExternalHost {
|
|
1069
|
+
/** A call at or over this is slow (the lens's egress threshold). */
|
|
1070
|
+
slowMs: number;
|
|
1071
|
+
/** Every endpoint of the host, slowest p95 first. */
|
|
1072
|
+
endpointList: DashEndpointSummary[];
|
|
1073
|
+
/** Newest first. */
|
|
1074
|
+
recent: DashExternalCall[];
|
|
1075
|
+
/** Newest first — kept apart, so a busy host does not push its one failure out. */
|
|
1076
|
+
failures: DashExternalCall[];
|
|
1077
|
+
}
|
|
1078
|
+
export interface DashExternalCatalog {
|
|
1079
|
+
at: number;
|
|
1080
|
+
/** Third parties only, failing first, then slowest. */
|
|
1081
|
+
hosts: DashExternalHost[];
|
|
1082
|
+
/** Every endpoint of those hosts — a row per domain, method and pathname (Data › External APIs' table). */
|
|
1083
|
+
endpoints?: DashEndpointSummary[];
|
|
1084
|
+
/**
|
|
1085
|
+
* Hosts the gateway fronts. A call to one is a hop to one of the cluster's own services, so it is
|
|
1086
|
+
* not an external API — listed so a reader knows where those calls went.
|
|
1087
|
+
*/
|
|
1088
|
+
gatewayHosts: Array<{
|
|
1089
|
+
host: string;
|
|
1090
|
+
services: string[];
|
|
1091
|
+
calls: number;
|
|
1092
|
+
}>;
|
|
1093
|
+
/** A call at or over this is slow (the lens's egress threshold). */
|
|
1094
|
+
slowMs: number;
|
|
1095
|
+
since?: number;
|
|
1096
|
+
}
|
|
1097
|
+
/** One execution of a statement, as the pg tap recorded it. */
|
|
1098
|
+
export interface DashSqlExecution {
|
|
1099
|
+
at: number;
|
|
1100
|
+
service: string;
|
|
1101
|
+
durationMs?: number;
|
|
1102
|
+
rows?: number;
|
|
1103
|
+
/** Its values as the tap kept them: credential columns masked, at most 10, each ≤ 80 characters. */
|
|
1104
|
+
params?: string[];
|
|
1105
|
+
/** The first rows it returned, as the tap kept them (redacted by column, ≤ 240 characters). */
|
|
1106
|
+
result?: string;
|
|
1107
|
+
error?: string;
|
|
1108
|
+
flowId?: string;
|
|
1109
|
+
/** The route whose run made it (a route catalog id), when the request was seen to end. */
|
|
1110
|
+
route?: string;
|
|
1111
|
+
}
|
|
1112
|
+
/** What the workspace's database analyzer (`db://analysis`) said about a table. */
|
|
1113
|
+
export interface DashSqlFinding {
|
|
1114
|
+
kind: string;
|
|
1115
|
+
table: string;
|
|
1116
|
+
detail: string;
|
|
1117
|
+
severity: string;
|
|
1118
|
+
tool: string;
|
|
1119
|
+
at: number;
|
|
1120
|
+
}
|
|
1121
|
+
export interface DashSqlTemplateSummary {
|
|
1122
|
+
/** 12 hex characters — the statement's shape (`@lensmcp/core/sql-shape`). */
|
|
1123
|
+
id: string;
|
|
1124
|
+
/** The statement with literals as `?` and value lists folded to `(…)`. */
|
|
1125
|
+
template: string;
|
|
1126
|
+
/** A leading comment naming it (`registry.product-composition`). */
|
|
1127
|
+
name?: string;
|
|
1128
|
+
/** For one of the services' statements without a name: a name to give it, unique across the workspace (db://lint suggests the same). */
|
|
1129
|
+
suggestedName?: string;
|
|
1130
|
+
/** Its newest finished SQL review, when it has one. */
|
|
1131
|
+
review?: DashReviewMark;
|
|
1132
|
+
/** SELECT · INSERT · UPDATE · DELETE · …; a WITH reads as the statement it leads to. */
|
|
1133
|
+
verb: string;
|
|
1134
|
+
tables: string[];
|
|
1135
|
+
/** The table an INSERT / UPDATE / DELETE writes. */
|
|
1136
|
+
target?: string;
|
|
1137
|
+
services: string[];
|
|
1138
|
+
/** The routes whose runs ran it (route catalog ids), most recent first. */
|
|
1139
|
+
routes: string[];
|
|
1140
|
+
/** The databases it ran on, most recent first — when the tap names them. */
|
|
1141
|
+
databases?: string[];
|
|
1142
|
+
/** `host:port` of the server it last ran on. */
|
|
1143
|
+
server?: string;
|
|
1144
|
+
runs: number;
|
|
1145
|
+
errors: number;
|
|
1146
|
+
/** Time spent in it, summed over the runs seen. */
|
|
1147
|
+
totalMs: number;
|
|
1148
|
+
p50?: number;
|
|
1149
|
+
p95?: number;
|
|
1150
|
+
maxMs: number;
|
|
1151
|
+
rowsTotal: number;
|
|
1152
|
+
rowsMax: number;
|
|
1153
|
+
/** The most times one request ran it — at the N+1 threshold or over, it runs in a loop. */
|
|
1154
|
+
perRequestMax: number;
|
|
1155
|
+
/** The flow of that request. */
|
|
1156
|
+
perRequestMaxFlow?: string;
|
|
1157
|
+
firstSeenAt: number;
|
|
1158
|
+
lastSeenAt: number;
|
|
1159
|
+
/** Runs the producer's flood guard dropped (when > 0, the counts are a floor). */
|
|
1160
|
+
suppressed: number;
|
|
1161
|
+
/** The statement is known whole (a tap that sends it), not only its first 500 characters. */
|
|
1162
|
+
complete: boolean;
|
|
1163
|
+
}
|
|
1164
|
+
export interface DashSqlTemplateDetail extends DashSqlTemplateSummary {
|
|
1165
|
+
/** The statement as sent, up to 8 KB — or its first 500 characters when `complete` is false. */
|
|
1166
|
+
statement: string;
|
|
1167
|
+
/** Newest first. */
|
|
1168
|
+
executions: DashSqlExecution[];
|
|
1169
|
+
/** Newest first — kept apart, so a busy statement does not push its one failure out. */
|
|
1170
|
+
failures: DashSqlExecution[];
|
|
1171
|
+
/** What the workspace's database analyzer said about the tables it touches. */
|
|
1172
|
+
findings: DashSqlFinding[];
|
|
1173
|
+
}
|
|
1174
|
+
export interface DashSqlTable {
|
|
1175
|
+
name: string;
|
|
1176
|
+
/** Runs of reading statements that touched it, and of writing ones. */
|
|
1177
|
+
reads: number;
|
|
1178
|
+
writes: number;
|
|
1179
|
+
statements: number;
|
|
1180
|
+
services: string[];
|
|
1181
|
+
/** The slowest p95 among the statements that touch it. */
|
|
1182
|
+
slowestP95?: number;
|
|
1183
|
+
findings: DashSqlFinding[];
|
|
1184
|
+
lastSeenAt: number;
|
|
1185
|
+
}
|
|
1186
|
+
/** A run of any statement, for the newest-first list across statements. */
|
|
1187
|
+
export interface DashSqlRun extends DashSqlExecution {
|
|
1188
|
+
/** Its statement's shape id, verb, template and tables. */
|
|
1189
|
+
id: string;
|
|
1190
|
+
verb: string;
|
|
1191
|
+
template: string;
|
|
1192
|
+
tables: string[];
|
|
1193
|
+
}
|
|
1194
|
+
export interface DashSqlCatalog {
|
|
1195
|
+
at: number;
|
|
1196
|
+
/** Costliest first: the most time spent in it. */
|
|
1197
|
+
templates: DashSqlTemplateSummary[];
|
|
1198
|
+
/** Most used first. */
|
|
1199
|
+
tables: DashSqlTable[];
|
|
1200
|
+
/** A run at or over this is slow (`slowQueryMs`). */
|
|
1201
|
+
slowMs: number;
|
|
1202
|
+
/** Runs per request at which a statement counts as a loop (`nPlusOne`). */
|
|
1203
|
+
loopAt: number;
|
|
1204
|
+
/** The latest run of the workspace's database analyzer, when one reported. */
|
|
1205
|
+
analysis?: {
|
|
1206
|
+
at: number;
|
|
1207
|
+
tool: string;
|
|
1208
|
+
findings: number;
|
|
1209
|
+
};
|
|
1210
|
+
since?: number;
|
|
1211
|
+
}
|
|
1212
|
+
/** `/api/db/stores` — every declared store as this checkout sees it, and what the lens could read of it. */
|
|
1213
|
+
export interface DashDbStores {
|
|
1214
|
+
at: number;
|
|
1215
|
+
/** `nx.json` → `lensmcp.stores` problems, as the checker words them. */
|
|
1216
|
+
errors: string[];
|
|
1217
|
+
notes: string[];
|
|
1218
|
+
stores: DashDbStore[];
|
|
1219
|
+
}
|
|
1220
|
+
export interface DashDbStore {
|
|
1221
|
+
name: string;
|
|
1222
|
+
kind: 'postgres' | 'redis';
|
|
1223
|
+
/** `host:port`. */
|
|
1224
|
+
server: string;
|
|
1225
|
+
/** `PostgreSQL 18.0`. */
|
|
1226
|
+
version?: string;
|
|
1227
|
+
/** Postgres's version number (`180000`) — what decides how a statement can be planned. */
|
|
1228
|
+
versionNum?: number;
|
|
1229
|
+
/** Why the lens read nothing, when it did not (an unset variable, reading off, no `pg` client, refused…). */
|
|
1230
|
+
unread?: string;
|
|
1231
|
+
/** Every database on the server, this checkout's first. */
|
|
1232
|
+
databases: DashDbDatabase[];
|
|
1233
|
+
/** Connections to the server, all databases. */
|
|
1234
|
+
connections?: number;
|
|
1235
|
+
}
|
|
1236
|
+
export interface DashDbDatabase {
|
|
1237
|
+
name: string;
|
|
1238
|
+
bytes?: number;
|
|
1239
|
+
connections?: number;
|
|
1240
|
+
/** One of this checkout's databases — declared on the store. */
|
|
1241
|
+
mine: boolean;
|
|
1242
|
+
}
|
|
1243
|
+
/** `/api/db/schema?store=&db=` — one database as Postgres describes it. */
|
|
1244
|
+
export interface DashDbSchema {
|
|
1245
|
+
at: number;
|
|
1246
|
+
store: string;
|
|
1247
|
+
database: string;
|
|
1248
|
+
/** Since when Postgres counted scans and statements (its last stats reset), if it says. */
|
|
1249
|
+
statsSince?: number;
|
|
1250
|
+
tables: DashDbTableInfo[];
|
|
1251
|
+
indexes: DashDbIndex[];
|
|
1252
|
+
/** What `pg_stat_statements` counted in this database — or why it could not be read. */
|
|
1253
|
+
statements: {
|
|
1254
|
+
state: 'ok' | 'missing' | 'not-loaded' | 'denied' | 'error';
|
|
1255
|
+
detail?: string;
|
|
1256
|
+
since?: number;
|
|
1257
|
+
rows: DashDbStatementStat[];
|
|
1258
|
+
};
|
|
1259
|
+
findings: DashDbFinding[];
|
|
1260
|
+
/** A read that failed, in Postgres's words. */
|
|
1261
|
+
error?: string;
|
|
1262
|
+
}
|
|
1263
|
+
export interface DashDbTableInfo {
|
|
1264
|
+
schema: string;
|
|
1265
|
+
name: string;
|
|
1266
|
+
/** Live rows, as Postgres's statistics count them (an estimate). */
|
|
1267
|
+
rows: number;
|
|
1268
|
+
deadRows: number;
|
|
1269
|
+
/** Table, its indexes and TOAST. */
|
|
1270
|
+
bytes: number;
|
|
1271
|
+
indexBytes: number;
|
|
1272
|
+
seqScans: number;
|
|
1273
|
+
/** Rows the sequential scans read, summed. */
|
|
1274
|
+
seqRowsRead: number;
|
|
1275
|
+
indexScans: number;
|
|
1276
|
+
inserts: number;
|
|
1277
|
+
updates: number;
|
|
1278
|
+
deletes: number;
|
|
1279
|
+
lastVacuumAt?: number;
|
|
1280
|
+
lastAnalyzeAt?: number;
|
|
1281
|
+
}
|
|
1282
|
+
export interface DashDbIndex {
|
|
1283
|
+
schema: string;
|
|
1284
|
+
table: string;
|
|
1285
|
+
name: string;
|
|
1286
|
+
/** `CREATE INDEX … ON … USING btree (tenant_id, created_at)`. */
|
|
1287
|
+
definition: string;
|
|
1288
|
+
unique: boolean;
|
|
1289
|
+
primary: boolean;
|
|
1290
|
+
/** False when a CREATE INDEX CONCURRENTLY failed and left it behind. */
|
|
1291
|
+
valid: boolean;
|
|
1292
|
+
/** Part of a partitioned table's index (or that index itself): changed on the parent, never on its own. */
|
|
1293
|
+
partition?: boolean;
|
|
1294
|
+
scans: number;
|
|
1295
|
+
rowsRead: number;
|
|
1296
|
+
bytes: number;
|
|
1297
|
+
}
|
|
1298
|
+
/** A table as a review needs it: its size and use, its columns and how their values spread, its keys and indexes. */
|
|
1299
|
+
export interface DashDbTableDesign {
|
|
1300
|
+
schema: string;
|
|
1301
|
+
name: string;
|
|
1302
|
+
rows: number;
|
|
1303
|
+
bytes: number;
|
|
1304
|
+
indexBytes: number;
|
|
1305
|
+
deadRows: number;
|
|
1306
|
+
seqScans: number;
|
|
1307
|
+
indexScans: number;
|
|
1308
|
+
lastAnalyzeAt?: number;
|
|
1309
|
+
/** Postgres's statistics about each column — fractions and counts, never a value. */
|
|
1310
|
+
columns: Array<{
|
|
1311
|
+
name: string;
|
|
1312
|
+
type: string;
|
|
1313
|
+
notNull: boolean;
|
|
1314
|
+
default?: string;
|
|
1315
|
+
nullFrac?: number;
|
|
1316
|
+
distinct?: number;
|
|
1317
|
+
correlation?: number;
|
|
1318
|
+
}>;
|
|
1319
|
+
/** `PRIMARY KEY (id)`, `FOREIGN KEY (tenant_id) REFERENCES tenants(id)`, `CHECK (…)`. */
|
|
1320
|
+
constraints: Array<{
|
|
1321
|
+
name: string;
|
|
1322
|
+
type: 'primary' | 'unique' | 'foreign' | 'check' | 'exclusion' | 'other';
|
|
1323
|
+
definition: string;
|
|
1324
|
+
}>;
|
|
1325
|
+
indexes: DashDbIndex[];
|
|
1326
|
+
/** A partitioned table, or a partition of one (its parent). */
|
|
1327
|
+
partitioned?: boolean;
|
|
1328
|
+
partitionOf?: string;
|
|
1329
|
+
/** Row-level security on it, and its policies' definitions. */
|
|
1330
|
+
rowSecurity?: boolean;
|
|
1331
|
+
policies?: Array<{
|
|
1332
|
+
name: string;
|
|
1333
|
+
command: string;
|
|
1334
|
+
using?: string;
|
|
1335
|
+
check?: string;
|
|
1336
|
+
}>;
|
|
1337
|
+
/** Its triggers' definitions (not the internal ones behind foreign keys). */
|
|
1338
|
+
triggers?: Array<{
|
|
1339
|
+
name: string;
|
|
1340
|
+
definition: string;
|
|
1341
|
+
}>;
|
|
1342
|
+
}
|
|
1343
|
+
/** The server a database is on, as a review needs it: version, the planner and memory settings, extensions. */
|
|
1344
|
+
export interface DashDbServer {
|
|
1345
|
+
version?: string;
|
|
1346
|
+
versionNum?: number;
|
|
1347
|
+
settings: Array<{
|
|
1348
|
+
name: string;
|
|
1349
|
+
value: string;
|
|
1350
|
+
}>;
|
|
1351
|
+
extensions: Array<{
|
|
1352
|
+
name: string;
|
|
1353
|
+
version: string;
|
|
1354
|
+
}>;
|
|
1355
|
+
}
|
|
1356
|
+
/**
|
|
1357
|
+
* `db://lint` — what a coding agent must fix about the SQL the services ran: a statement without a name (it is not
|
|
1358
|
+
* reviewed until it has one: the name is what follows it from one version to the next), one name on two statements
|
|
1359
|
+
* at once, and a statement whose SQL review scores under 70.
|
|
1360
|
+
*/
|
|
1361
|
+
export interface DashDbLint {
|
|
1362
|
+
at: number;
|
|
1363
|
+
/** Under this overall score a reviewed statement is a warning. */
|
|
1364
|
+
warnBelow: number;
|
|
1365
|
+
errors: number;
|
|
1366
|
+
warnings: number;
|
|
1367
|
+
/** The statements the lint looked at (the services' own: they touch a table), and how many have a name. */
|
|
1368
|
+
statements: number;
|
|
1369
|
+
named: number;
|
|
1370
|
+
/** Errors first, then warnings; the busiest first; at most 60 (`more` says how many were left out). */
|
|
1371
|
+
issues: DashDbLintIssue[];
|
|
1372
|
+
more: number;
|
|
1373
|
+
/** Each name: the statement it names now, and the versions it replaced (not seen since the current one came). */
|
|
1374
|
+
names: Array<{
|
|
1375
|
+
name: string;
|
|
1376
|
+
current: string;
|
|
1377
|
+
replaced: string[];
|
|
1378
|
+
overall?: number;
|
|
1379
|
+
}>;
|
|
1380
|
+
}
|
|
1381
|
+
export interface DashDbLintIssue {
|
|
1382
|
+
severity: 'error' | 'warning';
|
|
1383
|
+
rule: 'unnamed-statement' | 'name-reused' | 'review-below';
|
|
1384
|
+
statement: string;
|
|
1385
|
+
name?: string;
|
|
1386
|
+
verb: string;
|
|
1387
|
+
tables: string[];
|
|
1388
|
+
services: string[];
|
|
1389
|
+
routes: string[];
|
|
1390
|
+
runs: number;
|
|
1391
|
+
lastSeenAt: number;
|
|
1392
|
+
message: string;
|
|
1393
|
+
/** What to do, for a coding agent. */
|
|
1394
|
+
fix: string;
|
|
1395
|
+
/** For an unnamed statement: a name in the convention, to start from. */
|
|
1396
|
+
suggestedName?: string;
|
|
1397
|
+
review?: {
|
|
1398
|
+
runId: string;
|
|
1399
|
+
at: number;
|
|
1400
|
+
overall: number;
|
|
1401
|
+
title?: string;
|
|
1402
|
+
top?: string;
|
|
1403
|
+
};
|
|
1404
|
+
}
|
|
1405
|
+
/**
|
|
1406
|
+
* One statement's lint (`db://lint/<statement>`, by id or name): its issues, its newest SQL review, the routes that ran
|
|
1407
|
+
* it — what a route's lint issue (`routes://lint`) points at to check one of its statements.
|
|
1408
|
+
*/
|
|
1409
|
+
export interface DashDbStatementLint {
|
|
1410
|
+
at: number;
|
|
1411
|
+
/** The id or name asked for, and whether the catalog has it. */
|
|
1412
|
+
asked: string;
|
|
1413
|
+
found: boolean;
|
|
1414
|
+
statement?: string;
|
|
1415
|
+
name?: string;
|
|
1416
|
+
verb?: string;
|
|
1417
|
+
tables?: string[];
|
|
1418
|
+
services?: string[];
|
|
1419
|
+
/** Named, no issue, and its review (if any) at or over the threshold. */
|
|
1420
|
+
ok: boolean;
|
|
1421
|
+
issues: DashDbLintIssue[];
|
|
1422
|
+
review?: {
|
|
1423
|
+
runId: string;
|
|
1424
|
+
at: number;
|
|
1425
|
+
overall?: number;
|
|
1426
|
+
title?: string;
|
|
1427
|
+
top?: string;
|
|
1428
|
+
};
|
|
1429
|
+
/** The routes that ran it, newest first (`routes://lint` has their issues). */
|
|
1430
|
+
routes: string[];
|
|
1431
|
+
/** What to do next, for a coding agent. */
|
|
1432
|
+
next: string;
|
|
1433
|
+
}
|
|
1434
|
+
/** A statement a route's lint issue is about, with the `db://` resource that checks it. */
|
|
1435
|
+
export interface DashRoutesLintStatement {
|
|
1436
|
+
id: string;
|
|
1437
|
+
name?: string;
|
|
1438
|
+
verb: string;
|
|
1439
|
+
tables: string[];
|
|
1440
|
+
/** `db://lint/<id>` — its issues, its SQL review, the routes that run it. */
|
|
1441
|
+
db: string;
|
|
1442
|
+
overall?: number;
|
|
1443
|
+
/** The most times one request ran it (for a loop). */
|
|
1444
|
+
perRequest?: number;
|
|
1445
|
+
suggestedName?: string;
|
|
1446
|
+
}
|
|
1447
|
+
export interface DashRoutesLintIssue {
|
|
1448
|
+
severity: 'error' | 'warning';
|
|
1449
|
+
rule: 'unnamed-sql' | 'review-hole' | 'credential-in-url' | 'stack-in-error' | 'review-below' | 'sql-review-below' | 'sql-loop' | 'get-writes';
|
|
1450
|
+
/** The route's catalog id (`<service> <METHOD> <route>`), and its parts. */
|
|
1451
|
+
route: string;
|
|
1452
|
+
service: string;
|
|
1453
|
+
method: string;
|
|
1454
|
+
path: string;
|
|
1455
|
+
runs: number;
|
|
1456
|
+
lastSeenAt: number;
|
|
1457
|
+
message: string;
|
|
1458
|
+
/** What to do, for a coding agent. */
|
|
1459
|
+
fix: string;
|
|
1460
|
+
/** For an issue about its SQL: each statement, with the `db://` resource to check it. */
|
|
1461
|
+
statements?: DashRoutesLintStatement[];
|
|
1462
|
+
/** For an issue from its route review: the review, and the indicators it rests on. */
|
|
1463
|
+
review?: {
|
|
1464
|
+
runId: string;
|
|
1465
|
+
at: number;
|
|
1466
|
+
overall?: number;
|
|
1467
|
+
title?: string;
|
|
1468
|
+
top?: string;
|
|
1469
|
+
low?: Array<{
|
|
1470
|
+
id: string;
|
|
1471
|
+
score: number;
|
|
1472
|
+
}>;
|
|
1473
|
+
};
|
|
1474
|
+
/** The resources that check it — `db://lint/<statement>` for each statement it names. */
|
|
1475
|
+
check: string[];
|
|
1476
|
+
}
|
|
1477
|
+
/**
|
|
1478
|
+
* `routes://lint`: the endpoints the services answered, against what a coding agent fixes — from their route reviews,
|
|
1479
|
+
* their statements (each pointing at its `db://lint/<statement>`) and what their runs and recorded exchanges show.
|
|
1480
|
+
*/
|
|
1481
|
+
export interface DashRoutesLint {
|
|
1482
|
+
at: number;
|
|
1483
|
+
/** Under this overall score a reviewed route is a warning; a cap indicator (authorization, exposure, write safety) under `holeBelow` is an error. */
|
|
1484
|
+
warnBelow: number;
|
|
1485
|
+
holeBelow: number;
|
|
1486
|
+
/** One statement this many times in one request is a loop. */
|
|
1487
|
+
loopAt: number;
|
|
1488
|
+
errors: number;
|
|
1489
|
+
warnings: number;
|
|
1490
|
+
/** The routes the lint looked at, and how many have a route review. */
|
|
1491
|
+
routes: number;
|
|
1492
|
+
reviewed: number;
|
|
1493
|
+
/** Errors first, then warnings; the busiest first; at most 80 (`more` says how many were left out). */
|
|
1494
|
+
issues: DashRoutesLintIssue[];
|
|
1495
|
+
more: number;
|
|
1496
|
+
/** Every route with an issue, and how many of each — none left out. */
|
|
1497
|
+
byRoute: Record<string, {
|
|
1498
|
+
errors: number;
|
|
1499
|
+
warnings: number;
|
|
1500
|
+
}>;
|
|
1501
|
+
}
|
|
1502
|
+
/** The tables one catalog statement reads or writes, from the database it ran on. */
|
|
1503
|
+
export interface DashDbTables {
|
|
1504
|
+
at: number;
|
|
1505
|
+
id: string;
|
|
1506
|
+
store?: string;
|
|
1507
|
+
database?: string;
|
|
1508
|
+
state: 'ok' | 'unavailable' | 'error';
|
|
1509
|
+
reason?: string;
|
|
1510
|
+
tables: DashDbTableDesign[];
|
|
1511
|
+
server?: DashDbServer;
|
|
1512
|
+
}
|
|
1513
|
+
export interface DashDbStatementStat {
|
|
1514
|
+
/** The catalog statement it is (`DashSqlTemplateSummary.id`), when its shape matches one. */
|
|
1515
|
+
templateId?: string;
|
|
1516
|
+
/** As Postgres normalised it (`$n` for every constant). */
|
|
1517
|
+
query: string;
|
|
1518
|
+
calls: number;
|
|
1519
|
+
totalMs: number;
|
|
1520
|
+
meanMs: number;
|
|
1521
|
+
rows: number;
|
|
1522
|
+
/** Shared buffers found in memory over all buffers touched, 0..1. */
|
|
1523
|
+
hitRatio?: number;
|
|
1524
|
+
}
|
|
1525
|
+
export interface DashDbFinding {
|
|
1526
|
+
kind: 'unused-index' | 'duplicate-index' | 'invalid-index' | 'seq-scan' | 'dead-rows';
|
|
1527
|
+
severity: 'high' | 'medium' | 'low';
|
|
1528
|
+
table: string;
|
|
1529
|
+
index?: string;
|
|
1530
|
+
/** What Postgres's numbers say, in a sentence. */
|
|
1531
|
+
detail: string;
|
|
1532
|
+
/** The statement that would fix it — shown to copy, never run by the lens. */
|
|
1533
|
+
fix?: string;
|
|
1534
|
+
}
|
|
1535
|
+
/** One Redis command by its shape: the command and its key with ids folded (`GET session:*`); a script by its hash. */
|
|
1536
|
+
export interface DashKvCommandShape {
|
|
1537
|
+
id: string;
|
|
1538
|
+
op: string;
|
|
1539
|
+
pattern: string;
|
|
1540
|
+
/** EVAL / EVALSHA / FCALL — a script, kept by its hash. */
|
|
1541
|
+
script?: boolean;
|
|
1542
|
+
runs: number;
|
|
1543
|
+
/** GETs the tap saw answer a value, and none. */
|
|
1544
|
+
hits: number;
|
|
1545
|
+
misses: number;
|
|
1546
|
+
totalMs: number;
|
|
1547
|
+
p50?: number;
|
|
1548
|
+
p95?: number;
|
|
1549
|
+
maxMs: number;
|
|
1550
|
+
services: string[];
|
|
1551
|
+
/** Route catalog ids whose runs sent it, most recent first. */
|
|
1552
|
+
routes: string[];
|
|
1553
|
+
firstSeenAt: number;
|
|
1554
|
+
lastSeenAt: number;
|
|
1555
|
+
/** One real key of the shape — to open it on Keys. */
|
|
1556
|
+
sampleKey: string;
|
|
1557
|
+
}
|
|
1558
|
+
export interface DashKvCommandRun {
|
|
1559
|
+
shape: string;
|
|
1560
|
+
at: number;
|
|
1561
|
+
op: string;
|
|
1562
|
+
key: string;
|
|
1563
|
+
/** The command as sent, values sized or masked. */
|
|
1564
|
+
command: string;
|
|
1565
|
+
result?: 'hit' | 'miss';
|
|
1566
|
+
durationMs?: number;
|
|
1567
|
+
service: string;
|
|
1568
|
+
flowId?: string;
|
|
1569
|
+
route?: string;
|
|
1570
|
+
}
|
|
1571
|
+
/** `/api/catalog/kv` — every command shape the services sent, kept 30 days. */
|
|
1572
|
+
export interface DashKvCommands {
|
|
1573
|
+
at: number;
|
|
1574
|
+
shapes: DashKvCommandShape[];
|
|
1575
|
+
since?: number;
|
|
1576
|
+
}
|
|
1577
|
+
/** A model provider the lens can hold a key for. `systemone` is TypeSafe's typed-decision protocol (JEV; Laya later). */
|
|
1578
|
+
export type DashAiProvider = 'anthropic' | 'openai' | 'systemone';
|
|
1579
|
+
/** What a provider is, for the Add a token form. */
|
|
1580
|
+
export interface DashAiProviderInfo {
|
|
1581
|
+
id: DashAiProvider;
|
|
1582
|
+
/** `Anthropic API key`. */
|
|
1583
|
+
label: string;
|
|
1584
|
+
/** `Anthropic` — the kind line under a token's name. */
|
|
1585
|
+
short: string;
|
|
1586
|
+
/** `frontier` answers in text (System 2); `decisions` returns typed answers with confidence (System 1). */
|
|
1587
|
+
tier: 'frontier' | 'decisions';
|
|
1588
|
+
placeholder: string;
|
|
1589
|
+
help: string;
|
|
1590
|
+
/** Where the calls go unless the token names another endpoint (a gateway such as OpenRouter, or Laya). */
|
|
1591
|
+
defaultBaseUrl: string;
|
|
1592
|
+
/** Whether a token may name another endpoint. */
|
|
1593
|
+
baseUrlAllowed: boolean;
|
|
1594
|
+
defaultModel: string;
|
|
1595
|
+
/** Where to make a key. */
|
|
1596
|
+
console: string;
|
|
1597
|
+
}
|
|
1598
|
+
/** One key the lens holds — never its value: that stays in the keychain. */
|
|
1599
|
+
export interface DashAiToken {
|
|
1600
|
+
id: string;
|
|
1601
|
+
provider: DashAiProvider;
|
|
1602
|
+
label: string;
|
|
1603
|
+
/** `sk-ant-api03-…x7Qa`. */
|
|
1604
|
+
mask: string;
|
|
1605
|
+
/** Every workspace, or one workspace's key. */
|
|
1606
|
+
scope: 'all' | string;
|
|
1607
|
+
/** Usable in this workspace, and its place among those that are. */
|
|
1608
|
+
here: boolean;
|
|
1609
|
+
position?: number;
|
|
1610
|
+
/** The endpoint, when not the provider's own. */
|
|
1611
|
+
baseUrl?: string;
|
|
1612
|
+
model?: string;
|
|
1613
|
+
/** The models the provider listed at the last test. */
|
|
1614
|
+
models: string[];
|
|
1615
|
+
/** Monthly cap; spend this month, estimated from usage × the price table. */
|
|
1616
|
+
budgetUsd?: number;
|
|
1617
|
+
usedUsd: number;
|
|
1618
|
+
status: 'ok' | 'limited' | 'exhausted' | 'invalid' | 'unreachable' | 'unverified';
|
|
1619
|
+
note: string;
|
|
1620
|
+
/** The provider's organization and workspace the key belongs to, from its responses (Anthropic). */
|
|
1621
|
+
org?: string;
|
|
1622
|
+
workspace?: string;
|
|
1623
|
+
/** The workspace an identity-linked Anthropic key not scoped to one is sent to. */
|
|
1624
|
+
workspaceId?: string;
|
|
1625
|
+
/** Other keys of the same organization and workspace: they draw on one set of limits. */
|
|
1626
|
+
sharedWith: string[];
|
|
1627
|
+
/** What the key's limits hold now, per model it was used with — projected from the last response along the refill. */
|
|
1628
|
+
capacity: DashAiCapacity[];
|
|
1629
|
+
/** Cut off until then: rate-limited, or out of spend (a monthly cap, a self-set limit). */
|
|
1630
|
+
blocked?: {
|
|
1631
|
+
until: number;
|
|
1632
|
+
why: string;
|
|
1633
|
+
kind: 'rate' | 'spend';
|
|
1634
|
+
};
|
|
1635
|
+
/**
|
|
1636
|
+
* The credit on the key's account, as its console lists it — remaining, of how much, granted and expiring when —
|
|
1637
|
+
* less what the lens's calls cost since it was set (other use of the account is not seen). Shared by the keys of
|
|
1638
|
+
* one organization. A promotional grant is lost at its expiry: keys are spent by what they must spend a day not to
|
|
1639
|
+
* lose any.
|
|
1640
|
+
*/
|
|
1641
|
+
credit?: {
|
|
1642
|
+
/** Plan credit left plus purchased credit left. */
|
|
1643
|
+
leftUsd: number;
|
|
1644
|
+
/** Plan ("Promotional credits", spent first, lost at its expiry) and purchased (spent after, good for a year). */
|
|
1645
|
+
planUsd: number;
|
|
1646
|
+
purchasedUsd: number;
|
|
1647
|
+
/** What it must spend a day until it expires not to lose any — keys are spent in this order. */
|
|
1648
|
+
perDayUsd: number;
|
|
1649
|
+
out: boolean;
|
|
1650
|
+
expired: boolean;
|
|
1651
|
+
balanceUsd: number;
|
|
1652
|
+
setAt: number;
|
|
1653
|
+
spentUsd: number;
|
|
1654
|
+
grantUsd?: number;
|
|
1655
|
+
grantedAt?: number;
|
|
1656
|
+
expiresAt?: number;
|
|
1657
|
+
monthly?: boolean;
|
|
1658
|
+
nextGrantAt?: number;
|
|
1659
|
+
sharedWith: string[];
|
|
1660
|
+
};
|
|
1661
|
+
addedAt: number;
|
|
1662
|
+
testedAt?: number;
|
|
1663
|
+
lastUsedAt?: number;
|
|
1664
|
+
}
|
|
1665
|
+
/** What an analyzer suggests: what is wrong, why (from the evidence), the change, how to verify it. */
|
|
1666
|
+
export interface DashAiSuggestion {
|
|
1667
|
+
/** What the thing is, as the reviewer read it — a name and a description that later analyzers reuse (a route's review names its statements by them). */
|
|
1668
|
+
about?: {
|
|
1669
|
+
title: string;
|
|
1670
|
+
description: string;
|
|
1671
|
+
};
|
|
1672
|
+
summary: string;
|
|
1673
|
+
/** `fine` · `n+1` · `missing-index` · `slow-plan` · `over-fetch` · `lock-or-io` · `design` · `other`. */
|
|
1674
|
+
verdict: string;
|
|
1675
|
+
severity: 'none' | 'low' | 'medium' | 'high';
|
|
1676
|
+
/** The first finding's — the one most worth doing (or `Nothing to change`). */
|
|
1677
|
+
title: string;
|
|
1678
|
+
why: string[];
|
|
1679
|
+
change?: DashAiChange;
|
|
1680
|
+
verify: string;
|
|
1681
|
+
confidence: number;
|
|
1682
|
+
/** Every change the review asks for, most worth doing first. */
|
|
1683
|
+
findings?: DashAiFinding[];
|
|
1684
|
+
/** The statement as the reviewer would write it, when it would change. */
|
|
1685
|
+
rewrite?: {
|
|
1686
|
+
sql: string;
|
|
1687
|
+
why: string;
|
|
1688
|
+
};
|
|
1689
|
+
/** The analyzer's indicators, each 0–100 (100 is perfect) — what a decision model is later taught from. */
|
|
1690
|
+
indicators?: DashAiIndicator[];
|
|
1691
|
+
/** Who scored them: the frontier model, or the decision model when its answers stood. */
|
|
1692
|
+
scoredBy?: 'frontier' | 'decision';
|
|
1693
|
+
/** The indicators in one number, computed by the lens — under its threshold (70) a warning to the coding agents. */
|
|
1694
|
+
overall?: DashAiOverall;
|
|
1695
|
+
/** What evidence or approval the review was missing. */
|
|
1696
|
+
openQuestions?: string[];
|
|
1697
|
+
}
|
|
1698
|
+
/**
|
|
1699
|
+
* A review's indicators in one number, 0–100: their weighted mean — held at the score of a bug indicator (correct
|
|
1700
|
+
* results, security, locks) when that is lower, since one wrong result outweighs any number of fast ones.
|
|
1701
|
+
*/
|
|
1702
|
+
export interface DashAiOverall {
|
|
1703
|
+
score: number;
|
|
1704
|
+
/** Under the set's threshold (70): `db://lint` lists it, and coding agents are told. */
|
|
1705
|
+
warning: boolean;
|
|
1706
|
+
/** `weighted mean of 8` · `held at correct results (20)`. */
|
|
1707
|
+
why: string;
|
|
1708
|
+
/** The lowest indicators, lowest first. */
|
|
1709
|
+
lowest: string[];
|
|
1710
|
+
}
|
|
1711
|
+
/** One indicator a review scores, 0–100 where 100 is perfect — null when the evidence could not show it. */
|
|
1712
|
+
export interface DashAiIndicator {
|
|
1713
|
+
id: string;
|
|
1714
|
+
name: string;
|
|
1715
|
+
score: number | null;
|
|
1716
|
+
why: string;
|
|
1717
|
+
}
|
|
1718
|
+
/**
|
|
1719
|
+
* An analyzer's instruction file — Markdown kept beside the code, like Claude Code's skills and rules: the user's
|
|
1720
|
+
* (`~/.lensmcp/<analyzer>/*.md`, every project) or the project's (`.lensmcp/<analyzer>/*.md`).
|
|
1721
|
+
*/
|
|
1722
|
+
export interface DashAiInstructionFile {
|
|
1723
|
+
scope: 'user' | 'project';
|
|
1724
|
+
/** As shown: `~/.lensmcp/sql-review/conventions.md`, `.lensmcp/sql-review/registry.md`. */
|
|
1725
|
+
path: string;
|
|
1726
|
+
/** Its front matter's `description`. */
|
|
1727
|
+
description?: string;
|
|
1728
|
+
/** Its front matter's `tables`: only runs about one of these get it (`*` any characters). */
|
|
1729
|
+
tables?: string[];
|
|
1730
|
+
text: string;
|
|
1731
|
+
chars: number;
|
|
1732
|
+
sha: string;
|
|
1733
|
+
}
|
|
1734
|
+
/** What a run sent as its instructions: the built-in guide, the workspace's rules and memories, the indicators. */
|
|
1735
|
+
export interface DashAiPrompt {
|
|
1736
|
+
guide?: {
|
|
1737
|
+
id: string;
|
|
1738
|
+
version: number;
|
|
1739
|
+
sha: string;
|
|
1740
|
+
};
|
|
1741
|
+
/** The instruction files it was sent, user's then project's. */
|
|
1742
|
+
files?: Array<{
|
|
1743
|
+
scope: 'user' | 'project';
|
|
1744
|
+
path: string;
|
|
1745
|
+
sha: string;
|
|
1746
|
+
}>;
|
|
1747
|
+
rules?: {
|
|
1748
|
+
sha: string;
|
|
1749
|
+
chars: number;
|
|
1750
|
+
at: number;
|
|
1751
|
+
};
|
|
1752
|
+
memories: number;
|
|
1753
|
+
/** The indicator set and its version (`sql@1`). */
|
|
1754
|
+
indicators?: string;
|
|
1755
|
+
systemTokens: number;
|
|
1756
|
+
}
|
|
1757
|
+
/**
|
|
1758
|
+
* A workspace's note to one analyzer — kept for every worktree, in the workspace's history: its `rules` (one
|
|
1759
|
+
* document, written by its developers) or one thing it remembers (`memory`, from a developer or an agent). Both are
|
|
1760
|
+
* added after the built-in guide in every run, and win over it where they conflict.
|
|
1761
|
+
*/
|
|
1762
|
+
export interface DashAiNote {
|
|
1763
|
+
id: string;
|
|
1764
|
+
analyzer: string;
|
|
1765
|
+
kind: 'rules' | 'memory';
|
|
1766
|
+
/** A memory about one thing — a table, a statement id — goes only into runs about it. */
|
|
1767
|
+
subject?: string;
|
|
1768
|
+
text: string;
|
|
1769
|
+
/** `developer`, or `agent:<its client>`. */
|
|
1770
|
+
author: string;
|
|
1771
|
+
/** The checkout it was written from. */
|
|
1772
|
+
checkout: string;
|
|
1773
|
+
at: number;
|
|
1774
|
+
}
|
|
1775
|
+
/** One analyzer as Settings › Analyzers shows it: its guide, its indicators, the workspace's notes, what a run sends. */
|
|
1776
|
+
export interface DashAiAnalyzer {
|
|
1777
|
+
id: string;
|
|
1778
|
+
name: string;
|
|
1779
|
+
question: string;
|
|
1780
|
+
/** What it looks at, as a person would list it. */
|
|
1781
|
+
reads: string[];
|
|
1782
|
+
/** The kind of thing it reviews (`sql` — a statement), and the folder its instruction files are in. */
|
|
1783
|
+
target: string;
|
|
1784
|
+
folder?: string;
|
|
1785
|
+
/** Its runs in this checkout. */
|
|
1786
|
+
runs: number;
|
|
1787
|
+
guide?: {
|
|
1788
|
+
id: string;
|
|
1789
|
+
title: string;
|
|
1790
|
+
version: number;
|
|
1791
|
+
sha: string;
|
|
1792
|
+
text: string;
|
|
1793
|
+
};
|
|
1794
|
+
indicators: Array<{
|
|
1795
|
+
id: string;
|
|
1796
|
+
name: string;
|
|
1797
|
+
question: string;
|
|
1798
|
+
levels: string[];
|
|
1799
|
+
}>;
|
|
1800
|
+
indicatorSet?: string;
|
|
1801
|
+
rules?: DashAiNote;
|
|
1802
|
+
memories: DashAiNote[];
|
|
1803
|
+
/** Its instruction files in both scopes, the folders they are read from, and any skipped with why. */
|
|
1804
|
+
files: DashAiInstructionFile[];
|
|
1805
|
+
/** `ignored`: git ignores the project's folder, so its files do not reach the team (`.lensmcp/` instead of `.lensmcp/*`). */
|
|
1806
|
+
folders: Array<{
|
|
1807
|
+
scope: 'user' | 'project';
|
|
1808
|
+
path: string;
|
|
1809
|
+
ignored?: boolean;
|
|
1810
|
+
}>;
|
|
1811
|
+
skippedFiles: Array<{
|
|
1812
|
+
path: string;
|
|
1813
|
+
why: string;
|
|
1814
|
+
}>;
|
|
1815
|
+
/** The instructions every run of it sends now, before the evidence. */
|
|
1816
|
+
preview: {
|
|
1817
|
+
system: string;
|
|
1818
|
+
tokens: number;
|
|
1819
|
+
};
|
|
1820
|
+
lastRun?: {
|
|
1821
|
+
at: number;
|
|
1822
|
+
label: string;
|
|
1823
|
+
status: DashAiRun['status'];
|
|
1824
|
+
};
|
|
1825
|
+
/** What its reviews ask for, as chosen in Settings › Analyzers; absent, the defaults (`DashAiAnalyzers.defaults`). */
|
|
1826
|
+
choice?: {
|
|
1827
|
+
model?: string;
|
|
1828
|
+
effort?: DashAiEffort;
|
|
1829
|
+
};
|
|
1830
|
+
/** What its newest finished runs took, on average — what a run on another model is estimated from. */
|
|
1831
|
+
usage?: {
|
|
1832
|
+
runs: number;
|
|
1833
|
+
input: number;
|
|
1834
|
+
output: number;
|
|
1835
|
+
cacheRead: number;
|
|
1836
|
+
model: string;
|
|
1837
|
+
};
|
|
1838
|
+
}
|
|
1839
|
+
/** How hard a frontier model thinks (Claude 4.6 and later; other models are sent none). */
|
|
1840
|
+
export type DashAiEffort = 'low' | 'medium' | 'high' | 'xhigh' | 'max';
|
|
1841
|
+
export interface DashAiAnalyzers {
|
|
1842
|
+
workspace: string;
|
|
1843
|
+
/** False when there is no history to keep the notes in (Node before 24, or `LENSMCP_EVENT_HISTORY=0`). */
|
|
1844
|
+
notesKept: boolean;
|
|
1845
|
+
/** Where the notes are kept, or why they cannot be. */
|
|
1846
|
+
notesWhere: string;
|
|
1847
|
+
analyzers: DashAiAnalyzer[];
|
|
1848
|
+
/** An analyzer's model and effort can be chosen and kept (there is a workspace history). */
|
|
1849
|
+
choicesKept: boolean;
|
|
1850
|
+
/** The frontier models the keys list, in the keys' order — what an analyzer may ask for. */
|
|
1851
|
+
models: Array<{
|
|
1852
|
+
id: string;
|
|
1853
|
+
provider: DashAiProvider;
|
|
1854
|
+
keys: string[];
|
|
1855
|
+
effort: boolean;
|
|
1856
|
+
price?: {
|
|
1857
|
+
input: number;
|
|
1858
|
+
output: number;
|
|
1859
|
+
cacheRead?: number;
|
|
1860
|
+
};
|
|
1861
|
+
}>;
|
|
1862
|
+
/** Unset, a review runs on each key's own model (the first key's is `model`) at the workspace's effort. */
|
|
1863
|
+
defaults: {
|
|
1864
|
+
model?: string;
|
|
1865
|
+
modelKey?: string;
|
|
1866
|
+
effort: DashAiEffort;
|
|
1867
|
+
effortFrom: 'nx.json' | 'LensMCP';
|
|
1868
|
+
};
|
|
1869
|
+
}
|
|
1870
|
+
export interface DashAiChange {
|
|
1871
|
+
title: string;
|
|
1872
|
+
kind: 'sql' | 'index' | 'code' | 'config';
|
|
1873
|
+
lines: Array<['+' | '-' | ' ', string]>;
|
|
1874
|
+
}
|
|
1875
|
+
/** One change a review asks for. */
|
|
1876
|
+
export interface DashAiFinding {
|
|
1877
|
+
priority: 'critical' | 'high' | 'medium' | 'low';
|
|
1878
|
+
/** Seen in the evidence (`confirmed`), likely from it (`probable`), or a guess to check (`hypothesis`). */
|
|
1879
|
+
certainty?: 'confirmed' | 'probable' | 'hypothesis';
|
|
1880
|
+
/** The indicator it raises (`correctness`, `index_fit`…). */
|
|
1881
|
+
indicator?: string;
|
|
1882
|
+
/** What it changes: the statement, an index, the table's design, the data in it, the application's code, a setting, an endpoint's contract (its DTOs, statuses, headers), the gateway or proxy. */
|
|
1883
|
+
area: 'query' | 'index' | 'table' | 'data' | 'code' | 'config' | 'contract' | 'gateway' | 'network' | 'service' | 'cache' | 'queue' | 'ui' | 'state' | 'observability';
|
|
1884
|
+
title: string;
|
|
1885
|
+
why: string[];
|
|
1886
|
+
change?: DashAiChange;
|
|
1887
|
+
/** What it could break or cost: semantics, locks, write amplification, storage. */
|
|
1888
|
+
risk?: string;
|
|
1889
|
+
/** How to roll it out safely, and back. */
|
|
1890
|
+
deploy?: string;
|
|
1891
|
+
verify: string;
|
|
1892
|
+
}
|
|
1893
|
+
/** What a thing costs the app, measured by the lens before any model is asked; `low` holds every finding at low. */
|
|
1894
|
+
export interface DashAiImpact {
|
|
1895
|
+
level: 'low' | 'medium' | 'high';
|
|
1896
|
+
/** The numbers it rests on (`p95 3 ms, 12 runs, 40 ms of database time in 2 h`). */
|
|
1897
|
+
why: string;
|
|
1898
|
+
}
|
|
1899
|
+
/** One analyzer run (R30): its steps, what each tier answered and cost, what the model saw, the suggestion. */
|
|
1900
|
+
export interface DashAiRun {
|
|
1901
|
+
id: string;
|
|
1902
|
+
analyzer: string;
|
|
1903
|
+
analyzerName: string;
|
|
1904
|
+
question: string;
|
|
1905
|
+
/** What was reviewed; `name` is a statement's name (its leading comment) — the same across its versions. */
|
|
1906
|
+
target: {
|
|
1907
|
+
kind: 'sql' | 'route' | 'error' | 'flow' | 'ui' | 'kv' | 'queue';
|
|
1908
|
+
id: string;
|
|
1909
|
+
label: string;
|
|
1910
|
+
name?: string;
|
|
1911
|
+
};
|
|
1912
|
+
workspace: string;
|
|
1913
|
+
/** `queued`: waiting for a free slot (`queue` says how many are ahead); one job per target, at most N at once. */
|
|
1914
|
+
status: 'queued' | 'running' | 'done' | 'failed';
|
|
1915
|
+
/** Started by itself (Settings › Features — a route that changed, a statement once named); absent: someone asked. */
|
|
1916
|
+
trigger?: 'auto';
|
|
1917
|
+
queue?: {
|
|
1918
|
+
ahead: number;
|
|
1919
|
+
max: number;
|
|
1920
|
+
};
|
|
1921
|
+
step: string;
|
|
1922
|
+
steps: Array<{
|
|
1923
|
+
at: number;
|
|
1924
|
+
text: string;
|
|
1925
|
+
}>;
|
|
1926
|
+
startedAt: number;
|
|
1927
|
+
finishedAt?: number;
|
|
1928
|
+
costUsd: number;
|
|
1929
|
+
/** `summaries`: shapes, counts, timings and plans — never a run's values. */
|
|
1930
|
+
context: 'summaries' | 'full';
|
|
1931
|
+
/** Each source the models were shown, and its size in tokens (estimated). */
|
|
1932
|
+
saw: Array<{
|
|
1933
|
+
source: string;
|
|
1934
|
+
what: string;
|
|
1935
|
+
tokens: number;
|
|
1936
|
+
}>;
|
|
1937
|
+
/** System 1's typed answers, when there was a decision key. */
|
|
1938
|
+
decision?: {
|
|
1939
|
+
ledgerId: string;
|
|
1940
|
+
label: string;
|
|
1941
|
+
model: string;
|
|
1942
|
+
answers: Record<string, {
|
|
1943
|
+
type: string;
|
|
1944
|
+
value: boolean | string | number;
|
|
1945
|
+
probabilities: Record<string, number>;
|
|
1946
|
+
confidence: number;
|
|
1947
|
+
}>;
|
|
1948
|
+
costUsd?: number;
|
|
1949
|
+
};
|
|
1950
|
+
/** Why the question went on to a frontier model. */
|
|
1951
|
+
escalated?: string[];
|
|
1952
|
+
frontier?: {
|
|
1953
|
+
ledgerId: string;
|
|
1954
|
+
label: string;
|
|
1955
|
+
model: string;
|
|
1956
|
+
latencyMs: number;
|
|
1957
|
+
usage: {
|
|
1958
|
+
input: number;
|
|
1959
|
+
output: number;
|
|
1960
|
+
cacheRead?: number;
|
|
1961
|
+
cacheWrite?: number;
|
|
1962
|
+
};
|
|
1963
|
+
costUsd?: number;
|
|
1964
|
+
skipped: Array<{
|
|
1965
|
+
tokenId: string;
|
|
1966
|
+
label: string;
|
|
1967
|
+
why: string;
|
|
1968
|
+
}>;
|
|
1969
|
+
turns?: number;
|
|
1970
|
+
};
|
|
1971
|
+
/**
|
|
1972
|
+
* How the frontier model worked, turn by turn, as it streamed: its reasoning (summarized), what it wrote, each tool it
|
|
1973
|
+
* called with its input and what the tool returned. `live` marks a block still coming.
|
|
1974
|
+
*/
|
|
1975
|
+
transcript?: DashAiTranscriptEntry[];
|
|
1976
|
+
/** What the frontier model asked for during the review (its tools), in order: what each returned, how long it took. */
|
|
1977
|
+
asked?: Array<{
|
|
1978
|
+
tool: string;
|
|
1979
|
+
input: string;
|
|
1980
|
+
ok: boolean;
|
|
1981
|
+
ms: number;
|
|
1982
|
+
tokens: number;
|
|
1983
|
+
what: string;
|
|
1984
|
+
}>;
|
|
1985
|
+
/** What the lens did before the review: statistics refreshed, the plan measured — or why not. */
|
|
1986
|
+
prepared?: string[];
|
|
1987
|
+
/** Other analyzers' reviews it was shown: a route's statements, each with its SQL review as it stood. */
|
|
1988
|
+
reused?: DashAiReuse[];
|
|
1989
|
+
/** The reviews it had run before it (a route's statements' SQL reviews), as each went. */
|
|
1990
|
+
ranFirst?: DashAiRanFirst[];
|
|
1991
|
+
suggestion?: DashAiSuggestion;
|
|
1992
|
+
impact?: DashAiImpact;
|
|
1993
|
+
/** The instructions it sent: which guide, the workspace's rules and memories, the indicator set. */
|
|
1994
|
+
prompt?: DashAiPrompt;
|
|
1995
|
+
/**
|
|
1996
|
+
* What its answer rests on, as fingerprints: the thing's definition (`schema` — a statement's tables, columns,
|
|
1997
|
+
* constraints and indexes; not their counters) and the instructions (guide, indicators, rules). Another analyzer
|
|
1998
|
+
* reuses the review while both still match, instead of asking again.
|
|
1999
|
+
*/
|
|
2000
|
+
basis?: {
|
|
2001
|
+
schema?: string;
|
|
2002
|
+
instructions: string;
|
|
2003
|
+
};
|
|
2004
|
+
error?: string;
|
|
2005
|
+
}
|
|
2006
|
+
/**
|
|
2007
|
+
* An analysis running or waiting now, in the workspace's shared queue: one per `key` (the analyzer and its target),
|
|
2008
|
+
* at most the workspace's limit running together, the rest waiting in the order they came.
|
|
2009
|
+
*/
|
|
2010
|
+
export interface DashAiJob {
|
|
2011
|
+
key: string;
|
|
2012
|
+
runId: string;
|
|
2013
|
+
analyzer: string;
|
|
2014
|
+
/** `sql:<id>`. */
|
|
2015
|
+
target: string;
|
|
2016
|
+
label: string;
|
|
2017
|
+
checkout: string;
|
|
2018
|
+
/** The lens process that runs it (`<pid>@<host> <role>`). */
|
|
2019
|
+
owner: string;
|
|
2020
|
+
state: 'queued' | 'running';
|
|
2021
|
+
queuedAt: number;
|
|
2022
|
+
startedAt?: number;
|
|
2023
|
+
beatAt: number;
|
|
2024
|
+
}
|
|
2025
|
+
/** The workspace's analyses now: the limit, those running, those waiting with how many are ahead of each. */
|
|
2026
|
+
export interface DashAiQueue {
|
|
2027
|
+
max: number;
|
|
2028
|
+
running: DashAiJob[];
|
|
2029
|
+
queued: Array<DashAiJob & {
|
|
2030
|
+
ahead: number;
|
|
2031
|
+
}>;
|
|
2032
|
+
}
|
|
2033
|
+
/**
|
|
2034
|
+
* Another analyzer's review of a part, as a run was shown it — a route's statement and its SQL review. `current`: its
|
|
2035
|
+
* review held; `now`: reviewed for this run, just before it; `stale`: only an older review, shown as such; `none`:
|
|
2036
|
+
* not reviewed (`why`: no name, past the limit, or the review failed).
|
|
2037
|
+
*/
|
|
2038
|
+
export interface DashAiReuse {
|
|
2039
|
+
target: DashAiRun['target'];
|
|
2040
|
+
state: 'current' | 'now' | 'stale' | 'none';
|
|
2041
|
+
why?: string;
|
|
2042
|
+
runId?: string;
|
|
2043
|
+
overall?: DashAiOverall;
|
|
2044
|
+
about?: {
|
|
2045
|
+
title: string;
|
|
2046
|
+
description: string;
|
|
2047
|
+
};
|
|
2048
|
+
findings?: number;
|
|
2049
|
+
}
|
|
2050
|
+
/** A review run before another — a route's statement's SQL review — as it goes: its run, and its score once done. */
|
|
2051
|
+
export interface DashAiRanFirst {
|
|
2052
|
+
target: DashAiRun['target'];
|
|
2053
|
+
state: 'waiting' | 'running' | 'done' | 'failed';
|
|
2054
|
+
runId?: string;
|
|
2055
|
+
overall?: number;
|
|
2056
|
+
why?: string;
|
|
2057
|
+
}
|
|
2058
|
+
/** A finished review as a list shows it: its overall score (under 70 a warning), when, and its run. */
|
|
2059
|
+
export interface DashReviewMark {
|
|
2060
|
+
runId: string;
|
|
2061
|
+
at: number;
|
|
2062
|
+
overall?: number;
|
|
2063
|
+
warning?: boolean;
|
|
2064
|
+
}
|
|
2065
|
+
/** What became of a suggestion: new, accepted (for a coding agent to do), applied, or dismissed. */
|
|
2066
|
+
export type DashSuggestionState = 'new' | 'accepted' | 'applied' | 'dismissed';
|
|
2067
|
+
/**
|
|
2068
|
+
* One suggestion: a change a review asked for (a finding of the newest finished review of its target), with what
|
|
2069
|
+
* became of it. A later review of the same target that asks for the same change keeps its state (`key`).
|
|
2070
|
+
*/
|
|
2071
|
+
export interface DashSuggestion {
|
|
2072
|
+
/** `<runId>#<n>` — the run, and the finding's place in it. */
|
|
2073
|
+
id: string;
|
|
2074
|
+
/** The finding as the state follows it: its analyzer, its target, its title with numbers folded. */
|
|
2075
|
+
key: string;
|
|
2076
|
+
runId: string;
|
|
2077
|
+
analyzer: string;
|
|
2078
|
+
analyzerName: string;
|
|
2079
|
+
target: DashAiRun['target'];
|
|
2080
|
+
/** What the reviewed thing is, as the review named it. */
|
|
2081
|
+
about?: string;
|
|
2082
|
+
finding: DashAiFinding;
|
|
2083
|
+
/** When the review that asked for it finished. */
|
|
2084
|
+
at: number;
|
|
2085
|
+
state: DashSuggestionState;
|
|
2086
|
+
/** When the state was set, by whom (`developer`, or a coding agent's session), and why. */
|
|
2087
|
+
stateAt?: number;
|
|
2088
|
+
by?: string;
|
|
2089
|
+
note?: string;
|
|
2090
|
+
}
|
|
2091
|
+
/** `/api/ai/suggestions` and `agent://suggestions`: every open suggestion, with what became of each. */
|
|
2092
|
+
export interface DashSuggestions {
|
|
2093
|
+
at: number;
|
|
2094
|
+
suggestions: DashSuggestion[];
|
|
2095
|
+
counts: Record<DashSuggestionState, number>;
|
|
2096
|
+
/** What coding agents are shown (`nx.json` → `lensmcp.ai.agentFeed`): accepted suggestions only, or new ones too. */
|
|
2097
|
+
feed: 'accepted' | 'all';
|
|
2098
|
+
}
|
|
2099
|
+
/** One issue of `kv://lint`: a Redis / Valkey key family against what a coding agent fixes. */
|
|
2100
|
+
export interface DashKvLintIssue {
|
|
2101
|
+
severity: 'error' | 'warning';
|
|
2102
|
+
rule: 'secret-in-key' | 'review-hole' | 'counter-unbounded' | 'no-expiry' | 'ttl-dropped' | 'persist' | 'oversized-value' | 'personal-data-in-key' | 'miss-heavy' | 'review-below';
|
|
2103
|
+
/** The key family: the pattern with its ids folded (`session:*`). */
|
|
2104
|
+
pattern: string;
|
|
2105
|
+
/** The commands the services sent against it (`GET`, `INCRBY` …), its runs, who sent them, when last. */
|
|
2106
|
+
ops: string[];
|
|
2107
|
+
runs: number;
|
|
2108
|
+
services: string[];
|
|
2109
|
+
lastSeenAt: number;
|
|
2110
|
+
message: string;
|
|
2111
|
+
/** What to do, for a coding agent. */
|
|
2112
|
+
fix: string;
|
|
2113
|
+
/** For an issue from its cache review: the review, and the indicators it rests on. */
|
|
2114
|
+
review?: {
|
|
2115
|
+
runId: string;
|
|
2116
|
+
at: number;
|
|
2117
|
+
overall?: number;
|
|
2118
|
+
title?: string;
|
|
2119
|
+
top?: string;
|
|
2120
|
+
low?: Array<{
|
|
2121
|
+
id: string;
|
|
2122
|
+
score: number;
|
|
2123
|
+
}>;
|
|
2124
|
+
};
|
|
2125
|
+
/** How to check it: the review tool, the keys to read. */
|
|
2126
|
+
check: string[];
|
|
2127
|
+
}
|
|
2128
|
+
/**
|
|
2129
|
+
* `kv://lint`: the key families the services used, against what a coding agent fixes — from their commands, a sample
|
|
2130
|
+
* of their keys in the declared stores (read-only) and their cache reviews.
|
|
2131
|
+
*/
|
|
2132
|
+
export interface DashKvLint {
|
|
2133
|
+
at: number;
|
|
2134
|
+
warnBelow: number;
|
|
2135
|
+
holeBelow: number;
|
|
2136
|
+
errors: number;
|
|
2137
|
+
warnings: number;
|
|
2138
|
+
/** The families the lint looked at, how many have a cache review, and whether the store's keys were read. */
|
|
2139
|
+
families: number;
|
|
2140
|
+
reviewed: number;
|
|
2141
|
+
keysRead: boolean;
|
|
2142
|
+
/** Errors first, then warnings; the busiest first; at most 80 (`more` says how many were left out). */
|
|
2143
|
+
issues: DashKvLintIssue[];
|
|
2144
|
+
more: number;
|
|
2145
|
+
/** Every family with an issue, and how many of each. */
|
|
2146
|
+
byPattern: Record<string, {
|
|
2147
|
+
errors: number;
|
|
2148
|
+
warnings: number;
|
|
2149
|
+
}>;
|
|
2150
|
+
/** Each family's newest cache review, for its score in a list. */
|
|
2151
|
+
reviews: Record<string, DashReviewMark>;
|
|
2152
|
+
/** Why the store's keys were not read, when they were not. */
|
|
2153
|
+
unread?: string;
|
|
2154
|
+
}
|
|
2155
|
+
/** One issue of `queues://lint`: a BullMQ queue (or a family of per-run queues) against what a coding agent fixes. */
|
|
2156
|
+
export interface DashQueuesLintIssue {
|
|
2157
|
+
severity: 'error' | 'warning';
|
|
2158
|
+
rule: 'eviction' | 'no-worker' | 'failing-no-retry' | 'review-hole' | 'no-backoff' | 'retention-unbounded' | 'schedule-late' | 'stalls' | 'retries-exhausted' | 'paused-backlog' | 'review-below';
|
|
2159
|
+
/** `<store>/<db>/<queue>` — the queue the issue was read from (a family's busiest). */
|
|
2160
|
+
queue: string;
|
|
2161
|
+
name: string;
|
|
2162
|
+
/** `product-analysis-*` for queues made per run; the name otherwise. How many queues the family holds. */
|
|
2163
|
+
family: string;
|
|
2164
|
+
queues: number;
|
|
2165
|
+
counts: DashKvQueue['counts'];
|
|
2166
|
+
workers: number;
|
|
2167
|
+
message: string;
|
|
2168
|
+
fix: string;
|
|
2169
|
+
review?: {
|
|
2170
|
+
runId: string;
|
|
2171
|
+
at: number;
|
|
2172
|
+
overall?: number;
|
|
2173
|
+
title?: string;
|
|
2174
|
+
top?: string;
|
|
2175
|
+
low?: Array<{
|
|
2176
|
+
id: string;
|
|
2177
|
+
score: number;
|
|
2178
|
+
}>;
|
|
2179
|
+
};
|
|
2180
|
+
check: string[];
|
|
2181
|
+
}
|
|
2182
|
+
/** `queues://lint`: the BullMQ queues in the declared stores, against what a coding agent fixes — read-only. */
|
|
2183
|
+
export interface DashQueuesLint {
|
|
2184
|
+
at: number;
|
|
2185
|
+
warnBelow: number;
|
|
2186
|
+
holeBelow: number;
|
|
2187
|
+
errors: number;
|
|
2188
|
+
warnings: number;
|
|
2189
|
+
/** The queue families the lint looked at, and how many have a queue review. */
|
|
2190
|
+
families: number;
|
|
2191
|
+
reviewed: number;
|
|
2192
|
+
issues: DashQueuesLintIssue[];
|
|
2193
|
+
more: number;
|
|
2194
|
+
/** Every queue with an issue (by `<store>/<db>/<queue>`, and by family), and how many of each. */
|
|
2195
|
+
byQueue: Record<string, {
|
|
2196
|
+
errors: number;
|
|
2197
|
+
warnings: number;
|
|
2198
|
+
}>;
|
|
2199
|
+
/** Each queue's newest queue review, for its score in a list. */
|
|
2200
|
+
reviews: Record<string, DashReviewMark>;
|
|
2201
|
+
/** Stores that could not be read, and why. */
|
|
2202
|
+
unread?: string[];
|
|
2203
|
+
}
|
|
2204
|
+
/** One block of how a frontier model worked: reasoning, text, or a tool call with its result. */
|
|
2205
|
+
export interface DashAiTranscriptEntry {
|
|
2206
|
+
/** The turn it came in (each tool round trip is a turn). */
|
|
2207
|
+
turn: number;
|
|
2208
|
+
kind: 'thinking' | 'text' | 'tool';
|
|
2209
|
+
/** The reasoning summary, the text, or the tool's input (JSON). */
|
|
2210
|
+
text: string;
|
|
2211
|
+
tool?: string;
|
|
2212
|
+
/** What the tool returned to the model (clipped), whether it worked, how long it took, and the lens's one-line account. */
|
|
2213
|
+
result?: string;
|
|
2214
|
+
ok?: boolean;
|
|
2215
|
+
ms?: number;
|
|
2216
|
+
what?: string;
|
|
2217
|
+
live?: true;
|
|
2218
|
+
}
|
|
2219
|
+
/** A finished review, as another analyzer (or an agent) reuses it: what the thing is, its scores, what to change. */
|
|
2220
|
+
export interface DashAiReviewDigest {
|
|
2221
|
+
target: DashAiRun['target'];
|
|
2222
|
+
runId: string;
|
|
2223
|
+
at: number;
|
|
2224
|
+
analyzer: string;
|
|
2225
|
+
/** The statement's name (its leading comment) — what follows it from one version to the next. */
|
|
2226
|
+
name?: string;
|
|
2227
|
+
about?: {
|
|
2228
|
+
title: string;
|
|
2229
|
+
description: string;
|
|
2230
|
+
};
|
|
2231
|
+
overall?: DashAiOverall;
|
|
2232
|
+
verdict: string;
|
|
2233
|
+
severity: DashAiSuggestion['severity'];
|
|
2234
|
+
indicators?: DashAiIndicator[];
|
|
2235
|
+
findings: Array<{
|
|
2236
|
+
priority: DashAiFinding['priority'];
|
|
2237
|
+
title: string;
|
|
2238
|
+
indicator?: string;
|
|
2239
|
+
}>;
|
|
2240
|
+
impact?: DashAiImpact;
|
|
2241
|
+
basis?: DashAiRun['basis'];
|
|
2242
|
+
}
|
|
2243
|
+
/** One model's limits for a key's pool: each window's size, what it holds now, and when it is full again. */
|
|
2244
|
+
export interface DashAiCapacity {
|
|
2245
|
+
model: string;
|
|
2246
|
+
observedAt: number;
|
|
2247
|
+
/** Of the tightest window, the share left (0–1). */
|
|
2248
|
+
headroom: number;
|
|
2249
|
+
windows: Array<{
|
|
2250
|
+
name: 'requests' | 'input' | 'output' | 'tokens';
|
|
2251
|
+
limit: number;
|
|
2252
|
+
remaining: number;
|
|
2253
|
+
fullAt?: number;
|
|
2254
|
+
}>;
|
|
2255
|
+
}
|
|
2256
|
+
/** `/api/ai/tokens` — the keys this workspace may use, in order, and where their values are kept. */
|
|
2257
|
+
export interface DashAiTokens {
|
|
2258
|
+
at: number;
|
|
2259
|
+
workspace: string;
|
|
2260
|
+
secrets: {
|
|
2261
|
+
kind: 'macos-keychain' | 'secret-service' | 'none';
|
|
2262
|
+
available: boolean;
|
|
2263
|
+
note?: string;
|
|
2264
|
+
};
|
|
2265
|
+
providers: DashAiProviderInfo[];
|
|
2266
|
+
tokens: DashAiToken[];
|
|
2267
|
+
/** What this workspace's model calls cost today (UTC, estimated), and its daily cap. */
|
|
2268
|
+
today: {
|
|
2269
|
+
usd: number;
|
|
2270
|
+
capUsd?: number;
|
|
2271
|
+
};
|
|
2272
|
+
}
|
|
2273
|
+
/** One path a service read or wrote, by operation — from the fs tap's per-second aggregates. */
|
|
2274
|
+
export interface DashFilePath {
|
|
2275
|
+
/** `<service> <op> <path>`. */
|
|
2276
|
+
id: string;
|
|
2277
|
+
/** Relative to the service's working directory when inside it. */
|
|
2278
|
+
path: string;
|
|
2279
|
+
op: 'read' | 'write' | 'append';
|
|
2280
|
+
service: string;
|
|
2281
|
+
/** Calls, and the bytes they moved (a stream's are not counted). */
|
|
2282
|
+
n: number;
|
|
2283
|
+
bytes: number;
|
|
2284
|
+
/** Bytes per call in the latest second it was seen — what one read or write moves. */
|
|
2285
|
+
lastBytes: number;
|
|
2286
|
+
stream?: boolean;
|
|
2287
|
+
firstSeenAt: number;
|
|
2288
|
+
lastSeenAt: number;
|
|
2289
|
+
/** The flow of its latest call, when it ran in one. */
|
|
2290
|
+
flowId?: string;
|
|
2291
|
+
/** Route catalog ids whose requests touched it, most recent first. */
|
|
2292
|
+
routes: string[];
|
|
2293
|
+
}
|
|
2294
|
+
/** `/api/catalog/files` — every path the services read or wrote, newest first, kept 30 days. */
|
|
2295
|
+
export interface DashFiles {
|
|
2296
|
+
at: number;
|
|
2297
|
+
paths: DashFilePath[];
|
|
2298
|
+
since?: number;
|
|
2299
|
+
}
|
|
2300
|
+
/** `/api/kv/stores` — every declared Redis / Valkey store, as its server describes itself (read-only). */
|
|
2301
|
+
export interface DashKvStores {
|
|
2302
|
+
at: number;
|
|
2303
|
+
errors: string[];
|
|
2304
|
+
stores: DashKvStore[];
|
|
2305
|
+
}
|
|
2306
|
+
export interface DashKvStore {
|
|
2307
|
+
name: string;
|
|
2308
|
+
/** `host:port`. */
|
|
2309
|
+
server: string;
|
|
2310
|
+
/** `Valkey 9.1.1`, `Redis 7.2.4`. */
|
|
2311
|
+
engine?: string;
|
|
2312
|
+
mode?: string;
|
|
2313
|
+
uptimeSec?: number;
|
|
2314
|
+
/** Bytes in use, and the cap (0: none). */
|
|
2315
|
+
memory?: number;
|
|
2316
|
+
maxMemory?: number;
|
|
2317
|
+
policy?: string;
|
|
2318
|
+
clients?: number;
|
|
2319
|
+
opsPerSec?: number;
|
|
2320
|
+
hits?: number;
|
|
2321
|
+
misses?: number;
|
|
2322
|
+
/** Every logical database holding keys; this checkout's (the URL's) marked. */
|
|
2323
|
+
dbs: Array<{
|
|
2324
|
+
db: number;
|
|
2325
|
+
keys: number;
|
|
2326
|
+
expires: number;
|
|
2327
|
+
mine: boolean;
|
|
2328
|
+
}>;
|
|
2329
|
+
/** BullMQ queues found by their `<prefix>:<queue>:meta` hash, in this checkout's database. */
|
|
2330
|
+
queues?: number;
|
|
2331
|
+
unread?: string;
|
|
2332
|
+
}
|
|
2333
|
+
/** One key as a list shows it. */
|
|
2334
|
+
export interface DashKvKeyRow {
|
|
2335
|
+
key: string;
|
|
2336
|
+
type: string;
|
|
2337
|
+
/** Milliseconds to expiry; -1 none. */
|
|
2338
|
+
ttlMs: number;
|
|
2339
|
+
bytes?: number;
|
|
2340
|
+
/** Fields, items, members — or a string's length. */
|
|
2341
|
+
length?: number;
|
|
2342
|
+
/** The value's start, secrets masked. */
|
|
2343
|
+
preview: string;
|
|
2344
|
+
/** A BullMQ job hash: its queue and id. */
|
|
2345
|
+
job?: {
|
|
2346
|
+
queue: string;
|
|
2347
|
+
id: string;
|
|
2348
|
+
};
|
|
2349
|
+
}
|
|
2350
|
+
/** `/api/kv/keys?store=&db=&match=&cursor=` — one page of a SCAN. */
|
|
2351
|
+
export interface DashKvKeys {
|
|
2352
|
+
at: number;
|
|
2353
|
+
store: string;
|
|
2354
|
+
db: number;
|
|
2355
|
+
match: string;
|
|
2356
|
+
/** Where the next page starts; `0` when the scan went through every key. */
|
|
2357
|
+
cursor: string;
|
|
2358
|
+
/** Keys the server looked at for this page (SCAN visits more than it returns under a MATCH). */
|
|
2359
|
+
scanned: number;
|
|
2360
|
+
/** Keys in the database (DBSIZE). */
|
|
2361
|
+
total: number;
|
|
2362
|
+
keys: DashKvKeyRow[];
|
|
2363
|
+
error?: string;
|
|
2364
|
+
}
|
|
2365
|
+
/** `/api/kv/prefixes?store=&db=` — what the keys look like: their patterns, ids folded to `*`, from a sample. */
|
|
2366
|
+
export interface DashKvPrefixes {
|
|
2367
|
+
at: number;
|
|
2368
|
+
store: string;
|
|
2369
|
+
db: number;
|
|
2370
|
+
sampled: number;
|
|
2371
|
+
total: number;
|
|
2372
|
+
/**
|
|
2373
|
+
* Each pattern with its keys in the sample and its type; for the busiest, a few of its keys described — how many of
|
|
2374
|
+
* those have no expiry, and the largest one's memory (`MEMORY USAGE`).
|
|
2375
|
+
*/
|
|
2376
|
+
patterns: Array<{
|
|
2377
|
+
pattern: string;
|
|
2378
|
+
keys: number;
|
|
2379
|
+
type?: string;
|
|
2380
|
+
described?: number;
|
|
2381
|
+
noExpiry?: number;
|
|
2382
|
+
maxBytes?: number;
|
|
2383
|
+
}>;
|
|
2384
|
+
error?: string;
|
|
2385
|
+
}
|
|
2386
|
+
/** `/api/kv/key?store=&db=&key=` — one key in full (bounded), secrets masked. */
|
|
2387
|
+
export interface DashKvKey {
|
|
2388
|
+
at: number;
|
|
2389
|
+
store: string;
|
|
2390
|
+
db: number;
|
|
2391
|
+
key: string;
|
|
2392
|
+
type: string;
|
|
2393
|
+
ttlMs: number;
|
|
2394
|
+
bytes?: number;
|
|
2395
|
+
encoding?: string;
|
|
2396
|
+
length: number;
|
|
2397
|
+
/** Field → value, index → item, member → score, id → entry; a string is one row. */
|
|
2398
|
+
rows: Array<{
|
|
2399
|
+
a: string;
|
|
2400
|
+
b: string;
|
|
2401
|
+
}>;
|
|
2402
|
+
/** Only the first rows are shown. */
|
|
2403
|
+
truncated: boolean;
|
|
2404
|
+
job?: DashKvJob;
|
|
2405
|
+
error?: string;
|
|
2406
|
+
}
|
|
2407
|
+
export interface DashKvQueue {
|
|
2408
|
+
name: string;
|
|
2409
|
+
/** `product-analysis-*` when the name holds an id — the queues made per run fold under it. */
|
|
2410
|
+
family: string;
|
|
2411
|
+
prefix: string;
|
|
2412
|
+
counts: {
|
|
2413
|
+
waiting: number;
|
|
2414
|
+
active: number;
|
|
2415
|
+
delayed: number;
|
|
2416
|
+
prioritized: number;
|
|
2417
|
+
paused: number;
|
|
2418
|
+
waitingChildren: number;
|
|
2419
|
+
completed: number;
|
|
2420
|
+
failed: number;
|
|
2421
|
+
};
|
|
2422
|
+
version?: string;
|
|
2423
|
+
paused: boolean;
|
|
2424
|
+
/** Worker connections, from CLIENT LIST. */
|
|
2425
|
+
workers: number;
|
|
2426
|
+
}
|
|
2427
|
+
/** `/api/kv/queues?store=&db=` — the BullMQ queues in a database. */
|
|
2428
|
+
export interface DashKvQueues {
|
|
2429
|
+
at: number;
|
|
2430
|
+
store: string;
|
|
2431
|
+
db: number;
|
|
2432
|
+
queues: DashKvQueue[];
|
|
2433
|
+
error?: string;
|
|
2434
|
+
}
|
|
2435
|
+
/**
|
|
2436
|
+
* `/api/kv/queue?store=&db=&queue=` — how one BullMQ queue is set up and what its own event stream says: its meta hash,
|
|
2437
|
+
* its job schedulers (recurring work) and the newest entries of its QueueEvents stream. Read-only, values masked.
|
|
2438
|
+
*/
|
|
2439
|
+
export interface DashKvQueueDetail {
|
|
2440
|
+
at: number;
|
|
2441
|
+
store: string;
|
|
2442
|
+
db: number;
|
|
2443
|
+
queue: string;
|
|
2444
|
+
prefix?: string;
|
|
2445
|
+
/** The `<prefix>:<queue>:meta` hash as BullMQ keeps it: `version`, `opts.maxLenEvents`, `paused`, and — when set — the global `concurrency` and rate limit (`max` jobs per `duration` ms). */
|
|
2446
|
+
meta: Record<string, string>;
|
|
2447
|
+
/** The `repeat` set's members — job schedulers, next run first — with what each one's hash says. */
|
|
2448
|
+
schedulers: Array<{
|
|
2449
|
+
id: string;
|
|
2450
|
+
next?: number;
|
|
2451
|
+
name?: string;
|
|
2452
|
+
pattern?: string;
|
|
2453
|
+
every?: number;
|
|
2454
|
+
tz?: string;
|
|
2455
|
+
limit?: number;
|
|
2456
|
+
endDate?: number;
|
|
2457
|
+
count?: number;
|
|
2458
|
+
opts?: string;
|
|
2459
|
+
}>;
|
|
2460
|
+
schedulersTotal: number;
|
|
2461
|
+
/** The `<prefix>:<queue>:events` stream: entries kept, how many were read (newest first), the window they span, each event's count, and the failures by reason. */
|
|
2462
|
+
events: {
|
|
2463
|
+
kept: number;
|
|
2464
|
+
read: number;
|
|
2465
|
+
from?: number;
|
|
2466
|
+
to?: number;
|
|
2467
|
+
counts: Record<string, number>;
|
|
2468
|
+
failures: Array<{
|
|
2469
|
+
reason: string;
|
|
2470
|
+
count: number;
|
|
2471
|
+
attempts: number[];
|
|
2472
|
+
}>;
|
|
2473
|
+
};
|
|
2474
|
+
error?: string;
|
|
2475
|
+
}
|
|
2476
|
+
export interface DashKvJob {
|
|
2477
|
+
queue: string;
|
|
2478
|
+
id: string;
|
|
2479
|
+
name: string;
|
|
2480
|
+
state: 'waiting' | 'active' | 'delayed' | 'prioritized' | 'paused' | 'waiting-children' | 'completed' | 'failed' | 'unknown';
|
|
2481
|
+
/** Its data, as JSON, secrets masked, cut at 4 KB. */
|
|
2482
|
+
data: string;
|
|
2483
|
+
opts?: string;
|
|
2484
|
+
addedAt?: number;
|
|
2485
|
+
processedAt?: number;
|
|
2486
|
+
finishedAt?: number;
|
|
2487
|
+
delayMs?: number;
|
|
2488
|
+
attemptsMade: number;
|
|
2489
|
+
attemptsLimit?: number;
|
|
2490
|
+
failedReason?: string;
|
|
2491
|
+
stacktrace?: string[];
|
|
2492
|
+
returnValue?: string;
|
|
2493
|
+
progress?: string;
|
|
2494
|
+
/** The flow it ran in (the lens stamps one on jobs it sees enqueued; else `job:<queue>#<id>`). */
|
|
2495
|
+
flowId?: string;
|
|
2496
|
+
}
|
|
2497
|
+
/** `/api/kv/jobs?store=&db=&queue=&state=&offset=` — a queue's jobs in one state, newest first, and its history. */
|
|
2498
|
+
export interface DashKvJobs {
|
|
2499
|
+
at: number;
|
|
2500
|
+
store: string;
|
|
2501
|
+
db: number;
|
|
2502
|
+
queue: string;
|
|
2503
|
+
state: string;
|
|
2504
|
+
total: number;
|
|
2505
|
+
offset: number;
|
|
2506
|
+
jobs: DashKvJob[];
|
|
2507
|
+
/** Finished jobs per minute, newest last — from the scores of the completed and failed sets (what BullMQ keeps). */
|
|
2508
|
+
history: {
|
|
2509
|
+
binMs: number;
|
|
2510
|
+
from: number;
|
|
2511
|
+
completed: number[];
|
|
2512
|
+
failed: number[];
|
|
2513
|
+
};
|
|
2514
|
+
error?: string;
|
|
2515
|
+
}
|
|
2516
|
+
/** `/api/db/plan?id=` — how Postgres would run one statement: `EXPLAIN` (generic plan), never `ANALYZE`. */
|
|
2517
|
+
/**
|
|
2518
|
+
* What a statement returned, or a sample of a table's rows — read-only, rolled back, at most a few rows, each value
|
|
2519
|
+
* clipped and a column named like a credential masked. Read only for a review in a workspace that allows rows
|
|
2520
|
+
* (`nx.json` → `lensmcp.ai.rows`): the rows go to the model provider.
|
|
2521
|
+
*/
|
|
2522
|
+
export interface DashDbRows {
|
|
2523
|
+
at: number;
|
|
2524
|
+
store?: string;
|
|
2525
|
+
database?: string;
|
|
2526
|
+
state: 'ok' | 'unavailable' | 'error';
|
|
2527
|
+
reason?: string;
|
|
2528
|
+
columns: string[];
|
|
2529
|
+
rows: string[][];
|
|
2530
|
+
/** More rows than were read. */
|
|
2531
|
+
more: boolean;
|
|
2532
|
+
/** The columns whose values were masked (named like a credential). */
|
|
2533
|
+
masked: string[];
|
|
2534
|
+
ms?: number;
|
|
2535
|
+
}
|
|
2536
|
+
/**
|
|
2537
|
+
* A statement's whole text: the tap's, when it sent it whole — else, for one it cut at 500 characters, the text
|
|
2538
|
+
* pg_stat_statements keeps of it (its constants as `$n`) — or why there is none.
|
|
2539
|
+
*/
|
|
2540
|
+
export type DashDbWhole = {
|
|
2541
|
+
text: string;
|
|
2542
|
+
from: 'tap' | 'pg_stat_statements';
|
|
2543
|
+
} | {
|
|
2544
|
+
reason: string;
|
|
2545
|
+
};
|
|
2546
|
+
export interface DashDbPlan {
|
|
2547
|
+
/** The whole text it planned, when the tap had cut it and pg_stat_statements kept it whole. */
|
|
2548
|
+
statement?: string;
|
|
2549
|
+
statementFrom?: 'pg_stat_statements';
|
|
2550
|
+
at: number;
|
|
2551
|
+
id: string;
|
|
2552
|
+
store?: string;
|
|
2553
|
+
database?: string;
|
|
2554
|
+
state: 'ok' | 'unavailable' | 'error';
|
|
2555
|
+
/** Why there is no plan (`the statement reached the lens cut at 500 characters`…), or Postgres's error. */
|
|
2556
|
+
reason?: string;
|
|
2557
|
+
/** A plan for any parameter values (Postgres 16+ `GENERIC_PLAN`), not for one run's values. */
|
|
2558
|
+
generic?: boolean;
|
|
2559
|
+
/** Measured: the statement ran (`EXPLAIN ANALYZE`, read-only, rolled back) with one recorded run's values. */
|
|
2560
|
+
measured?: {
|
|
2561
|
+
executionMs: number;
|
|
2562
|
+
planningMs: number;
|
|
2563
|
+
run: {
|
|
2564
|
+
at: number;
|
|
2565
|
+
durationMs?: number;
|
|
2566
|
+
rows?: number;
|
|
2567
|
+
route?: string;
|
|
2568
|
+
};
|
|
2569
|
+
};
|
|
2570
|
+
totalCost?: number;
|
|
2571
|
+
rows?: number;
|
|
2572
|
+
nodes: DashDbPlanNode[];
|
|
2573
|
+
/** What in the plan is worth a look (`a Seq Scan reads all of business_facts — ≈1.2M rows — to keep some`). */
|
|
2574
|
+
warnings: string[];
|
|
2575
|
+
}
|
|
2576
|
+
export interface DashDbPlanNode {
|
|
2577
|
+
depth: number;
|
|
2578
|
+
/** `Seq Scan`, `Index Scan`, `Hash Join`… */
|
|
2579
|
+
type: string;
|
|
2580
|
+
relation?: string;
|
|
2581
|
+
index?: string;
|
|
2582
|
+
/** Postgres's estimate for this step: the total cost and the rows it expects. */
|
|
2583
|
+
cost: number;
|
|
2584
|
+
rows: number;
|
|
2585
|
+
/** `Filter: (tenant_id = $1)`, `Index Cond: …`, `Sort Key: …`. */
|
|
2586
|
+
detail: string[];
|
|
2587
|
+
/** Measured plans only: the rows it produced (per loop), how many times it ran, its time over all loops, buffers. */
|
|
2588
|
+
actualRows?: number;
|
|
2589
|
+
loops?: number;
|
|
2590
|
+
ms?: number;
|
|
2591
|
+
hitBlocks?: number;
|
|
2592
|
+
readBlocks?: number;
|
|
2593
|
+
tempBlocks?: number;
|
|
2594
|
+
}
|
|
2595
|
+
/** What the lens did to a statement's database before a review: statistics refreshed, and why any were not. */
|
|
2596
|
+
export interface DashDbPrep {
|
|
2597
|
+
/** `schema.table — never analyzed` / `— 1,240 changes since its last ANALYZE`. */
|
|
2598
|
+
analyzed: string[];
|
|
2599
|
+
/** Tables whose statistics were fresh enough. */
|
|
2600
|
+
fresh: string[];
|
|
2601
|
+
/** Why it could not (the role may not ANALYZE, `lens.prepare: false`…). */
|
|
2602
|
+
refused?: string;
|
|
2603
|
+
}
|
|
2604
|
+
/** One thing an agent did through the lens: a tool call, a resource read, what it said it is doing. */
|
|
2605
|
+
export interface DashAgentEvent {
|
|
2606
|
+
at: number;
|
|
2607
|
+
kind: 'tool' | 'read' | 'say';
|
|
2608
|
+
/** The tool or the resource URI; `agent.task` for `say`. */
|
|
2609
|
+
name: string;
|
|
2610
|
+
/** Its arguments, redacted, as JSON (at most 2 KB). */
|
|
2611
|
+
args?: string;
|
|
2612
|
+
ms?: number;
|
|
2613
|
+
/** The answer's size. */
|
|
2614
|
+
bytes?: number;
|
|
2615
|
+
isError?: boolean;
|
|
2616
|
+
/** What the answer said first, a line (a tool's first text; a read's MIME type). */
|
|
2617
|
+
result?: string;
|
|
2618
|
+
}
|
|
2619
|
+
/** One MCP session — one agent connected through the lensmcp plugin and the shim to this lens. */
|
|
2620
|
+
export interface DashAgentSession {
|
|
2621
|
+
/** A digest of its MCP session id (never the id itself). */
|
|
2622
|
+
id: string;
|
|
2623
|
+
client: {
|
|
2624
|
+
name: string;
|
|
2625
|
+
version?: string;
|
|
2626
|
+
};
|
|
2627
|
+
/** The lens server it talks to: the worktree's key. */
|
|
2628
|
+
server: string;
|
|
2629
|
+
state: 'live' | 'idle' | 'ended';
|
|
2630
|
+
startedAt: number;
|
|
2631
|
+
lastSeenAt: number;
|
|
2632
|
+
/** Ended: when its shim exited (or, without one, its last call). */
|
|
2633
|
+
endedAt?: number;
|
|
2634
|
+
/** What it says it is doing (`agent.task`): the task's first line, and the step it is on. */
|
|
2635
|
+
task?: string;
|
|
2636
|
+
step?: string;
|
|
2637
|
+
saidAt?: number;
|
|
2638
|
+
conn: {
|
|
2639
|
+
transport: string;
|
|
2640
|
+
/** The lens server's pid, and the shim's (the `lensmcp mcp` process the client started), when it said. */
|
|
2641
|
+
pid: number;
|
|
2642
|
+
shim?: number;
|
|
2643
|
+
protocol?: string;
|
|
2644
|
+
/** New MCP sessions the shim opened for it (a server restart, a replayed `initialize`). */
|
|
2645
|
+
reconnects: number;
|
|
2646
|
+
calls: number;
|
|
2647
|
+
reads: number;
|
|
2648
|
+
errors: number;
|
|
2649
|
+
lastMs?: number;
|
|
2650
|
+
p95Ms?: number;
|
|
2651
|
+
};
|
|
2652
|
+
/** Newest last, at most 500. */
|
|
2653
|
+
events: DashAgentEvent[];
|
|
2654
|
+
}
|
|
2655
|
+
export interface DashAgentSessions {
|
|
2656
|
+
at: number;
|
|
2657
|
+
/** Live first, then by last seen; at most 20 per server. */
|
|
2658
|
+
sessions: DashAgentSession[];
|
|
2659
|
+
}
|