@wizeworks/lens-hub 0.0.0-stage → 0.3.0-alpha.2

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.
Files changed (115) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +60 -2
  3. package/dist/api.d.ts +20 -0
  4. package/dist/api.d.ts.map +1 -0
  5. package/dist/api.js +90 -0
  6. package/dist/api.js.map +1 -0
  7. package/dist/auth.d.ts +9 -0
  8. package/dist/auth.d.ts.map +1 -0
  9. package/dist/auth.js +55 -0
  10. package/dist/auth.js.map +1 -0
  11. package/dist/body.d.ts +9 -0
  12. package/dist/body.d.ts.map +1 -0
  13. package/dist/body.js +25 -0
  14. package/dist/body.js.map +1 -0
  15. package/dist/cli-args.d.ts +10 -0
  16. package/dist/cli-args.d.ts.map +1 -0
  17. package/dist/cli-args.js +63 -0
  18. package/dist/cli-args.js.map +1 -0
  19. package/dist/cli.d.ts +3 -0
  20. package/dist/cli.d.ts.map +1 -0
  21. package/dist/cli.js +35 -0
  22. package/dist/cli.js.map +1 -0
  23. package/dist/data.d.ts +17 -0
  24. package/dist/data.d.ts.map +1 -0
  25. package/dist/data.js +39 -0
  26. package/dist/data.js.map +1 -0
  27. package/dist/hub.d.ts +14 -0
  28. package/dist/hub.d.ts.map +1 -0
  29. package/dist/hub.js +89 -0
  30. package/dist/hub.js.map +1 -0
  31. package/dist/import.d.ts +4 -0
  32. package/dist/import.d.ts.map +1 -0
  33. package/dist/import.js +41 -0
  34. package/dist/import.js.map +1 -0
  35. package/dist/index.d.ts +7 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +3 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/ingest.d.ts +9 -0
  40. package/dist/ingest.d.ts.map +1 -0
  41. package/dist/ingest.js +83 -0
  42. package/dist/ingest.js.map +1 -0
  43. package/dist/kit.d.ts +10 -0
  44. package/dist/kit.d.ts.map +1 -0
  45. package/dist/kit.js +8 -0
  46. package/dist/kit.js.map +1 -0
  47. package/dist/mcp.d.ts +4 -0
  48. package/dist/mcp.d.ts.map +1 -0
  49. package/dist/mcp.js +18 -0
  50. package/dist/mcp.js.map +1 -0
  51. package/dist/metrics-read.d.ts +12 -0
  52. package/dist/metrics-read.d.ts.map +1 -0
  53. package/dist/metrics-read.js +18 -0
  54. package/dist/metrics-read.js.map +1 -0
  55. package/dist/metrics-write.d.ts +12 -0
  56. package/dist/metrics-write.d.ts.map +1 -0
  57. package/dist/metrics-write.js +107 -0
  58. package/dist/metrics-write.js.map +1 -0
  59. package/dist/models-api.d.ts +5 -0
  60. package/dist/models-api.d.ts.map +1 -0
  61. package/dist/models-api.js +36 -0
  62. package/dist/models-api.js.map +1 -0
  63. package/dist/options.d.ts +36 -0
  64. package/dist/options.d.ts.map +1 -0
  65. package/dist/options.js +22 -0
  66. package/dist/options.js.map +1 -0
  67. package/dist/otlp/index.d.ts +8 -0
  68. package/dist/otlp/index.d.ts.map +1 -0
  69. package/dist/otlp/index.js +41 -0
  70. package/dist/otlp/index.js.map +1 -0
  71. package/dist/otlp/logs.d.ts +24 -0
  72. package/dist/otlp/logs.d.ts.map +1 -0
  73. package/dist/otlp/logs.js +44 -0
  74. package/dist/otlp/logs.js.map +1 -0
  75. package/dist/otlp/metrics.d.ts +59 -0
  76. package/dist/otlp/metrics.d.ts.map +1 -0
  77. package/dist/otlp/metrics.js +73 -0
  78. package/dist/otlp/metrics.js.map +1 -0
  79. package/dist/otlp/proto.d.ts +14 -0
  80. package/dist/otlp/proto.d.ts.map +1 -0
  81. package/dist/otlp/proto.js +132 -0
  82. package/dist/otlp/proto.js.map +1 -0
  83. package/dist/otlp/spans.d.ts +33 -0
  84. package/dist/otlp/spans.d.ts.map +1 -0
  85. package/dist/otlp/spans.js +80 -0
  86. package/dist/otlp/spans.js.map +1 -0
  87. package/dist/otlp/values.d.ts +40 -0
  88. package/dist/otlp/values.d.ts.map +1 -0
  89. package/dist/otlp/values.js +78 -0
  90. package/dist/otlp/values.js.map +1 -0
  91. package/dist/scale-exec.d.ts +10 -0
  92. package/dist/scale-exec.d.ts.map +1 -0
  93. package/dist/scale-exec.js +22 -0
  94. package/dist/scale-exec.js.map +1 -0
  95. package/dist/schema-scale.d.ts +7 -0
  96. package/dist/schema-scale.d.ts.map +1 -0
  97. package/dist/schema-scale.js +99 -0
  98. package/dist/schema-scale.js.map +1 -0
  99. package/dist/schema.d.ts +13 -0
  100. package/dist/schema.d.ts.map +1 -0
  101. package/dist/schema.js +49 -0
  102. package/dist/schema.js.map +1 -0
  103. package/dist/settle.d.ts +28 -0
  104. package/dist/settle.d.ts.map +1 -0
  105. package/dist/settle.js +103 -0
  106. package/dist/settle.js.map +1 -0
  107. package/dist/store-read.d.ts +27 -0
  108. package/dist/store-read.d.ts.map +1 -0
  109. package/dist/store-read.js +74 -0
  110. package/dist/store-read.js.map +1 -0
  111. package/dist/store.d.ts +42 -0
  112. package/dist/store.d.ts.map +1 -0
  113. package/dist/store.js +118 -0
  114. package/dist/store.js.map +1 -0
  115. 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
