/** * Environment / credential configuration. * * Resolution chain, tried in order. Each path returns either a fully * populated Environments object or undefined (path didn't apply). * * 1. **`LOG10X_METRICS_BACKEND_KIND` + `LOG10X_METRICS_*` env vars** * (new in phase 3). Single-env mode for laptop-pointing-at-one- * cluster setups. Any backend kind (prometheus, mimir, cortex, * amp, datadog, gcp_managed_prom, grafana_cloud_prom, log10x). * Nickname defaults to `default`. No file required. * * 2. **`~/.log10x/envs.json`** (new in phase 3). Multi-env file with * per-env backend declarations. Authoritative when present. The * `log10x_configure_env` tool writes this file from a * conversational onboarding flow; users can also hand-edit it. * * 3. **`LOG10X_API_KEY` env var** (legacy). The MCP calls * `GET /api/v1/user` and populates the env list from the response. * Each env gets `metricsBackend: { kind: 'log10x', apiKey, envId }` * auto-populated. Phase 7 makes this path require an explicit * `LOG10X_METRICS_BACKEND_KIND=log10x` to opt in. * * 4. **`~/.log10x/credentials`** (legacy). Persistent file written by * `log10x_signin_complete` after Auth0 device flow. Same shape as * path 3 — calls `/api/v1/user` with the cached key. * * 5. **Demo mode** (legacy, removed in phase 7). Public read-only * log10x demo env so a user can play without signing up. * * If both path 1 and path 2 produce envs, the MCP refuses to start * (loud error — user picks one). Paths 1+2 vs paths 3+4 do NOT * collide; the new paths just win. * * Multi-account access for the log10x backend uses backend-side env * sharing: an env owner grants READ/WRITE/OWNER to another user's * account, and the recipient's `/api/v1/user` response includes the * shared env. No client-side multi-credential juggling. * * Per-call env resolution (used by every tool that accepts an * `environment` arg) follows the chain: explicit-nickname → * last-used-this-session → user's default env. */ import { type Permission, type RemoteUserProfile } from './api.js'; import { type MetricsBackend } from './metrics-backend.js'; import { type LabelNameMap } from './promql.js'; export interface EnvConfig { nickname: string; /** * The metrics backend this env queries. Required field — every env * has a backend, even legacy log10x ones (auto-populated from * apiKey + envId). */ metricsBackend: MetricsBackend; /** * Per-env label name map. Defaults to `DEFAULT_LABELS`. Customers * who renamed the engine's `metricFieldNames` set this to match. */ labels: LabelNameMap; /** * Legacy fields, only meaningful for `kind: 'log10x'` envs. * Auto-populated when the env came from a log10x backend; left as * empty strings for other backends. Phase 4 swaps tools to use * `env.metricsBackend` directly, after which these fields become * informational only. */ apiKey: string; envId: string; /** Present when env was loaded via `/api/v1/user` autodiscovery. */ owner?: string; /** Present when env was loaded via `/api/v1/user` autodiscovery. */ permissions?: Permission; /** True if the backend marked this env as the user's default. */ isDefault?: boolean; /** * GitOps configuration for tools that open PRs against the user's * config repo (log10x_configure_engine and the log10x_pattern_mitigate * menu's PR-based options). * * The advisor's resolveTarget chain prefers, in order: * 1. Explicit `gitops_repo` arg on the tool call * 2. This `gitops` field (from envs.json or LOG10X_GH_REPO env var) * 3. The receiver pod's `GH_REPO` env var, discovered via * `log10x_discover_env` (requires kubectl access) * * For users without kubectl access from their laptop, sources (1) * and (2) keep the GitOps-based mitigation paths working without * any cluster reach. */ gitops?: { /** GitHub owner/name of the config repo, e.g. `acme/log10x-config`. */ repo: string; /** * Path inside the repo where the receiver's compact lookup file * lives (e.g. `compact/lookup.csv`). Optional — advisors fall back * to their own default path when absent. */ lookupPath?: string; }; /** * Which forwarder the customer runs. Same fallback rationale as * `gitops` — the kubectl-based snapshot is the authoritative source * when available (it classifies the running container image), but * users without cluster reach from their laptop need a stable * declarative path. Loadable from envs.json or `LOG10X_FORWARDER` * env var. Used by `log10x_pattern_mitigate` to fill in option 2's * vendor name without guessing. * * Supported values match `ForwarderKind` in * `src/lib/discovery/types.ts`: fluentbit, fluentd, filebeat, * logstash, otel-collector, unknown. */ forwarder?: 'fluentbit' | 'fluentd' | 'filebeat' | 'logstash' | 'otel-collector' | 'unknown'; /** * Which log analyzer / SIEM the customer ships logs to. Used by * `log10x_pattern_mitigate` to fill in option 1's vendor name and by * `log10x_exclusion_filter` as the default `vendor` arg. Same fallback * chain as `forwarder`: envs.json field → `LOG10X_ANALYZER` env var → * user profile's `metadata.analyzer_vendor` (populated for * log10x-backed envs from `/api/v1/user`) → undefined. * * The string is loose by design — the exclusion_filter tool only * knows how to generate native configs for the 4 most common * analyzers (datadog, splunk, elasticsearch, cloudwatch). When the * user's analyzer is outside that set the mitigate menu still names * it correctly but notes the agent has to hand-instruct rather than * generate a config. */ analyzer?: string; /** * Customer-supplied analyzer $/GB rate. Rung 2 of the shared * rate-resolution chain (see lib/rate-resolution.ts) — beats the * destination list price and tags every cost-emitting tool's dollar * surface as `rate_source='customer_supplied'`. Loadable from envs.json * `analyzerCost` field; the env-var rung (`LOG10X_ANALYZER_COST`) is * consulted directly by the resolver. */ analyzerCost?: number; } /** Parsed environment list + default + mutable last-used slot. */ export interface Environments { all: EnvConfig[]; byNickname: Map; default: EnvConfig; /** Set by `resolveEnv` each time a caller names an env explicitly. */ lastUsed?: EnvConfig; /** The user profile backing the env list — populated only for log10x-backed envs. */ profile?: RemoteUserProfile; /** * `true` if the MCP is running against the public demo key (either * because nothing was configured, or because the user's own * LOG10X_API_KEY failed validation and we fell back). Tools should * treat this as a "you're in a demo sandbox" signal and prefer to * surface upgrade guidance over silent failures on write attempts. * * Phase 7 removes the silent fallback path; this field stays true * only when the user explicitly opts into the demo env. */ isDemoMode: boolean; /** * When set, the MCP fell back to demo mode because the user's * configured API key did NOT work. The string is the underlying * failure (HTTP status, validation error, network reason). Surfaced * in doctor and prepended to every API-hitting tool result so the * user knows the data is demo, not their own. * * Distinguished from "pure demo" (no key set, demoFallbackReason * undefined) because the user's intent matters: a typo'd key silently * downgrading to demo is a footgun, so it is flagged loudly. */ demoFallbackReason?: string; } /** * Normalize a `LOG10X_ANALYZER` / `metadata.analyzer_vendor` / * envs.json `analyzer` value into the canonical token the * exclusion_filter tool understands. Accepts both casing-mangled * versions ("Splunk", "splunk") and common aliases ("AWS CloudWatch", * "elastic", "kibana", "azure", "GCP", "newrelic"). Unrecognized * values pass through verbatim — the mitigate menu still names them * for the user even if exclusion_filter can't generate a native * config for them. */ export declare function parseAnalyzerEnv(raw: string | undefined): string | undefined; /** * Resolve the active credentials and load the env list. Async because * the legacy log10x paths hit the log10x API for env autodiscovery. * * Returns demo-mode `Environments` (with `demoFallbackReason` set) on * any non-fatal failure of the user-supplied credential — never * throws on a typo'd key. Tools surface a loud banner when * `demoFallbackReason` is non-empty so the user is told their data is * demo, not their own. */ export declare function loadEnvironments(): Promise; /** * Probe a demo-license backend for pattern data (all_events_* series). Returns * false on any error/empty — the caller then falls back to the shared demo, so * a probe failure degrades to "show the demo" rather than "show nothing". The * query is bounded by the backend's own fetch timeout. */ export declare function demoTenantHasPatternData(backend: MetricsBackend): Promise; /** * Re-run `loadEnvironments()` from scratch and overwrite the contents * of an existing `Environments` object in place. Used by * `log10x_signin_complete` and `log10x_signout` to swap credentials * without forcing the user to restart the MCP host. * * In-place mutation matters: every tool callback closes over a * reference to the same Environments object via `getEnvs()` in * `index.ts`. If we returned a fresh object, we'd have to chase * references through the entire codebase to swap them all. */ export declare function reloadEnvironmentsInPlace(target: Environments): Promise; /** * Delete `LOG10X_API_KEY` from `process.env` if set. Returns whether * a deletion occurred so the caller can mention it in the result. */ export declare function clearOverridingEnvVar(): boolean; /** * Force a revalidation of credentials and refresh the in-memory `envs` * object so the next tool call sees ground truth instead of cached * boot-time state. * * Always calls `clearOverridingEnvVar` first. Without that, a stale * `LOG10X_API_KEY` in `process.env` (from the MCP host config) would * keep beating the freshly-written credentials file and reload would * just re-fail the same way. */ export declare function revalidateEnvironments(target: Environments): Promise<{ envVarCleared: boolean; }>; export declare class EnvironmentValidationError extends Error { /** * True when the failure was the NETWORK being unreachable rather than * anything wrong with the caller's credentials. Boot treats these two * cases differently: a bad key is a configuration error the user must * fix (exit), an unreachable gateway is an environment fact the MCP * must survive (offline mode). With log10x hosts blackholed, a demo-boot * probe that treats unreachable as fatal exits 1 before completing * `initialize`, so a no-egress prospect cannot run even the tools that * never touch the gateway (poc_from_local, advise_install, the * offload/CDK recipes). */ readonly offline: boolean; constructor(msg: string, offline?: boolean); } /** * Is this failure the network being unreachable, as opposed to a rejected * credential? Undici surfaces DNS/connect/TLS faults as a bare * `TypeError: fetch failed` with the real reason on `.cause`, so both the * message and the cause chain are inspected. */ export declare function isNetworkUnreachable(e: unknown): boolean; /** * The env set an offline boot runs with: none. Every gateway-backed tool * then takes the existing, designed "not configured" path instead of * crashing, and the local-only tools work untouched. */ export declare function offlineEnvironments(): Environments; /** * Resolve an environment using the priority chain: * 1. Explicit nickname (if passed) * 2. Last-used this session * 3. User's default env * * When an explicit nickname is resolved successfully, it's recorded as * the new last-used so subsequent unscoped calls stay on the same env. */ export declare function resolveEnv(envs: Environments, nickname?: string): EnvConfig; /** Programmatic override for tests or future "stick to this env" features. */ export declare function setLastUsed(envs: Environments, env: EnvConfig): void;