@wizeworks/lens-hub 0.0.0-stage → 0.3.0-alpha.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/LICENSE +21 -0
- package/README.md +60 -2
- package/dist/api.d.ts +20 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +90 -0
- package/dist/api.js.map +1 -0
- package/dist/auth.d.ts +9 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +55 -0
- package/dist/auth.js.map +1 -0
- package/dist/body.d.ts +9 -0
- package/dist/body.d.ts.map +1 -0
- package/dist/body.js +25 -0
- package/dist/body.js.map +1 -0
- package/dist/cli-args.d.ts +10 -0
- package/dist/cli-args.d.ts.map +1 -0
- package/dist/cli-args.js +63 -0
- package/dist/cli-args.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +35 -0
- package/dist/cli.js.map +1 -0
- package/dist/data.d.ts +17 -0
- package/dist/data.d.ts.map +1 -0
- package/dist/data.js +39 -0
- package/dist/data.js.map +1 -0
- package/dist/hub.d.ts +14 -0
- package/dist/hub.d.ts.map +1 -0
- package/dist/hub.js +89 -0
- package/dist/hub.js.map +1 -0
- package/dist/import.d.ts +4 -0
- package/dist/import.d.ts.map +1 -0
- package/dist/import.js +41 -0
- package/dist/import.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/ingest.d.ts +9 -0
- package/dist/ingest.d.ts.map +1 -0
- package/dist/ingest.js +83 -0
- package/dist/ingest.js.map +1 -0
- package/dist/kit.d.ts +10 -0
- package/dist/kit.d.ts.map +1 -0
- package/dist/kit.js +8 -0
- package/dist/kit.js.map +1 -0
- package/dist/mcp.d.ts +4 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +18 -0
- package/dist/mcp.js.map +1 -0
- package/dist/metrics-read.d.ts +12 -0
- package/dist/metrics-read.d.ts.map +1 -0
- package/dist/metrics-read.js +18 -0
- package/dist/metrics-read.js.map +1 -0
- package/dist/metrics-write.d.ts +12 -0
- package/dist/metrics-write.d.ts.map +1 -0
- package/dist/metrics-write.js +107 -0
- package/dist/metrics-write.js.map +1 -0
- package/dist/models-api.d.ts +5 -0
- package/dist/models-api.d.ts.map +1 -0
- package/dist/models-api.js +36 -0
- package/dist/models-api.js.map +1 -0
- package/dist/options.d.ts +36 -0
- package/dist/options.d.ts.map +1 -0
- package/dist/options.js +22 -0
- package/dist/options.js.map +1 -0
- package/dist/otlp/index.d.ts +8 -0
- package/dist/otlp/index.d.ts.map +1 -0
- package/dist/otlp/index.js +41 -0
- package/dist/otlp/index.js.map +1 -0
- package/dist/otlp/logs.d.ts +24 -0
- package/dist/otlp/logs.d.ts.map +1 -0
- package/dist/otlp/logs.js +44 -0
- package/dist/otlp/logs.js.map +1 -0
- package/dist/otlp/metrics.d.ts +59 -0
- package/dist/otlp/metrics.d.ts.map +1 -0
- package/dist/otlp/metrics.js +73 -0
- package/dist/otlp/metrics.js.map +1 -0
- package/dist/otlp/proto.d.ts +14 -0
- package/dist/otlp/proto.d.ts.map +1 -0
- package/dist/otlp/proto.js +132 -0
- package/dist/otlp/proto.js.map +1 -0
- package/dist/otlp/spans.d.ts +33 -0
- package/dist/otlp/spans.d.ts.map +1 -0
- package/dist/otlp/spans.js +80 -0
- package/dist/otlp/spans.js.map +1 -0
- package/dist/otlp/values.d.ts +40 -0
- package/dist/otlp/values.d.ts.map +1 -0
- package/dist/otlp/values.js +78 -0
- package/dist/otlp/values.js.map +1 -0
- package/dist/scale-exec.d.ts +10 -0
- package/dist/scale-exec.d.ts.map +1 -0
- package/dist/scale-exec.js +22 -0
- package/dist/scale-exec.js.map +1 -0
- package/dist/schema-scale.d.ts +7 -0
- package/dist/schema-scale.d.ts.map +1 -0
- package/dist/schema-scale.js +99 -0
- package/dist/schema-scale.js.map +1 -0
- package/dist/schema.d.ts +13 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +49 -0
- package/dist/schema.js.map +1 -0
- package/dist/settle.d.ts +28 -0
- package/dist/settle.d.ts.map +1 -0
- package/dist/settle.js +103 -0
- package/dist/settle.js.map +1 -0
- package/dist/store-read.d.ts +27 -0
- package/dist/store-read.d.ts.map +1 -0
- package/dist/store-read.js +74 -0
- package/dist/store-read.js.map +1 -0
- package/dist/store.d.ts +42 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +118 -0
- package/dist/store.js.map +1 -0
- package/package.json +64 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 WizeWorks LLC
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,61 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @wizeworks/lens-hub
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The local hub of [WizeLens](https://github.com/brandonkorous/wizelens): one place for the traces and metrics of every service on your
|
|
4
|
+
machine or team network, with the full dashboard, OTLP intake (traces, logs, and metrics), and a read-only MCP endpoint.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
npx @wizeworks/lens-hub
|
|
8
|
+
# or
|
|
9
|
+
docker run -p 5960:5960 -v wizelens:/data ghcr.io/brandonkorous/wizelens-hub
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Open http://localhost:5960/_wizelens/. Then point your services at it and restart them:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
WIZELENS_HUB=http://localhost:5960 # WizeLens add-ons (.NET and Node)
|
|
16
|
+
WIZELENS_PROJECT=my-shop # optional: group services by project
|
|
17
|
+
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:5960 # any OpenTelemetry app
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Options
|
|
21
|
+
|
|
22
|
+
| Flag | Environment variable | Default |
|
|
23
|
+
|---|---|---|
|
|
24
|
+
| `--port <n>` | `WIZELENS_HUB_PORT` | `5960` |
|
|
25
|
+
| `--host <addr>` | `WIZELENS_HUB_HOST` | `127.0.0.1` (this machine only) |
|
|
26
|
+
| `--data <file>` | `WIZELENS_HUB_DATA` | `~/.wizelens/hub/hub.db` (`memory` for none) |
|
|
27
|
+
| `--token <text>` | `WIZELENS_HUB_TOKEN` | none |
|
|
28
|
+
| `--retention <time>` | `WIZELENS_HUB_RETENTION` | `7d` |
|
|
29
|
+
| `--max-traces <n>` | `WIZELENS_HUB_MAX_TRACES` | `50000` |
|
|
30
|
+
| `--max-metric-points <n>` | `WIZELENS_HUB_MAX_METRIC_POINTS` | `2000000` raw points (they are also kept for `--retention`; rollups are not capped) |
|
|
31
|
+
| `--mcp` | `WIZELENS_HUB_MCP=1` | off |
|
|
32
|
+
| `--import <file>` | | Copy an add-on's `.wizelens/traces.db` in at start |
|
|
33
|
+
|
|
34
|
+
To share the hub with a team, run it with `--host 0.0.0.0 --token <text>`. Services send `WIZELENS_HUB_TOKEN`; OTLP apps send
|
|
35
|
+
`OTEL_EXPORTER_OTLP_HEADERS=Authorization=Bearer <text>`; in a browser, open `/_wizelens/?token=<text>` once.
|
|
36
|
+
|
|
37
|
+
## Stats
|
|
38
|
+
|
|
39
|
+
The dashboard asks the hub for whole pages (`GET /_wizelens/api/models/{page}?range=&project=&env=`), computed from stats tables
|
|
40
|
+
in the hub file: per-trace facts and rollups in 10 s, 5 min, and 1 h buckets. Ranges reach the hub's `--retention`. Traces are
|
|
41
|
+
settled a few seconds after their last span (every 2 s, at most 150 ms at a time); pages say "Preparing stats" while traces wait.
|
|
42
|
+
Files from older hubs upgrade when opened and backfill, newest traces first. See `docs/CONTRACT.md`, "Stats tables".
|
|
43
|
+
|
|
44
|
+
## Metrics
|
|
45
|
+
|
|
46
|
+
OpenTelemetry apps send metrics to `POST /v1/metrics` (JSON or protobuf): gauges, sums, histograms, exponential histograms
|
|
47
|
+
(kept as explicit buckets), and summaries (kept as count and sum). WizeLens add-ons send theirs in their batches. The Metrics page
|
|
48
|
+
reads `GET /_wizelens/api/models/metrics?range=1h&project=&env=&service=&metric=&group=&stat=&q=`, computed in the hub from
|
|
49
|
+
metric rollups (per series, in 10 s, 5 min, and 1 h buckets, written as points arrive), so every range reads all of its points.
|
|
50
|
+
With `--mcp`, agents get `list_metrics` and `get_metric`.
|
|
51
|
+
|
|
52
|
+
## In code
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
import { createHub } from "@wizeworks/lens-hub";
|
|
56
|
+
|
|
57
|
+
const hub = createHub({ port: 5960, storagePath: "./hub.db" });
|
|
58
|
+
console.log(await hub.listen());
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The wire format is in [CONTRACT.md](https://github.com/brandonkorous/wizelens/blob/main/docs/CONTRACT.md#local-hub-v06-phase-2). MIT licensed.
|
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
|
+
import { type ServiceInfo } from "@wizeworks/lens-core";
|
|
3
|
+
import type { HubData } from "./data.js";
|
|
4
|
+
import type { ResolvedHubOptions } from "./options.js";
|
|
5
|
+
import { type TraceQuery } from "./store-read.js";
|
|
6
|
+
export interface ApiContext {
|
|
7
|
+
data: HubData;
|
|
8
|
+
o: ResolvedHubOptions;
|
|
9
|
+
version: string;
|
|
10
|
+
startedMs: number;
|
|
11
|
+
}
|
|
12
|
+
export declare function hubInfo(c: ApiContext): ServiceInfo;
|
|
13
|
+
/** `api/traces`: summaries by default, full traces with `full=true`. Filters: since, before, env, project, session. */
|
|
14
|
+
export declare function queryOf(p: URLSearchParams): {
|
|
15
|
+
q: TraceQuery;
|
|
16
|
+
full: boolean;
|
|
17
|
+
};
|
|
18
|
+
/** The hub API below `/_wizelens/`. */
|
|
19
|
+
export declare function handleApi(c: ApiContext, sub: string, req: IncomingMessage, res: ServerResponse, url: URL): Promise<void>;
|
|
20
|
+
//# sourceMappingURL=api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACjE,OAAO,EAAc,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAGpE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAGvD,OAAO,EAAuC,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAEvF,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,OAAO,CAAC;IACd,CAAC,EAAE,kBAAkB,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAKD,wBAAgB,OAAO,CAAC,CAAC,EAAE,UAAU,GAAG,WAAW,CAclD;AAED,uHAAuH;AACvH,wBAAgB,OAAO,CAAC,CAAC,EAAE,eAAe,GAAG;IAAE,CAAC,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,CAM5E;AAmCD,uCAAuC;AACvC,wBAAsB,SAAS,CAAC,CAAC,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAc9H"}
|
package/dist/api.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { hostname } from "node:os";
|
|
2
|
+
import { HEADER_HOP } from "@wizeworks/lens-core";
|
|
3
|
+
import { DEFAULT_N_PLUS_ONE, DEFAULT_SLOW_QUERY_MS, firstHeader, sendJson, VERSION as NODE_VERSION } from "@wizeworks/lens-node/kit";
|
|
4
|
+
import { parseJson, readBody } from "./body.js";
|
|
5
|
+
import { parseIngest } from "./ingest.js";
|
|
6
|
+
import { metricParams, metricsPage, rangeOf } from "./metrics-read.js";
|
|
7
|
+
import { sendPageModel } from "./models-api.js";
|
|
8
|
+
import { countRows, distinctOf, listServices } from "./store-read.js";
|
|
9
|
+
const clamp = (v, min, max) => Math.min(max, Math.max(min, v));
|
|
10
|
+
const num = (p, k) => (p.has(k) && Number.isFinite(Number(p.get(k))) ? Number(p.get(k)) : undefined);
|
|
11
|
+
export function hubInfo(c) {
|
|
12
|
+
return {
|
|
13
|
+
mode: "hub",
|
|
14
|
+
capabilities: ["federation", "environments", "registry", "metrics", "serverMetrics", "serverStats"],
|
|
15
|
+
service: c.o.name,
|
|
16
|
+
version: c.version,
|
|
17
|
+
runtime: `node ${process.version} · lens-node ${NODE_VERSION}`,
|
|
18
|
+
mcp: c.o.enableMcp,
|
|
19
|
+
host: hostname(),
|
|
20
|
+
startedMs: c.startedMs,
|
|
21
|
+
storage: { kind: "sqlite", maxTraces: c.o.maxTraces, maxAgeMs: c.o.maxAgeMs },
|
|
22
|
+
analysis: { slowQueryMs: DEFAULT_SLOW_QUERY_MS, nPlusOneThreshold: DEFAULT_N_PLUS_ONE },
|
|
23
|
+
redactKeys: [...c.o.redactKeys],
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/** `api/traces`: summaries by default, full traces with `full=true`. Filters: since, before, env, project, session. */
|
|
27
|
+
export function queryOf(p) {
|
|
28
|
+
const full = p.get("full") === "true";
|
|
29
|
+
const limit = clamp(num(p, "limit") ?? (full ? 1000 : 50), 1, full ? 5000 : 1000);
|
|
30
|
+
const q = { limit, since: num(p, "since"), before: num(p, "before") };
|
|
31
|
+
for (const k of ["env", "project", "session"])
|
|
32
|
+
q[k] = p.get(k) || undefined;
|
|
33
|
+
return { q, full };
|
|
34
|
+
}
|
|
35
|
+
function services(c) {
|
|
36
|
+
const db = c.data.store.db;
|
|
37
|
+
return {
|
|
38
|
+
services: listServices(db, c.o.now()),
|
|
39
|
+
environments: distinctOf(db, "env"),
|
|
40
|
+
projects: distinctOf(db, "project"),
|
|
41
|
+
...countRows(db),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
async function ingest(c, req, res) {
|
|
45
|
+
try {
|
|
46
|
+
const w = parseIngest(parseJson(await readBody(req)), c.o.redactKeys);
|
|
47
|
+
if (!w)
|
|
48
|
+
return sendJson(res, 400, { error: "sender.service is required" });
|
|
49
|
+
c.data.write(w);
|
|
50
|
+
res.writeHead(204).end();
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
sendJson(res, 400, { error: "invalid body" });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function metrics(c, p) {
|
|
57
|
+
const scope = { project: p.get("project") || undefined, env: p.get("env") || undefined };
|
|
58
|
+
return metricsPage(c.data.store.db, rangeOf(p.get("range")), scope, metricParams(p), c.o.now());
|
|
59
|
+
}
|
|
60
|
+
async function sendTrace(c, traceId, req, res, url) {
|
|
61
|
+
const hop = Number(firstHeader(req.headers[HEADER_HOP.toLowerCase()])) || 0;
|
|
62
|
+
const t = await c.data.getTrace(traceId, url.searchParams.get("federate") === "true", hop);
|
|
63
|
+
if (t)
|
|
64
|
+
sendJson(res, 200, t);
|
|
65
|
+
else
|
|
66
|
+
sendJson(res, 404, { error: "trace not found" });
|
|
67
|
+
}
|
|
68
|
+
/** The hub API below `/_wizelens/`. */
|
|
69
|
+
export async function handleApi(c, sub, req, res, url) {
|
|
70
|
+
const method = req.method ?? "GET";
|
|
71
|
+
if (sub === "api/info" && method === "GET")
|
|
72
|
+
return sendJson(res, 200, hubInfo(c));
|
|
73
|
+
if (sub === "api/services" && method === "GET")
|
|
74
|
+
return sendJson(res, 200, services(c));
|
|
75
|
+
if (sub === "api/ingest" && method === "POST")
|
|
76
|
+
return ingest(c, req, res);
|
|
77
|
+
if (sub === "api/traces" && method === "GET") {
|
|
78
|
+
const { q, full } = queryOf(url.searchParams);
|
|
79
|
+
return sendJson(res, 200, full ? c.data.traces(q) : c.data.summaries(q));
|
|
80
|
+
}
|
|
81
|
+
if (sub === "api/models/metrics" && method === "GET")
|
|
82
|
+
return sendJson(res, 200, metrics(c, url.searchParams));
|
|
83
|
+
if (sub.startsWith("api/models/") && method === "GET")
|
|
84
|
+
return sendPageModel(c, sub.slice("api/models/".length), url.searchParams, res);
|
|
85
|
+
const m = /^api\/traces\/([0-9a-f]{32})$/.exec(sub);
|
|
86
|
+
if (m && method === "GET")
|
|
87
|
+
return sendTrace(c, m[1], req, res, url);
|
|
88
|
+
sendJson(res, 404, { error: "not found" });
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=api.js.map
|
package/dist/api.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
|
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
|
+
export declare const COOKIE = "wizelens_hub";
|
|
3
|
+
/** True when no token is set, or the call carries it (bearer header or the dashboard cookie). */
|
|
4
|
+
export declare function isAllowed(req: IncomingMessage, token: string | undefined): boolean;
|
|
5
|
+
/** `?token=` on a dashboard page: keep it in a cookie, then drop it from the address bar. True when handled. */
|
|
6
|
+
export declare function acceptTokenLink(url: URL, token: string | undefined, res: ServerResponse): boolean;
|
|
7
|
+
/** Answer a call without the token: JSON for the API, a small sign-in page for the dashboard. */
|
|
8
|
+
export declare function deny(res: ServerResponse, api: boolean, url: URL): void;
|
|
9
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEjE,eAAO,MAAM,MAAM,iBAAiB,CAAC;AAgBrC,iGAAiG;AACjG,wBAAgB,SAAS,CAAC,GAAG,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAMlF;AAED,gHAAgH;AAChH,wBAAgB,eAAe,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAYjG;AAUD,iGAAiG;AACjG,wBAAgB,IAAI,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,GAAG,IAAI,CAMtE"}
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { timingSafeEqual } from "node:crypto";
|
|
2
|
+
export const COOKIE = "wizelens_hub";
|
|
3
|
+
const same = (a, b) => {
|
|
4
|
+
const x = Buffer.from(a);
|
|
5
|
+
const y = Buffer.from(b);
|
|
6
|
+
return x.length === y.length && timingSafeEqual(x, y);
|
|
7
|
+
};
|
|
8
|
+
function cookieOf(req) {
|
|
9
|
+
for (const part of String(req.headers.cookie ?? "").split(";")) {
|
|
10
|
+
const [k, ...v] = part.trim().split("=");
|
|
11
|
+
if (k === COOKIE)
|
|
12
|
+
return decodeURIComponent(v.join("="));
|
|
13
|
+
}
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
/** True when no token is set, or the call carries it (bearer header or the dashboard cookie). */
|
|
17
|
+
export function isAllowed(req, token) {
|
|
18
|
+
if (!token)
|
|
19
|
+
return true;
|
|
20
|
+
const auth = String(req.headers.authorization ?? "");
|
|
21
|
+
const bearer = auth.toLowerCase().startsWith("bearer ") ? auth.slice(7).trim() : undefined;
|
|
22
|
+
const given = bearer ?? cookieOf(req);
|
|
23
|
+
return given !== undefined && same(given, token);
|
|
24
|
+
}
|
|
25
|
+
/** `?token=` on a dashboard page: keep it in a cookie, then drop it from the address bar. True when handled. */
|
|
26
|
+
export function acceptTokenLink(url, token, res) {
|
|
27
|
+
const given = url.searchParams.get("token");
|
|
28
|
+
if (!token || given === null)
|
|
29
|
+
return false;
|
|
30
|
+
url.searchParams.delete("token");
|
|
31
|
+
const target = `${url.pathname}${url.search}`;
|
|
32
|
+
if (!same(given, token)) {
|
|
33
|
+
res.writeHead(302, { Location: `${target}${url.search ? "&" : "?"}denied=1` }).end();
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
const cookie = `${COOKIE}=${encodeURIComponent(given)}; Path=/; HttpOnly; SameSite=Strict; Max-Age=2592000`;
|
|
37
|
+
res.writeHead(302, { Location: target, "Set-Cookie": cookie }).end();
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
const LOGIN = (denied) => `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
|
41
|
+
<title>WizeLens hub</title><style>body{font-family:system-ui;background:#0f1115;color:#e8eaee;display:grid;place-items:center;min-height:100vh;margin:0}
|
|
42
|
+
form{display:grid;gap:12px;width:min(340px,90vw)}input,button{font:inherit;padding:10px 12px;border-radius:8px;border:1px solid #2a2f3a}
|
|
43
|
+
input{background:#171a21;color:inherit}button{background:#3b82f6;color:#fff;border:0;cursor:pointer}p{margin:0;color:#9aa3b2}.e{color:#f87171}</style></head>
|
|
44
|
+
<body><form method="get"><h1 style="margin:0;font-size:20px">WizeLens hub</h1><p>This hub needs an access token.</p>
|
|
45
|
+
${denied ? '<p class="e">That token is not right.</p>' : ""}<input name="token" type="password" placeholder="Access token" autofocus required aria-label="Access token">
|
|
46
|
+
<button type="submit">Open the dashboard</button></form></body></html>`;
|
|
47
|
+
/** Answer a call without the token: JSON for the API, a small sign-in page for the dashboard. */
|
|
48
|
+
export function deny(res, api, url) {
|
|
49
|
+
if (api) {
|
|
50
|
+
res.writeHead(401, { "Content-Type": "application/json", "WWW-Authenticate": "Bearer" }).end(JSON.stringify({ error: "token required" }));
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
res.writeHead(401, { "Content-Type": "text/html; charset=utf-8" }).end(LOGIN(url.searchParams.has("denied")));
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=auth.js.map
|
package/dist/auth.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
|
package/dist/body.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { IncomingMessage } from "node:http";
|
|
2
|
+
/** Most bytes one request body may hold, after unzip. */
|
|
3
|
+
export declare const MAX_BODY: number;
|
|
4
|
+
/** Read the raw body, and unzip it when it is gzip. Throws when it is too large. */
|
|
5
|
+
export declare function readBody(req: IncomingMessage): Promise<Buffer>;
|
|
6
|
+
export declare const isProtobuf: (req: IncomingMessage) => boolean;
|
|
7
|
+
/** Parse a JSON body. Undefined when it is empty. */
|
|
8
|
+
export declare function parseJson(body: Buffer): unknown;
|
|
9
|
+
//# sourceMappingURL=body.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"body.d.ts","sourceRoot":"","sources":["../src/body.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAGjD,yDAAyD;AACzD,eAAO,MAAM,QAAQ,QAAmB,CAAC;AAEzC,oFAAoF;AACpF,wBAAsB,QAAQ,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CAYpE;AAED,eAAO,MAAM,UAAU,QAAS,eAAe,YAAmE,CAAC;AAEnH,qDAAqD;AACrD,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAG/C"}
|
package/dist/body.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { gunzipSync } from "node:zlib";
|
|
2
|
+
/** Most bytes one request body may hold, after unzip. */
|
|
3
|
+
export const MAX_BODY = 16 * 1024 * 1024;
|
|
4
|
+
/** Read the raw body, and unzip it when it is gzip. Throws when it is too large. */
|
|
5
|
+
export async function readBody(req) {
|
|
6
|
+
const chunks = [];
|
|
7
|
+
let size = 0;
|
|
8
|
+
for await (const chunk of req) {
|
|
9
|
+
const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
10
|
+
size += buf.length;
|
|
11
|
+
if (size > MAX_BODY)
|
|
12
|
+
throw new Error("body too large");
|
|
13
|
+
chunks.push(buf);
|
|
14
|
+
}
|
|
15
|
+
const raw = Buffer.concat(chunks);
|
|
16
|
+
const gzip = String(req.headers["content-encoding"] ?? "").toLowerCase() === "gzip";
|
|
17
|
+
return gzip ? gunzipSync(raw, { maxOutputLength: MAX_BODY }) : raw;
|
|
18
|
+
}
|
|
19
|
+
export const isProtobuf = (req) => String(req.headers["content-type"] ?? "").includes("protobuf");
|
|
20
|
+
/** Parse a JSON body. Undefined when it is empty. */
|
|
21
|
+
export function parseJson(body) {
|
|
22
|
+
const text = body.toString("utf8");
|
|
23
|
+
return text ? JSON.parse(text) : undefined;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=body.js.map
|
package/dist/body.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"body.js","sourceRoot":"","sources":["../src/body.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEvC,yDAAyD;AACzD,MAAM,CAAC,MAAM,QAAQ,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAEzC,oFAAoF;AACpF,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,GAAoB;IACjD,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,EAAE,CAAC;QAC9B,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAe,CAAC,CAAC;QAC1E,IAAI,IAAI,GAAG,CAAC,MAAM,CAAC;QACnB,IAAI,IAAI,GAAG,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC;QACvD,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACnB,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAClC,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,kBAAkB,CAAC,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,KAAK,MAAM,CAAC;IACpF,OAAO,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,EAAE,eAAe,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;AACrE,CAAC;AAED,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,GAAoB,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;AAEnH,qDAAqD;AACrD,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACnC,OAAO,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAC7C,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { HubOptions } from "./options.js";
|
|
2
|
+
export declare const USAGE = "wizelens-hub: one place for the traces of every service.\n\nUsage: wizelens-hub [options]\n\n --port <n> Port. Default 5960. (WIZELENS_HUB_PORT)\n --host <addr> Address. Default 127.0.0.1. Use 0.0.0.0\n for the network, with --token. (WIZELENS_HUB_HOST)\n --data <file> Hub file, or \"memory\".\n Default ~/.wizelens/hub/hub.db. (WIZELENS_HUB_DATA)\n --token <text> Require this token on every call. (WIZELENS_HUB_TOKEN)\n --retention <time> Keep traces this long: 30m, 24h, 7d.\n Default 7d. (WIZELENS_HUB_RETENTION)\n --max-traces <n> Keep at most this many. Default 50000. (WIZELENS_HUB_MAX_TRACES)\n --max-metric-points <n>\n Keep at most this many metric points.\n Default 2000000. (WIZELENS_HUB_MAX_METRIC_POINTS)\n --mcp Turn on the read-only MCP endpoint. (WIZELENS_HUB_MCP=1)\n --import <file> Copy an add-on's .wizelens/traces.db into\n the hub at start. Can repeat.\n --help Show this help.\n";
|
|
3
|
+
/** "30m", "24h", "7d", or plain ms. Undefined when it is not valid. */
|
|
4
|
+
export declare function parseDuration(v: string | undefined): number | undefined;
|
|
5
|
+
/** Options from the command line, then from the environment. */
|
|
6
|
+
export type CliOptions = HubOptions & {
|
|
7
|
+
imports: string[];
|
|
8
|
+
};
|
|
9
|
+
export declare function optionsFromArgs(argv: string[], env: NodeJS.ProcessEnv): CliOptions | "help";
|
|
10
|
+
//# sourceMappingURL=cli-args.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-args.d.ts","sourceRoot":"","sources":["../src/cli-args.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C,eAAO,MAAM,KAAK,4rCAoBjB,CAAC;AAIF,uEAAuE;AACvE,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAIvE;AAID,gEAAgE;AAChE,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG;IAAE,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAE5D,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,UAAU,GAAG,MAAM,CA4B3F"}
|
package/dist/cli-args.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { parseArgs } from "node:util";
|
|
2
|
+
import { HUB_TOKEN_ENV } from "@wizeworks/lens-core";
|
|
3
|
+
export const USAGE = `wizelens-hub: one place for the traces of every service.
|
|
4
|
+
|
|
5
|
+
Usage: wizelens-hub [options]
|
|
6
|
+
|
|
7
|
+
--port <n> Port. Default 5960. (WIZELENS_HUB_PORT)
|
|
8
|
+
--host <addr> Address. Default 127.0.0.1. Use 0.0.0.0
|
|
9
|
+
for the network, with --token. (WIZELENS_HUB_HOST)
|
|
10
|
+
--data <file> Hub file, or "memory".
|
|
11
|
+
Default ~/.wizelens/hub/hub.db. (WIZELENS_HUB_DATA)
|
|
12
|
+
--token <text> Require this token on every call. (WIZELENS_HUB_TOKEN)
|
|
13
|
+
--retention <time> Keep traces this long: 30m, 24h, 7d.
|
|
14
|
+
Default 7d. (WIZELENS_HUB_RETENTION)
|
|
15
|
+
--max-traces <n> Keep at most this many. Default 50000. (WIZELENS_HUB_MAX_TRACES)
|
|
16
|
+
--max-metric-points <n>
|
|
17
|
+
Keep at most this many metric points.
|
|
18
|
+
Default 2000000. (WIZELENS_HUB_MAX_METRIC_POINTS)
|
|
19
|
+
--mcp Turn on the read-only MCP endpoint. (WIZELENS_HUB_MCP=1)
|
|
20
|
+
--import <file> Copy an add-on's .wizelens/traces.db into
|
|
21
|
+
the hub at start. Can repeat.
|
|
22
|
+
--help Show this help.
|
|
23
|
+
`;
|
|
24
|
+
const UNITS = { s: 1000, m: 60_000, h: 3_600_000, d: 86_400_000 };
|
|
25
|
+
/** "30m", "24h", "7d", or plain ms. Undefined when it is not valid. */
|
|
26
|
+
export function parseDuration(v) {
|
|
27
|
+
const m = /^(\d+(?:\.\d+)?)\s*([smhd]?)$/i.exec(v?.trim() ?? "");
|
|
28
|
+
if (!m)
|
|
29
|
+
return undefined;
|
|
30
|
+
return Number(m[1]) * (m[2] ? UNITS[m[2].toLowerCase()] : 1);
|
|
31
|
+
}
|
|
32
|
+
const int = (v) => (v && /^\d+$/.test(v) ? Number(v) : undefined);
|
|
33
|
+
export function optionsFromArgs(argv, env) {
|
|
34
|
+
const { values: a } = parseArgs({
|
|
35
|
+
args: argv,
|
|
36
|
+
options: {
|
|
37
|
+
port: { type: "string" },
|
|
38
|
+
host: { type: "string" },
|
|
39
|
+
data: { type: "string" },
|
|
40
|
+
token: { type: "string" },
|
|
41
|
+
retention: { type: "string" },
|
|
42
|
+
"max-traces": { type: "string" },
|
|
43
|
+
"max-metric-points": { type: "string" },
|
|
44
|
+
mcp: { type: "boolean" },
|
|
45
|
+
import: { type: "string", multiple: true },
|
|
46
|
+
help: { type: "boolean", short: "h" },
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
if (a.help)
|
|
50
|
+
return "help";
|
|
51
|
+
return {
|
|
52
|
+
port: int(a.port ?? env.WIZELENS_HUB_PORT),
|
|
53
|
+
host: a.host ?? env.WIZELENS_HUB_HOST,
|
|
54
|
+
storagePath: a.data ?? env.WIZELENS_HUB_DATA,
|
|
55
|
+
token: a.token ?? env[HUB_TOKEN_ENV],
|
|
56
|
+
maxAgeMs: parseDuration(a.retention ?? env.WIZELENS_HUB_RETENTION),
|
|
57
|
+
maxTraces: int(a["max-traces"] ?? env.WIZELENS_HUB_MAX_TRACES),
|
|
58
|
+
maxMetricPoints: int(a["max-metric-points"] ?? env.WIZELENS_HUB_MAX_METRIC_POINTS),
|
|
59
|
+
enableMcp: a.mcp ?? env.WIZELENS_HUB_MCP === "1",
|
|
60
|
+
imports: a.import ?? [],
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=cli-args.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-args.js","sourceRoot":"","sources":["../src/cli-args.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAGrD,MAAM,CAAC,MAAM,KAAK,GAAG;;;;;;;;;;;;;;;;;;;;CAoBpB,CAAC;AAEF,MAAM,KAAK,GAA2B,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC;AAE1F,uEAAuE;AACvE,MAAM,UAAU,aAAa,CAAC,CAAqB;IACjD,MAAM,CAAC,GAAG,gCAAgC,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IACjE,IAAI,CAAC,CAAC;QAAE,OAAO,SAAS,CAAC;IACzB,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAChE,CAAC;AAED,MAAM,GAAG,GAAG,CAAC,CAAqB,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;AAKtF,MAAM,UAAU,eAAe,CAAC,IAAc,EAAE,GAAsB;IACpE,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,SAAS,CAAC;QAC9B,IAAI,EAAE,IAAI;QACV,OAAO,EAAE;YACP,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACxB,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACxB,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACxB,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACzB,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YAC7B,YAAY,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YAChC,mBAAmB,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACvC,GAAG,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;YACxB,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE;YAC1C,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE;SACtC;KACF,CAAC,CAAC;IACH,IAAI,CAAC,CAAC,IAAI;QAAE,OAAO,MAAM,CAAC;IAC1B,OAAO;QACL,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,GAAG,CAAC,iBAAiB,CAAC;QAC1C,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,GAAG,CAAC,iBAAiB;QACrC,WAAW,EAAE,CAAC,CAAC,IAAI,IAAI,GAAG,CAAC,iBAAiB;QAC5C,KAAK,EAAE,CAAC,CAAC,KAAK,IAAI,GAAG,CAAC,aAAa,CAAC;QACpC,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,SAAS,IAAI,GAAG,CAAC,sBAAsB,CAAC;QAClE,SAAS,EAAE,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,GAAG,CAAC,uBAAuB,CAAC;QAC9D,eAAe,EAAE,GAAG,CAAC,CAAC,CAAC,mBAAmB,CAAC,IAAI,GAAG,CAAC,8BAA8B,CAAC;QAClF,SAAS,EAAE,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,gBAAgB,KAAK,GAAG;QAChD,OAAO,EAAE,CAAC,CAAC,MAAM,IAAI,EAAE;KACxB,CAAC;AACJ,CAAC"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createHub, HUB_VERSION } from "./hub.js";
|
|
3
|
+
import { optionsFromArgs, USAGE } from "./cli-args.js";
|
|
4
|
+
import { importAddonFile } from "./import.js";
|
|
5
|
+
import { DEFAULT_STORAGE_PATH } from "./options.js";
|
|
6
|
+
async function main() {
|
|
7
|
+
const options = optionsFromArgs(process.argv.slice(2), process.env);
|
|
8
|
+
if (options === "help")
|
|
9
|
+
return void process.stdout.write(USAGE);
|
|
10
|
+
const open = options.host === "0.0.0.0" || options.host === "::";
|
|
11
|
+
if (open && !options.token)
|
|
12
|
+
console.warn("[wizelens-hub] Warning: the hub is open to the network without a token. Set --token.");
|
|
13
|
+
const hub = createHub(options);
|
|
14
|
+
for (const file of options.imports)
|
|
15
|
+
console.info(`[wizelens-hub] Imported ${importAddonFile(hub.data, file)} traces from ${file}`);
|
|
16
|
+
const url = await hub.listen();
|
|
17
|
+
const lines = [
|
|
18
|
+
`WizeLens hub ${HUB_VERSION}`,
|
|
19
|
+
` Dashboard ${url}/_wizelens/`,
|
|
20
|
+
` Add-ons WIZELENS_HUB=${url}`,
|
|
21
|
+
` OTLP/HTTP OTEL_EXPORTER_OTLP_ENDPOINT=${url}`,
|
|
22
|
+
` Data ${options.storagePath ?? DEFAULT_STORAGE_PATH}`,
|
|
23
|
+
];
|
|
24
|
+
if (options.enableMcp)
|
|
25
|
+
lines.push(` MCP ${url}/_wizelens/mcp`);
|
|
26
|
+
console.info(lines.join("\n"));
|
|
27
|
+
const stop = () => void hub.close().then(() => process.exit(0));
|
|
28
|
+
process.once("SIGINT", stop);
|
|
29
|
+
process.once("SIGTERM", stop);
|
|
30
|
+
}
|
|
31
|
+
main().catch((err) => {
|
|
32
|
+
console.error(`[wizelens-hub] ${err instanceof Error ? err.message : String(err)}`);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
});
|
|
35
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAEpD,KAAK,UAAU,IAAI;IACjB,MAAM,OAAO,GAAG,eAAe,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;IACpE,IAAI,OAAO,KAAK,MAAM;QAAE,OAAO,KAAK,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAChE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,KAAK,SAAS,IAAI,OAAO,CAAC,IAAI,KAAK,IAAI,CAAC;IACjE,IAAI,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK;QAAE,OAAO,CAAC,IAAI,CAAC,sFAAsF,CAAC,CAAC;IACjI,MAAM,GAAG,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC;IAC/B,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,OAAO;QAAE,OAAO,CAAC,IAAI,CAAC,2BAA2B,eAAe,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,gBAAgB,IAAI,EAAE,CAAC,CAAC;IACnI,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,MAAM,EAAE,CAAC;IAC/B,MAAM,KAAK,GAAG;QACZ,gBAAgB,WAAW,EAAE;QAC7B,iBAAiB,GAAG,aAAa;QACjC,8BAA8B,GAAG,EAAE;QACnC,6CAA6C,GAAG,EAAE;QAClD,iBAAiB,OAAO,CAAC,WAAW,IAAI,oBAAoB,EAAE;KAC/D,CAAC;IACF,IAAI,OAAO,CAAC,SAAS;QAAE,KAAK,CAAC,IAAI,CAAC,iBAAiB,GAAG,gBAAgB,CAAC,CAAC;IACxE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAC/B,MAAM,IAAI,GAAG,GAAG,EAAE,CAAC,KAAK,GAAG,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAChE,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC7B,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;AAChC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IAC5B,OAAO,CAAC,KAAK,CAAC,kBAAkB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACpF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
package/dist/data.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type Trace, type TraceSummary } from "@wizeworks/lens-core";
|
|
2
|
+
import { type TraceQuery } from "./store-read.js";
|
|
3
|
+
import type { HubStore, HubWrite } from "./store.js";
|
|
4
|
+
/** The hub's data: writes, reads, and federation to add-ons that do not send to the hub. */
|
|
5
|
+
export declare class HubData {
|
|
6
|
+
#private;
|
|
7
|
+
readonly store: HubStore;
|
|
8
|
+
constructor(store: HubStore);
|
|
9
|
+
write(w: HubWrite): void;
|
|
10
|
+
/** Keep a remote only when the hub does not get that service's data. */
|
|
11
|
+
readonly keepRemote: (endpoint: string) => boolean;
|
|
12
|
+
traces(q: TraceQuery): Trace[];
|
|
13
|
+
summaries(q: TraceQuery): TraceSummary[];
|
|
14
|
+
/** One trace, plus the parts held by add-ons that do not send here. */
|
|
15
|
+
getTrace(traceId: string, federate?: boolean, hop?: number): Promise<Trace | undefined>;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=data.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data.d.ts","sourceRoot":"","sources":["../src/data.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoC,KAAK,KAAK,EAAE,KAAK,YAAY,EAAa,MAAM,sBAAsB,CAAC;AAElH,OAAO,EAA0D,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC1G,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAIrD,4FAA4F;AAC5F,qBAAa,OAAO;;IAClB,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IAIzB,YAAY,KAAK,EAAE,QAAQ,EAG1B;IAED,KAAK,CAAC,CAAC,EAAE,QAAQ,GAAG,IAAI,CAGvB;IAED,wEAAwE;IACxE,QAAQ,CAAC,UAAU,aAAc,MAAM,KAAG,OAAO,CAA8C;IAE/F,MAAM,CAAC,CAAC,EAAE,UAAU,GAAG,KAAK,EAAE,CAE7B;IAED,SAAS,CAAC,CAAC,EAAE,UAAU,GAAG,YAAY,EAAE,CAEvC;IAED,uEAAuE;IACjE,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,UAAO,EAAE,GAAG,SAAI,GAAG,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,CAMpF;CACF"}
|
package/dist/data.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { MAX_FEDERATION_HOPS, mergeTraces, toSummary } from "@wizeworks/lens-core";
|
|
2
|
+
import { fetchRemote } from "@wizeworks/lens-node/kit";
|
|
3
|
+
import { findTraceIds, getStoredTrace, listServices, loadTraces } from "./store-read.js";
|
|
4
|
+
const normalize = (e) => e.replace(/\/+$/, "");
|
|
5
|
+
/** The hub's data: writes, reads, and federation to add-ons that do not send to the hub. */
|
|
6
|
+
export class HubData {
|
|
7
|
+
store;
|
|
8
|
+
/** WizeLens endpoints of add-ons that send here. Their parts are already in the hub. */
|
|
9
|
+
#connected = new Set();
|
|
10
|
+
constructor(store) {
|
|
11
|
+
this.store = store;
|
|
12
|
+
for (const s of listServices(store.db, store.now()))
|
|
13
|
+
if (s.endpoint)
|
|
14
|
+
this.#connected.add(normalize(s.endpoint));
|
|
15
|
+
}
|
|
16
|
+
write(w) {
|
|
17
|
+
this.store.write(w);
|
|
18
|
+
if (w.sender.endpoint)
|
|
19
|
+
this.#connected.add(normalize(w.sender.endpoint));
|
|
20
|
+
}
|
|
21
|
+
/** Keep a remote only when the hub does not get that service's data. */
|
|
22
|
+
keepRemote = (endpoint) => !this.#connected.has(normalize(endpoint));
|
|
23
|
+
traces(q) {
|
|
24
|
+
return loadTraces(this.store.db, findTraceIds(this.store.db, q), this.keepRemote);
|
|
25
|
+
}
|
|
26
|
+
summaries(q) {
|
|
27
|
+
return this.traces(q).map(toSummary);
|
|
28
|
+
}
|
|
29
|
+
/** One trace, plus the parts held by add-ons that do not send here. */
|
|
30
|
+
async getTrace(traceId, federate = true, hop = 0) {
|
|
31
|
+
const local = getStoredTrace(this.store.db, traceId, this.keepRemote);
|
|
32
|
+
if (!federate || hop + 1 > MAX_FEDERATION_HOPS)
|
|
33
|
+
return local;
|
|
34
|
+
const parts = await Promise.all((local?.remotes ?? []).map((r) => fetchRemote(r, traceId, hop)));
|
|
35
|
+
const all = [local, ...parts].filter((t) => !!t && Array.isArray(t.spans));
|
|
36
|
+
return all.length ? mergeTraces(traceId, all) : undefined;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=data.js.map
|
package/dist/data.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data.js","sourceRoot":"","sources":["../src/data.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAiC,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAClH,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,UAAU,EAAmB,MAAM,iBAAiB,CAAC;AAG1G,MAAM,SAAS,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AAEvD,4FAA4F;AAC5F,MAAM,OAAO,OAAO;IACT,KAAK,CAAW;IACzB,wFAAwF;IAC/E,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;IAExC,YAAY,KAAe;QACzB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,KAAK,MAAM,CAAC,IAAI,YAAY,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC;YAAE,IAAI,CAAC,CAAC,QAAQ;gBAAE,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;IAClH,CAAC;IAED,KAAK,CAAC,CAAW;QACf,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACpB,IAAI,CAAC,CAAC,MAAM,CAAC,QAAQ;YAAE,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC3E,CAAC;IAED,wEAAwE;IAC/D,UAAU,GAAG,CAAC,QAAgB,EAAW,EAAE,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;IAE/F,MAAM,CAAC,CAAa;QAClB,OAAO,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;IACpF,CAAC;IAED,SAAS,CAAC,CAAa;QACrB,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACvC,CAAC;IAED,uEAAuE;IACvE,KAAK,CAAC,QAAQ,CAAC,OAAe,EAAE,QAAQ,GAAG,IAAI,EAAE,GAAG,GAAG,CAAC;QACtD,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QACtE,IAAI,CAAC,QAAQ,IAAI,GAAG,GAAG,CAAC,GAAG,mBAAmB;YAAE,OAAO,KAAK,CAAC;QAC7D,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,OAAO,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;QACjG,MAAM,GAAG,GAAG,CAAC,KAAK,EAAE,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAc,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;QACvF,OAAO,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC5D,CAAC;CACF"}
|
package/dist/hub.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type Server } from "node:http";
|
|
2
|
+
import { HubData } from "./data.js";
|
|
3
|
+
import { type HubOptions } from "./options.js";
|
|
4
|
+
export declare const HUB_VERSION: string;
|
|
5
|
+
export interface Hub {
|
|
6
|
+
readonly server: Server;
|
|
7
|
+
readonly data: HubData;
|
|
8
|
+
/** Start listening. Resolves to the hub's base URL. */
|
|
9
|
+
listen(): Promise<string>;
|
|
10
|
+
close(): Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
/** Create the local hub: one place for the traces of every service, with the dashboard, the API, OTLP, and MCP. */
|
|
13
|
+
export declare function createHub(options?: HubOptions): Hub;
|
|
14
|
+
//# sourceMappingURL=hub.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hub.d.ts","sourceRoot":"","sources":["../src/hub.ts"],"names":[],"mappings":"AACA,OAAO,EAAsC,KAAK,MAAM,EAAuB,MAAM,WAAW,CAAC;AAMjG,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,KAAK,UAAU,EAAqB,MAAM,cAAc,CAAC;AAMlE,eAAO,MAAM,WAAW,EAAE,MAAoE,CAAC;AAK/F,MAAM,WAAW,GAAG;IAClB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,uDAAuD;IACvD,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAmCD,mHAAmH;AACnH,wBAAgB,SAAS,CAAC,OAAO,GAAE,UAAe,GAAG,GAAG,CAiCvD"}
|