- # Temporary Holding Version
1
+ # @wizeworks/lens-hub
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,EAAE,UAAU,EAAoB,MAAM,sBAAsB,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,WAAW,EAAE,QAAQ,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACrI,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAEhD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACvE,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,YAAY,EAAmB,MAAM,iBAAiB,CAAC;AASvF,MAAM,KAAK,GAAG,CAAC,CAAS,EAAE,GAAW,EAAE,GAAW,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;AACvF,MAAM,GAAG,GAAG,CAAC,CAAkB,EAAE,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;AAE9H,MAAM,UAAU,OAAO,CAAC,CAAa;IACnC,OAAO;QACL,IAAI,EAAE,KAAK;QACX,YAAY,EAAE,CAAC,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,SAAS,EAAE,eAAe,EAAE,aAAa,CAAC;QACnG,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI;QACjB,OAAO,EAAE,CAAC,CAAC,OAAO;QAClB,OAAO,EAAE,QAAQ,OAAO,CAAC,OAAO,gBAAgB,YAAY,EAAE;QAC9D,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS;QAClB,IAAI,EAAE,QAAQ,EAAE;QAChB,SAAS,EAAE,CAAC,CAAC,SAAS;QACtB,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;QAC7E,QAAQ,EAAE,EAAE,WAAW,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE;QACvF,UAAU,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC;KAChC,CAAC;AACJ,CAAC;AAED,uHAAuH;AACvH,MAAM,UAAU,OAAO,CAAC,CAAkB;IACxC,MAAM,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,MAAM,CAAC;IACtC,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAClF,MAAM,CAAC,GAAe,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC,EAAE,CAAC;IAClF,KAAK,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,SAAS,EAAE,SAAS,CAAU;QAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,SAAS,CAAC;IACrF,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC;AACrB,CAAC;AAED,SAAS,QAAQ,CAAC,CAAa;IAC7B,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;IAC3B,OAAO;QACL,QAAQ,EAAE,YAAY,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC;QACrC,YAAY,EAAE,UAAU,CAAC,EAAE,EAAE,KAAK,CAAC;QACnC,QAAQ,EAAE,UAAU,CAAC,EAAE,EAAE,SAAS,CAAC;QACnC,GAAG,SAAS,CAAC,EAAE,CAAC;KACjB,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,MAAM,CAAC,CAAa,EAAE,GAAoB,EAAE,GAAmB;IAC5E,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,WAAW,CAAC,SAAS,CAAC,MAAM,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;QACtE,IAAI,CAAC,CAAC;YAAE,OAAO,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,4BAA4B,EAAE,CAAC,CAAC;QAC3E,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAChB,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAC;IAChD,CAAC;AACH,CAAC;AAED,SAAS,OAAO,CAAC,CAAa,EAAE,CAAkB;IAChD,MAAM,KAAK,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,SAAS,EAAE,CAAC;IACzF,OAAO,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;AAClG,CAAC;AAED,KAAK,UAAU,SAAS,CAAC,CAAa,EAAE,OAAe,EAAE,GAAoB,EAAE,GAAmB,EAAE,GAAQ;IAC1G,MAAM,GAAG,GAAG,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAC5E,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,MAAM,EAAE,GAAG,CAAC,CAAC;IAC3F,IAAI,CAAC;QAAE,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;;QACxB,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,iBAAiB,EAAE,CAAC,CAAC;AACxD,CAAC;AAED,uCAAuC;AACvC,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,CAAa,EAAE,GAAW,EAAE,GAAoB,EAAE,GAAmB,EAAE,GAAQ;IAC7G,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,IAAI,KAAK,CAAC;IACnC,IAAI,GAAG,KAAK,UAAU,IAAI,MAAM,KAAK,KAAK;QAAE,OAAO,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IAClF,IAAI,GAAG,KAAK,cAAc,IAAI,MAAM,KAAK,KAAK;QAAE,OAAO,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IACvF,IAAI,GAAG,KAAK,YAAY,IAAI,MAAM,KAAK,MAAM;QAAE,OAAO,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IAC1E,IAAI,GAAG,KAAK,YAAY,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;QAC7C,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QAC9C,OAAO,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3E,CAAC;IACD,IAAI,GAAG,KAAK,oBAAoB,IAAI,MAAM,KAAK,KAAK;QAAE,OAAO,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC,EAAE,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC;IAC9G,IAAI,GAAG,CAAC,UAAU,CAAC,aAAa,CAAC,IAAI,MAAM,KAAK,KAAK;QAAE,OAAO,aAAa,CAAC,CAAC,EAAE,GAAG,CAAC,KAAK,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,YAAY,EAAE,GAAG,CAAC,CAAC;IACvI,MAAM,CAAC,GAAG,+BAA+B,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACpD,IAAI,CAAC,IAAI,MAAM,KAAK,KAAK;QAAE,OAAO,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IACrE,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;AAC7C,CAAC"}
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
@@ -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
@@ -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"}
@@ -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,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -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
@@ -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
@@ -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"}