/** * Local `tenx` dev CLI runner. * * Spawns a local Log10x engine (a native tenx CLI or a local Docker * container) with a packaged runtime config, reads the resulting * templates + encoded rows + aggregated summary from a per-invocation * temp dir. Two modes: * * - stdin: batch piped to stdin (log10x_resolve_batch) * - file: reads from a path/glob (log10x_extract_templates) * * Backend selection via LOG10X_TENX_MODE (see resolveTenxMode): * - unset (default): auto-detect. Prefer the host-installed tenx binary; * fall back to docker only when no binary is on PATH. The host install is * a version the operator chose, whereas the default image tag is mutable, * so preferring docker made a report's engine build depend on whatever * `:latest` happened to be that day. * - "local": invoke the host-installed tenx binary. * Binary lookup: LOG10X_TENX_PATH env var wins; otherwise `tenx` on PATH. * - "docker": `docker run --rm -i log10x/pipeline-10x:latest` (or * LOG10X_RUNTIME_IMAGE / LOG10X_TENX_IMAGE — see resolveRuntimeImage; * `LOG10X_RUNTIME_IMAGE=native` selects the GraalVM-native * log10x/edge-10x, which runs @apps/mcp identically at 391 MB instead of * 926 MB). Works on hosts without a native tenx install, and for * hermetic/offline-capable invocation. * * Config lookup: LOG10X_MCP_STDIN_CONFIG_PATH / LOG10X_MCP_FILE_CONFIG_PATH * wins; otherwise the packaged configs shipped alongside the MCP. * * Concurrency safety: each invocation gets its own /tmp/log10x-mcp-/ * tempdir with a shadow template config (empty files list). Parallel calls * don't collide. */ export interface DevCliResult { templatesJson: string; encodedLog: string; decodedLog: string; aggregatedCsv: string; wallTimeMs: number; cliVersion?: string; configPath: string; tempDir: string; } export declare class DevCliNotInstalledError extends Error { constructor(); } export declare class DockerNotAvailableError extends Error { constructor(cause: string); } export declare class DevCliRunError extends Error { readonly exitCode: number; readonly stderr: string; readonly stdout: string; readonly configPath: string; /** Which backend produced this failure, when the caller recorded it. */ readonly tenxMode?: 'local' | 'docker'; constructor(exitCode: number, stderr: string, stdout: string, configPath: string, tenxMode?: 'local' | 'docker'); } /** * True when the engine refused a license it was handed, as opposed to * refusing to start because it was handed none. * * An engine handed a non-JWT `TENX_LICENSE_KEY` prints: * * could not launch pipeline: 'run' * Invalid serialized unsecured/JWS/JWE object: Missing part delimiters * details: * error initializating engine environment * license verification failed: MALFORMED — license token is not a parseable JWT * LicenseException: license token is not a parseable JWT * * `license verification failed:` carries the state word (MALFORMED here, * EXPIRED and the rest elsewhere), so it is the anchor, with * `LicenseException` as the second reading. * * The "handed none" case reads `license required: set --licenseFile, …` and * deliberately does NOT match: withholding a key that was never forwarded * cannot fix it. */ export declare function isEngineLicenseRejection(stderr: string): boolean; /** * Bare `-e VAR` pass-through for the engine license, mirroring the compile * path (compile-runner buildDockerArgs). Bare form so the value is inherited * from the spawning process env and never lands in argv. * * The runtime docker path used to forward nothing, so a caller with * TENX_LICENSE_KEY set watched the key get silently dropped. */ export declare function dockerLicenseArgs(env?: NodeJS.ProcessEnv): string[]; /** * Run a docker attempt with the license forwarded, and retry once with it * withheld if the engine rejects it. * * Forwarding `TENX_LICENSE_KEY` is an improvement for the caller who has a * good key and a regression for everyone else: the runtime images carry their * own built-in limited license, so before forwarding, a stale or malformed * `TENX_LICENSE_KEY` sitting in the environment was simply ignored and the run * succeeded. Measured on the same batch, engine 1.1.39: no key forwarded gives * `2 events -> 1 pattern`; a non-JWT key forwarded gives exit 1 and * `license verification failed: MALFORMED`. Docker mode is also not opt-in — a * host with no `tenx` on PATH auto-resolves to it — so the forwarding change * alone would take a working call away from a user who never asked for it. * * Withholding the key on a license rejection restores exactly the pre-forward * behaviour, and only in the case where the forwarded key is what broke the * run. A run that fails for any other reason is re-thrown untouched, and a run * with no key set never makes a second attempt. * * The downgrade is announced on stderr rather than swallowed: a caller who * meant to run under their own license should not silently end up on the * image's limited one. */ export declare function withDockerLicenseFallback(attempt: (licenseArgs: string[]) => Promise, env?: NodeJS.ProcessEnv): Promise; /** * Turn a non-zero local-engine exit into a hint the caller can act on. * * The hint must name what the engine actually refused on. `TENX_API_KEY` is * not it, and the engine's own diagnosis sits in `debug_stderr`; an * unlicensed `tenx` on PATH (the state every Homebrew install starts in) * otherwise produces a message that points nowhere. * * Two traps: * * - Appending "Set LOG10X_TENX_MODE=docker … (no license needed)" on every * failure tells a reader already in docker mode to turn on the mode they * are in, and tells them no license is needed in the same breath as their * license refusing the run. `mode` decides which escape is named. * - Promoting only `license required:` leaves the far commoner refusal, * `license verification failed:`, buried under * `could not launch pipeline: 'run'`, which is what `lines[0]` is. */ export declare function describeDevCliFailure(exitCode: number, stderr: string, opts?: { mode?: 'local' | 'docker'; licenseKeyForwarded?: boolean; }): string; /** * Thrown before spawning the CLI when a required configuration value is * absent (e.g. LOG10X_API_KEY unset and the bootstrap config path requires * it). Callers convert this to a `config_missing` chassis error envelope * rather than surfacing a raw CLI argument-validation error. */ export declare class DevCliConfigMissingError extends Error { readonly field: string; readonly hint: string; constructor(field: string, hint: string); } /** * The credentials a local engine run actually needs. * * These are two different things and the distinction is the whole point: * * - `licenseKey` is the REAL credential. The engine verifies it offline * against embedded ES256 keys (signature + expiry, nothing else — see * PipelineLauncher.resolveLicense), so once minted it works with no * egress, including airgapped. A not-signed-in user gets an anonymous * 14-day demo license from `POST /api/v1/license/demo`, cached in * `~/.log10x/demo-license.json` and reused until it expires. This is the * same license the website's generated install command carries, and the * same one `advise_install` bakes into its plan — one credential, every * surface. * * - `apiKey` authenticates NOTHING here. `apps/shared → run/bootstrap` * declares `apiKey` as a required commandLine argument, so an absent * `TENX_API_KEY` surfaces as a tilde-prefixed positional-arg error. It is * a validator placeholder. * * Requiring a real API key for both is what made the POC path unreachable for * exactly the users it was built for: measured 2026-08-10 in a no-egress * container, `poc_from_local` refused with "LOG10X_API_KEY is not configured" * on a tool whose own contract reads "no vendor credentials needed... events * never leave the machine". */ export interface EngineCredentials { licenseKey: string; apiKey: string; } export declare function resolveEngineCredentials(): Promise; /** * Run `tenx @apps/mcp-file` with batch piped to stdin and read the three * artifact files the engine writes to * `/tmp/log10x-mcp-pull//`: * * encoded.log — one anchored-encoded line per event * templates.json — one JSON-per-line: {"templateHash":"...","template":"..."} * aggregated.csv — one row per unique (severity, message_pattern, tenx_hash) * * Use this path when the input volume is too large for the stdout-based * runner (which buffers everything in process memory). The file runner * scales to multi-million-event pulls because the engine streams to disk * and the parser reads the files after the CLI exits. * * `runtimeName` is the unique key in the output path. Defaults to * `mcp--` so multiple parallel invocations don't clash. * Cleanup of the output directory is the caller's responsibility. */ export declare function runDevCliFileOutput(rawLogText: string, runtimeName?: string): Promise; /** * Run `tenx @apps/mcp` with batch piped to stdin and demultiplex the * resulting stdout into the four buffers the parser expects. * * The @apps/mcp engine app emits a single stdout stream with three * discriminable line types: * `~hash,vals...` — encoded TenXObject * `{"templateHash":"...","template":"..."}` — new TenXTemplate * `summary=,SEVERITY,pattern,vol,bytes,...` — aggregated TenXSummary * Any other line (engine info, JS console output) is skipped. * * Path resolution: the engine finds `apps/mcp` via the user's * `TENX_HOME` / `TENX_MODULES` / `TENX_CONFIG` env vars, or OS defaults. * See https://doc.log10x.com/install/paths/. Requires an engine release * that ships `apps/mcp`. * * No tempdir, no shadow template config, no file I/O — eliminates the * macOS `/var/folders` config-resolver bug, the system-cache dedup, and * the `LOG10X_MCP_OUTPUT_DIR` empty-path crash. */ export declare function runDevCliStdin(rawLogText: string): Promise; /** * Run the local tenx CLI reading from a file path/glob. * Used by log10x_extract_templates. */ export declare function runDevCliFile(inputPath: string): Promise; /** * Legacy alias for resolve-batch.ts backward compatibility. */ export declare function runDevCli(rawLogText: string): Promise<{ templatesJson: string; encodedLog: string; aggregatedCsv: string; wallTimeMs: number; cliVersion?: string; }>; /** * Pick the backend. * * - Explicit `LOG10X_TENX_MODE=local|docker` wins. * - Unset: prefer docker (no host install, easy updates via `docker pull`) * and fall back to the local binary if docker isn't reachable. * - Invalid value throws. * * The auto-detect probe runs `docker info` with a short timeout. If a user * wants to guarantee local mode (avoid the probe latency), they can set * `LOG10X_TENX_MODE=local` explicitly. */ export declare function resolveTenxMode(): Promise<'local' | 'docker'>; /** * Locate the user's tenx install (modules + config). Mirrors the engine's * own resolver (https://doc.log10x.com/install/paths/), skipping // own resolver (https://doc.log10x.com/install/paths/), skipping * step (not meaningful when spawned from the MCP). * * Precedence: * 1. TENX_MODULES + TENX_CONFIG (both required) * 2. TENX_HOME → $TENX_HOME/lib/app/modules (or /modules) + /config * 3. Per-OS defaults — Linux /opt/tenx-{cloud,edge}, Windows %ProgramFiles%/TenX * (or %LOCALAPPDATA%/TenX), macOS Homebrew (/opt/homebrew or /usr/local) */ export declare function resolveInstallPaths(): { config: string; modules: string; }; export declare function isBinaryOnPath(binary: string): Promise;