qyl-mcp-server 2.1.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -0
- package/dist/collector.d.ts +15 -2
- package/dist/collector.js +67 -3
- package/dist/collector.js.map +1 -1
- package/dist/contract-operations.d.ts +37 -0
- package/dist/contract-operations.js +124 -0
- package/dist/contract-operations.js.map +1 -0
- package/dist/contract-validation.d.ts +7 -2
- package/dist/contract-validation.js +11 -3
- package/dist/contract-validation.js.map +1 -1
- package/dist/demo.d.ts +18 -0
- package/dist/demo.js +99 -0
- package/dist/demo.js.map +1 -1
- package/dist/mcp-app.html +1 -1
- package/dist/mcp-dashboard.html +1 -1
- package/dist/mcp-home.html +1 -1
- package/dist/metrics-tools.d.ts +23 -0
- package/dist/metrics-tools.js +96 -0
- package/dist/metrics-tools.js.map +1 -0
- package/dist/metrics.d.ts +45 -0
- package/dist/metrics.js +274 -0
- package/dist/metrics.js.map +1 -0
- package/dist/observe-graph.html +1 -1
- package/dist/server.js +2 -0
- package/dist/server.js.map +1 -1
- package/dist/summaries.d.ts +13 -0
- package/dist/summaries.js +67 -0
- package/dist/summaries.js.map +1 -1
- package/package.json +2 -2
package/dist/mcp-home.html
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<meta name="color-scheme" content="dark">
|
|
7
7
|
<meta name="description" content="Bring qyl traces, logs, and MCP telemetry into your coding agent.">
|
|
8
8
|
<title>qyl · MCP telemetry for coding agents</title>
|
|
9
|
-
<script type="module" crossorigin>(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var e={name:`qyl-mcp-server`,version:`
|
|
9
|
+
<script type="module" crossorigin>(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var e={name:`qyl-mcp-server`,version:`3.0.0`,type:`module`,description:`qyl telemetry MCP Apps server: trace explorer and MCP dashboard for a live collector or explicit demo mode`,repository:{type:`git`,url:`https://github.com/ANcpLua/qyl.mcp`,directory:`server`},license:`MIT`,main:`dist/server.js`,types:`dist/server.d.ts`,exports:{".":{types:`./dist/server.d.ts`,default:`./dist/server.js`},"./contract-validation":{types:`./dist/contract-validation.d.ts`,default:`./dist/contract-validation.js`},"./secret-redactor":{types:`./dist/secret-redactor.d.ts`,default:`./dist/secret-redactor.js`},"./atomic-json-store":{types:`./dist/atomic-json-store.d.ts`,default:`./dist/atomic-json-store.js`},"./execution-evidence":{types:`./dist/execution-evidence.d.ts`,default:`./dist/execution-evidence.js`},"./execution-result":{types:`./dist/execution-result.d.ts`,default:`./dist/execution-result.js`},"./mcp-semconv":{types:`./dist/mcp-semconv.d.ts`,default:`./dist/mcp-semconv.js`},"./observability-suppression":{types:`./dist/observability-suppression.d.ts`,default:`./dist/observability-suppression.js`},"./telemetry":{types:`./dist/telemetry.d.ts`,default:`./dist/telemetry.js`}},bin:{"qyl-mcp-server":`dist/main.js`},files:[`dist`],scripts:{clean:`node -e "require('node:fs').rmSync('dist', { recursive: true, force: true }); require('node:fs').rmSync('dist-test', { recursive: true, force: true })"`,build:`bun run clean && tsc --noEmit && vite build --mode mcp-home && vite build --mode mcp-app && vite build --mode mcp-dashboard && vite build --mode observe-graph && tsc -p tsconfig.server.json && node -e "require('node:fs').chmodSync('dist/main.js', 0o755)"`,postbuild:`bun run verify:deployment-guidance`,"verify:deployment-guidance":`node verify-deployment-guidance.mjs`,"verify:shapes":`node verify-generated-shapes.mjs`,"verify:readme":`node verify-package-readme.mjs`,"snapshot:tools":`bun run build && tsc -p tsconfig.test.json && node update-tool-manifest-snapshot.mjs`,test:`bun run verify:shapes && bun run verify:readme && bun run build && tsc -p tsconfig.test.json && node -e "const fs=require('node:fs'); for (const name of ['mcp-app.html','mcp-dashboard.html','observe-graph.html']) fs.copyFileSync('dist/'+name,'dist-test/'+name)" && node --test dist-test/*.test.js`,smoke:`node smoke-test.mjs`,prepack:`bun run build`},dependencies:{"@ancplua/qyl-api-schema":`8.0.0`,"@modelcontextprotocol/core":`2.0.0`,"@modelcontextprotocol/server":`2.0.0`,"@opentelemetry/api":`^1.9.1`,"@opentelemetry/api-logs":`^0.221.0`,"@opentelemetry/core":`^2.10.0`,"@opentelemetry/exporter-logs-otlp-proto":`^0.221.0`,"@opentelemetry/exporter-metrics-otlp-proto":`^0.221.0`,"@opentelemetry/exporter-trace-otlp-proto":`^0.221.0`,"@opentelemetry/resources":`^2.10.0`,"@opentelemetry/sdk-logs":`^0.221.0`,"@opentelemetry/sdk-metrics":`^2.10.0`,"@opentelemetry/sdk-trace-base":`^2.10.0`,"@opentelemetry/sdk-trace-node":`^2.10.0`,"@opentelemetry/semantic-conventions":`^1.43.0`,jose:`^6.2.3`,zod:`^4.5.0`},devDependencies:{"@modelcontextprotocol/client":`2.0.0`,"@types/node":`26.4.0`,typescript:`7.0.2`,vite:`8.2.2`,"vite-plugin-singlefile":`2.3.3`},engines:{node:`24.x`}},t=`https://mcp.qyl.at/mcp`,n={codex:{title:`terminal`,value:`codex mcp add qyl --url ${t}`},claude:{title:`terminal`,value:`claude mcp add --transport http qyl ${t}`},cursor:{title:`mcp.json`,value:`{
|
|
10
10
|
"mcpServers": {
|
|
11
11
|
"qyl": {
|
|
12
12
|
"url": "${t}"
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The metrics read tools: catalog, series discovery, range query.
|
|
3
|
+
*
|
|
4
|
+
* Three tools rather than one because they answer three different questions an
|
|
5
|
+
* agent asks in order — "what is recorded here", "which streams exist under
|
|
6
|
+
* this name", "what did this one do over that window" — and folding them into a
|
|
7
|
+
* single tool with a mode argument would make every call carry the arguments of
|
|
8
|
+
* the other two.
|
|
9
|
+
*
|
|
10
|
+
* Output shapes are the operations' own 200 bodies from contract-validation.ts.
|
|
11
|
+
* Input shapes are derived from the same operations' published parameters (see
|
|
12
|
+
* contract-operations.ts): the metrics API landed in @ancplua/qyl-api-schema
|
|
13
|
+
* 8.0.0 with responses but without `Mcp.Tools.*Metric*` input models, and
|
|
14
|
+
* generating them from the OpenAPI document keeps gate G10a honest instead of
|
|
15
|
+
* exempting three hand-written objects from it.
|
|
16
|
+
*/
|
|
17
|
+
import type { McpServer } from "@modelcontextprotocol/server";
|
|
18
|
+
/**
|
|
19
|
+
* The list operations page, and their pages are the tool's output. `has_more`
|
|
20
|
+
* travels with the items so a reader can tell a complete catalog from a first
|
|
21
|
+
* page; the cursor does not, because these tools take no cursor.
|
|
22
|
+
*/
|
|
23
|
+
export declare function registerMetricsTools(server: McpServer): void;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The metrics read tools: catalog, series discovery, range query.
|
|
3
|
+
*
|
|
4
|
+
* Three tools rather than one because they answer three different questions an
|
|
5
|
+
* agent asks in order — "what is recorded here", "which streams exist under
|
|
6
|
+
* this name", "what did this one do over that window" — and folding them into a
|
|
7
|
+
* single tool with a mode argument would make every call carry the arguments of
|
|
8
|
+
* the other two.
|
|
9
|
+
*
|
|
10
|
+
* Output shapes are the operations' own 200 bodies from contract-validation.ts.
|
|
11
|
+
* Input shapes are derived from the same operations' published parameters (see
|
|
12
|
+
* contract-operations.ts): the metrics API landed in @ancplua/qyl-api-schema
|
|
13
|
+
* 8.0.0 with responses but without `Mcp.Tools.*Metric*` input models, and
|
|
14
|
+
* generating them from the OpenAPI document keeps gate G10a honest instead of
|
|
15
|
+
* exempting three hand-written objects from it.
|
|
16
|
+
*/
|
|
17
|
+
import { operationInputSchema } from "./contract-operations.js";
|
|
18
|
+
import { MetricQueryResultSchema, MetricsListResponseSchema, MetricSeriesListResponseSchema, compactOutputSchema, } from "./contract-validation.js";
|
|
19
|
+
import { listMetricSeries, listMetrics, queryMetric, } from "./metrics.js";
|
|
20
|
+
import { summarizeMetricCatalog, summarizeMetricQuery, summarizeMetricSeries, } from "./summaries.js";
|
|
21
|
+
import { telemetryToolResult } from "./telemetry-redaction.js";
|
|
22
|
+
import { READ_ONLY_TELEMETRY_TOOL_ANNOTATIONS, toolError } from "./tools.js";
|
|
23
|
+
const METRICS_PATH = "/api/v1/metrics";
|
|
24
|
+
const SERIES_PATH = "/api/v1/metrics/{metric_name}/series";
|
|
25
|
+
const QUERY_PATH = "/api/v1/metrics/{metric_name}/query";
|
|
26
|
+
/**
|
|
27
|
+
* The list operations page, and their pages are the tool's output. `has_more`
|
|
28
|
+
* travels with the items so a reader can tell a complete catalog from a first
|
|
29
|
+
* page; the cursor does not, because these tools take no cursor.
|
|
30
|
+
*/
|
|
31
|
+
export function registerMetricsTools(server) {
|
|
32
|
+
server.registerTool("list_metrics", {
|
|
33
|
+
title: "List Metrics",
|
|
34
|
+
description: "List the metric instruments recorded for this project: name, kind " +
|
|
35
|
+
"(gauge/sum/histogram), unit, how many attribute streams exist under each " +
|
|
36
|
+
"name, and when it was last written. Start here — query_metric needs an " +
|
|
37
|
+
"exact instrument name, and this is where the names come from.",
|
|
38
|
+
inputSchema: operationInputSchema(METRICS_PATH),
|
|
39
|
+
outputSchema: compactOutputSchema(MetricsListResponseSchema),
|
|
40
|
+
annotations: READ_ONLY_TELEMETRY_TOOL_ANNOTATIONS,
|
|
41
|
+
}, async (args) => {
|
|
42
|
+
try {
|
|
43
|
+
const { metrics, mode } = await listMetrics(args);
|
|
44
|
+
return telemetryToolResult(summarizeMetricCatalog(metrics, mode), {
|
|
45
|
+
items: metrics,
|
|
46
|
+
has_more: false,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
return toolError(error);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
server.registerTool("get_metric_series", {
|
|
54
|
+
title: "Get Metric Series",
|
|
55
|
+
description: "List the distinct attribute streams recorded under one metric name, with " +
|
|
56
|
+
"each stream's attributes, service, and first/last seen. Use it to discover " +
|
|
57
|
+
"which attribute keys are worth passing to query_metric as group_by or attr " +
|
|
58
|
+
"filters, before running a range query that would otherwise collapse or " +
|
|
59
|
+
"explode the result.",
|
|
60
|
+
inputSchema: operationInputSchema(SERIES_PATH),
|
|
61
|
+
outputSchema: compactOutputSchema(MetricSeriesListResponseSchema),
|
|
62
|
+
annotations: READ_ONLY_TELEMETRY_TOOL_ANNOTATIONS,
|
|
63
|
+
}, async (args) => {
|
|
64
|
+
try {
|
|
65
|
+
const { series, mode } = await listMetricSeries(args);
|
|
66
|
+
return telemetryToolResult(summarizeMetricSeries(series, mode), {
|
|
67
|
+
items: series,
|
|
68
|
+
has_more: false,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
return toolError(error);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
server.registerTool("query_metric", {
|
|
76
|
+
title: "Query Metric",
|
|
77
|
+
description: "Run a time-bucketed range query over one metric: a window (start_time, " +
|
|
78
|
+
"end_time), a bucket width (step_ms), a reducer (aggregation: avg, min, max, " +
|
|
79
|
+
"sum, count, last, p50, p90, p95, p99), optional group_by attribute keys, and " +
|
|
80
|
+
"optional attr/attr_prefix matchers written 'key=value'. Returns one stream " +
|
|
81
|
+
"per grouping with its buckets. One bucket spanning the whole window collapses " +
|
|
82
|
+
"the answer to a single number.",
|
|
83
|
+
inputSchema: operationInputSchema(QUERY_PATH),
|
|
84
|
+
outputSchema: compactOutputSchema(MetricQueryResultSchema),
|
|
85
|
+
annotations: READ_ONLY_TELEMETRY_TOOL_ANNOTATIONS,
|
|
86
|
+
}, async (args) => {
|
|
87
|
+
try {
|
|
88
|
+
const { result, mode } = await queryMetric(args);
|
|
89
|
+
return telemetryToolResult(summarizeMetricQuery(result, mode), result);
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
return toolError(error);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=metrics-tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics-tools.js","sourceRoot":"","sources":["../src/metrics-tools.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Metric reading over the contract 8.0.0 metrics API, live and demo.
|
|
3
|
+
*
|
|
4
|
+
* Live mode forwards to the collector's three published operations and revalidates
|
|
5
|
+
* the answer against the operation's own 200 body. Demo mode holds stored points
|
|
6
|
+
* and runs the same bucketing and reducer the collector would, so a demo answer
|
|
7
|
+
* honors step_ms, aggregation, group_by and the attribute matchers rather than
|
|
8
|
+
* pretending they were applied — the invariant the rest of the demo dataset keeps.
|
|
9
|
+
*/
|
|
10
|
+
import type { MetricAggregation, MetricDescriptor, MetricQueryResult, MetricSeries } from "@ancplua/qyl-api-schema/types";
|
|
11
|
+
import type { Mode } from "./wire.js";
|
|
12
|
+
/** Tool arguments, mirroring the published operation parameters that shape them. */
|
|
13
|
+
export interface ListMetricsArgs {
|
|
14
|
+
name_prefix?: string;
|
|
15
|
+
limit?: number;
|
|
16
|
+
}
|
|
17
|
+
export interface MetricSeriesArgs {
|
|
18
|
+
metric_name: string;
|
|
19
|
+
attr?: readonly string[];
|
|
20
|
+
attr_prefix?: readonly string[];
|
|
21
|
+
limit?: number;
|
|
22
|
+
}
|
|
23
|
+
export interface QueryMetricArgs {
|
|
24
|
+
metric_name: string;
|
|
25
|
+
start_time: string;
|
|
26
|
+
end_time: string;
|
|
27
|
+
step_ms?: number;
|
|
28
|
+
aggregation?: MetricAggregation;
|
|
29
|
+
group_by?: readonly string[];
|
|
30
|
+
attr?: readonly string[];
|
|
31
|
+
attr_prefix?: readonly string[];
|
|
32
|
+
series_limit?: number;
|
|
33
|
+
}
|
|
34
|
+
export declare function listMetrics(args: ListMetricsArgs): Promise<{
|
|
35
|
+
metrics: MetricDescriptor[];
|
|
36
|
+
mode: Mode;
|
|
37
|
+
}>;
|
|
38
|
+
export declare function listMetricSeries(args: MetricSeriesArgs): Promise<{
|
|
39
|
+
series: MetricSeries[];
|
|
40
|
+
mode: Mode;
|
|
41
|
+
}>;
|
|
42
|
+
export declare function queryMetric(args: QueryMetricArgs): Promise<{
|
|
43
|
+
result: MetricQueryResult;
|
|
44
|
+
mode: Mode;
|
|
45
|
+
}>;
|
package/dist/metrics.js
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Metric reading over the contract 8.0.0 metrics API, live and demo.
|
|
3
|
+
*
|
|
4
|
+
* Live mode forwards to the collector's three published operations and revalidates
|
|
5
|
+
* the answer against the operation's own 200 body. Demo mode holds stored points
|
|
6
|
+
* and runs the same bucketing and reducer the collector would, so a demo answer
|
|
7
|
+
* honors step_ms, aggregation, group_by and the attribute matchers rather than
|
|
8
|
+
* pretending they were applied — the invariant the rest of the demo dataset keeps.
|
|
9
|
+
*/
|
|
10
|
+
import { CollectorError, collectorGet, parseCollectorMetricDescriptor, parseCollectorMetricQueryResult, parseCollectorMetricSeries, parseCollectorPage, resolveMode, } from "./collector.js";
|
|
11
|
+
import { MetricsListResponseSchema, MetricSeriesListResponseSchema, } from "./contract-validation.js";
|
|
12
|
+
import { getDemoMetrics } from "./demo.js";
|
|
13
|
+
import { redactTelemetry } from "./telemetry-redaction.js";
|
|
14
|
+
const METRICS_PATH = "/api/v1/metrics";
|
|
15
|
+
/**
|
|
16
|
+
* `key=value` and `key=prefix` are the published spellings of both matcher
|
|
17
|
+
* lists. A matcher without `=` is the model's mistake, not the collector's, so
|
|
18
|
+
* it fails here with the offending text rather than reaching the wire as a
|
|
19
|
+
* silently ignored filter.
|
|
20
|
+
*/
|
|
21
|
+
function parseMatchers(raw, parameter) {
|
|
22
|
+
return (raw ?? []).map((entry) => {
|
|
23
|
+
const separator = entry.indexOf("=");
|
|
24
|
+
if (separator <= 0) {
|
|
25
|
+
throw new CollectorError(`${parameter} entries are written 'key=value'; received ${JSON.stringify(entry)}`);
|
|
26
|
+
}
|
|
27
|
+
return { key: entry.slice(0, separator), value: entry.slice(separator + 1) };
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
function attributeText(attributes, key) {
|
|
31
|
+
const found = attributes.find((attribute) => attribute.key === key);
|
|
32
|
+
if (found === undefined)
|
|
33
|
+
return undefined;
|
|
34
|
+
const { value } = found;
|
|
35
|
+
if (typeof value === "string")
|
|
36
|
+
return value;
|
|
37
|
+
if (typeof value === "boolean")
|
|
38
|
+
return String(value);
|
|
39
|
+
if (value !== null && typeof value === "object" && "value" in value) {
|
|
40
|
+
return String(value.value);
|
|
41
|
+
}
|
|
42
|
+
return value === null ? undefined : String(value);
|
|
43
|
+
}
|
|
44
|
+
function matchesStream(stream, exact, prefixes) {
|
|
45
|
+
const read = (key) => key === "service.name" ? stream.serviceName : attributeText(stream.attributes, key);
|
|
46
|
+
return (exact.every((matcher) => read(matcher.key) === matcher.value) &&
|
|
47
|
+
prefixes.every((matcher) => read(matcher.key)?.startsWith(matcher.value) === true));
|
|
48
|
+
}
|
|
49
|
+
/** Stable across rebuilds because it is derived from identity, not from order. */
|
|
50
|
+
function demoSeriesId(stream) {
|
|
51
|
+
const identity = [
|
|
52
|
+
stream.name,
|
|
53
|
+
stream.serviceName,
|
|
54
|
+
...stream.attributes.map((attribute) => `${attribute.key}=${attributeText([attribute], attribute.key) ?? ""}`),
|
|
55
|
+
].join("|");
|
|
56
|
+
let hash = 0x811c9dc5;
|
|
57
|
+
for (let index = 0; index < identity.length; index += 1) {
|
|
58
|
+
hash ^= identity.charCodeAt(index);
|
|
59
|
+
hash = Math.imul(hash, 0x01000193) >>> 0;
|
|
60
|
+
}
|
|
61
|
+
return hash.toString(16).padStart(8, "0");
|
|
62
|
+
}
|
|
63
|
+
export async function listMetrics(args) {
|
|
64
|
+
const mode = await resolveMode();
|
|
65
|
+
const limit = args.limit ?? 200;
|
|
66
|
+
if (mode === "demo") {
|
|
67
|
+
const byName = new Map();
|
|
68
|
+
for (const stream of getDemoMetrics()) {
|
|
69
|
+
if (args.name_prefix !== undefined && !stream.name.startsWith(args.name_prefix))
|
|
70
|
+
continue;
|
|
71
|
+
const bucket = byName.get(stream.name);
|
|
72
|
+
if (bucket)
|
|
73
|
+
bucket.push(stream);
|
|
74
|
+
else
|
|
75
|
+
byName.set(stream.name, [stream]);
|
|
76
|
+
}
|
|
77
|
+
const metrics = [...byName.entries()]
|
|
78
|
+
.sort(([left], [right]) => (left < right ? -1 : left > right ? 1 : 0))
|
|
79
|
+
.slice(0, limit)
|
|
80
|
+
.map(([name, streams]) => {
|
|
81
|
+
const [first] = streams;
|
|
82
|
+
if (first === undefined)
|
|
83
|
+
throw new CollectorError(`demo metric ${name} has no streams`);
|
|
84
|
+
const lastSeen = Math.max(...streams.map((stream) => stream.points.at(-1)?.atMs ?? 0));
|
|
85
|
+
return {
|
|
86
|
+
name,
|
|
87
|
+
kind: first.kind,
|
|
88
|
+
temporality: first.temporality,
|
|
89
|
+
monotonic: first.monotonic,
|
|
90
|
+
unit: first.unit,
|
|
91
|
+
description: first.description,
|
|
92
|
+
series_count: streams.length,
|
|
93
|
+
last_seen: new Date(lastSeen).toISOString(),
|
|
94
|
+
};
|
|
95
|
+
});
|
|
96
|
+
return redactTelemetry({ metrics, mode });
|
|
97
|
+
}
|
|
98
|
+
const body = await collectorGet(METRICS_PATH, {
|
|
99
|
+
name_prefix: args.name_prefix,
|
|
100
|
+
limit,
|
|
101
|
+
});
|
|
102
|
+
return redactTelemetry({
|
|
103
|
+
metrics: parseCollectorPage(body, METRICS_PATH, MetricsListResponseSchema, parseCollectorMetricDescriptor).items,
|
|
104
|
+
mode,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
export async function listMetricSeries(args) {
|
|
108
|
+
const mode = await resolveMode();
|
|
109
|
+
const limit = args.limit ?? 200;
|
|
110
|
+
const exact = parseMatchers(args.attr, "attr");
|
|
111
|
+
const prefixes = parseMatchers(args.attr_prefix, "attr_prefix");
|
|
112
|
+
if (mode === "demo") {
|
|
113
|
+
const streams = getDemoMetrics().filter((stream) => stream.name === args.metric_name && matchesStream(stream, exact, prefixes));
|
|
114
|
+
if (streams.length === 0 && !getDemoMetrics().some((s) => s.name === args.metric_name)) {
|
|
115
|
+
throw new CollectorError(`metric not found: ${args.metric_name}`);
|
|
116
|
+
}
|
|
117
|
+
const series = streams.slice(0, limit).map((stream) => ({
|
|
118
|
+
series_id: demoSeriesId(stream),
|
|
119
|
+
name: stream.name,
|
|
120
|
+
kind: stream.kind,
|
|
121
|
+
attributes: stream.attributes,
|
|
122
|
+
service_name: stream.serviceName,
|
|
123
|
+
unit: stream.unit,
|
|
124
|
+
first_seen: new Date(stream.points[0]?.atMs ?? 0).toISOString(),
|
|
125
|
+
last_seen: new Date(stream.points.at(-1)?.atMs ?? 0).toISOString(),
|
|
126
|
+
}));
|
|
127
|
+
return redactTelemetry({ series, mode });
|
|
128
|
+
}
|
|
129
|
+
const path = `${METRICS_PATH}/${encodeURIComponent(args.metric_name)}/series`;
|
|
130
|
+
try {
|
|
131
|
+
const body = await collectorGet(path, {
|
|
132
|
+
attr: args.attr,
|
|
133
|
+
attr_prefix: args.attr_prefix,
|
|
134
|
+
limit,
|
|
135
|
+
});
|
|
136
|
+
return redactTelemetry({
|
|
137
|
+
series: parseCollectorPage(body, path, MetricSeriesListResponseSchema, parseCollectorMetricSeries).items,
|
|
138
|
+
mode,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
if (error instanceof CollectorError && error.status === 404) {
|
|
143
|
+
throw new CollectorError(`metric not found: ${args.metric_name}`);
|
|
144
|
+
}
|
|
145
|
+
throw error;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/** The published reducers, over the points that landed in one bucket. */
|
|
149
|
+
function reduce(values, aggregation) {
|
|
150
|
+
if (values.length === 0)
|
|
151
|
+
return null;
|
|
152
|
+
const sorted = [...values].sort((left, right) => left - right);
|
|
153
|
+
const quantile = (fraction) => {
|
|
154
|
+
const index = Math.min(sorted.length - 1, Math.floor(fraction * sorted.length));
|
|
155
|
+
return sorted[index];
|
|
156
|
+
};
|
|
157
|
+
switch (aggregation) {
|
|
158
|
+
case "avg":
|
|
159
|
+
return values.reduce((total, value) => total + value, 0) / values.length;
|
|
160
|
+
case "min":
|
|
161
|
+
return sorted[0];
|
|
162
|
+
case "max":
|
|
163
|
+
return sorted[sorted.length - 1];
|
|
164
|
+
case "sum":
|
|
165
|
+
return values.reduce((total, value) => total + value, 0);
|
|
166
|
+
case "count":
|
|
167
|
+
return values.length;
|
|
168
|
+
case "last":
|
|
169
|
+
return values[values.length - 1];
|
|
170
|
+
case "p50":
|
|
171
|
+
return quantile(0.5);
|
|
172
|
+
case "p90":
|
|
173
|
+
return quantile(0.9);
|
|
174
|
+
case "p95":
|
|
175
|
+
return quantile(0.95);
|
|
176
|
+
case "p99":
|
|
177
|
+
return quantile(0.99);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
function groupingKey(stream, groupBy) {
|
|
181
|
+
const attributes = [];
|
|
182
|
+
for (const key of groupBy) {
|
|
183
|
+
const value = key === "service.name"
|
|
184
|
+
? stream.serviceName
|
|
185
|
+
: attributeText(stream.attributes, key);
|
|
186
|
+
if (value !== undefined)
|
|
187
|
+
attributes.push({ key, value });
|
|
188
|
+
}
|
|
189
|
+
return { key: attributes.map((a) => `${a.key}=${String(a.value)}`).join("|"), attributes };
|
|
190
|
+
}
|
|
191
|
+
export async function queryMetric(args) {
|
|
192
|
+
const mode = await resolveMode();
|
|
193
|
+
const stepMs = args.step_ms ?? 60_000;
|
|
194
|
+
const aggregation = args.aggregation ?? "avg";
|
|
195
|
+
const seriesLimit = args.series_limit ?? 50;
|
|
196
|
+
const exact = parseMatchers(args.attr, "attr");
|
|
197
|
+
const prefixes = parseMatchers(args.attr_prefix, "attr_prefix");
|
|
198
|
+
if (mode === "demo") {
|
|
199
|
+
const streams = getDemoMetrics().filter((stream) => stream.name === args.metric_name && matchesStream(stream, exact, prefixes));
|
|
200
|
+
if (!getDemoMetrics().some((stream) => stream.name === args.metric_name)) {
|
|
201
|
+
throw new CollectorError(`metric not found: ${args.metric_name}`);
|
|
202
|
+
}
|
|
203
|
+
const startMs = Date.parse(args.start_time);
|
|
204
|
+
const endMs = Date.parse(args.end_time);
|
|
205
|
+
if (!Number.isFinite(startMs) || !Number.isFinite(endMs) || endMs <= startMs) {
|
|
206
|
+
throw new CollectorError("end_time must be a valid timestamp after start_time");
|
|
207
|
+
}
|
|
208
|
+
const groups = new Map();
|
|
209
|
+
for (const stream of streams) {
|
|
210
|
+
const { key, attributes } = groupingKey(stream, args.group_by ?? []);
|
|
211
|
+
const existing = groups.get(key);
|
|
212
|
+
if (existing)
|
|
213
|
+
existing.streams.push(stream);
|
|
214
|
+
else
|
|
215
|
+
groups.set(key, { attributes, streams: [stream] });
|
|
216
|
+
}
|
|
217
|
+
const ordered = [...groups.values()];
|
|
218
|
+
const truncated = ordered.length > seriesLimit;
|
|
219
|
+
const bucketCount = Math.max(1, Math.ceil((endMs - startMs) / stepMs));
|
|
220
|
+
const series = ordered.slice(0, seriesLimit).map((group) => {
|
|
221
|
+
const buckets = Array.from({ length: bucketCount }, () => []);
|
|
222
|
+
for (const stream of group.streams) {
|
|
223
|
+
for (const point of stream.points) {
|
|
224
|
+
if (point.atMs < startMs || point.atMs >= endMs)
|
|
225
|
+
continue;
|
|
226
|
+
const index = Math.min(bucketCount - 1, Math.floor((point.atMs - startMs) / stepMs));
|
|
227
|
+
buckets[index].push(point.value);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return {
|
|
231
|
+
attributes: group.attributes,
|
|
232
|
+
buckets: buckets.map((values, index) => ({
|
|
233
|
+
bucket_start: new Date(startMs + index * stepMs).toISOString(),
|
|
234
|
+
value: reduce(values, aggregation),
|
|
235
|
+
point_count: values.length,
|
|
236
|
+
})),
|
|
237
|
+
};
|
|
238
|
+
});
|
|
239
|
+
const first = streams[0];
|
|
240
|
+
const result = {
|
|
241
|
+
name: args.metric_name,
|
|
242
|
+
kind: first?.kind ?? "gauge",
|
|
243
|
+
unit: first?.unit,
|
|
244
|
+
aggregation,
|
|
245
|
+
step_ms: stepMs,
|
|
246
|
+
start_time: new Date(startMs).toISOString(),
|
|
247
|
+
end_time: new Date(endMs).toISOString(),
|
|
248
|
+
series,
|
|
249
|
+
truncated,
|
|
250
|
+
};
|
|
251
|
+
return redactTelemetry({ result, mode });
|
|
252
|
+
}
|
|
253
|
+
const path = `${METRICS_PATH}/${encodeURIComponent(args.metric_name)}/query`;
|
|
254
|
+
try {
|
|
255
|
+
const body = await collectorGet(path, {
|
|
256
|
+
start_time: args.start_time,
|
|
257
|
+
end_time: args.end_time,
|
|
258
|
+
step_ms: stepMs,
|
|
259
|
+
aggregation,
|
|
260
|
+
group_by: args.group_by,
|
|
261
|
+
attr: args.attr,
|
|
262
|
+
attr_prefix: args.attr_prefix,
|
|
263
|
+
series_limit: seriesLimit,
|
|
264
|
+
});
|
|
265
|
+
return redactTelemetry({ result: parseCollectorMetricQueryResult(body, path), mode });
|
|
266
|
+
}
|
|
267
|
+
catch (error) {
|
|
268
|
+
if (error instanceof CollectorError && error.status === 404) {
|
|
269
|
+
throw new CollectorError(`metric not found: ${args.metric_name}`);
|
|
270
|
+
}
|
|
271
|
+
throw error;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
//# sourceMappingURL=metrics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics.js","sourceRoot":"","sources":["../src/metrics.ts"],"names":[],"mappings":"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"}
|