import { BotGuardConfig } from '../config'; import { ProtectAllArgs } from './mcpGateway'; import { windowsTaskRunsInteractive, windowsTaskRunsVisibly } from '../windowsTaskState'; export interface DaemonArgs extends ProtectAllArgs { install?: string; uninstall?: string; status?: string; /** Suppress OS toasts (still logs). */ quiet?: string; } /** Kill a process and wait (up to ~5s) for it to actually exit. */ /** * Uncaught-exception circuit breaker: one bad tick is survivable (log and * continue — no supervisor restarts a Windows process instantly), but a BURST * means process state is corrupted (broken closure, poisoned cache, leaked * handle storm). Limping on undermines enforcement silently; exiting lets the * supervisor (watchdog task / systemd / launchd) revive us with clean state. */ export declare const UNCAUGHT_BURST_MAX = 5; export declare const UNCAUGHT_BURST_WINDOW_MS: number; /** Pure: mutates `timestamps` (drops entries outside the window), returns whether the burst bound is hit. */ export declare function shouldExitForUncaughtBurst(timestamps: number[], nowMs: number): boolean; /** Arguments shared by first-boot, stale catch-up, and web-triggered scans. */ export declare function daemonDiscoverSweepArgs(): string[]; export interface DiscoverSweepOutcome { exitCode: number; /** One-line summary of the child's stderr — the actual failure reason. */ errorSummary?: string; } /** * Collapse a discover child's stderr tail into a single loggable line. * Prefers the last error-looking line (the CLI's fatal handler prints the real * cause last); pure so the regression suite can cover it without spawning. */ export declare function summarizeDiscoverStderr(stderr: string): string | undefined; /** * Hand the daemon's already-resolved credentials to the discover child via the * env fallbacks resolveCliCredentials() supports. The child otherwise has to * re-decrypt the DPAPI-protected shield key by spawning powershell.exe — which * hardened/EDR machines can block, silently killing every sweep with exit 1 * while the daemon (in-memory creds) keeps heartbeating. Pure — unit-tested. */ export declare function discoverSweepCredentialEnv(credentials?: { shieldId?: string; shieldKey?: string; apiUrl?: string; }): Record; export declare function cliVersion(): string | undefined; export type TaskLogonType = 'S4U' | 'InteractiveToken'; /** Fully-qualified current user (DOMAIN\\user) for the task principal. */ export declare function taskUserId(): string; /** Local wall-clock timestamp (no ms, no tz) as Task Scheduler expects. */ export declare function taskLocalTimestamp(date: Date): string; /** Full path to conhost.exe — the signed Windows binary whose `--headless` * flag hosts a console child on a hidden pseudoconsole (no window, ever). */ export declare function conhostPath(): string; /** * Task Scheduler XML that runs `node.exe ` hidden. * * @param command CLI subcommand + args ('daemon' | 'watchdog' | 'discover …'). * @param triggers Inner XML — logon for the daemon, a repeating * time trigger for the 5-minute watchdog. * @param logonType S4U (windowless by session, needs elevation to register) or * InteractiveToken (registers unelevated — the action is * wrapped in `conhost --headless` so it is ALSO windowless). * * Why the conhost wrapper: an InteractiveToken task runs console apps on the * interactive desktop — a visible window on EVERY trigger (the "console flash * every 5 minutes" complaint). Current Windows builds deny S4U registration to * everyone except the task's own user running elevated (even SYSTEM gets * Access denied — verified empirically; the updater's SYSTEM repair pass can * no longer fix it). `conhost.exe --headless` sidesteps the whole fight: the * child gets a hidden pseudoconsole, no window ever exists, registration * stays unelevated, and no script host is involved (Defender-safe). */ export declare function buildTaskXml(command: string, triggers: string, logonType?: TaskLogonType): string; /** * Windowless-first XML variants for one task: S4U (no window, needs elevation * to register) first, InteractiveToken (unelevated fallback) second. * registerHiddenTask() tries them in order. */ export declare function buildTaskXmlVariants(command: string, triggers: string): string[]; /** * Register a scheduled task from XML (node run directly). Tries each XML * variant in order — S4U (windowless) first, then InteractiveToken — and * finally falls back to a plain `/TR node …` task. The fallbacks may briefly * flash a console window but still contain NO script host, so the malware * fingerprint never returns. Returns true if any path succeeds. * * @param taskName Scheduled-task name. * @param xml Task Scheduler XML variant(s), tried in order. * @param command CLI subcommand for the /TR fallback ('daemon'|'watchdog'). * @param scheduleArgs schtasks schedule flags for the fallback (e.g. ['/SC','MINUTE','/MO','5']). */ export declare function registerHiddenTask(taskName: string, xml: string | string[], command: string, scheduleArgs: string[]): boolean; /** True when an existing scheduled task's action still runs wscript/cscript * (the pre-1.22.1 VBS launcher) — the migration trigger. */ export declare function windowsTaskReferencesScriptHost(taskName: string): boolean; export { windowsTaskRunsInteractive, windowsTaskRunsVisibly }; /** Snapshot of the resident daemon for out-of-process callers (watchdog/status). */ export interface DaemonRuntimeState { alive: boolean; pid?: number; version?: string; startedAt?: string; } export declare function daemonRuntimeState(): DaemonRuntimeState; /** Relaunch the daemon outside our process tree (plain node — no shells). */ export declare function spawnDetachedDaemon(): boolean; /** Pid-alive check shared with the watchdog (EPERM still means alive). */ export declare function pidIsAlive(pid: number): boolean; /** Force-stop a pid (graceful, then taskkill/SIGKILL) — shared with the watchdog's hung-daemon recycle. */ export declare function forceStopPid(pid: number): boolean; export declare function macosLaunchdPath(currentPath?: string, execPath?: string): string; export declare function xmlEscape(value: string): string; /** Whether the daemon has been registered to start automatically. */ /** Is the 5-minute watchdog scheduled task registered? (Windows-only feature.) */ export declare function isWatchdogTaskInstalled(): boolean; export declare function isDaemonAutostartInstalled(): boolean; export declare function daemonCommand(args: DaemonArgs, config: BotGuardConfig): Promise;