/** * Customer metric backend abstraction for the v1.4 cross-pillar bridge. * * The cross-pillar correlation tools need to read PromQL-compatible metrics * from wherever the customer's existing metrics live — Grafana Cloud / Mimir, * AWS Managed Prometheus, self-hosted Prometheus, VictoriaMetrics, Thanos, * Datadog's Prometheus-compatible read API, etc. * * This module exposes a pluggable backend interface so the higher-level * tools (discover_join, metrics_that_moved, rank_by_shape_similarity, * metric_overlay) don't carry per-backend conditionals. A backend * instance is constructed from env vars at tool-call time and cached * per-process. * * Explicit configuration (wins over any auto-detect): * LOG10X_CUSTOMER_METRICS_URL endpoint base URL * LOG10X_CUSTOMER_METRICS_TYPE backend type: * grafana_cloud | amp | datadog_prom | generic_prom | log10x * Default: generic_prom * LOG10X_CUSTOMER_METRICS_AUTH auth credential (format depends on type) * For type=log10x: / * LOG10X_CUSTOMER_METRICS_INSTANCE_ID optional Grafana Cloud instance ID * (numeric, used as HTTP basic auth username) * * Ambient auto-detect (tried in order when explicit URL is not set): * 1. Grafana Cloud GRAFANA_CLOUD_API_KEY (+ GRAFANA_CLOUD_URL / * GRAFANA_CLOUD_INSTANCE_ID) * 2. Datadog Prometheus DD_API_KEY + DD_APP_KEY (+ DD_SITE) * 3. AWS AMP AWS_REGION + `aws amp list-workspaces` → single workspace * 4. GCP Managed Prom GOOGLE_APPLICATION_CREDENTIALS + `gcloud config get project` * 5. Self-hosted PROMETHEUS_URL * * When none of these resolve, the resolver returns `undefined` and the * cross-pillar tools return a structured "not configured" response that * lists every detection path that was tried. */ import type { PrometheusResponse } from './api.js'; export type CustomerMetricsBackendType = 'grafana_cloud' | 'amp' | 'datadog_prom' | 'generic_prom' | 'log10x' | 'mock'; export interface CustomerMetricsBackend { /** Backend type identifier for output metadata. */ readonly backendType: CustomerMetricsBackendType; /** Human-readable base URL for output metadata (no credentials). */ readonly endpoint: string; /** Instant PromQL query. */ queryInstant(promql: string): Promise; /** Range PromQL query. */ queryRange(promql: string, start: number, end: number, step: number): Promise; /** List all label names present in the backend. */ listLabels(): Promise; /** * List distinct values for a specific label. When `window` is provided, * restrict the result to values observed inside `[now - window, now]` * via Prometheus's `start`/`end` query parameters. This filters out * stale label values from series that stopped receiving samples, which * is essential for join discovery — stale values drag the Jaccard down * and produce false-negative `no_join_available` results. */ listLabelValues(label: string, opts?: { windowSeconds?: number; }): Promise; /** * Return the Prometheus remote_write URL that corresponds to this * backend's read endpoint, if derivable. Returns `undefined` when the * backend has no natural write endpoint (e.g. Datadog uses its own * ingest API, not remote_write) or when the read URL cannot be mapped * to a write path automatically. * * Used by `log10x_backfill_metric` to avoid forcing users to configure * PROMETHEUS_REMOTE_WRITE_URL separately when the read endpoint is a * managed Prometheus with a well-known write path. */ remoteWriteUrl(): string | undefined; } export declare class CustomerMetricsNotConfiguredError extends Error { constructor(diagnostic?: string); } /** * Markdown form of the not-configured message for tools that participate * in autonomous chains. Throwing aborts the parent chain; returning * structured markdown lets the parent log "no cross-pillar data, continuing * without it" and complete the rest of the investigation. * * customer_metrics_query (the human escape-hatch tool) keeps the throw * behavior intentionally — a user-issued PromQL passthrough should fail * loudly when the backend isn't there. The cross-pillar primitives * (metrics_that_moved, rank_by_shape_similarity, metric_overlay) and * discover_join (chain participants) call this helper instead. */ export declare function customerMetricsNotConfiguredMessage(diagnostic?: string): string; export type DetectionPath = 'explicit_env' | 'grafana_cloud' | 'datadog_prom' | 'amp' | 'gcp_managed_prometheus' | 'prometheus_url'; export interface BackendResolution { backend?: CustomerMetricsBackend; /** Which detection path produced the backend, if any. */ detectionPath?: DetectionPath; /** All paths tried, with a one-line reason each — feed to `log10x_doctor`. */ trace: Array<{ path: DetectionPath; status: 'matched' | 'skipped' | 'failed'; reason: string; }>; } /** * Per-call overrides for the backend resolver. When supplied, they take * priority over LOG10X_CUSTOMER_METRICS_* env vars but do NOT mutate * process.env (other concurrent tool calls keep their env-derived view). * * Use case: an MCP launched with a stale or empty LOG10X_CUSTOMER_METRICS_URL * can be redirected at tool-call time without restarting the server. */ export interface BackendOverrides { url?: string; type?: string; auth?: string; instanceId?: string; } /** * Resolve a customer-metrics backend from the ambient shell environment. * * Detection order (first hit wins): * 1. explicit `LOG10X_CUSTOMER_METRICS_URL` (or per-call `overrides.url`) * 2. Grafana Cloud via `GRAFANA_CLOUD_API_KEY` / `GCLOUD_*` env or * `~/.grafana/grafana-cli-config.yaml` * 3. Datadog Prometheus-compatible read API via `DD_API_KEY + DD_APP_KEY` * 4. AWS AMP via `AWS_REGION` + `aws amp list-workspaces` * 5. GCP Managed Prometheus via `GOOGLE_APPLICATION_CREDENTIALS` + * `gcloud config get project` * 6. Self-hosted Prometheus via `PROMETHEUS_URL` */ export declare function resolveBackend(overrides?: BackendOverrides): Promise; /** * Back-compat facade. Resolves the ambient environment and returns the * backend (or undefined). Callers that need the detection trace should * call `resolveBackend()` directly. */ export declare function loadBackendFromEnv(): Promise; /** Render a detection trace as human-readable bullets for error messages. */ export declare function formatDetectionTrace(trace: BackendResolution['trace']): string; /** * Grafana Cloud Prometheus endpoint client. * * Grafana Cloud's hosted Prometheus (backed by Mimir) uses HTTP basic * auth with the numeric instance ID as the username and the API key as * the password. The base URL looks like: * * https://prometheus-prod-XX-prod-us-central-0.grafana.net/api/prom * * Instance ID is available from the Grafana Cloud portal under * "Prometheus" → "Details". It's numeric (e.g., "123456"). * * When instanceId is omitted, the backend falls back to Bearer auth * with the API key, which works for self-hosted Mimir / Thanos / other * Prometheus-compatible backends configured behind an authenticating * proxy. */ export declare class GrafanaCloudBackend implements CustomerMetricsBackend { readonly backendType = "grafana_cloud"; readonly endpoint: string; private authHeader; constructor(config: { endpoint: string; apiKey: string; instanceId?: string; }); queryInstant(promql: string): Promise; queryRange(promql: string, start: number, end: number, step: number): Promise; listLabels(): Promise; listLabelValues(label: string, opts?: { windowSeconds?: number; }): Promise; remoteWriteUrl(): string | undefined; private fetchJson; } /** * Catch-all backend for any Prometheus-compatible endpoint with optional * Bearer auth or no auth at all. Used for self-hosted Prometheus, * VictoriaMetrics, Thanos, Cortex, or any custom endpoint. * * Expects the standard Prometheus API path (`/api/v1/query`, etc.) at * the configured base URL. If the customer's endpoint is prefixed (e.g., * Grafana Cloud's `/api/prom`), include the prefix in the base URL. */ export declare class GenericPromBackend implements CustomerMetricsBackend { readonly backendType = "generic_prom"; readonly endpoint: string; private authHeader; constructor(config: { endpoint: string; bearerToken?: string; }); queryInstant(promql: string): Promise; queryRange(promql: string, start: number, end: number, step: number): Promise; listLabels(): Promise; listLabelValues(label: string, opts?: { windowSeconds?: number; }): Promise; remoteWriteUrl(): string | undefined; private fetchJson; } /** * Log10x cloud metrics backend (prometheus.log10x.com). * * Same Prometheus API surface as GenericPromBackend but authenticates with * `X-10X-Auth: /` instead of Bearer. */ export declare class Log10xBackend implements CustomerMetricsBackend { readonly backendType = "log10x"; readonly endpoint: string; private authHeader; /** * Per-backend Prom query timeout. Scoped to this shared backend (where the * latency symptom lives) so AMP/Grafana-Cloud/GCP timeouts stay at the * global 30s default. Override via `LOG10X_PROM_TIMEOUT_MS` — typical * cold-cache reads on prometheus.log10x.com can take 40-60s when the * inner increase() scan is large (deep histories, broad container regex). */ private timeoutMs; constructor(config: { endpoint: string; apiKey: string; envId: string; }); queryInstant(promql: string): Promise; queryRange(promql: string, start: number, end: number, step: number): Promise; listLabels(): Promise; listLabelValues(label: string, opts?: { windowSeconds?: number; }): Promise; remoteWriteUrl(): string | undefined; private fetchJson; } /** * Datadog's `/api/v1/query` and `/api/v1/query_range` endpoints accept a * PromQL expression and return a Prometheus-shaped response. Auth is * `DD-API-KEY` + `DD-APPLICATION-KEY` headers — the same keys used for * the rest of the Datadog API. * * The backend does NOT implement `remoteWriteUrl()` because Datadog's * ingest path is its own `/api/v2/series` endpoint (covered by the * `datadog` destination in `log10x_backfill_metric`) rather than * Prometheus remote_write. */ export declare class DatadogPromBackend implements CustomerMetricsBackend { readonly backendType = "datadog_prom"; readonly endpoint: string; private headers; constructor(config: { endpoint: string; apiKey: string; appKey: string; }); queryInstant(promql: string): Promise; queryRange(promql: string, start: number, end: number, step: number): Promise; listLabels(): Promise; listLabelValues(label: string, opts?: { windowSeconds?: number; }): Promise; remoteWriteUrl(): string | undefined; private fetchJson; } /** * AWS Managed Prometheus backend. The endpoint must include the workspace * prefix, e.g. `https://aps-workspaces.us-east-1.amazonaws.com/workspaces/ws-abc/`. * All requests are SigV4-signed against the `aps` service. * * Credentials are resolved from the ambient environment: * - AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY (+ optional AWS_SESSION_TOKEN) * * IMDS-based credential resolution is NOT implemented here. Users running * inside EKS/EC2 without exported credentials can either run * `aws configure export-credentials --format env-no-export` or point * LOG10X_CUSTOMER_METRICS_URL at a sigv4-proxy sidecar instead. */ export declare class AmpBackend implements CustomerMetricsBackend { readonly backendType = "amp"; readonly endpoint: string; private region; constructor(config: { endpoint: string; region: string; }); queryInstant(promql: string): Promise; queryRange(promql: string, start: number, end: number, step: number): Promise; listLabels(): Promise; listLabelValues(label: string, opts?: { windowSeconds?: number; }): Promise; remoteWriteUrl(): string | undefined; private signedFetch; } /** * GCP Managed Prometheus (Monarch) exposes a PromQL-compatible read API * under `https://monitoring.googleapis.com/v1/projects//location/global/prometheus/api/v1/…`. * * Auth uses Google OAuth2 access tokens. This backend shells out to * `gcloud auth print-access-token` on each request to avoid pulling in the * googleapis SDK. Tokens are cached for ~55 minutes between refreshes. * * Remote write is NOT a native concept for Managed Prometheus — customers * typically push via the GMP collector. `remoteWriteUrl()` returns * undefined; callers must set PROMETHEUS_REMOTE_WRITE_URL explicitly. */ export declare class GcpManagedPrometheusBackend implements CustomerMetricsBackend { readonly backendType = "generic_prom"; readonly endpoint: string; private tokenCache; constructor(config: { endpoint: string; project: string; }); queryInstant(promql: string): Promise; queryRange(promql: string, start: number, end: number, step: number): Promise; listLabels(): Promise; listLabelValues(label: string, opts?: { windowSeconds?: number; }): Promise; remoteWriteUrl(): string | undefined; private authedFetch; private accessToken; } interface AwsCreds { accessKeyId: string; secretAccessKey: string; sessionToken?: string; } export declare function awsCredentials(): AwsCreds | undefined; export declare function sigV4Sign(opts: { method: string; url: URL; region: string; service: string; accessKeyId: string; secretAccessKey: string; sessionToken?: string; body?: string; now?: Date; }): Record; /** * In-process backend that returns pre-seeded responses. Used by the * cross-pillar test suite to verify correlation, join discovery, and * structural validation without hitting a real Prometheus endpoint. */ export declare class MockBackend implements CustomerMetricsBackend { readonly backendType = "mock"; readonly endpoint = "mock://in-process"; labels: string[]; labelValues: Record; instantResponses: Record; rangeResponses: Record; /** Override the remote_write URL derivation. Undefined by default so tests * can assert the "no derivation available" path. */ remoteWriteOverride?: string; queryInstant(promql: string): Promise; queryRange(promql: string, start: number, end: number, step: number): Promise; listLabels(): Promise; listLabelValues(label: string, opts?: { windowSeconds?: number; }): Promise; remoteWriteUrl(): string | undefined; } export {};