/** * MetricsBackend — interface + factory for the log10x engine's metrics store. * * The MCP queries this for every metric tool (top_patterns, whats_changing, * pattern_trend, event_lookup, etc.). Instead of a hardcoded path to * `prometheus.log10x.com` is replaced by a per-env discriminated union: * log10x hosted, customer's self-hosted Prometheus / Mimir / Cortex, * AMP, Datadog (Prom-compat), Grafana Cloud Prom, or GCP Managed Prom. * * The customer's 10x engine writes metrics through one of the engine's * output modules (`prometheus/remote-write`, `datadog`, `cloudwatch`, * etc.) to the same store the MCP queries here. * * Parallel to but distinct from `CustomerMetricsBackend` in * `customer-metrics.ts` (which targets the customer's cross-pillar APM * metrics, not 10x's own engine output). Both use Prometheus-shaped * reads; we keep the interfaces separate to reflect the different * schemas while sharing transport idioms. * * Phase 1 of the CUSTOMER-PROM-BACKEND design. No callers yet — this * file only defines the interface + adapters. Wiring tools to use it * happens in phase 4. */ import type { PrometheusResponse } from './api.js'; /** PromQL auth schemes for self-hosted backends (Prom / Mimir / Cortex). */ export type PromAuth = { type: 'none'; } | { type: 'bearer'; token: string; } | { type: 'basic'; user: string; password: string; } | { type: 'header'; name: string; value: string; }; /** * Discriminated union of all supported metrics backends. * * Each kind only carries the fields it needs; consumers narrow via * `switch (config.kind)`. Adding a new backend kind is a new variant * here plus an adapter class below. * * `log10x` is the hosted-log10x backend — `apiKey + envId` map to the * existing `X-10X-Auth: /` header against * `prometheus.log10x.com`. It's one option among many; the MCP never * picks it silently. */ export type MetricsBackendConfig = { kind: 'log10x'; apiKey: string; envId: string; } | { kind: 'log10x_demo'; licenseJwt: string; endpoint?: string; } | { kind: 'prometheus'; url: string; auth: PromAuth; } | { kind: 'mimir'; url: string; auth: PromAuth; orgId?: string; } | { kind: 'cortex'; url: string; auth: PromAuth; orgId: string; } | { kind: 'amp'; url: string; region: string; } | { kind: 'datadog'; site: string; apiKey: string; appKey: string; } | { kind: 'grafana_cloud_prom'; url: string; user: string; apiKey: string; } | { kind: 'gcp_managed_prom'; url?: string; projectId: string; serviceAccountKeyFile?: string; accessToken?: string; } | { kind: 'cloudwatch_metrics'; region: string; namespace: string; awsAccessKeyId?: string; awsSecretAccessKey?: string; } | { kind: 'elastic_metrics'; url: string; index?: string; user?: string; password?: string; apiKey?: string; } | { kind: 'opensearch_metrics'; url: string; index?: string; user?: string; password?: string; apiKey?: string; }; export type MetricsBackendKind = MetricsBackendConfig['kind']; /** * Runtime backend instance. Tools call these methods instead of hitting * `api.ts` directly. Each concrete class wraps the auth + transport for * one backend kind. * * Mirrors the shape of `CustomerMetricsBackend` in `customer-metrics.ts` * minus `remoteWriteUrl()` — the 10x engine writes through its own * configured output module, not via the MCP, so MCP-side write paths * aren't needed here. */ export interface MetricsBackend { /** Discriminator matching `MetricsBackendConfig['kind']`. */ readonly kind: MetricsBackendKind; /** Human-readable endpoint URL for logging / doctor output — never includes secrets. */ readonly endpoint: string; queryInstant(promql: string, timeoutMs?: number): Promise; queryRange(promql: string, startSec: number, endSec: number, stepSec: number, timeoutMs?: number): Promise; listLabels(): Promise; listLabelValues(label: string, opts?: { windowSeconds?: number; }): Promise; } /** * Resolve a `${VAR}` reference in a config field from `process.env`. The * file format permits any auth field to be either a literal OR a * `${ENV_VAR_NAME}` reference. References are resolved at load time — * the literal token lives in the user's shell or password manager, * never in the config file. * * Throws if the referenced variable is unset; the caller surfaces the * error with the field name so the user knows which env var to export. */ export declare function resolveVarReference(value: string): string; /** * Detect a value that looks like a plaintext secret in a field that * should hold a `${VAR}` reference. Heuristic — meant to catch * copy-paste-once-then-forget mistakes that leak secrets into committed * dotfiles or backups. * * Triggers when ALL of: * - length >= 32 (most API keys / tokens are at least this long) * - mix of letters AND digits (random tokens almost always have both) * - no `${...}` syntax (already a reference, not a literal) * - no whitespace, slashes, or path separators (URLs / file paths * excluded) * * False positives: a user explicitly putting a literal secret in the * file. They get an error pointing at the `${VAR}` pattern. Tradeoff * accepted — better than silently storing the secret. */ export declare function looksLikeLiteralSecret(value: string): boolean; /** * Error class for config-time validation failures (unset `${VAR}`, * detected literal secret, missing required field, etc.). Distinct * from runtime backend errors so callers can choose to surface * configuration problems differently from transient HTTP errors. */ export declare class MetricsBackendConfigError extends Error { constructor(message: string); } /** * Instantiate the right MetricsBackend implementation for a config. * Resolves `${VAR}` references on every string field, refuses to start * if any auth field looks like a literal secret. * * This is the only export tools should use. Direct construction of * the adapter classes bypasses the secret-detection guard. */ export declare function createMetricsBackend(config: MetricsBackendConfig): MetricsBackend;