@lensmcp/protocol-types 1.25.5 → 1.25.6
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 +1 -0
- package/index.js +1 -1
- package/lib/dashboard.d.ts +125 -6
- package/lib/dashboard.js +1 -1
- package/lib/http-exchange.d.ts +8 -4
- package/lib/sources.d.ts +225 -0
- package/lib/sources.js +1 -0
- package/package.json +1 -1
package/index.d.ts
CHANGED
package/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";export*from"./lib/schema-version.js";export*from"./lib/source-location.js";export*from"./lib/context.js";export*from"./lib/event.js";export*from"./lib/node.js";export*from"./lib/edge.js";export*from"./lib/attributes.js";export*from"./lib/producer-health.js";export*from"./lib/resources.js";export*from"./lib/tokens.js";export*from"./lib/capture-demand.js";export*from"./lib/bounded-clone.js";export*from"./lib/bounded-map.js";export*from"./lib/flood-guard.js";export*from"./lib/dashboard.js";export*from"./lib/log-query.js";export*from"./lib/error-dismissal.js";export*from"./lib/ai-content.js";export*from"./lib/http-exchange.js";export*from"./lib/pod-memory.js";export*from"./lib/perf-rules.js";
|
|
1
|
+
"use strict";export*from"./lib/schema-version.js";export*from"./lib/source-location.js";export*from"./lib/context.js";export*from"./lib/event.js";export*from"./lib/node.js";export*from"./lib/edge.js";export*from"./lib/attributes.js";export*from"./lib/producer-health.js";export*from"./lib/resources.js";export*from"./lib/tokens.js";export*from"./lib/capture-demand.js";export*from"./lib/bounded-clone.js";export*from"./lib/bounded-map.js";export*from"./lib/flood-guard.js";export*from"./lib/dashboard.js";export*from"./lib/log-query.js";export*from"./lib/error-dismissal.js";export*from"./lib/ai-content.js";export*from"./lib/http-exchange.js";export*from"./lib/pod-memory.js";export*from"./lib/perf-rules.js";export*from"./lib/sources.js";
|
package/lib/dashboard.d.ts
CHANGED
|
@@ -656,6 +656,19 @@ export interface DashDiagnostics {
|
|
|
656
656
|
slow: number;
|
|
657
657
|
warnings: number;
|
|
658
658
|
};
|
|
659
|
+
/**
|
|
660
|
+
* Set on the stream's frame, which carries only what a page lists: the findings of a {@link DASH_PERF_KINDS} kind
|
|
661
|
+
* (failures are the Errors screen's, from `DashErrors`), the open ones and then the recently resolved ones, each up
|
|
662
|
+
* to a cap. These are their numbers before the cap — `open - problems.filter(open).length` is a list's «N more».
|
|
663
|
+
* Absent from older servers, whose frame held every problem.
|
|
664
|
+
*/
|
|
665
|
+
totals?: {
|
|
666
|
+
open: number;
|
|
667
|
+
resolved: number;
|
|
668
|
+
high: number;
|
|
669
|
+
medium: number;
|
|
670
|
+
low: number;
|
|
671
|
+
};
|
|
659
672
|
}
|
|
660
673
|
/** The problem kinds that are "slow or wasteful" — Performance's findings and the slow counter. */
|
|
661
674
|
export declare const DASH_PERF_KINDS: readonly DashProblemKind[];
|
|
@@ -742,6 +755,11 @@ export interface DashErrors {
|
|
|
742
755
|
recentOccurrences: number;
|
|
743
756
|
/** Hidden by hand, and still hidden: nothing of them has been seen since. Listed so they can be restored. */
|
|
744
757
|
dismissed: DashErrorGroup[];
|
|
758
|
+
/**
|
|
759
|
+
* Set on the stream's frame: each group carries at most this many `occurrences`, newest first (and the newest with a
|
|
760
|
+
* flow when none of those has one) — what Errors shows of a group. The error log keeps more; `count` is all of them.
|
|
761
|
+
*/
|
|
762
|
+
occurrencesShown?: number;
|
|
745
763
|
}
|
|
746
764
|
/** `POST /api/errors/dismiss` and `/api/errors/restore` — the body, and the answer. */
|
|
747
765
|
export interface DashErrorIds {
|
|
@@ -798,8 +816,55 @@ export type DashStreamEvent = {
|
|
|
798
816
|
} | {
|
|
799
817
|
type: 'errors';
|
|
800
818
|
data: DashErrors;
|
|
819
|
+
}
|
|
820
|
+
/** The shell's numbers, on every tab whatever page it shows. */
|
|
821
|
+
| {
|
|
822
|
+
type: 'summary';
|
|
823
|
+
data: DashSummary;
|
|
824
|
+
}
|
|
825
|
+
/** Every 15 s on every connection — what tells a tab its stream is alive when its page's frames are quiet. */
|
|
826
|
+
| {
|
|
827
|
+
type: 'ping';
|
|
828
|
+
data: Record<string, never>;
|
|
801
829
|
};
|
|
802
830
|
export type DashStreamEventType = DashStreamEvent['type'];
|
|
831
|
+
/**
|
|
832
|
+
* What a tab asks the stream for: `GET /api/stream?topics=cluster,flows` — the frames its page reads. `hello` and
|
|
833
|
+
* `ping` go to every tab, `summary` too (asked or not); `logs-resync` goes with `logs`, and the log backlog is sent
|
|
834
|
+
* only to a tab that asks for `logs`. A tab that sends no `topics` gets every frame (a UI from before topics).
|
|
835
|
+
*/
|
|
836
|
+
export type DashStreamTopic = 'summary' | 'session' | 'cluster' | 'kpis' | 'flows' | 'flow-rev' | 'logs' | 'diagnostics' | 'errors';
|
|
837
|
+
export declare const DASH_STREAM_TOPICS: readonly DashStreamTopic[];
|
|
838
|
+
/**
|
|
839
|
+
* The shell's numbers — the sidebar's badges, the top bar's counters, the scope card — for a tab whose page does not
|
|
840
|
+
* take the frame they come from. Counted by the same code as those frames, from what their producers last built.
|
|
841
|
+
* A part is absent until its producer first ran.
|
|
842
|
+
*/
|
|
843
|
+
export interface DashSummary {
|
|
844
|
+
/** `DashErrors.recentGroups`, and how many groups it lists (`groups.length`). */
|
|
845
|
+
errors?: {
|
|
846
|
+
recentGroups: number;
|
|
847
|
+
groups: number;
|
|
848
|
+
};
|
|
849
|
+
/** `DashDiagnostics.counts`, and each warning's title (`warnings[].title`) — the top bar's tooltip. */
|
|
850
|
+
diagnostics?: {
|
|
851
|
+
slow: number;
|
|
852
|
+
warnings: number;
|
|
853
|
+
warningTitles: string[];
|
|
854
|
+
};
|
|
855
|
+
/** Each listed flow's `lastSeenAt`, newest first, as many as `flows` lists: the «new flows» badge counts those after the tab last looked. */
|
|
856
|
+
flows?: {
|
|
857
|
+
lastSeenAt: number[];
|
|
858
|
+
};
|
|
859
|
+
/** Services down, wedged or crash-looping; and the scope card's `DashCluster.instance` and `worktrees`. */
|
|
860
|
+
cluster?: {
|
|
861
|
+
troubled: number;
|
|
862
|
+
instance?: DashCluster['instance'];
|
|
863
|
+
worktrees?: DashWorktree[];
|
|
864
|
+
};
|
|
865
|
+
/** `DashSession.setupIssues.length`. */
|
|
866
|
+
setupIssues?: number;
|
|
867
|
+
}
|
|
803
868
|
/**
|
|
804
869
|
* What one run of a route touched, by key, with how many times:
|
|
805
870
|
* `svc:<callee>` a service it called · `ext:<host>` a host outside the cluster · `sql:<VERB> <table>`
|
|
@@ -816,6 +881,8 @@ export interface DashRouteRun {
|
|
|
816
881
|
/** Who called it: another service by name, else `client`. */
|
|
817
882
|
caller?: string;
|
|
818
883
|
touches: DashTouches;
|
|
884
|
+
/** Its request and response as they came in and went out (an `ingress` exchange, `/api/exchange?id=`). */
|
|
885
|
+
exchangeId?: string;
|
|
819
886
|
}
|
|
820
887
|
export type DashRouteChangeKind = 'new-external' | 'new-service' | 'new-query' | 'new-queue' | 'new-cache' | 'new-error' | 'gone' | 'gone-error' | 'count' | 'status' | 'duration';
|
|
821
888
|
export interface DashRouteChange {
|
|
@@ -1051,8 +1118,13 @@ export interface DashExternalCall {
|
|
|
1051
1118
|
/** The request it was made in — the calls that share it are one request's steps. */
|
|
1052
1119
|
requestId?: string;
|
|
1053
1120
|
}
|
|
1054
|
-
/**
|
|
1121
|
+
/**
|
|
1122
|
+
* One exchange opened in the Request & response panel: an outbound call (Data › External APIs, a flow's step) or a
|
|
1123
|
+
* route's run (Routes › a route › Runs) — the call or the run, its exchange, its request's other steps.
|
|
1124
|
+
*/
|
|
1055
1125
|
export interface DashExchangeView {
|
|
1126
|
+
/** What was opened: an outbound call (`host` is its host), or a route's run (`host` is the service that served it). */
|
|
1127
|
+
of?: 'call' | 'run';
|
|
1056
1128
|
host: string;
|
|
1057
1129
|
call: DashExternalCall;
|
|
1058
1130
|
/** Absent: not captured (capture off, headers rotated away) or withheld (`withheld` says why). */
|
|
@@ -1166,6 +1238,14 @@ export interface DashSqlFinding {
|
|
|
1166
1238
|
tool: string;
|
|
1167
1239
|
at: number;
|
|
1168
1240
|
}
|
|
1241
|
+
/**
|
|
1242
|
+
* Why a statement is test SQL: every site it ran from is a test file (`sites`), its name starts with `test.` (`name`), or
|
|
1243
|
+
* nx.json → `lensmcp.sql.test` lists it (`listed`).
|
|
1244
|
+
*/
|
|
1245
|
+
export interface DashSqlTestMark {
|
|
1246
|
+
by: 'sites' | 'name' | 'listed';
|
|
1247
|
+
why: string;
|
|
1248
|
+
}
|
|
1169
1249
|
/**
|
|
1170
1250
|
* A place in the services' own code where something ran — what a coding agent opens. From the stack the tap took at
|
|
1171
1251
|
* the call: the service's nearest frame, and the callers above it.
|
|
@@ -1210,6 +1290,8 @@ export interface DashSqlTemplateSummary {
|
|
|
1210
1290
|
sites?: DashCodeSite[];
|
|
1211
1291
|
/** An unnamed statement its named version replaced (edited as it was named): that statement's id — not one to name. */
|
|
1212
1292
|
replacedBy?: string;
|
|
1293
|
+
/** Test SQL — written in a test, not in the services' code (apps/catalog sql-test.ts): not linted, named or reviewed by itself. */
|
|
1294
|
+
test?: DashSqlTestMark;
|
|
1213
1295
|
/** SELECT · INSERT · UPDATE · DELETE · …; a WITH reads as the statement it leads to. */
|
|
1214
1296
|
verb: string;
|
|
1215
1297
|
tables: string[];
|
|
@@ -1926,12 +2008,18 @@ export interface DashAiAnalyzer {
|
|
|
1926
2008
|
folder?: string;
|
|
1927
2009
|
/** Its runs in this checkout. */
|
|
1928
2010
|
runs: number;
|
|
2011
|
+
/**
|
|
2012
|
+
* LensMCP's guide it reviews by. `/api/ai/analyzers` leaves its `text` out and says its length (`chars`):
|
|
2013
|
+
* `/api/ai/analyzers/guide?id=` ({@link DashAiAnalyzerGuide}) returns it when Settings shows it — six guides and
|
|
2014
|
+
* six previews in every list were 1 MB a read (FoodGuard, 2026-10-20).
|
|
2015
|
+
*/
|
|
1929
2016
|
guide?: {
|
|
1930
2017
|
id: string;
|
|
1931
2018
|
title: string;
|
|
1932
2019
|
version: number;
|
|
1933
2020
|
sha: string;
|
|
1934
|
-
text
|
|
2021
|
+
text?: string;
|
|
2022
|
+
chars?: number;
|
|
1935
2023
|
};
|
|
1936
2024
|
indicators: Array<{
|
|
1937
2025
|
id: string;
|
|
@@ -1954,10 +2042,14 @@ export interface DashAiAnalyzer {
|
|
|
1954
2042
|
path: string;
|
|
1955
2043
|
why: string;
|
|
1956
2044
|
}>;
|
|
1957
|
-
/**
|
|
2045
|
+
/**
|
|
2046
|
+
* The instructions every run of it sends now, before the evidence: their size in the list (`tokens`, `chars`); the
|
|
2047
|
+
* text itself (`system`) from `/api/ai/analyzers/guide?id=`, like the guide's.
|
|
2048
|
+
*/
|
|
1958
2049
|
preview: {
|
|
1959
|
-
system
|
|
2050
|
+
system?: string;
|
|
1960
2051
|
tokens: number;
|
|
2052
|
+
chars?: number;
|
|
1961
2053
|
};
|
|
1962
2054
|
lastRun?: {
|
|
1963
2055
|
at: number;
|
|
@@ -1978,6 +2070,21 @@ export interface DashAiAnalyzer {
|
|
|
1978
2070
|
model: string;
|
|
1979
2071
|
};
|
|
1980
2072
|
}
|
|
2073
|
+
/** `GET /api/ai/analyzers/guide?id=<analyzer>`: one analyzer's guide and what a run of it sends — fetched when Settings shows them. */
|
|
2074
|
+
export interface DashAiAnalyzerGuide {
|
|
2075
|
+
id: string;
|
|
2076
|
+
guide?: {
|
|
2077
|
+
id: string;
|
|
2078
|
+
title: string;
|
|
2079
|
+
version: number;
|
|
2080
|
+
sha: string;
|
|
2081
|
+
text: string;
|
|
2082
|
+
};
|
|
2083
|
+
preview: {
|
|
2084
|
+
system: string;
|
|
2085
|
+
tokens: number;
|
|
2086
|
+
};
|
|
2087
|
+
}
|
|
1981
2088
|
/** How hard a frontier model thinks (Claude 4.6 and later; other models are sent none). */
|
|
1982
2089
|
export type DashAiEffort = 'low' | 'medium' | 'high' | 'xhigh' | 'max';
|
|
1983
2090
|
export interface DashAiAnalyzers {
|
|
@@ -2045,8 +2152,13 @@ export interface DashAiRun {
|
|
|
2045
2152
|
analyzerName: string;
|
|
2046
2153
|
question: string;
|
|
2047
2154
|
/** What was reviewed; `name` is a statement's name (its leading comment) — the same across its versions. */
|
|
2155
|
+
/**
|
|
2156
|
+
* `db`: one database (`<store>/<database>`, the health check); `dbtable`: one table (`<store>/<database>/<schema>.<table>`);
|
|
2157
|
+
* `kvstore` / `queuestore`: one Redis store's keys / BullMQ queues (`<store>`); `files`: the services' file access
|
|
2158
|
+
* (`files`); `external`: the external APIs the services call (`external`); `log`: one log line (its `seq`).
|
|
2159
|
+
*/
|
|
2048
2160
|
target: {
|
|
2049
|
-
kind: 'sql' | 'route' | 'error' | 'flow' | 'ui' | 'kv' | 'queue';
|
|
2161
|
+
kind: 'sql' | 'route' | 'error' | 'flow' | 'ui' | 'kv' | 'queue' | 'db' | 'dbtable' | 'kvstore' | 'queuestore' | 'files' | 'external' | 'log';
|
|
2050
2162
|
id: string;
|
|
2051
2163
|
label: string;
|
|
2052
2164
|
name?: string;
|
|
@@ -2144,7 +2256,13 @@ export interface DashAiRun {
|
|
|
2144
2256
|
instructions: string;
|
|
2145
2257
|
};
|
|
2146
2258
|
error?: string;
|
|
2259
|
+
/**
|
|
2260
|
+
* The run was never sent, so nothing was reviewed: no key could pay (`no-key`), the trigger's daily allowance was
|
|
2261
|
+
* spent (`over-daily-cap`), or every key was out of rate-limit room (`no-room`). A refusal, not a failed review.
|
|
2262
|
+
*/
|
|
2263
|
+
refused?: DashAiRefusal;
|
|
2147
2264
|
}
|
|
2265
|
+
export type DashAiRefusal = 'no-key' | 'over-daily-cap' | 'no-room';
|
|
2148
2266
|
/**
|
|
2149
2267
|
* An analysis running or waiting now, in the workspace's shared queue: one per `key` (the analyzer and its target),
|
|
2150
2268
|
* at most the workspace's limit running together, the rest waiting in the order they came.
|
|
@@ -2230,11 +2348,12 @@ export interface DashReviewStatus {
|
|
|
2230
2348
|
turns?: number;
|
|
2231
2349
|
tools?: number;
|
|
2232
2350
|
};
|
|
2233
|
-
/** The newest run, when it failed: when, and its error. */
|
|
2351
|
+
/** The newest run, when it failed: when, and its error — `refused` when it was never sent (DashAiRun.refused). */
|
|
2234
2352
|
failed?: {
|
|
2235
2353
|
runId: string;
|
|
2236
2354
|
at: number;
|
|
2237
2355
|
error: string;
|
|
2356
|
+
refused?: DashAiRefusal;
|
|
2238
2357
|
};
|
|
2239
2358
|
}
|
|
2240
2359
|
/** A finished review in a few lines: when, its score, what it is about, the changes it asks for first. */
|
package/lib/dashboard.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";export const DASHBOARD_PROTOCOL=1,DASH_PERF_KINDS=["n-plus-one","db-in-loop","render-storm","slow-path","slow-route","slow-query","missing-index","leak","egress-slow","duplicate-requests","perf-rule","db-connection","other"],DASH_AI_TRIGGERS=["auto","click","agent"];
|
|
1
|
+
"use strict";export const DASHBOARD_PROTOCOL=1,DASH_PERF_KINDS=["n-plus-one","db-in-loop","render-storm","slow-path","slow-route","slow-query","missing-index","leak","egress-slow","duplicate-requests","perf-rule","db-connection","other"],DASH_STREAM_TOPICS=["summary","session","cluster","kpis","flows","flow-rev","logs","diagnostics","errors"],DASH_AI_TRIGGERS=["auto","click","agent"];
|
package/lib/http-exchange.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* One HTTP exchange the lens captured (R6): a service's outbound call
|
|
2
|
+
* One HTTP exchange the lens captured (R6): a service's outbound call (`egress`), or a request it served (`ingress`
|
|
3
|
+
* — a route's run, as it came in and went out), as it went over the wire — its request and
|
|
3
4
|
* response headers (redacted), the query string and the bodies (redacted, the first 64 KB of each), and for a
|
|
4
5
|
* server-sent-events response every event with the moment it arrived. Kept OFF the bus, one file per exchange under
|
|
5
6
|
* `.lensmcp/http/`; the bus event carries its `exchangeId`. Capture: `LENSMCP_HTTP_CAPTURE=off | headers | bodies`.
|
|
@@ -17,8 +18,11 @@ export interface HttpExchangeBody {
|
|
|
17
18
|
/** A binary body: its digest instead of its bytes (when it was small enough to hash). */
|
|
18
19
|
sha256?: string;
|
|
19
20
|
truncated: boolean;
|
|
20
|
-
/**
|
|
21
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Why there is no text: `binary`, `form-data`, `stream` (a request body sent as a stream), `ai`, `encoded` (a
|
|
23
|
+
* compressed answer too big to unpack).
|
|
24
|
+
*/
|
|
25
|
+
omitted?: 'binary' | 'form-data' | 'stream' | 'ai' | 'encoded';
|
|
22
26
|
}
|
|
23
27
|
export interface HttpExchangeStreamEvent {
|
|
24
28
|
/** Since the request started. */
|
|
@@ -32,7 +36,7 @@ export interface HttpExchange {
|
|
|
32
36
|
schemaVersion: 1;
|
|
33
37
|
id: string;
|
|
34
38
|
capturedAt: number;
|
|
35
|
-
side: 'egress';
|
|
39
|
+
side: 'egress' | 'ingress';
|
|
36
40
|
service?: string;
|
|
37
41
|
flowId?: string;
|
|
38
42
|
requestId?: string;
|
package/lib/sources.d.ts
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CUSTOM SOURCES (issues/open/foodguard-81) — an application REPORTS what the lens cannot observe, as one of a closed
|
|
3
|
+
* set of typed kinds the lens knows how to draw, threshold and explain to an agent: `queue` · `loop` · `store` ·
|
|
4
|
+
* `traffic`. Phase 1 reads `queue` only — work a service keeps waiting in its OWN storage (an outbox table), which no
|
|
5
|
+
* event stream can show: FoodGuard's lens showed its BullMQ queues nearly empty while 6,389 outbox rows waited.
|
|
6
|
+
*
|
|
7
|
+
* Three hops, each with its own shape:
|
|
8
|
+
*
|
|
9
|
+
* 1. The WIRE — the snapshot a service answers on the dev route it declares in project.json (`cluster.sources`):
|
|
10
|
+
* `{ schema: 'lensmcp.queue/v1', at, truncated?: { groups }, groups: [{ key, labels?, pending, inFlight?, failed?,
|
|
11
|
+
* oldestPendingAt?, completedInWindow? }] }`. The service owns its query; the lens never connects to an app's
|
|
12
|
+
* database. `@lensmcp/cluster` parses it (`source-poller.ts`) — a snapshot that does not parse is `malformed`, never
|
|
13
|
+
* zero.
|
|
14
|
+
* 2. The BUS — the gateway's poller reports each poll as a `source: 'gateway'`, `category: 'cluster'` event whose
|
|
15
|
+
* `raw.kind` is `source-poll` ({@link SourcePollRaw}), and each service's declaration as `source-declaration`
|
|
16
|
+
* ({@link SourceDeclarationRaw}). A group's `key` travels as `group`: the bus redacts any field NAMED `key`
|
|
17
|
+
* (`@lensmcp/core` `isSecretParam`), which would have turned every group into `[REDACTED]`. The cluster MIRRORS
|
|
18
|
+
* these shapes (it does not depend on this package); a parity fixture pins both sides.
|
|
19
|
+
* 3. The READ — `apps/agent` folds them into `process://sources` ({@link SourcesReport}); the `observe.sources` and
|
|
20
|
+
* `source.get` tools, the status signal and the dashboard read that.
|
|
21
|
+
*
|
|
22
|
+
* Everything here is REPORTED by the app, never measured by the lens: every read carries `provenance: 'reported'`.
|
|
23
|
+
* Additive only.
|
|
24
|
+
*/
|
|
25
|
+
/** The closed set of source kinds. */
|
|
26
|
+
export declare const SOURCE_KINDS: readonly ["queue", "loop", "store", "traffic"];
|
|
27
|
+
export type SourceKind = (typeof SOURCE_KINDS)[number];
|
|
28
|
+
/** The kinds this release reads; a declared kind outside it is said, not polled. */
|
|
29
|
+
export declare const SOURCE_KINDS_READ: readonly ["queue"];
|
|
30
|
+
export type SourceKindRead = (typeof SOURCE_KINDS_READ)[number];
|
|
31
|
+
/** Every snapshot's schema: `lensmcp.<kind>/v1`. */
|
|
32
|
+
export declare const sourceWireSchema: (kind: SourceKind) => string;
|
|
33
|
+
export declare const QUEUE_WIRE_SCHEMA = "lensmcp.queue/v1";
|
|
34
|
+
/** The resource the lens folds the polls into. */
|
|
35
|
+
export declare const URI_SOURCES = "process://sources";
|
|
36
|
+
/** Said on every read of a source: the app's claim, not the lens's measurement. */
|
|
37
|
+
export declare const SOURCE_PROVENANCE: "reported";
|
|
38
|
+
/** The finding's rule id: a queue group whose oldest pending item has waited longer than `thresholds.oldestWaitMin`. */
|
|
39
|
+
export declare const QUEUE_OLDEST_WAIT_RULE = "queue/oldest-wait";
|
|
40
|
+
export declare const SOURCE_EVENT_KINDS: readonly ["source-poll", "source-declaration"];
|
|
41
|
+
export type SourceEventKind = (typeof SOURCE_EVENT_KINDS)[number];
|
|
42
|
+
/** What ONE poll found. */
|
|
43
|
+
export declare const SOURCE_POLL_STATES: readonly ["current", "unreachable", "refused", "malformed", "not-running"];
|
|
44
|
+
export type SourcePollState = (typeof SOURCE_POLL_STATES)[number];
|
|
45
|
+
/**
|
|
46
|
+
* What the lens says of a source when it is READ: a poll's state; `stale` — its newest report is overdue (the gateway
|
|
47
|
+
* stopped polling, or its polls are held up), so whatever it last said is no longer known; or `never-polled` —
|
|
48
|
+
* declared, and no poll has reported yet.
|
|
49
|
+
*/
|
|
50
|
+
export declare const SOURCE_STATES: readonly ["current", "unreachable", "refused", "malformed", "not-running", "stale", "never-polled"];
|
|
51
|
+
export type SourceState = (typeof SOURCE_STATES)[number];
|
|
52
|
+
export declare const SOURCE_DEFAULT_INTERVAL_SEC = 10;
|
|
53
|
+
export declare const SOURCE_MIN_INTERVAL_SEC = 2;
|
|
54
|
+
export declare const QUEUE_DEFAULT_OLDEST_WAIT_MIN = 10;
|
|
55
|
+
/** A snapshot holds at most this many groups; a producer with more caps it and says `truncated: { groups }`. */
|
|
56
|
+
export declare const SOURCE_MAX_GROUPS = 500;
|
|
57
|
+
export declare const SOURCE_MAX_BYTES: number;
|
|
58
|
+
/** A report is overdue — `stale` — once it is this many report periods old (and never sooner than 30 s). */
|
|
59
|
+
export declare const SOURCE_STALE_PERIODS = 3;
|
|
60
|
+
export declare const SOURCE_STALE_MIN_MS = 30000;
|
|
61
|
+
/** When a source's newest report stops being trusted. */
|
|
62
|
+
export declare function sourceStaleAfterMs(reportEveryMs: number): number;
|
|
63
|
+
/** A queue source's thresholds. `stalledMin` is accepted now and judged once the lens keeps a history (phase 2). */
|
|
64
|
+
export interface QueueThresholds {
|
|
65
|
+
oldestWaitMin: number;
|
|
66
|
+
stalledMin?: number;
|
|
67
|
+
}
|
|
68
|
+
/** One declared source, as the gateway polls it. */
|
|
69
|
+
export interface SourceDeclRaw {
|
|
70
|
+
sourceKind: SourceKindRead;
|
|
71
|
+
name: string;
|
|
72
|
+
path: string;
|
|
73
|
+
intervalSec: number;
|
|
74
|
+
thresholds: QueueThresholds;
|
|
75
|
+
}
|
|
76
|
+
/** One group of a queue snapshot, as the bus carries it. */
|
|
77
|
+
export interface QueueGroupRaw {
|
|
78
|
+
/** The service's own key for the group (`tenant/kind`) — the wire's `key`. */
|
|
79
|
+
group: string;
|
|
80
|
+
labels?: Record<string, string>;
|
|
81
|
+
pending: number;
|
|
82
|
+
inFlight?: number;
|
|
83
|
+
failed?: number;
|
|
84
|
+
/** When the oldest pending item of the group was written (ISO). `null`/absent: the service did not say. */
|
|
85
|
+
oldestPendingAt?: string | null;
|
|
86
|
+
completedInWindow?: number;
|
|
87
|
+
}
|
|
88
|
+
export interface QueueTotals {
|
|
89
|
+
groups: number;
|
|
90
|
+
pending: number;
|
|
91
|
+
inFlight: number;
|
|
92
|
+
failed: number;
|
|
93
|
+
/** Present when any group said it. */
|
|
94
|
+
completedInWindow?: number;
|
|
95
|
+
}
|
|
96
|
+
/** A queue snapshot that parsed. */
|
|
97
|
+
export interface QueueSnapshotRaw {
|
|
98
|
+
/** When the SERVICE measured it (its `at`, ISO) — a group's wait is measured at this time. */
|
|
99
|
+
at: string;
|
|
100
|
+
/** When the gateway received it (epoch ms) — the snapshot's age is measured from this. */
|
|
101
|
+
receivedAt: number;
|
|
102
|
+
totals: QueueTotals;
|
|
103
|
+
/** Every group the service answered (at most {@link SOURCE_MAX_GROUPS}), the oldest-waiting first. */
|
|
104
|
+
groups: QueueGroupRaw[];
|
|
105
|
+
/** The service capped its answer: this many groups were dropped, and are in no total. */
|
|
106
|
+
truncated?: {
|
|
107
|
+
groups: number;
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
/** One poll of one declared source (`raw.kind: 'source-poll'`). */
|
|
111
|
+
export interface SourcePollRaw extends SourceDeclRaw {
|
|
112
|
+
kind: 'source-poll';
|
|
113
|
+
provenance: typeof SOURCE_PROVENANCE;
|
|
114
|
+
/** The Nx project that declares it. */
|
|
115
|
+
project: string;
|
|
116
|
+
state: SourcePollState;
|
|
117
|
+
/** Why, for every state but `current`: the error, the status line, what did not parse, why the service was not asked. */
|
|
118
|
+
detail?: string;
|
|
119
|
+
/** The HTTP status of a `refused` answer. */
|
|
120
|
+
status?: number;
|
|
121
|
+
polledAt: number;
|
|
122
|
+
durationMs?: number;
|
|
123
|
+
/** The gateway reports this source again within this many ms — what decides when the lens calls it `stale`. */
|
|
124
|
+
reportEveryMs: number;
|
|
125
|
+
/** Polls in a row that failed (`unreachable`/`refused`/`malformed`) — the gateway backs off while it grows. */
|
|
126
|
+
failures?: number;
|
|
127
|
+
/** The LAST GOOD snapshot: this poll's when `current`, else the one before (kept, with its age), if any. */
|
|
128
|
+
snapshot?: QueueSnapshotRaw;
|
|
129
|
+
/** The owning workspace (stamped by the gateway on every service event). */
|
|
130
|
+
wsKey?: string;
|
|
131
|
+
floodSuppressed?: number;
|
|
132
|
+
}
|
|
133
|
+
/** A service's `cluster.sources`, as the gateway read it (`raw.kind: 'source-declaration'`). */
|
|
134
|
+
export interface SourceDeclarationRaw {
|
|
135
|
+
kind: 'source-declaration';
|
|
136
|
+
project: string;
|
|
137
|
+
/** The entries it polls. */
|
|
138
|
+
sources: SourceDeclRaw[];
|
|
139
|
+
/** Each entry it does NOT poll, and why — an unknown kind or a malformed entry is said, never silently dropped. */
|
|
140
|
+
problems: string[];
|
|
141
|
+
wsKey?: string;
|
|
142
|
+
}
|
|
143
|
+
/** A `source-poll` event's `raw`, checked just enough to be read safely (telemetry is only partly trusted). */
|
|
144
|
+
export declare function asSourcePollRaw(raw: unknown): SourcePollRaw | undefined;
|
|
145
|
+
/** A `source-declaration` event's `raw`, checked just enough to be read safely. */
|
|
146
|
+
export declare function asSourceDeclarationRaw(raw: unknown): SourceDeclarationRaw | undefined;
|
|
147
|
+
/** One queue group, as the lens shows it. */
|
|
148
|
+
export interface QueueGroupView extends QueueGroupRaw {
|
|
149
|
+
/** How long its oldest pending item had waited when the service measured it (`at − oldestPendingAt`). */
|
|
150
|
+
oldestWaitMs?: number;
|
|
151
|
+
/** That wait is longer than the source's `thresholds.oldestWaitMin`. */
|
|
152
|
+
overThreshold: boolean;
|
|
153
|
+
}
|
|
154
|
+
/** A queue source's headline numbers. */
|
|
155
|
+
export interface QueueHeadline extends QueueTotals {
|
|
156
|
+
/** The longest wait among its groups, as of the snapshot. */
|
|
157
|
+
oldestWaitMs?: number;
|
|
158
|
+
/** Groups whose oldest wait is over the threshold. */
|
|
159
|
+
overThreshold: number;
|
|
160
|
+
}
|
|
161
|
+
/** One declared source, as the lens shows it. */
|
|
162
|
+
export interface SourceView extends SourceDeclRaw {
|
|
163
|
+
provenance: typeof SOURCE_PROVENANCE;
|
|
164
|
+
project: string;
|
|
165
|
+
state: SourceState;
|
|
166
|
+
/** Why, for every state but `current`. */
|
|
167
|
+
detail?: string;
|
|
168
|
+
status?: number;
|
|
169
|
+
/** When the gateway last polled it (absent: `never-polled`). */
|
|
170
|
+
polledAt?: number;
|
|
171
|
+
failures?: number;
|
|
172
|
+
/** The last good snapshot's age — present whenever one is held, whatever the state. */
|
|
173
|
+
ageMs?: number;
|
|
174
|
+
/** When the service measured the last good snapshot (its `at`). */
|
|
175
|
+
snapshotAt?: string;
|
|
176
|
+
headline?: QueueHeadline;
|
|
177
|
+
/** The oldest-waiting first. Empty with `state: 'current'` is a real «nothing pending». */
|
|
178
|
+
groups: QueueGroupView[];
|
|
179
|
+
truncated?: {
|
|
180
|
+
groups: number;
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
/** A queue group whose oldest pending item has waited longer than its source allows. */
|
|
184
|
+
export interface SourceFinding {
|
|
185
|
+
rule: typeof QUEUE_OLDEST_WAIT_RULE;
|
|
186
|
+
provenance: typeof SOURCE_PROVENANCE;
|
|
187
|
+
project: string;
|
|
188
|
+
name: string;
|
|
189
|
+
sourceKind: SourceKindRead;
|
|
190
|
+
group: string;
|
|
191
|
+
labels?: Record<string, string>;
|
|
192
|
+
pending: number;
|
|
193
|
+
oldestWaitMs: number;
|
|
194
|
+
thresholdMin: number;
|
|
195
|
+
title: string;
|
|
196
|
+
fix: string[];
|
|
197
|
+
verify: string;
|
|
198
|
+
}
|
|
199
|
+
/** `process://sources`. */
|
|
200
|
+
export interface SourcesReport {
|
|
201
|
+
schemaVersion?: string;
|
|
202
|
+
revision?: number;
|
|
203
|
+
updatedAt?: string;
|
|
204
|
+
provenance: typeof SOURCE_PROVENANCE;
|
|
205
|
+
/** `none` until a service declares a source: nothing reports what the taps cannot see — not «nothing waits». */
|
|
206
|
+
producer: 'connected' | 'none';
|
|
207
|
+
detail?: string;
|
|
208
|
+
/** The clock every age here was measured against. */
|
|
209
|
+
readAt: number;
|
|
210
|
+
sources: SourceView[];
|
|
211
|
+
/** Groups over their threshold, from `current` snapshots — the longest wait first. */
|
|
212
|
+
findings: SourceFinding[];
|
|
213
|
+
/** Declaration entries the gateway does not poll, and why. */
|
|
214
|
+
problems: Array<{
|
|
215
|
+
project: string;
|
|
216
|
+
problem: string;
|
|
217
|
+
}>;
|
|
218
|
+
counts: Record<SourceState, number>;
|
|
219
|
+
/** Over the `current` queue snapshots only. */
|
|
220
|
+
totals: {
|
|
221
|
+
pending: number;
|
|
222
|
+
inFlight: number;
|
|
223
|
+
failed: number;
|
|
224
|
+
};
|
|
225
|
+
}
|
package/lib/sources.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var E=Object.defineProperty;var n=(r,t)=>E(r,"name",{value:t,configurable:!0});var f=Object.defineProperty,e=n((r,t)=>f(r,"name",{value:t,configurable:!0}),"n");export const SOURCE_KINDS=["queue","loop","store","traffic"],SOURCE_KINDS_READ=["queue"],sourceWireSchema=e(r=>`lensmcp.${r}/v1`,"sourceWireSchema"),QUEUE_WIRE_SCHEMA="lensmcp.queue/v1",URI_SOURCES="process://sources",SOURCE_PROVENANCE="reported",QUEUE_OLDEST_WAIT_RULE="queue/oldest-wait",SOURCE_EVENT_KINDS=["source-poll","source-declaration"],SOURCE_POLL_STATES=["current","unreachable","refused","malformed","not-running"],SOURCE_STATES=[...SOURCE_POLL_STATES,"stale","never-polled"],SOURCE_DEFAULT_INTERVAL_SEC=10,SOURCE_MIN_INTERVAL_SEC=2,QUEUE_DEFAULT_OLDEST_WAIT_MIN=10,SOURCE_MAX_GROUPS=500,SOURCE_MAX_BYTES=256*1024,SOURCE_STALE_PERIODS=3,SOURCE_STALE_MIN_MS=3e4;export function sourceStaleAfterMs(r){return Math.max(SOURCE_STALE_MIN_MS,SOURCE_STALE_PERIODS*Math.max(0,r))}n(sourceStaleAfterMs,"sourceStaleAfterMs"),e(sourceStaleAfterMs,"sourceStaleAfterMs");const o=e(r=>typeof r=="object"&&r!==null&&!Array.isArray(r),"isObj"),i=e(r=>typeof r=="number"&&Number.isInteger(r)&&r>=0,"isCount"),c=e(r=>typeof r=="number"&&Number.isFinite(r)&&r>0,"isTime"),a=e((r,t)=>typeof r=="string"&&r.length>0&&r.length<=t,"isStr");function l(r){if(!SOURCE_KINDS_READ.includes(r.sourceKind)||!a(r.name,200)||typeof r.path!="string"||typeof r.intervalSec!="number")return!1;const t=r.thresholds;return o(t)&&typeof t.oldestWaitMin=="number"&&t.oldestWaitMin>0}n(l,"f"),e(l,"asDecl");function p(r){if(!o(r)||!a(r.group,1e3)||!i(r.pending))return!1;for(const u of["inFlight","failed","completedInWindow"])if(r[u]!==void 0&&!i(r[u]))return!1;const t=r.oldestPendingAt;if(t!=null&&typeof t!="string")return!1;const s=r.labels;return s===void 0||o(s)&&Object.values(s).every(u=>typeof u=="string")}n(p,"l"),e(p,"asGroup");function S(r){if(!o(r)||typeof r.at!="string"||!c(r.receivedAt)||!Array.isArray(r.groups))return!1;const t=r.totals;if(!o(t)||!i(t.groups)||!i(t.pending)||!i(t.inFlight)||!i(t.failed))return!1;const s=r.truncated;return s!==void 0&&!(o(s)&&i(s.groups))?!1:r.groups.length<=SOURCE_MAX_GROUPS&&r.groups.every(p)}n(S,"d"),e(S,"asSnapshot");export function asSourcePollRaw(r){if(!(!o(r)||r.kind!=="source-poll"||!a(r.project,200)||!l(r))&&SOURCE_POLL_STATES.includes(r.state)&&!(!c(r.polledAt)||!c(r.reportEveryMs))&&!(r.snapshot!==void 0&&!S(r.snapshot))&&!(r.state==="current"&&r.snapshot===void 0))return r}n(asSourcePollRaw,"asSourcePollRaw"),e(asSourcePollRaw,"asSourcePollRaw");export function asSourceDeclarationRaw(r){if(!(!o(r)||r.kind!=="source-declaration"||!a(r.project,200))&&!(!Array.isArray(r.sources)||!r.sources.every(t=>o(t)&&l(t)))&&!(!Array.isArray(r.problems)||!r.problems.every(t=>typeof t=="string")))return r}n(asSourceDeclarationRaw,"asSourceDeclarationRaw"),e(asSourceDeclarationRaw,"asSourceDeclarationRaw");
|