@datasynx/agentic-ai-cartography 2.4.0 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/tools.ts","../src/sanitize.ts","../src/scanners/cloud-util.ts","../src/scanners/cloud-aws.ts","../src/scanners/cloud-gcp.ts","../src/scanners/cloud-azure.ts","../src/scanners/k8s.ts","../src/scanners/databases.ts","../src/db.ts","../src/compliance/types.ts","../src/compliance/engine.ts","../src/diff.ts","../src/anomaly.ts","../src/auth/identity.ts","../src/api/auth.ts","../src/auth/rbac.ts"],"sourcesContent":["import { z } from 'zod';\nimport type { CartographyDB } from './db.js';\nimport { NODE_TYPES, EDGE_RELATIONSHIPS } from './types.js';\nimport { sanitizeUntrusted } from './sanitize.js';\nimport { scanAllBookmarks, scanAllHistory } from './bookmarks.js';\nimport { logDebug } from './logger.js';\nimport {\n IS_WIN, IS_MAC, IS_LINUX, HOME, PLATFORM,\n run, commandExists, findFiles, dbScanDirs,\n scanListeningPorts, scanProcesses,\n scanWindowsPrograms, scanWindowsDbServices,\n} from './platform.js';\nimport type { Scanner, ScanContext } from './scanners/types.js';\nimport { cloudAwsScanner } from './scanners/cloud-aws.js';\nimport { cloudGcpScanner } from './scanners/cloud-gcp.js';\nimport { cloudAzureScanner } from './scanners/cloud-azure.js';\nimport { k8sScanner } from './scanners/k8s.js';\nimport { databasesScanner } from './scanners/databases.js';\n\n// Type-only import — no runtime dependency on SDK at module parse time\nimport type { McpServerConfig } from '@anthropic-ai/claude-agent-sdk';\n\n/**\n * Circuit breaker for sequential CLI scans.\n * After `threshold` consecutive failures, remaining commands are skipped.\n */\nexport function createScanRunner(\n runFn: (cmd: string, opts?: { timeout?: number; env?: NodeJS.ProcessEnv }) => string,\n opts: { timeout?: number; env?: NodeJS.ProcessEnv; threshold?: number } = {},\n) {\n const threshold = opts.threshold ?? 3;\n let consecutiveFailures = 0;\n let tripped = false;\n\n return (cmd: string): string => {\n if (tripped) {\n logDebug(`Circuit breaker: skipping \"${cmd}\" (${consecutiveFailures} consecutive failures)`);\n return '(skipped — circuit breaker: too many consecutive failures)';\n }\n const result = runFn(cmd, { timeout: opts.timeout ?? 20_000, env: opts.env });\n if (!result) {\n consecutiveFailures++;\n if (consecutiveFailures >= threshold) {\n tripped = true;\n logDebug(`Circuit breaker tripped after ${threshold} failures, last command: \"${cmd}\"`);\n }\n return '(error or not available)';\n }\n consecutiveFailures = 0;\n return result;\n };\n}\n\nexport interface CartographyToolsOptions {\n /** Called when the agent needs a human answer. Return the user's response. */\n onAskUser?: (question: string, context?: string) => Promise<string>;\n /** Max characters of a single tool response (sanitized + truncated). Default 100 000. */\n maxResponseBytes?: number;\n}\n\n/**\n * Sanitize untrusted scan output (strip invisible/control characters) and cap it\n * at `max` characters so a single verbose scan can never blow the agent's context\n * window. Appends an explicit truncation notice when clamped.\n */\nexport function clampText(raw: string, max: number): string {\n const clean = sanitizeUntrusted(raw);\n if (clean.length <= max) return clean;\n return clean.slice(0, max) +\n `\\n\\n… [output truncated: ${clean.length - max} more characters omitted — narrow the scan (e.g. a namespace/region/hint) or query the catalog instead]`;\n}\n\nexport function stripSensitive(target: string): string {\n const raw = target.trim();\n if (!raw) return raw;\n try {\n const url = new URL(raw.startsWith('http') ? raw : `tcp://${raw}`);\n const stripped = `${url.hostname}${url.port ? ':' + url.port : ''}`;\n return stripped || raw;\n } catch {\n // Linear string ops (no regex) so an untrusted host string can never trigger\n // polynomial backtracking (ReDoS). Equivalent to the previous\n // `/\\/.*$/`, `/\\?.*$/`, `/@.*:/` chain: drop path, drop query, drop userinfo.\n let s = raw;\n const slash = s.indexOf('/'); if (slash >= 0) s = s.slice(0, slash);\n const q = s.indexOf('?'); if (q >= 0) s = s.slice(0, q);\n const at = s.indexOf('@');\n if (at >= 0) {\n const colon = s.lastIndexOf(':');\n if (colon > at) s = s.slice(0, at) + ':' + s.slice(colon + 1);\n }\n return s || raw;\n }\n}\n\n// ── Argument hardening for shell-backed scan tools ───────────────────────────\n\n/**\n * Strict patterns for the cloud/k8s scan parameters that get spliced into a\n * shell command. `run()` re-checks the final command against the read-only\n * allowlist, but values are validated here first so an injection payload never\n * reaches the shell — not even a read-only one (e.g. `; cat ~/.ssh/id_rsa`)\n * that the allowlist would otherwise permit and which could disclose files.\n */\nexport const SCAN_ARG_PATTERNS = {\n 'k8s-namespace': /^[a-z0-9]([-a-z0-9]*[a-z0-9])?$/,\n 'aws-region': /^[A-Za-z0-9-]+$/,\n 'aws-profile': /^[A-Za-z0-9_.-]+$/,\n 'gcp-project': /^[a-z0-9][a-z0-9.-]*(:[a-z0-9][a-z0-9-]*)?$/,\n 'azure-subscription': /^[0-9a-fA-F-]+$/,\n 'azure-resource-group': /^[A-Za-z0-9_.()-]+$/,\n} as const;\n\nexport type ScanArgKind = keyof typeof SCAN_ARG_PATTERNS;\n\n/** Throw if `value` fails the strict pattern for `kind`; otherwise return it. */\nexport function assertSafeScanArg(kind: ScanArgKind, value: string): string {\n if (!SCAN_ARG_PATTERNS[kind].test(value)) {\n throw new Error(`Invalid ${kind} \"${value}\": contains characters that are not allowed`);\n }\n return value;\n}\n\n/**\n * Redact `user:password@` credentials embedded in any URL/DSN-like string. The\n * quantifiers are length-bounded (schemes <64, userinfo/password <256 chars — far\n * beyond any real DSN) so the pattern is linear and cannot polynomially backtrack\n * (ReDoS) on adversarial input like `aaaa…` with no `://`.\n */\nexport function redactSecrets(value: string): string {\n return value.replace(/([a-z][a-z0-9+.-]{0,63}:\\/\\/[^:@/\\s]{1,256}):[^@/\\s]{1,256}@/gi, '$1:***@');\n}\n\n/** Recursively redact secrets from arbitrary metadata before persistence. */\nexport function redactValue(value: unknown): unknown {\n if (typeof value === 'string') return redactSecrets(value);\n if (Array.isArray(value)) return value.map(redactValue);\n if (value && typeof value === 'object') {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) out[k] = redactValue(v);\n return out;\n }\n return value;\n}\n\n/**\n * Tool annotations — untrusted hints for MCP hosts (host-side gating), never a\n * security boundary. Mirror the server-side query tools in src/mcp/server.ts.\n */\nconst READ_SCAN = { readOnlyHint: true, openWorldHint: true } as const; // scanners reach external/system state\nconst READ_LOCAL = { readOnlyHint: true, openWorldHint: false } as const; // reads the local catalog / asks the user\nconst WRITE_CATALOG = { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false } as const;\n\n/** The MCP text-content shape every tool handler returns. */\nexport interface ToolResult {\n content: { type: 'text'; text: string }[];\n}\n\n/**\n * Provider-neutral tool definition: a zod input shape plus a handler that returns\n * the MCP text-content shape. This is the single source of truth for the discovery\n * tools — the Claude provider wraps these via the SDK `tool()` factory, while\n * OpenAI/Ollama convert `inputShape` to JSON Schema and call `handler` directly.\n */\nexport interface AgentTool {\n name: string;\n description: string;\n /** Raw zod shape (ZodRawShape) for the tool input. */\n inputShape: z.ZodRawShape;\n /** Host-side gating hints (never a security boundary). */\n annotations: Record<string, boolean>;\n handler: (args: Record<string, unknown>) => Promise<ToolResult>;\n}\n\n/**\n * Build the discovery tool handlers with **no** SDK dependency. Returns the same\n * handler bodies used by the Claude path, exposed neutrally so any provider can\n * reuse them. `buildCartographyToolDefinitions` wraps these for the Claude SDK.\n */\nexport async function buildCartographyToolHandlers(\n db: CartographyDB,\n sessionId: string,\n opts: CartographyToolsOptions = {},\n): Promise<AgentTool[]> {\n const maxResponseBytes = opts.maxResponseBytes ?? 100_000;\n /** Wrap sanitized + truncated scan output in the MCP text-content shape. */\n const textResult = (raw: string): ToolResult => ({ content: [{ type: 'text' as const, text: clampText(raw, maxResponseBytes) }] });\n\n /**\n * Run a registry {@link Scanner} as an agent tool: detect → scan → return a\n * structured node summary plus the raw report. The deterministic scanners are\n * the single source of truth for cloud/k8s/db discovery; this wrapper delegates\n * to them so the agent path stays in lock-step with `run_discovery`.\n */\n const runScannerTool = async (scanner: Scanner, hint: string): Promise<ToolResult> => {\n const ctx: ScanContext = { hint, platform: PLATFORM, run };\n if (!(await scanner.detect(ctx))) return textResult(`(${scanner.title}: CLI not available)`);\n const result = await scanner.scan(ctx);\n const structured = `=== NODES (${result.nodes.length}) / EDGES (${result.edges.length}) ===\\n` +\n JSON.stringify({ nodes: result.nodes, edges: result.edges });\n return textResult([structured, result.report ?? ''].filter(Boolean).join('\\n\\n'));\n };\n\n /** Local declarative helper mirroring the SDK `tool()` signature, but SDK-free. */\n const tool = (\n name: string,\n description: string,\n inputShape: z.ZodRawShape,\n handler: (args: Record<string, unknown>) => Promise<ToolResult>,\n extra: { annotations: Record<string, boolean> },\n ): AgentTool => ({ name, description, inputShape, annotations: extra.annotations, handler });\n\n const tools: AgentTool[] = [\n tool('save_node', 'Save an infrastructure node to the catalog', {\n id: z.string(),\n type: z.enum(NODE_TYPES),\n name: z.string(),\n discoveredVia: z.string(),\n confidence: z.number().min(0).max(1),\n metadata: z.record(z.string(), z.unknown()).optional(),\n tags: z.array(z.string()).optional(),\n domain: z.string().optional().describe('Business domain, e.g. \"Marketing\", \"Finance\"'),\n subDomain: z.string().optional().describe('Sub-domain, e.g. \"Forecast client orders\"'),\n qualityScore: z.number().min(0).max(100).optional().describe('Data quality score 0–100'),\n }, async (args) => {\n const node = {\n id: stripSensitive(args['id'] as string),\n type: args['type'] as typeof NODE_TYPES[number],\n name: args['name'] as string,\n discoveredVia: args['discoveredVia'] as string,\n confidence: args['confidence'] as number,\n metadata: redactValue((args['metadata'] as Record<string, unknown>) ?? {}) as Record<string, unknown>,\n tags: (args['tags'] as string[]) ?? [],\n domain: args['domain'] as string | undefined,\n subDomain: args['subDomain'] as string | undefined,\n qualityScore: args['qualityScore'] as number | undefined,\n };\n db.upsertNode(sessionId, node);\n return { content: [{ type: 'text', text: `✓ Node: ${node.id}` }] };\n }, { annotations: WRITE_CATALOG }),\n\n tool('save_edge', 'Save a relationship (edge) between two nodes — ALWAYS save edges when connections are clear', {\n sourceId: z.string(),\n targetId: z.string(),\n relationship: z.enum(EDGE_RELATIONSHIPS),\n evidence: z.string(),\n confidence: z.number().min(0).max(1),\n }, async (args) => {\n db.insertEdge(sessionId, {\n sourceId: args['sourceId'] as string,\n targetId: args['targetId'] as string,\n relationship: args['relationship'] as typeof EDGE_RELATIONSHIPS[number],\n evidence: redactSecrets(args['evidence'] as string),\n confidence: args['confidence'] as number,\n });\n return { content: [{ type: 'text', text: `✓ ${args['sourceId']}→${args['targetId']}` }] };\n }, { annotations: WRITE_CATALOG }),\n\n tool('get_catalog', 'Get the current catalog — use before save_node to avoid duplicates', {\n includeEdges: z.boolean().default(true),\n }, async (args) => {\n const nodes = db.getNodes(sessionId);\n const edges = (args['includeEdges'] as boolean) ? db.getEdges(sessionId) : [];\n return {\n content: [{\n type: 'text',\n text: JSON.stringify({\n count: { nodes: nodes.length, edges: edges.length },\n nodeIds: nodes.map(n => n.id),\n }),\n }],\n };\n }, { annotations: READ_LOCAL }),\n\n tool('ask_user', 'Ask the user a question — for clarifications, missing context, or consent (e.g. before scanning browser history)', {\n question: z.string().describe('The question for the user (clear and specific)'),\n context: z.string().optional().describe('Optional context explaining why this is relevant'),\n }, async (args) => {\n const question = args['question'] as string;\n const context = args['context'] as string | undefined;\n\n if (opts.onAskUser) {\n const answer = await opts.onAskUser(question, context);\n return { content: [{ type: 'text', text: answer }] };\n }\n\n // Fallback when not interactive (piped input, daemon, etc.)\n return {\n content: [{ type: 'text', text: '(Non-interactive mode — please continue without this information)' }],\n };\n }, { annotations: READ_LOCAL }),\n\n tool('scan_bookmarks', 'Scan all browser bookmarks — hostnames only, no personal data (Chrome, Chromium, Edge, Brave, Vivaldi, Opera, Firefox)', {\n minConfidence: z.number().min(0).max(1).default(0.5).optional(),\n }, async () => {\n const hosts = await scanAllBookmarks();\n return {\n content: [{\n type: 'text',\n text: JSON.stringify({\n count: hosts.length,\n hosts: hosts.map(h => ({\n hostname: h.hostname,\n port: h.port,\n protocol: h.protocol,\n source: h.source,\n })),\n note: 'Hostnames only — no paths, no personal data. Classify each as a business tool (save_node) or ignore (social media, news, shopping).',\n }),\n }],\n };\n }, { annotations: READ_SCAN }),\n\n tool('scan_browser_history', 'Scan browser history — anonymized hostnames + visit frequency. ALWAYS call ask_user for consent before using this tool.', {\n minVisits: z.number().min(1).default(3).optional().describe('Minimum visit count to include a host (filters rarely-visited sites)'),\n }, async (args) => {\n const minVisits = (args['minVisits'] as number | undefined) ?? 3;\n const hosts = await scanAllHistory();\n const filtered = hosts.filter(h => h.visitCount >= minVisits);\n return {\n content: [{\n type: 'text',\n text: JSON.stringify({\n count: filtered.length,\n note: 'Anonymized — hostnames only, no URLs, no paths, no personal data. Classify business tools as saas_tool nodes.',\n hosts: filtered.map(h => ({\n hostname: h.hostname,\n visitCount: h.visitCount,\n protocol: h.protocol,\n source: h.source,\n })),\n }),\n }],\n };\n }, { annotations: READ_SCAN }),\n\n tool('scan_local_databases', 'Scan for local database files and running DB servers — PostgreSQL databases, MySQL, SQLite files from installed apps', {\n deep: z.boolean().default(false).optional().describe('Also search home directory recursively for SQLite/DB files (slower)'),\n }, async (args) => {\n const deep = (args['deep'] as boolean | undefined) ?? false;\n return runScannerTool(databasesScanner, deep ? 'deep' : '');\n }, { annotations: READ_SCAN }),\n\n tool('scan_k8s_resources', 'Scan Kubernetes cluster via kubectl — 100% readonly (get, describe)', {\n namespace: z.string().regex(SCAN_ARG_PATTERNS['k8s-namespace'], 'invalid Kubernetes namespace').optional().describe('Filter by namespace — empty = all namespaces'),\n }, async (args) => {\n const ns = args['namespace'] as string | undefined;\n if (ns) assertSafeScanArg('k8s-namespace', ns);\n return runScannerTool(k8sScanner, ns ? `namespace=${ns}` : '');\n }, { annotations: READ_SCAN }),\n\n tool('scan_aws_resources', 'Scan AWS infrastructure via AWS CLI — 100% readonly (describe, list)', {\n region: z.string().regex(SCAN_ARG_PATTERNS['aws-region'], 'invalid AWS region').optional().describe('AWS Region — default: AWS_DEFAULT_REGION or profile'),\n profile: z.string().regex(SCAN_ARG_PATTERNS['aws-profile'], 'invalid AWS profile').optional().describe('AWS CLI profile'),\n }, async (args) => {\n const region = args['region'] as string | undefined;\n const profile = args['profile'] as string | undefined;\n if (region) assertSafeScanArg('aws-region', region);\n if (profile) assertSafeScanArg('aws-profile', profile);\n const hint = [region ? `region=${region}` : '', profile ? `profile=${profile}` : ''].filter(Boolean).join(' ');\n return runScannerTool(cloudAwsScanner, hint);\n }, { annotations: READ_SCAN }),\n\n tool('scan_gcp_resources', 'Scan Google Cloud Platform via gcloud CLI — 100% readonly (list, describe)', {\n project: z.string().regex(SCAN_ARG_PATTERNS['gcp-project'], 'invalid GCP project id').optional().describe('GCP Project ID — default: current gcloud project'),\n }, async (args) => {\n const project = args['project'] as string | undefined;\n if (project) assertSafeScanArg('gcp-project', project);\n return runScannerTool(cloudGcpScanner, project ? `project=${project}` : '');\n }, { annotations: READ_SCAN }),\n\n tool('scan_azure_resources', 'Scan Azure infrastructure via az CLI — 100% readonly (list, show)', {\n subscription: z.string().regex(SCAN_ARG_PATTERNS['azure-subscription'], 'invalid Azure subscription id').optional().describe('Azure Subscription ID'),\n resourceGroup: z.string().regex(SCAN_ARG_PATTERNS['azure-resource-group'], 'invalid Azure resource group').optional().describe('Filter by resource group'),\n }, async (args) => {\n const sub = args['subscription'] as string | undefined;\n const rg = args['resourceGroup'] as string | undefined;\n if (sub) assertSafeScanArg('azure-subscription', sub);\n if (rg) assertSafeScanArg('azure-resource-group', rg);\n const hint = [sub ? `subscription=${sub}` : '', rg ? `resource-group=${rg}` : ''].filter(Boolean).join(' ');\n return runScannerTool(cloudAzureScanner, hint);\n }, { annotations: READ_SCAN }),\n\n tool('scan_installed_apps', 'Scan all installed apps and tools — IDEs, office, dev tools, business apps, databases', {\n searchHint: z.string().optional().describe('Optional search term to find specific tools (e.g. \"hubspot windsurf cursor\")'),\n }, async (args) => {\n const hint = args['searchHint'] as string | undefined;\n const results: Record<string, string> = {};\n results['PLATFORM'] = `${PLATFORM} (${IS_WIN ? 'Windows' : IS_MAC ? 'macOS' : 'Linux'})`;\n\n if (IS_MAC) {\n // macOS: scan /Applications\n results['APPLICATIONS'] = run('ls /Applications/ 2>/dev/null | head -200') || '(empty)';\n results['USER_APPLICATIONS'] = run('ls ~/Applications/ 2>/dev/null | head -100') || '(empty)';\n // Homebrew\n results['BREW_CASKS'] = run('brew list --cask 2>/dev/null | head -100') || '(brew not installed)';\n results['BREW_FORMULAE'] = run('brew list --formula 2>/dev/null | head -150') || '(brew not installed)';\n // Spotlight — find .app bundles\n results['SPOTLIGHT_APPS'] = run('mdfind \"kMDItemKind == \\'Application\\'\" 2>/dev/null | grep -v \"^/System\" | grep -v \"^/Library/Apple\" | head -100') || '(Spotlight not available)';\n } else if (IS_LINUX) {\n // Linux: dpkg, snap, flatpak, .desktop files\n results['DPKG'] = run('dpkg --list 2>/dev/null | awk \\'{print $2}\\' | head -200') || '(dpkg not available)';\n results['SNAP'] = run('snap list 2>/dev/null | head -50') || '(snap not available)';\n results['FLATPAK'] = run('flatpak list 2>/dev/null | head -50') || '(flatpak not available)';\n results['DESKTOP_FILES'] = run('ls /usr/share/applications/*.desktop ~/.local/share/applications/*.desktop 2>/dev/null | xargs -I{} basename {} .desktop 2>/dev/null | head -100') || '(no .desktop files)';\n results['RPM'] = run('rpm -qa 2>/dev/null | head -200') || '(rpm not available)';\n } else if (IS_WIN) {\n // Windows: winget, registry, Get-Package\n results['WINGET'] = run('winget list --accept-source-agreements', { timeout: 20_000 }) || '(winget not available)';\n results['INSTALLED_PROGRAMS'] = scanWindowsPrograms() || '(registry scan failed)';\n // Chocolatey\n results['CHOCO'] = run('choco list --local-only', { timeout: 15_000 }) || '(chocolatey not installed)';\n // Scoop\n results['SCOOP'] = run('scoop list', { timeout: 15_000 }) || '(scoop not installed)';\n }\n\n // ── Check known dev/business tools via cross-platform commandExists ──\n const knownTools = [\n // IDEs & Editors\n 'code', 'code-insiders', 'cursor', 'windsurf', 'zed', 'vim', 'nvim', 'emacs', 'nano', 'sublime_text', 'atom',\n 'idea', 'webstorm', 'pycharm', 'goland', 'datagrip', 'clion', 'rider', 'phpstorm', 'rubymine', 'appcode',\n // Dev Tools\n 'git', 'gh', 'docker', 'docker-compose', 'podman', 'kubectl', 'helm', 'terraform', 'ansible',\n 'node', 'npm', 'npx', 'yarn', 'pnpm', 'bun', 'deno',\n 'python', 'python3', 'pip', 'pip3', 'pipenv', 'poetry', 'conda',\n 'ruby', 'gem', 'bundler', 'rails',\n 'java', 'mvn', 'gradle', 'kotlin',\n 'go', 'cargo', 'rustc',\n 'php', 'composer',\n 'dotnet',\n // Databases\n 'psql', 'mysql', 'mysqladmin', 'mongo', 'mongosh', 'redis-cli', 'sqlite3', 'clickhouse-client',\n // Cloud CLIs\n 'aws', 'gcloud', 'az', 'heroku', 'fly', 'vercel', 'netlify', 'wrangler',\n // Infra\n 'vagrant', 'packer', 'consul', 'vault', 'nomad',\n // Communication / SaaS\n 'slack', 'discord', 'zoom', 'teams', 'skype', 'telegram', 'signal',\n // Browsers\n 'google-chrome', 'chromium', 'firefox', 'safari', 'brave', 'opera', 'edge',\n // Windows-specific\n ...(IS_WIN ? ['pwsh', 'powershell', 'wsl', 'winget', 'choco', 'scoop', 'notepad++'] : []),\n // Monitoring / Analytics\n 'datadog-agent', 'newrelic-agent', 'prometheus', 'grafana-cli',\n // Other tools\n 'ngrok', 'stripe', 'supabase', 'neon',\n ];\n\n const found: string[] = [];\n const notFound: string[] = [];\n for (const t of knownTools) {\n const r = commandExists(t);\n if (r) found.push(`${t}: ${r}`);\n else notFound.push(t);\n }\n results['TOOLS_FOUND'] = found.join('\\n') || '(none found)';\n results['TOOLS_NOT_FOUND'] = notFound.join(', ');\n\n // Hint-based search: targeted lookup for user-specified tools\n if (hint) {\n const terms = hint.split(/[\\s,]+/).filter(Boolean);\n const hintResults: string[] = [];\n for (const term of terms) {\n const safe = term.replace(/[^a-zA-Z0-9._-]/g, '');\n if (!safe) continue;\n // First try commandExists\n const cmdPath = commandExists(safe);\n if (cmdPath) {\n hintResults.push(`${term}: ${cmdPath}`);\n continue;\n }\n // Platform-specific fallback search\n let fallback = '';\n if (IS_WIN) {\n fallback = run(\n `Get-ChildItem -Path 'C:\\\\Program Files','C:\\\\Program Files (x86)','${HOME}\\\\AppData\\\\Local\\\\Programs' ` +\n `-Recurse -Depth 3 -Filter '*${safe}*' -ErrorAction SilentlyContinue | ` +\n `Select-Object -First 5 -ExpandProperty FullName`,\n { timeout: 10_000 },\n );\n } else if (IS_MAC) {\n fallback = run(`mdfind -name \"${safe}\" 2>/dev/null | head -5`);\n } else {\n fallback = run(`find /usr/bin /usr/local/bin /opt/homebrew/bin ~/.local/bin /Applications ~/Applications 2>/dev/null -iname \"*${safe}*\" -maxdepth 3 2>/dev/null | head -5`);\n }\n hintResults.push(fallback ? `${term}: ${fallback}` : `${term}: (not found)`);\n }\n results['HINT_SEARCH'] = hintResults.join('\\n');\n }\n\n const out = Object.entries(results)\n .map(([k, v]) => `=== ${k} ===\\n${v}`)\n .join('\\n\\n');\n\n return textResult(out);\n }, { annotations: READ_SCAN }),\n ];\n\n return tools;\n}\n\n/**\n * Build the in-process discovery tool definitions for the Claude SDK (with\n * read/write annotations). Wraps the neutral handlers from\n * `buildCartographyToolHandlers` through the SDK `tool()` factory, preserving the\n * exact behavior the Claude path has always had. Exposed for host-side gating and\n * structural tests; consumed by `createCartographyTools`.\n */\nexport async function buildCartographyToolDefinitions(\n db: CartographyDB,\n sessionId: string,\n opts: CartographyToolsOptions = {},\n) {\n // Dynamically import the SDK so a missing package doesn't crash at load time.\n const { tool } = await import('@anthropic-ai/claude-agent-sdk');\n const handlers = await buildCartographyToolHandlers(db, sessionId, opts);\n // The neutral handler signature (Record<string,unknown> → ToolResult) is structurally\n // compatible with the SDK's per-tool callback; the cast bridges the generic gap.\n type SdkHandler = Parameters<typeof tool>[3];\n return handlers.map((t) =>\n tool(t.name, t.description, t.inputShape, t.handler as unknown as SdkHandler, { annotations: t.annotations }),\n );\n}\n\nexport async function createCartographyTools(\n db: CartographyDB,\n sessionId: string,\n opts: CartographyToolsOptions = {},\n): Promise<McpServerConfig> {\n const { createSdkMcpServer } = await import('@anthropic-ai/claude-agent-sdk');\n const tools = await buildCartographyToolDefinitions(db, sessionId, opts);\n return createSdkMcpServer({ name: 'cartography', version: '0.1.0', tools });\n}\n","/**\n * Sanitization of untrusted text before it enters the catalog or an LLM context\n * window. Discovery ingests text from sources outside our control — browser\n * bookmark titles, command output, scanner reports — which can carry hidden\n * prompt-injection payloads using invisible Unicode (zero-width spaces,\n * bidi/format controls, soft hyphens) or stray control characters.\n *\n * `sanitizeUntrusted` strips those while preserving ordinary whitespace\n * (tab/0x09, line feed/0x0A, carriage return/0x0D) and NFC-normalizes the\n * result. It is a no-op for normal ASCII/printable text.\n *\n * The set of stripped code points is defined numerically (below) rather than as\n * a regex of literal invisible characters, so the source stays pure ASCII and\n * auditable.\n */\n\n// Inclusive code-point ranges to remove.\nconst STRIP_RANGES: ReadonlyArray<readonly [number, number]> = [\n // C0 controls except 0x09 (tab), 0x0A (LF), 0x0D (CR)\n [0x00, 0x08], [0x0b, 0x0c], [0x0e, 0x1f],\n [0x7f, 0x7f], // DEL\n [0x80, 0x9f], // C1 controls\n [0x00ad, 0x00ad], // soft hyphen\n [0x200b, 0x200f], // ZWSP, ZWNJ, ZWJ, LRM, RLM\n [0x202a, 0x202e], // bidi embeddings & overrides\n [0x2060, 0x2064], // word joiner, invisible math operators\n [0x2066, 0x2069], // bidi isolates\n [0x206a, 0x206f], // deprecated format characters\n [0xfeff, 0xfeff], // BOM / ZWNBSP\n];\n\nconst STRIP = new Set<number>();\nfor (const [start, end] of STRIP_RANGES) {\n for (let cp = start; cp <= end; cp++) STRIP.add(cp);\n}\n\n/** Strip invisible/control characters and NFC-normalize untrusted text. */\nexport function sanitizeUntrusted(text: string): string {\n if (!text) return text;\n let out = '';\n for (const ch of text.normalize('NFC')) {\n if (!STRIP.has(ch.codePointAt(0) as number)) out += ch;\n }\n return out;\n}\n\n/** Recursively apply `sanitizeUntrusted` to every string in an arbitrary value. */\nexport function sanitizeValue(value: unknown): unknown {\n if (typeof value === 'string') return sanitizeUntrusted(value);\n if (Array.isArray(value)) return value.map(sanitizeValue);\n if (value && typeof value === 'object') {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) out[k] = sanitizeValue(v);\n return out;\n }\n return value;\n}\n","/**\n * Shared helpers for the cloud / k8s / database scanners.\n *\n * These keep the provider scanners (`cloud-aws`, `cloud-gcp`, `cloud-azure`,\n * `k8s`, `databases`) small and consistent: JSON parsing that never throws,\n * provider-parameter extraction from the single `ScanContext.hint` channel\n * (validated against the same strict patterns the agent tools use), and the\n * `=== KEY ===` report formatter so the structured + raw-report output shape\n * stays identical to the legacy agent-tool reports.\n */\n\nimport { assertSafeScanArg, type ScanArgKind } from '../tools.js';\n\n/**\n * Parse JSON CLI output. Returns `undefined` for empty output, our sentinel\n * placeholders (`(error or not available)`, `(skipped …)`), or malformed JSON —\n * it never throws, so a scanner degrades to \"no nodes\" instead of crashing the\n * discovery run.\n */\nexport function safeJson<T = unknown>(raw: string): T | undefined {\n const s = raw.trim();\n if (!s || s.startsWith('(')) return undefined;\n try {\n return JSON.parse(s) as T;\n } catch {\n return undefined;\n }\n}\n\n/** Provider parameters parsed out of a {@link ScanContext.hint}. */\nexport interface ScanHintParams {\n namespace?: string;\n region?: string;\n profile?: string;\n project?: string;\n subscription?: string;\n resourceGroup?: string;\n /** Leftover non-`key=value` terms (back-compat with the installed-apps hint use). */\n free: string;\n}\n\n/** Recognised `key=value` hint keys → the strict {@link ScanArgKind} that validates them. */\nconst HINT_ARG_KINDS: Record<string, ScanArgKind> = {\n namespace: 'k8s-namespace',\n region: 'aws-region',\n profile: 'aws-profile',\n project: 'gcp-project',\n subscription: 'azure-subscription',\n 'resource-group': 'azure-resource-group',\n};\n\n/**\n * Parse `key=value` provider parameters from a `ScanContext.hint`, validating\n * each value against its strict pattern via {@link assertSafeScanArg} (throws on\n * an injection payload — even a read-only one the allowlist would otherwise\n * permit). Unknown tokens flow through to {@link ScanHintParams.free} unchanged.\n */\nexport function parseScanHint(hint: string | undefined): ScanHintParams {\n const out: ScanHintParams = { free: '' };\n const free: string[] = [];\n for (const tok of (hint ?? '').split(/[\\s,]+/).filter(Boolean)) {\n const eq = tok.indexOf('=');\n if (eq <= 0) {\n free.push(tok);\n continue;\n }\n const key = tok.slice(0, eq);\n const value = tok.slice(eq + 1);\n const kind = HINT_ARG_KINDS[key];\n if (!kind) {\n free.push(tok);\n continue;\n }\n assertSafeScanArg(kind, value);\n if (key === 'resource-group') out.resourceGroup = value;\n else if (key === 'namespace') out.namespace = value;\n else if (key === 'region') out.region = value;\n else if (key === 'profile') out.profile = value;\n else if (key === 'project') out.project = value;\n else if (key === 'subscription') out.subscription = value;\n }\n out.free = free.join(' ');\n return out;\n}\n\n/** Render `=== KEY ===\\n…` report sections (the legacy agent-tool report shape). */\nexport function buildReport(sections: Array<[string, string]>): string {\n return sections.map(([k, v]) => `=== ${k} ===\\n${v}`).join('\\n\\n');\n}\n","/**\n * AWS infrastructure scanner.\n *\n * Read-only AWS CLI discovery (`describe`/`list` actions, JSON output) mapped to\n * deterministic nodes. Detection is a cheap `commandExists('aws')` check, so an\n * absent CLI skips the scanner without throwing. Every command stays within the\n * read-only allowlist's `aws` gate and uses `--output json` for stable parsing.\n */\n\nimport type { Scanner, ScanContext, ScanResult } from './types.js';\nimport type { DiscoveryNode, DiscoveryEdge } from '../types.js';\nimport { commandExists } from '../platform.js';\nimport { createScanRunner, redactValue } from '../tools.js';\nimport { safeJson, parseScanHint, buildReport } from './cloud-util.js';\n\ninterface Ec2Instance {\n InstanceId?: string;\n InstanceType?: string;\n State?: { Name?: string };\n PrivateIpAddress?: string;\n}\ninterface Ec2Result {\n Reservations?: Array<{ Instances?: Ec2Instance[] }>;\n}\ninterface RdsInstance {\n DBInstanceIdentifier?: string;\n Engine?: string;\n DBInstanceStatus?: string;\n Endpoint?: { Address?: string; Port?: number };\n}\ninterface RdsResult {\n DBInstances?: RdsInstance[];\n}\ninterface CacheCluster {\n CacheClusterId?: string;\n Engine?: string;\n CacheClusterStatus?: string;\n}\ninterface CacheResult {\n CacheClusters?: CacheCluster[];\n}\ninterface EksResult {\n clusters?: string[];\n}\ninterface LoadBalancer {\n LoadBalancerName?: string;\n DNSName?: string;\n Type?: string;\n State?: { Code?: string };\n}\ninterface ElbResult {\n LoadBalancers?: LoadBalancer[];\n}\n\nexport const cloudAwsScanner: Scanner = {\n id: 'cloud-aws',\n title: 'AWS infrastructure',\n platforms: 'all',\n allowedCommands: ['aws'],\n detect: (ctx) => Boolean((ctx.commandExists ?? commandExists)('aws')),\n async scan(ctx: ScanContext): Promise<ScanResult> {\n const { region, profile } = parseScanHint(ctx.hint);\n const env: NodeJS.ProcessEnv = region ? { ...process.env, AWS_DEFAULT_REGION: region } : process.env;\n const pf = profile ? ` --profile ${profile}` : '';\n const runA = createScanRunner((c) => ctx.run(c, { timeout: 20_000, env }), { threshold: 3 });\n\n const nodes: DiscoveryNode[] = [];\n const edges: DiscoveryEdge[] = [];\n const report: Array<[string, string]> = [];\n\n report.push(['IDENTITY', runA(`aws sts get-caller-identity${pf} --output json`)]);\n\n const ec2Raw = runA(`aws ec2 describe-instances${pf} --output json`);\n report.push(['EC2', ec2Raw]);\n const ec2 = safeJson<Ec2Result>(ec2Raw);\n for (const r of ec2?.Reservations ?? []) {\n for (const i of r.Instances ?? []) {\n const id = String(i.InstanceId ?? '');\n if (!id) continue;\n nodes.push({\n id: `host:aws:${id}`,\n type: 'host',\n name: id,\n discoveredVia: 'aws-ec2',\n confidence: 0.95,\n tags: ['cloud', 'aws', 'ec2'],\n metadata: redactValue({ instanceType: i.InstanceType, state: i.State?.Name, privateIp: i.PrivateIpAddress }) as Record<string, unknown>,\n });\n }\n }\n\n const rdsRaw = runA(`aws rds describe-db-instances${pf} --output json`);\n report.push(['RDS', rdsRaw]);\n const rds = safeJson<RdsResult>(rdsRaw);\n for (const db of rds?.DBInstances ?? []) {\n const id = String(db.DBInstanceIdentifier ?? '');\n if (!id) continue;\n nodes.push({\n id: `database_server:rds:${id}`,\n type: 'database_server',\n name: id,\n discoveredVia: 'aws-rds',\n confidence: 0.95,\n tags: ['cloud', 'aws', 'rds'],\n metadata: redactValue({ engine: db.Engine, status: db.DBInstanceStatus, endpoint: db.Endpoint?.Address, port: db.Endpoint?.Port }) as Record<string, unknown>,\n });\n }\n\n const cacheRaw = runA(`aws elasticache describe-cache-clusters${pf} --output json`);\n report.push(['ELASTICACHE', cacheRaw]);\n const cache = safeJson<CacheResult>(cacheRaw);\n for (const c of cache?.CacheClusters ?? []) {\n const id = String(c.CacheClusterId ?? '');\n if (!id) continue;\n nodes.push({\n id: `cache_server:aws:${id}`,\n type: 'cache_server',\n name: id,\n discoveredVia: 'aws-elasticache',\n confidence: 0.95,\n tags: ['cloud', 'aws', 'elasticache'],\n metadata: redactValue({ engine: c.Engine, status: c.CacheClusterStatus }) as Record<string, unknown>,\n });\n }\n\n const eksRaw = runA(`aws eks list-clusters${pf} --output json`);\n report.push(['EKS', eksRaw]);\n const eks = safeJson<EksResult>(eksRaw);\n for (const name of eks?.clusters ?? []) {\n if (!name) continue;\n nodes.push({\n id: `k8s_cluster:eks:${name}`,\n type: 'k8s_cluster',\n name,\n discoveredVia: 'aws-eks',\n confidence: 0.95,\n tags: ['cloud', 'aws', 'eks', 'kubernetes'],\n metadata: { provider: 'eks' },\n });\n }\n\n const elbRaw = runA(`aws elbv2 describe-load-balancers${pf} --output json`);\n report.push(['ELB_V2', elbRaw]);\n const elb = safeJson<ElbResult>(elbRaw);\n for (const lb of elb?.LoadBalancers ?? []) {\n const dns = String(lb.DNSName ?? '');\n if (!dns) continue;\n nodes.push({\n id: `web_service:aws:${dns}`,\n type: 'web_service',\n name: lb.LoadBalancerName ?? dns,\n discoveredVia: 'aws-elbv2',\n confidence: 0.9,\n tags: ['cloud', 'aws', 'elb'],\n metadata: redactValue({ dnsName: dns, type: lb.Type, state: lb.State?.Code }) as Record<string, unknown>,\n });\n }\n\n return { nodes, edges, report: buildReport(report) };\n },\n};\n","/**\n * Google Cloud Platform scanner.\n *\n * Read-only `gcloud … list` discovery with `--format=json` for stable parsing,\n * mapped to deterministic nodes. Detection is `commandExists('gcloud')`; an\n * absent CLI skips the scanner. Every command stays within the read-only\n * allowlist's `gcloud` gate.\n */\n\nimport type { Scanner, ScanContext, ScanResult } from './types.js';\nimport type { DiscoveryNode, DiscoveryEdge } from '../types.js';\nimport { commandExists } from '../platform.js';\nimport { createScanRunner, redactValue } from '../tools.js';\nimport { safeJson, parseScanHint, buildReport } from './cloud-util.js';\n\ninterface ComputeInstance {\n name?: string;\n machineType?: string;\n status?: string;\n zone?: string;\n}\ninterface SqlInstance {\n name?: string;\n databaseVersion?: string;\n state?: string;\n region?: string;\n}\ninterface GkeCluster {\n name?: string;\n status?: string;\n location?: string;\n}\ninterface RedisInstance {\n name?: string;\n tier?: string;\n state?: string;\n}\ninterface RunService {\n metadata?: { name?: string };\n status?: { url?: string };\n}\ninterface PubsubTopic {\n name?: string;\n}\n\n/** gcloud `machineType`/`zone`/`name` are often full resource URLs — keep the last path segment. */\nfunction lastSegment(value: string | undefined): string | undefined {\n if (!value) return undefined;\n const parts = value.split('/');\n return parts[parts.length - 1] || value;\n}\n\nexport const cloudGcpScanner: Scanner = {\n id: 'cloud-gcp',\n title: 'Google Cloud Platform infrastructure',\n platforms: 'all',\n allowedCommands: ['gcloud'],\n detect: (ctx) => Boolean((ctx.commandExists ?? commandExists)('gcloud')),\n async scan(ctx: ScanContext): Promise<ScanResult> {\n const { project } = parseScanHint(ctx.hint);\n const pf = project ? ` --project ${project}` : '';\n const runG = createScanRunner((c) => ctx.run(c, { timeout: 20_000 }), { threshold: 3 });\n\n const nodes: DiscoveryNode[] = [];\n const edges: DiscoveryEdge[] = [];\n const report: Array<[string, string]> = [];\n\n report.push(['IDENTITY', runG(`gcloud config list account --format=json`)]);\n\n const computeRaw = runG(`gcloud compute instances list${pf} --format=json`);\n report.push(['COMPUTE_INSTANCES', computeRaw]);\n for (const i of safeJson<ComputeInstance[]>(computeRaw) ?? []) {\n const name = String(i.name ?? '');\n if (!name) continue;\n nodes.push({\n id: `host:gcp:${name}`,\n type: 'host',\n name,\n discoveredVia: 'gcp-compute',\n confidence: 0.95,\n tags: ['cloud', 'gcp', 'compute'],\n metadata: { machineType: lastSegment(i.machineType), status: i.status, zone: lastSegment(i.zone) },\n });\n }\n\n const sqlRaw = runG(`gcloud sql instances list${pf} --format=json`);\n report.push(['SQL_INSTANCES', sqlRaw]);\n for (const s of safeJson<SqlInstance[]>(sqlRaw) ?? []) {\n const name = String(s.name ?? '');\n if (!name) continue;\n nodes.push({\n id: `database_server:gcp-sql:${name}`,\n type: 'database_server',\n name,\n discoveredVia: 'gcp-sql',\n confidence: 0.95,\n tags: ['cloud', 'gcp', 'cloudsql'],\n metadata: { engine: s.databaseVersion, state: s.state, region: s.region },\n });\n }\n\n const gkeRaw = runG(`gcloud container clusters list${pf} --format=json`);\n report.push(['GKE_CLUSTERS', gkeRaw]);\n for (const c of safeJson<GkeCluster[]>(gkeRaw) ?? []) {\n const name = String(c.name ?? '');\n if (!name) continue;\n nodes.push({\n id: `k8s_cluster:gke:${name}`,\n type: 'k8s_cluster',\n name,\n discoveredVia: 'gcp-gke',\n confidence: 0.95,\n tags: ['cloud', 'gcp', 'gke', 'kubernetes'],\n metadata: { status: c.status, location: c.location },\n });\n }\n\n const redisRaw = runG(`gcloud redis instances list --regions=-${pf} --format=json`);\n report.push(['REDIS', redisRaw]);\n for (const r of safeJson<RedisInstance[]>(redisRaw) ?? []) {\n const name = lastSegment(String(r.name ?? ''));\n if (!name) continue;\n nodes.push({\n id: `cache_server:gcp:${name}`,\n type: 'cache_server',\n name,\n discoveredVia: 'gcp-redis',\n confidence: 0.95,\n tags: ['cloud', 'gcp', 'memorystore'],\n metadata: { tier: r.tier, state: r.state },\n });\n }\n\n const runRaw = runG(`gcloud run services list --platform managed${pf} --format=json`);\n report.push(['CLOUD_RUN', runRaw]);\n for (const svc of safeJson<RunService[]>(runRaw) ?? []) {\n const name = String(svc.metadata?.name ?? '');\n if (!name) continue;\n nodes.push({\n id: `web_service:gcp-run:${name}`,\n type: 'web_service',\n name,\n discoveredVia: 'gcp-run',\n confidence: 0.9,\n tags: ['cloud', 'gcp', 'cloudrun'],\n metadata: redactValue({ url: svc.status?.url }) as Record<string, unknown>,\n });\n }\n\n const pubsubRaw = runG(`gcloud pubsub topics list${pf} --format=json`);\n report.push(['PUBSUB', pubsubRaw]);\n for (const t of safeJson<PubsubTopic[]>(pubsubRaw) ?? []) {\n const name = lastSegment(String(t.name ?? ''));\n if (!name) continue;\n nodes.push({\n id: `topic:gcp:${name}`,\n type: 'topic',\n name,\n discoveredVia: 'gcp-pubsub',\n confidence: 0.9,\n tags: ['cloud', 'gcp', 'pubsub'],\n metadata: { fullName: t.name },\n });\n }\n\n return { nodes, edges, report: buildReport(report) };\n },\n};\n","/**\n * Azure infrastructure scanner.\n *\n * Read-only `az … list/show` discovery with `--output json` for stable parsing,\n * mapped to deterministic nodes. Detection is `commandExists('az')`; an absent\n * CLI skips the scanner. Subscription / resource-group scoping comes from the\n * hint. Every command stays within the read-only allowlist's `az` gate.\n */\n\nimport type { Scanner, ScanContext, ScanResult } from './types.js';\nimport type { DiscoveryNode, DiscoveryEdge } from '../types.js';\nimport { commandExists } from '../platform.js';\nimport { createScanRunner, redactValue } from '../tools.js';\nimport { safeJson, parseScanHint, buildReport } from './cloud-util.js';\n\ninterface AzVm {\n name?: string;\n location?: string;\n hardwareProfile?: { vmSize?: string };\n powerState?: string;\n}\ninterface AzAks {\n name?: string;\n location?: string;\n kubernetesVersion?: string;\n provisioningState?: string;\n}\ninterface AzSqlServer {\n name?: string;\n location?: string;\n version?: string;\n}\ninterface AzPostgres {\n name?: string;\n location?: string;\n version?: string;\n}\ninterface AzRedis {\n name?: string;\n location?: string;\n provisioningState?: string;\n}\ninterface AzWebApp {\n name?: string;\n defaultHostName?: string;\n state?: string;\n}\n\nexport const cloudAzureScanner: Scanner = {\n id: 'cloud-azure',\n title: 'Azure infrastructure',\n platforms: 'all',\n allowedCommands: ['az'],\n detect: (ctx) => Boolean((ctx.commandExists ?? commandExists)('az')),\n async scan(ctx: ScanContext): Promise<ScanResult> {\n const { subscription, resourceGroup } = parseScanHint(ctx.hint);\n const sf = subscription ? ` --subscription ${subscription}` : '';\n const rf = resourceGroup ? ` --resource-group ${resourceGroup}` : '';\n const scope = `${sf}${rf}`;\n const runZ = createScanRunner((c) => ctx.run(c, { timeout: 20_000 }), { threshold: 3 });\n\n const nodes: DiscoveryNode[] = [];\n const edges: DiscoveryEdge[] = [];\n const report: Array<[string, string]> = [];\n\n report.push(['IDENTITY', runZ(`az account show --output json${sf}`)]);\n\n const vmRaw = runZ(`az vm list${scope} --output json`);\n report.push(['VMS', vmRaw]);\n for (const vm of safeJson<AzVm[]>(vmRaw) ?? []) {\n const name = String(vm.name ?? '');\n if (!name) continue;\n nodes.push({\n id: `host:azure:${name}`,\n type: 'host',\n name,\n discoveredVia: 'azure-vm',\n confidence: 0.95,\n tags: ['cloud', 'azure', 'vm'],\n metadata: { vmSize: vm.hardwareProfile?.vmSize, location: vm.location, powerState: vm.powerState },\n });\n }\n\n const aksRaw = runZ(`az aks list${scope} --output json`);\n report.push(['AKS', aksRaw]);\n for (const aks of safeJson<AzAks[]>(aksRaw) ?? []) {\n const name = String(aks.name ?? '');\n if (!name) continue;\n nodes.push({\n id: `k8s_cluster:aks:${name}`,\n type: 'k8s_cluster',\n name,\n discoveredVia: 'azure-aks',\n confidence: 0.95,\n tags: ['cloud', 'azure', 'aks', 'kubernetes'],\n metadata: { location: aks.location, version: aks.kubernetesVersion, state: aks.provisioningState },\n });\n }\n\n const sqlRaw = runZ(`az sql server list${scope} --output json`);\n report.push(['SQL_SERVERS', sqlRaw]);\n for (const s of safeJson<AzSqlServer[]>(sqlRaw) ?? []) {\n const name = String(s.name ?? '');\n if (!name) continue;\n nodes.push({\n id: `database_server:azure-sql:${name}`,\n type: 'database_server',\n name,\n discoveredVia: 'azure-sql',\n confidence: 0.95,\n tags: ['cloud', 'azure', 'sql'],\n metadata: { engine: 'sqlserver', location: s.location, version: s.version },\n });\n }\n\n const pgRaw = runZ(`az postgres server list${scope} --output json`);\n report.push(['POSTGRES', pgRaw]);\n for (const p of safeJson<AzPostgres[]>(pgRaw) ?? []) {\n const name = String(p.name ?? '');\n if (!name) continue;\n nodes.push({\n id: `database_server:azure-postgres:${name}`,\n type: 'database_server',\n name,\n discoveredVia: 'azure-postgres',\n confidence: 0.95,\n tags: ['cloud', 'azure', 'postgres'],\n metadata: { engine: 'postgresql', location: p.location, version: p.version },\n });\n }\n\n const redisRaw = runZ(`az redis list${scope} --output json`);\n report.push(['REDIS', redisRaw]);\n for (const r of safeJson<AzRedis[]>(redisRaw) ?? []) {\n const name = String(r.name ?? '');\n if (!name) continue;\n nodes.push({\n id: `cache_server:azure:${name}`,\n type: 'cache_server',\n name,\n discoveredVia: 'azure-redis',\n confidence: 0.95,\n tags: ['cloud', 'azure', 'redis'],\n metadata: { location: r.location, state: r.provisioningState },\n });\n }\n\n const webRaw = runZ(`az webapp list${scope} --output json`);\n report.push(['WEBAPPS', webRaw]);\n for (const w of safeJson<AzWebApp[]>(webRaw) ?? []) {\n const name = String(w.name ?? '');\n if (!name) continue;\n nodes.push({\n id: `web_service:azure:${name}`,\n type: 'web_service',\n name,\n discoveredVia: 'azure-webapp',\n confidence: 0.9,\n tags: ['cloud', 'azure', 'webapp'],\n metadata: redactValue({ hostName: w.defaultHostName, state: w.state }) as Record<string, unknown>,\n });\n }\n\n return { nodes, edges, report: buildReport(report) };\n },\n};\n","/**\n * Kubernetes scanner.\n *\n * Read-only `kubectl get … -o json` discovery mapped to deterministic nodes and\n * edges. Detection is `commandExists('kubectl')`; an absent CLI skips the\n * scanner. The cluster node anchors `contains` edges to its hosts and pods, and\n * a `connects_to` edge is emitted from a service to a running pod when the pod's\n * labels match the service's selector — only when both endpoints are in the\n * result set (the driver prunes dangling edges anyway). Pod enumeration is\n * bounded to keep `run_discovery` latency predictable on large clusters.\n */\n\nimport type { Scanner, ScanContext, ScanResult } from './types.js';\nimport type { DiscoveryNode, DiscoveryEdge } from '../types.js';\nimport { commandExists } from '../platform.js';\nimport { createScanRunner } from '../tools.js';\nimport { safeJson, parseScanHint, buildReport } from './cloud-util.js';\n\nconst MAX_PODS = 200;\n\ninterface KubeListItem {\n metadata?: { name?: string; namespace?: string; labels?: Record<string, string> };\n}\ninterface KubeList<T extends KubeListItem = KubeListItem> {\n items?: T[];\n}\ninterface ServiceItem extends KubeListItem {\n spec?: { selector?: Record<string, string>; type?: string; clusterIP?: string };\n}\ninterface PodItem extends KubeListItem {\n spec?: { nodeName?: string };\n status?: { phase?: string };\n}\n\n/** True when every key/value in `selector` is present in `labels`. */\nfunction selectorMatches(selector: Record<string, string>, labels: Record<string, string>): boolean {\n const keys = Object.keys(selector);\n if (keys.length === 0) return false;\n return keys.every((k) => labels[k] === selector[k]);\n}\n\nexport const k8sScanner: Scanner = {\n id: 'k8s',\n title: 'Kubernetes resources',\n platforms: 'all',\n allowedCommands: ['kubectl'],\n detect: (ctx) => Boolean((ctx.commandExists ?? commandExists)('kubectl')),\n async scan(ctx: ScanContext): Promise<ScanResult> {\n const { namespace } = parseScanHint(ctx.hint);\n const nsFlag = namespace ? `-n ${namespace}` : '--all-namespaces';\n const runK = createScanRunner((c) => ctx.run(c, { timeout: 15_000 }), { threshold: 3 });\n\n const nodes: DiscoveryNode[] = [];\n const edges: DiscoveryEdge[] = [];\n const report: Array<[string, string]> = [];\n const nodeIds = new Set<string>();\n const add = (n: DiscoveryNode): void => {\n if (nodeIds.has(n.id)) return;\n nodeIds.add(n.id);\n nodes.push(n);\n };\n\n const contextRaw = runK('kubectl config current-context').trim();\n report.push(['CONTEXT', contextRaw]);\n // The circuit breaker returns a `(…)` sentinel on empty output — never a cluster name.\n const context = contextRaw.startsWith('(') ? '' : contextRaw;\n const clusterName = context || 'current';\n const clusterId = `k8s_cluster:${clusterName}`;\n add({\n id: clusterId,\n type: 'k8s_cluster',\n name: clusterName,\n discoveredVia: 'kubectl-context',\n confidence: 0.95,\n tags: ['kubernetes', 'cluster'],\n metadata: { context: clusterName },\n });\n\n const nodesRaw = runK('kubectl get nodes -o json');\n report.push(['NODES', nodesRaw]);\n for (const item of safeJson<KubeList>(nodesRaw)?.items ?? []) {\n const name = String(item.metadata?.name ?? '');\n if (!name) continue;\n const id = `host:k8s:${name}`;\n add({\n id,\n type: 'host',\n name,\n discoveredVia: 'kubectl-node',\n confidence: 0.9,\n tags: ['kubernetes', 'node'],\n metadata: { cluster: clusterName },\n });\n edges.push({ sourceId: clusterId, targetId: id, relationship: 'contains', evidence: 'kubectl get nodes', confidence: 0.9 });\n }\n\n const svcRaw = runK(`kubectl get services ${nsFlag} -o json`);\n report.push(['SERVICES', svcRaw]);\n const services = safeJson<KubeList<ServiceItem>>(svcRaw)?.items ?? [];\n for (const svc of services) {\n const name = String(svc.metadata?.name ?? '');\n const ns = String(svc.metadata?.namespace ?? 'default');\n if (!name) continue;\n add({\n id: `web_service:k8s:${ns}/${name}`,\n type: 'web_service',\n name: `${ns}/${name}`,\n discoveredVia: 'kubectl-service',\n confidence: 0.9,\n tags: ['kubernetes', 'service'],\n metadata: { namespace: ns, type: svc.spec?.type, clusterIP: svc.spec?.clusterIP },\n });\n }\n\n const podsRaw = runK(`kubectl get pods ${nsFlag} --field-selector=status.phase=Running -o json`);\n report.push(['PODS_RUNNING', podsRaw]);\n const allPods = safeJson<KubeList<PodItem>>(podsRaw)?.items ?? [];\n const pods = allPods.slice(0, MAX_PODS);\n if (allPods.length > MAX_PODS) {\n report.push(['PODS_OVERFLOW', `${allPods.length} running pods found; first ${MAX_PODS} catalogued`]);\n }\n for (const pod of pods) {\n const name = String(pod.metadata?.name ?? '');\n const ns = String(pod.metadata?.namespace ?? 'default');\n if (!name) continue;\n const podId = `pod:${ns}/${name}`;\n add({\n id: podId,\n type: 'pod',\n name: `${ns}/${name}`,\n discoveredVia: 'kubectl-pod',\n confidence: 0.9,\n tags: ['kubernetes', 'pod'],\n metadata: { namespace: ns, node: pod.spec?.nodeName },\n });\n edges.push({ sourceId: clusterId, targetId: podId, relationship: 'contains', evidence: 'kubectl get pods', confidence: 0.9 });\n\n // service → pod when the pod's labels match a same-namespace service selector.\n const labels = pod.metadata?.labels ?? {};\n for (const svc of services) {\n const svcNs = String(svc.metadata?.namespace ?? 'default');\n if (svcNs !== ns) continue;\n const selector = svc.spec?.selector;\n if (!selector || !selectorMatches(selector, labels)) continue;\n const svcId = `web_service:k8s:${svcNs}/${String(svc.metadata?.name ?? '')}`;\n edges.push({ sourceId: svcId, targetId: podId, relationship: 'connects_to', evidence: 'label selector match', confidence: 0.85 });\n }\n }\n\n return { nodes, edges, report: buildReport(report) };\n },\n};\n","/**\n * Local database scanner.\n *\n * Probes locally-installed DB clients (read-only) and discovers SQLite files in\n * app-data directories, mapping reachable servers to deterministic nodes. Each\n * client is only probed when present (`commandExists`), so an absent CLI never\n * throws and never produces a node. The expensive home-directory / config-file\n * find is opt-in via the `deep` hint token, keeping the default deterministic\n * `run_discovery` path fast. Connection strings are credential-redacted before\n * persistence.\n */\n\nimport type { Scanner, ScanContext, ScanResult } from './types.js';\nimport type { DiscoveryNode, DiscoveryEdge } from '../types.js';\nimport {\n IS_WIN, HOME, commandExists, findFiles, dbScanDirs, scanWindowsDbServices,\n} from '../platform.js';\nimport { redactValue } from '../tools.js';\nimport { parseScanHint, buildReport } from './cloud-util.js';\n\n/** Extract a basename from an absolute path (cross-platform). */\nfunction baseName(path: string): string {\n const parts = path.split(/[\\\\/]/);\n return parts[parts.length - 1] || path;\n}\n\nexport const databasesScanner: Scanner = {\n id: 'databases',\n title: 'Local database servers',\n platforms: 'all',\n allowedCommands: ['psql', 'mysql', 'mongosh', 'redis-cli', 'sqlite3', 'pg_lsclusters', 'find', 'head', 'grep', 'awk', 'Get-Service', 'Get-ChildItem', 'Select-Object', 'Where-Object'],\n detect: (ctx) => {\n const exists = ctx.commandExists ?? commandExists;\n return ['psql', 'mysql', 'mongosh', 'redis-cli'].some((c) => Boolean(exists(c)));\n },\n async scan(ctx: ScanContext): Promise<ScanResult> {\n const exists = ctx.commandExists ?? commandExists;\n const run = (cmd: string, opts?: { timeout?: number }) => ctx.run(cmd, { timeout: opts?.timeout ?? 10_000 });\n const deep = parseScanHint(ctx.hint).free.split(/\\s+/).includes('deep');\n\n const nodes: DiscoveryNode[] = [];\n const edges: DiscoveryEdge[] = [];\n const report: Array<[string, string]> = [];\n const seen = new Set<string>();\n const add = (n: DiscoveryNode): void => {\n if (seen.has(n.id)) return;\n seen.add(n.id);\n nodes.push(n);\n };\n\n // ── PostgreSQL ──\n if (exists('psql')) {\n const out = IS_WIN\n ? run('psql -lqt')\n : run('psql -lqt 2>/dev/null | grep -v \"template0\\\\|template1\" | awk \\'{print $1}\\' | grep -v \"^$\\\\|^|\"');\n report.push(['POSTGRES_DATABASES', out || '(psql not running or requires auth)']);\n if (out) {\n const databases = out.split('\\n').map((l) => l.trim()).filter(Boolean);\n add({\n id: 'database_server:postgresql:localhost',\n type: 'database_server',\n name: 'PostgreSQL (localhost)',\n discoveredVia: 'psql',\n confidence: 0.9,\n tags: ['local', 'postgresql'],\n metadata: redactValue({ engine: 'postgresql', host: 'localhost', databases }) as Record<string, unknown>,\n });\n }\n if (!IS_WIN) {\n const clusters = run('pg_lsclusters 2>/dev/null');\n if (clusters) report.push(['POSTGRES_CLUSTERS', clusters]);\n }\n }\n\n // ── MySQL / MariaDB ──\n if (exists('mysql')) {\n const out = IS_WIN\n ? run('mysql --connect-timeout=3 -e \"SHOW DATABASES;\"')\n : run('mysql --connect-timeout=3 -e \"SHOW DATABASES;\" 2>/dev/null');\n report.push(['MYSQL_DATABASES', out || '(mysql not running or requires auth)']);\n if (out) {\n const databases = out.split('\\n').map((l) => l.trim()).filter((l) => l && l !== 'Database');\n add({\n id: 'database_server:mysql:localhost',\n type: 'database_server',\n name: 'MySQL (localhost)',\n discoveredVia: 'mysql',\n confidence: 0.9,\n tags: ['local', 'mysql'],\n metadata: redactValue({ engine: 'mysql', host: 'localhost', databases }) as Record<string, unknown>,\n });\n }\n }\n\n // ── MongoDB ── (JSON output — no arrow function, so it passes the allowlist)\n if (exists('mongosh')) {\n const evalExpr = 'JSON.stringify(db.adminCommand({listDatabases:1}))';\n const out = IS_WIN\n ? run(`mongosh --quiet --eval \"${evalExpr}\"`)\n : run(`mongosh --quiet --eval \"${evalExpr}\" 2>/dev/null`);\n report.push(['MONGODB_DATABASES', out || '(mongosh not available)']);\n if (out) {\n add({\n id: 'database_server:mongodb:localhost',\n type: 'database_server',\n name: 'MongoDB (localhost)',\n discoveredVia: 'mongosh',\n confidence: 0.9,\n tags: ['local', 'mongodb'],\n metadata: { engine: 'mongodb', host: 'localhost' },\n });\n }\n }\n\n // ── Redis ──\n if (exists('redis-cli')) {\n const out = IS_WIN\n ? run('redis-cli info server')\n : run('redis-cli info server 2>/dev/null | head -5');\n report.push(['REDIS_INFO', out || '(redis-cli not available)']);\n if (out) {\n add({\n id: 'cache_server:redis:localhost',\n type: 'cache_server',\n name: 'Redis (localhost)',\n discoveredVia: 'redis-cli',\n confidence: 0.9,\n tags: ['local', 'redis'],\n metadata: { engine: 'redis', host: 'localhost' },\n });\n }\n }\n\n // ── SQLite files in app-data directories ──\n const appDirs = dbScanDirs();\n if (appDirs.length > 0) {\n const out = findFiles(appDirs, ['*.sqlite', '*.sqlite3', '*.db'], 4, 80);\n report.push(['SQLITE_APP_FILES', out || '(none found)']);\n for (const path of out.split('\\n').map((l) => l.trim()).filter(Boolean)) {\n const base = baseName(path);\n add({\n id: `database:sqlite:${base}`,\n type: 'database',\n name: base,\n discoveredVia: 'sqlite-file',\n confidence: 0.7,\n tags: ['local', 'sqlite'],\n metadata: redactValue({ path }) as Record<string, unknown>,\n });\n }\n }\n\n // ── Windows DB services (port-based detection is covered by local-ports) ──\n if (IS_WIN) {\n report.push(['DB_SERVICES', scanWindowsDbServices() || '(no database services found)']);\n }\n\n // ── Opt-in expensive scans (gated by the `deep` hint token) ──\n if (deep) {\n const deepOut = IS_WIN\n ? run(\n `Get-ChildItem -Path '${HOME}' -Recurse -Depth 6 -Include '*.sqlite','*.sqlite3','*.db' -ErrorAction SilentlyContinue | ` +\n `Where-Object { $_.FullName -notmatch 'node_modules|\\\\.git' } | ` +\n `Select-Object -First 100 -ExpandProperty FullName`,\n )\n : run(`find \"${HOME}\" -maxdepth 6 \\\\( -name \"*.sqlite\" -o -name \"*.sqlite3\" -o -name \"*.db\" \\\\) -not -path \"*/node_modules/*\" -not -path \"*/.git/*\" 2>/dev/null | head -100`, { timeout: 30_000 });\n report.push(['SQLITE_DEEP_SCAN', deepOut || '(none found)']);\n for (const path of deepOut.split('\\n').map((l) => l.trim()).filter(Boolean)) {\n const base = baseName(path);\n add({\n id: `database:sqlite:${base}`,\n type: 'database',\n name: base,\n discoveredVia: 'sqlite-deep-scan',\n confidence: 0.6,\n tags: ['local', 'sqlite', 'deep'],\n metadata: redactValue({ path }) as Record<string, unknown>,\n });\n }\n const configOut = IS_WIN\n ? run(\n `Get-ChildItem -Path '${HOME}' -Recurse -Depth 4 -Include '.env','.env.local','database.yml','database.json','docker-compose.yml' -ErrorAction SilentlyContinue | ` +\n `Select-Object -First 20 -ExpandProperty FullName`,\n { timeout: 15_000 },\n )\n : run(`find \"${HOME}\" -maxdepth 4 \\\\( -name \".env\" -o -name \".env.local\" -o -name \"database.yml\" -o -name \"database.json\" -o -name \"docker-compose.yml\" \\\\) 2>/dev/null | head -20`, { timeout: 15_000 });\n report.push(['DB_CONFIG_FILES', configOut || '(none found)']);\n }\n\n return { nodes, edges, report: buildReport(report) };\n },\n};\n","import Database from 'better-sqlite3';\nimport { mkdirSync } from 'node:fs';\nimport { dirname } from 'node:path';\nimport { createHash } from 'node:crypto';\nimport { z } from 'zod';\nimport { NODE_TYPES, EDGE_RELATIONSHIPS, NODE_TYPE_GROUPS, SharingLevelSchema, CostEntrySchema } from './types.js';\nimport type {\n CartographyConfig, DiscoveryNode, DiscoveryEdge,\n NodeRow, EdgeRow, SessionRow, Connection, Contributor, TopologyDiff, DriftRunRow,\n SharingLevel, SharingPolicy, PendingShareRow, PendingStatus, CostEntry,\n} from './types.js';\n\nimport { scoreTopology } from './compliance/engine.js';\nimport type { Ruleset, ComplianceReport } from './compliance/types.js';\n\n/** Attribution applied by an enrichment pass (3.3). `null` clears the field; `undefined` leaves it unchanged. */\nexport interface NodeAttribution {\n owner?: string | null;\n cost?: CostEntry | null;\n}\nimport { diffTopology } from './diff.js';\nimport type { TopologyDelta } from './diff.js';\nimport { detectAnomalies, newAnomalies, DEFAULT_ANOMALY_THRESHOLDS } from './anomaly.js';\nimport type { Anomaly, AnomalyThresholds, AnomalyConfig } from './types.js';\nimport type { Role, CredentialRecord } from './auth/types.js';\nimport { sanitizeUntrusted, sanitizeValue } from './sanitize.js';\nimport { hostname, osUser, machineId } from './platform.js';\n\n/** Default tenant for single-user / pre-migration installs. */\nexport const DEFAULT_TENANT = 'local';\n\n/**\n * Normalize an untrusted tenant id: strip invisible/control characters, trim,\n * cap length, and enforce a conservative key charset. Falls back to DEFAULT_TENANT\n * when the input is missing or invalid. The tenant is a data-scoping partition key\n * (not an auth boundary — RBAC is Phase 4), so the rule is shared by the DB and MCP\n * layers via this one helper.\n */\nexport function normalizeTenant(raw?: string | null): string {\n if (raw == null) return DEFAULT_TENANT;\n const cleaned = sanitizeUntrusted(String(raw)).trim().slice(0, 128);\n return /^[\\w.@:+-]{1,128}$/.test(cleaned) ? cleaned : DEFAULT_TENANT;\n}\n\n// ── Global node identity (2.9) ───────────────────────────────────────────────\n\n/** Default ports stripped during id normalization. */\nconst DEFAULT_PORTS = /:(80|443)$/;\n\n/**\n * Deterministic, pure normalization of a node id so the same logical resource\n * yields the same key across machines: trim, lowercase, collapse internal\n * whitespace, strip a single trailing default port (:80/:443).\n */\nexport function normalizeId(id: string): string {\n return id.trim().toLowerCase().replace(/\\s+/g, ' ').replace(DEFAULT_PORTS, '');\n}\n\n/** Stable subset of metadata that feeds the content hash. */\nconst KEY_META_KEYS = ['host', 'port', 'path', 'url'] as const;\n\n/**\n * Extract only the stable key-meta subset (`host`/`port`/`path`/`url`) from a\n * metadata blob (pure). Never the whole blob, which carries machine-local noise\n * such as timestamps and absolute paths.\n */\nexport function keyMetaOf(metadata: Record<string, unknown>): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const k of KEY_META_KEYS) {\n if (metadata[k] != null) out[k] = metadata[k];\n }\n return out;\n}\n\n/**\n * Secondary dedup key: sha256 over type + normalized name + sorted key-meta.\n * Catches `id` drift between machines for the same logical resource. Truncated\n * to 32 hex chars (128 bits) — ample for collision resistance at org scale.\n */\nexport function contentHash(type: string, name: string, keyMeta: Record<string, unknown>): string {\n const ordered = Object.keys(keyMeta).sort().map((k) => `${k}=${String(keyMeta[k])}`).join('|');\n const payload = `${type}␞${name.trim().toLowerCase()}␞${ordered}`;\n return createHash('sha256').update(payload).digest('hex').slice(0, 32);\n}\n\n/**\n * Human-readable primary identity: org-scoped normalized id (`{org}:{normalizedId}`).\n * `org` is the session's normalized tenant (2.8's partition) — the `'_'` sentinel\n * isolates org-less sessions into their own namespace so two organizations never\n * collapse onto one logical node even with identical infra.\n */\nexport function globalId(organization: string | undefined, id: string): string {\n const org = organization && organization.trim() ? organization.trim() : '_';\n return `${org}:${normalizeId(id)}`;\n}\n\n/** Parse a JSON column, falling back to `fallback` if the stored value is corrupt. */\nfunction safeJsonParse<T>(raw: string, fallback: T): T {\n try {\n return JSON.parse(raw) as T;\n } catch {\n return fallback;\n }\n}\n\n// ── Row validation schemas ──────────────────────────────────────────────────\n\nconst SessionRowSchema = z.object({\n id: z.string(),\n mode: z.literal('discover'),\n started_at: z.string(),\n completed_at: z.string().nullable().optional(),\n config: z.string(),\n name: z.string().nullable().optional(),\n tenant: z.string().default(DEFAULT_TENANT),\n hostname: z.string().nullable().optional(),\n user: z.string().nullable().optional(),\n machine_id: z.string().nullable().optional(),\n organization: z.string().nullable().optional(),\n last_scanned_at: z.string().nullable().optional(),\n});\n\nconst NodeRowSchema = z.object({\n id: z.string(),\n session_id: z.string(),\n type: z.enum(NODE_TYPES),\n name: z.string(),\n discovered_via: z.string().nullable().optional(),\n discovered_at: z.string(),\n path_id: z.string().nullable().optional(),\n depth: z.number().default(0),\n confidence: z.number().default(0.5),\n metadata: z.string().default('{}'),\n tags: z.string().default('[]'),\n domain: z.string().nullable().optional(),\n sub_domain: z.string().nullable().optional(),\n quality_score: z.number().nullable().optional(),\n owner: z.string().nullable().optional(),\n cost: z.string().nullable().optional(),\n global_id: z.string().nullable().optional(),\n content_hash: z.string().nullable().optional(),\n});\n\n/** Row shape of the `node_contributors` child table (2.9). */\nconst ContributorRowSchema = z.object({\n global_id: z.string(),\n machine_id: z.string(),\n hostname: z.string(),\n user: z.string(),\n organization: z.string().nullable().optional(),\n at: z.string(),\n confidence: z.number().default(0.5),\n});\n\n/** Row shape of the `drift_runs` table (2.5 scheduled discovery). */\nconst DriftRunRowSchema = z.object({\n id: z.string(),\n session_id: z.string(),\n base_session_id: z.string().nullable().optional(),\n ran_at: z.string(),\n nodes_added: z.number(),\n nodes_removed: z.number(),\n nodes_changed: z.number(),\n edges_added: z.number(),\n edges_removed: z.number(),\n delta: z.string(),\n});\n\nconst EdgeRowSchema = z.object({\n id: z.string(),\n session_id: z.string(),\n source_id: z.string(),\n target_id: z.string(),\n relationship: z.enum(EDGE_RELATIONSHIPS),\n evidence: z.string().nullable().optional(),\n confidence: z.number().default(0.5),\n discovered_at: z.string(),\n});\n\n/** Row shape of the `pending_shares` review queue (2.11). */\nconst PendingShareRowSchema = z.object({\n content_hash: z.string(),\n session_id: z.string(),\n node_id: z.string().nullable().optional(),\n kind: z.enum(['node', 'edge']),\n payload: z.string(),\n status: z.enum(['pending', 'approved', 'shared', 'withheld']),\n decided_by: z.enum(['user', 'rule']).nullable().optional(),\n created_at: z.string(),\n decided_at: z.string().nullable().optional(),\n shared_at: z.string().nullable().optional(),\n});\n\nconst EventRowSchema = z.object({\n id: z.string(),\n session_id: z.string(),\n task_id: z.string().nullable().optional(),\n timestamp: z.string(),\n event_type: z.string(),\n process: z.string(),\n pid: z.number(),\n target: z.string().nullable().optional(),\n target_type: z.string().nullable().optional(),\n port: z.number().nullable().optional(),\n duration_ms: z.number().nullable().optional(),\n command: z.string().nullable().optional(),\n result_bytes: z.number().nullable().optional(),\n});\n\nconst TaskRowSchema = z.object({\n id: z.string(),\n session_id: z.string(),\n description: z.string().nullable().optional(),\n started_at: z.string(),\n completed_at: z.string().nullable().optional(),\n steps: z.string().default('[]'),\n involved_services: z.string().default('[]'),\n status: z.enum(['active', 'completed', 'cancelled']),\n});\n\nconst WorkflowRowSchema = z.object({\n id: z.string(),\n session_id: z.string(),\n name: z.string().nullable().optional(),\n pattern: z.string(),\n task_ids: z.string().default('[]'),\n occurrences: z.number().default(1),\n first_seen: z.string(),\n last_seen: z.string(),\n avg_duration_ms: z.number().nullable().optional(),\n involved_services: z.string().default('[]'),\n});\n\nconst ConnectionRowSchema = z.object({\n id: z.string(),\n session_id: z.string(),\n source_asset_id: z.string(),\n target_asset_id: z.string(),\n type: z.string().nullable().optional(),\n created_at: z.string(),\n});\n\nexport interface ConnectionRow extends Connection {\n sessionId: string;\n createdAt: string;\n}\n\n/** Aggregate, low-token index of a topology — used for progressive disclosure. */\nexport interface GraphSummary {\n sessionId: string;\n totals: { nodes: number; edges: number };\n nodesByType: Record<string, number>;\n nodesByDomain: Record<string, number>;\n edgesByRelationship: Record<string, number>;\n topConnected: Array<{ id: string; name: string; type: string; degree: number }>;\n /** Standing structural anomalies (orphans / shadow-IT), computed at read time (3.6). */\n anomalies: Anomaly[];\n /** Distinct machines that contributed to this session's nodes (2.9 attribution). */\n contributors: number;\n /** Cost rolled up by domain, bucketed by (currency, period) — never FX-normalized (3.3). */\n costByDomain: Array<{ domain: string; currency: string; period: string; total: number; nodes: number }>;\n /** Cost rolled up by owner, bucketed by (currency, period) (3.3). */\n costByOwner: Array<{ owner: string; currency: string; period: string; total: number; nodes: number }>;\n /** Nodes carrying a cost vs. total — attribution coverage (3.3). */\n costCoverage: { withCost: number; total: number };\n}\n\n/**\n * Org-wide aggregate index (2.12) — the central-collector analogue of\n * {@link GraphSummary}. Scoped to a single tenant (`org`) rather than one session,\n * so it merges every machine's contribution into one organization-wide topology.\n */\nexport interface OrgSummary {\n org: string;\n totals: { nodes: number; edges: number };\n nodesByType: Record<string, number>;\n nodesByDomain: Record<string, number>;\n edgesByRelationship: Record<string, number>;\n topConnected: Array<{ id: string; name: string; type: string; degree: number }>;\n /** Distinct machines that contributed to this org's nodes (2.9 attribution, org-wide). */\n contributors: number;\n}\n\n/** Map a node type to its semantic group (or 'other' if ungrouped). */\nfunction typeGroup(type: string): string {\n for (const [group, types] of Object.entries(NODE_TYPE_GROUPS)) {\n if ((types as readonly string[]).includes(type)) return group;\n }\n return 'other';\n}\n\n/**\n * Derive a deterministic, human-friendly session label from its graph summary,\n * e.g. `\"infra+data · 42 nodes · 2026-06-11\"`. Pure: same summary + timestamp\n * always yields the same name. No LLM call.\n */\nexport function deriveSessionName(summary: GraphSummary, startedAt: string): string {\n const date = startedAt.slice(0, 10);\n const count = summary.totals.nodes;\n if (count === 0) return `empty · 0 nodes · ${date}`;\n\n const byGroup = new Map<string, number>();\n for (const [type, n] of Object.entries(summary.nodesByType)) {\n const g = typeGroup(type);\n byGroup.set(g, (byGroup.get(g) ?? 0) + n);\n }\n const topGroups = [...byGroup.entries()]\n .sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))\n .slice(0, 2)\n .map(([g]) => g);\n\n const noun = count === 1 ? 'node' : 'nodes';\n return `${topGroups.join('+')} · ${count} ${noun} · ${date}`;\n}\n\n/** Result of a recursive dependency traversal. */\nexport interface TraversalResult {\n root?: NodeRow;\n direction: 'downstream' | 'upstream' | 'both';\n maxDepth: number;\n nodes: Array<NodeRow & { depth: number }>;\n edges: EdgeRow[];\n}\n\n// ── DB Row Types ──\n\nexport interface EventRow {\n id: string;\n sessionId: string;\n taskId?: string;\n timestamp: string;\n eventType: string;\n process: string;\n pid: number;\n target?: string;\n targetType?: string;\n port?: number;\n durationMs?: number;\n command?: string;\n resultBytes?: number;\n}\n\nexport interface TaskRow {\n id: string;\n sessionId: string;\n description?: string;\n startedAt: string;\n completedAt?: string;\n steps: string;\n involvedServices: string;\n status: 'active' | 'completed' | 'cancelled';\n}\n\nexport interface WorkflowRow {\n id: string;\n sessionId: string;\n name?: string;\n pattern: string;\n taskIds: string;\n occurrences: number;\n firstSeen: string;\n lastSeen: string;\n avgDurationMs: number;\n involvedServices: string;\n}\n\nconst SCHEMA = `\nPRAGMA journal_mode = WAL;\nPRAGMA foreign_keys = ON;\nPRAGMA busy_timeout = 5000;\n\nCREATE TABLE IF NOT EXISTS sessions (\n id TEXT PRIMARY KEY,\n mode TEXT NOT NULL CHECK (mode IN ('discover')),\n started_at TEXT NOT NULL,\n completed_at TEXT,\n config TEXT NOT NULL DEFAULT '{}',\n name TEXT,\n tenant TEXT NOT NULL DEFAULT 'local',\n hostname TEXT,\n user TEXT,\n machine_id TEXT,\n organization TEXT,\n last_scanned_at TEXT\n);\n\nCREATE TABLE IF NOT EXISTS nodes (\n id TEXT NOT NULL,\n session_id TEXT NOT NULL REFERENCES sessions(id),\n type TEXT NOT NULL,\n name TEXT NOT NULL,\n discovered_via TEXT,\n discovered_at TEXT NOT NULL,\n path_id TEXT,\n depth INTEGER DEFAULT 0,\n confidence REAL DEFAULT 0.5,\n metadata TEXT NOT NULL DEFAULT '{}',\n tags TEXT NOT NULL DEFAULT '[]',\n domain TEXT,\n sub_domain TEXT,\n quality_score REAL,\n owner TEXT,\n cost TEXT,\n tenant TEXT NOT NULL DEFAULT 'local',\n global_id TEXT,\n content_hash TEXT,\n PRIMARY KEY (id, session_id)\n);\n\nCREATE TABLE IF NOT EXISTS connections (\n id TEXT PRIMARY KEY,\n session_id TEXT NOT NULL REFERENCES sessions(id),\n source_asset_id TEXT NOT NULL,\n target_asset_id TEXT NOT NULL,\n type TEXT,\n created_at TEXT NOT NULL,\n tenant TEXT NOT NULL DEFAULT 'local'\n);\n\nCREATE TABLE IF NOT EXISTS edges (\n id TEXT PRIMARY KEY,\n session_id TEXT NOT NULL REFERENCES sessions(id),\n source_id TEXT NOT NULL,\n target_id TEXT NOT NULL,\n relationship TEXT NOT NULL,\n evidence TEXT,\n confidence REAL DEFAULT 0.5,\n discovered_at TEXT NOT NULL,\n tenant TEXT NOT NULL DEFAULT 'local'\n);\n\nCREATE TABLE IF NOT EXISTS activity_events (\n id TEXT PRIMARY KEY,\n session_id TEXT NOT NULL REFERENCES sessions(id),\n task_id TEXT,\n timestamp TEXT NOT NULL,\n event_type TEXT NOT NULL,\n process TEXT NOT NULL,\n pid INTEGER NOT NULL,\n target TEXT,\n target_type TEXT,\n port INTEGER,\n duration_ms INTEGER,\n command TEXT,\n result_bytes INTEGER,\n tenant TEXT NOT NULL DEFAULT 'local',\n actor_subject TEXT,\n actor_role TEXT,\n actor_tenant TEXT\n);\n\nCREATE TABLE IF NOT EXISTS tasks (\n id TEXT PRIMARY KEY,\n session_id TEXT NOT NULL REFERENCES sessions(id),\n description TEXT,\n started_at TEXT NOT NULL,\n completed_at TEXT,\n steps TEXT NOT NULL DEFAULT '[]',\n involved_services TEXT NOT NULL DEFAULT '[]',\n status TEXT DEFAULT 'active' CHECK (status IN ('active','completed','cancelled')),\n tenant TEXT NOT NULL DEFAULT 'local'\n);\n\nCREATE TABLE IF NOT EXISTS workflows (\n id TEXT PRIMARY KEY,\n session_id TEXT NOT NULL REFERENCES sessions(id),\n name TEXT,\n pattern TEXT NOT NULL,\n task_ids TEXT NOT NULL DEFAULT '[]',\n occurrences INTEGER DEFAULT 1,\n first_seen TEXT NOT NULL,\n last_seen TEXT NOT NULL,\n avg_duration_ms INTEGER,\n involved_services TEXT NOT NULL DEFAULT '[]',\n tenant TEXT NOT NULL DEFAULT 'local'\n);\n\nCREATE TABLE IF NOT EXISTS node_approvals (\n pattern TEXT PRIMARY KEY,\n action TEXT NOT NULL CHECK (action IN ('save','ignore','auto')),\n created_at TEXT NOT NULL\n);\n\nCREATE TABLE IF NOT EXISTS sharing_policy (\n pattern TEXT PRIMARY KEY, -- '*' row holds the global default\n level TEXT NOT NULL CHECK (level IN ('none','anonymized','full')),\n created_at TEXT NOT NULL\n);\n\nCREATE TABLE IF NOT EXISTS pseudonym_reversal (\n token TEXT PRIMARY KEY,\n ciphertext TEXT NOT NULL, -- base64(iv ‖ tag ‖ AES-256-GCM(plaintext)) under reversalKey(orgKey)\n created_at TEXT NOT NULL\n);\n\nCREATE TABLE IF NOT EXISTS pending_shares (\n content_hash TEXT PRIMARY KEY, -- sha256 over the policy-transformed payload (stable dedup key)\n session_id TEXT NOT NULL REFERENCES sessions(id),\n node_id TEXT, -- original node id (NULL for edge rows)\n kind TEXT NOT NULL CHECK (kind IN ('node','edge')),\n payload TEXT NOT NULL, -- JSON of the already-anonymized projection (exactly what would leave)\n status TEXT NOT NULL CHECK (status IN ('pending','approved','shared','withheld')),\n decided_by TEXT CHECK (decided_by IN ('user','rule')),\n created_at TEXT NOT NULL,\n decided_at TEXT,\n shared_at TEXT\n);\n\nCREATE TABLE IF NOT EXISTS node_contributors (\n global_id TEXT NOT NULL,\n machine_id TEXT NOT NULL,\n hostname TEXT NOT NULL,\n user TEXT NOT NULL,\n organization TEXT,\n at TEXT NOT NULL,\n confidence REAL NOT NULL DEFAULT 0.5,\n PRIMARY KEY (global_id, machine_id)\n);\n\nCREATE TABLE IF NOT EXISTS drift_runs (\n id TEXT PRIMARY KEY,\n session_id TEXT NOT NULL REFERENCES sessions(id),\n base_session_id TEXT,\n ran_at TEXT NOT NULL,\n nodes_added INTEGER NOT NULL,\n nodes_removed INTEGER NOT NULL,\n nodes_changed INTEGER NOT NULL,\n edges_added INTEGER NOT NULL,\n edges_removed INTEGER NOT NULL,\n delta TEXT NOT NULL\n);\n\nCREATE INDEX IF NOT EXISTS idx_nodes_session ON nodes(session_id);\nCREATE INDEX IF NOT EXISTS idx_nodes_type ON nodes(session_id, type);\nCREATE INDEX IF NOT EXISTS idx_edges_session ON edges(session_id);\nCREATE INDEX IF NOT EXISTS idx_edges_source ON edges(session_id, source_id);\nCREATE INDEX IF NOT EXISTS idx_edges_target ON edges(session_id, target_id);\nCREATE INDEX IF NOT EXISTS idx_events_session ON activity_events(session_id);\nCREATE INDEX IF NOT EXISTS idx_events_task ON activity_events(task_id);\nCREATE INDEX IF NOT EXISTS idx_tasks_session ON tasks(session_id);\nCREATE INDEX IF NOT EXISTS idx_connections_session ON connections(session_id);\nCREATE INDEX IF NOT EXISTS idx_connections_lookup ON connections(session_id, source_asset_id, target_asset_id);\nCREATE INDEX IF NOT EXISTS idx_sessions_tenant ON sessions(tenant);\nCREATE INDEX IF NOT EXISTS idx_nodes_tenant_session ON nodes(tenant, session_id);\nCREATE INDEX IF NOT EXISTS idx_edges_tenant_session ON edges(tenant, session_id);\nCREATE INDEX IF NOT EXISTS idx_nodes_global ON nodes(global_id);\nCREATE INDEX IF NOT EXISTS idx_nodes_content ON nodes(content_hash);\nCREATE INDEX IF NOT EXISTS idx_contrib_global ON node_contributors(global_id);\nCREATE INDEX IF NOT EXISTS idx_drift_runs_session ON drift_runs(session_id);\nCREATE INDEX IF NOT EXISTS idx_drift_runs_ran_at ON drift_runs(ran_at);\nCREATE INDEX IF NOT EXISTS idx_pending_status ON pending_shares(status);\nCREATE INDEX IF NOT EXISTS idx_pending_session ON pending_shares(session_id);\nCREATE INDEX IF NOT EXISTS idx_nodes_tenant_global ON nodes(tenant, global_id);\nCREATE INDEX IF NOT EXISTS idx_nodes_tenant_content ON nodes(tenant, content_hash);\nCREATE INDEX IF NOT EXISTS idx_contrib_org ON node_contributors(organization, global_id);\nCREATE INDEX IF NOT EXISTS idx_nodes_owner ON nodes(session_id, owner);\n`;\n\n/**\n * RBAC credential store (4.5, schema v15). Separate constant so a fresh DB and the\n * v14→v15 migration both create it from one source. Tokens are stored **hashed**\n * (sha256 hex); the raw token is never persisted. `tenant` scopes every read the\n * credential can perform.\n */\nconst AUTH_SCHEMA = `\nCREATE TABLE IF NOT EXISTS auth_credentials (\n token_hash TEXT PRIMARY KEY,\n subject TEXT NOT NULL,\n tenant TEXT NOT NULL DEFAULT 'local',\n role TEXT NOT NULL,\n created_at TEXT NOT NULL\n);\nCREATE INDEX IF NOT EXISTS idx_auth_subject ON auth_credentials(subject);\n`;\n\nexport class CartographyDB {\n private db: Database.Database;\n /** 3.6 anomaly settings; defaults apply when no `anomaly` config is supplied. */\n private readonly anomalyEnabled: boolean;\n private readonly anomalyThresholds: AnomalyThresholds;\n\n constructor(dbPath: string, opts?: { anomaly?: AnomalyConfig }) {\n mkdirSync(dirname(dbPath), { recursive: true });\n this.db = new Database(dbPath);\n this.db.pragma('journal_mode = WAL');\n this.db.pragma('foreign_keys = ON');\n this.db.pragma('busy_timeout = 5000');\n this.anomalyEnabled = opts?.anomaly?.enabled ?? true;\n this.anomalyThresholds = opts?.anomaly ?? DEFAULT_ANOMALY_THRESHOLDS;\n this.migrate();\n }\n\n private migrate(): void {\n const version = (this.db.pragma('user_version', { simple: true }) as number);\n if (version === 0) {\n this.db.exec(SCHEMA);\n this.db.exec(AUTH_SCHEMA);\n this.db.pragma('user_version = 15');\n return;\n } else if (version === 1) {\n // v1 → v2: add hex map columns to nodes + connections table\n const cols = (this.db.prepare(\"PRAGMA table_info(nodes)\").all() as Array<{ name: string }>).map(c => c.name);\n if (!cols.includes('domain')) this.db.exec('ALTER TABLE nodes ADD COLUMN domain TEXT');\n if (!cols.includes('sub_domain')) this.db.exec('ALTER TABLE nodes ADD COLUMN sub_domain TEXT');\n if (!cols.includes('quality_score')) this.db.exec('ALTER TABLE nodes ADD COLUMN quality_score REAL');\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS connections (\n id TEXT PRIMARY KEY,\n session_id TEXT NOT NULL REFERENCES sessions(id),\n source_asset_id TEXT NOT NULL,\n target_asset_id TEXT NOT NULL,\n type TEXT,\n created_at TEXT NOT NULL\n );\n CREATE INDEX IF NOT EXISTS idx_connections_session ON connections(session_id);\n CREATE INDEX IF NOT EXISTS idx_connections_lookup ON connections(session_id, source_asset_id, target_asset_id);\n `);\n this.db.pragma('user_version = 3');\n }\n if (version === 2) {\n // v2 → v3: add composite index for connection upsert lookups\n this.db.exec('CREATE INDEX IF NOT EXISTS idx_connections_lookup ON connections(session_id, source_asset_id, target_asset_id)');\n this.db.pragma('user_version = 3');\n }\n // v3 → v4: add graph-traversal indexes (idempotent for any pre-v4 DB)\n const current = this.db.pragma('user_version', { simple: true }) as number;\n if (current < 4) {\n this.db.exec(`\n CREATE INDEX IF NOT EXISTS idx_nodes_type ON nodes(session_id, type);\n CREATE INDEX IF NOT EXISTS idx_edges_source ON edges(session_id, source_id);\n CREATE INDEX IF NOT EXISTS idx_edges_target ON edges(session_id, target_id);\n `);\n this.db.pragma('user_version = 4');\n }\n // v4 → v5: human-friendly session names (idempotent column add)\n const v4 = this.db.pragma('user_version', { simple: true }) as number;\n if (v4 < 5) {\n const cols = (this.db.prepare(\"PRAGMA table_info(sessions)\").all() as Array<{ name: string }>).map(c => c.name);\n if (!cols.includes('name')) this.db.exec('ALTER TABLE sessions ADD COLUMN name TEXT');\n this.db.pragma('user_version = 5');\n }\n // v5 → v6: audit-log columns on activity_events (idempotent column adds)\n const v5 = this.db.pragma('user_version', { simple: true }) as number;\n if (v5 < 6) {\n const cols = (this.db.prepare(\"PRAGMA table_info(activity_events)\").all() as Array<{ name: string }>).map(c => c.name);\n if (!cols.includes('command')) this.db.exec('ALTER TABLE activity_events ADD COLUMN command TEXT');\n if (!cols.includes('result_bytes')) this.db.exec('ALTER TABLE activity_events ADD COLUMN result_bytes INTEGER');\n this.db.pragma('user_version = 6');\n }\n // v6 → v7: tenant partition column on every session-scoped table (idempotent column adds).\n // `ALTER TABLE … ADD COLUMN tenant TEXT NOT NULL DEFAULT 'local'` backfills every existing\n // row with the default, so a v6 single-tenant DB upgrades in place with no data loss.\n const v6 = this.db.pragma('user_version', { simple: true }) as number;\n if (v6 < 7) {\n const tables = ['sessions', 'nodes', 'edges', 'connections', 'activity_events', 'tasks', 'workflows'] as const;\n for (const t of tables) {\n const cols = (this.db.prepare(`PRAGMA table_info(${t})`).all() as Array<{ name: string }>).map((c) => c.name);\n // PRAGMA table_info returns no rows for a non-existent table; skip those\n // (a real v6 catalog always has every table — this guards partial fixtures).\n if (cols.length > 0 && !cols.includes('tenant')) {\n this.db.exec(`ALTER TABLE ${t} ADD COLUMN tenant TEXT NOT NULL DEFAULT '${DEFAULT_TENANT}'`);\n }\n }\n const hasTable = (name: string): boolean =>\n (this.db.prepare(`PRAGMA table_info(${name})`).all() as unknown[]).length > 0;\n if (hasTable('sessions')) this.db.exec('CREATE INDEX IF NOT EXISTS idx_sessions_tenant ON sessions(tenant)');\n if (hasTable('nodes')) this.db.exec('CREATE INDEX IF NOT EXISTS idx_nodes_tenant_session ON nodes(tenant, session_id)');\n if (hasTable('edges')) this.db.exec('CREATE INDEX IF NOT EXISTS idx_edges_tenant_session ON edges(tenant, session_id)');\n this.db.pragma('user_version = 7');\n }\n // v7 → v8: source attribution + global node identity + contributor list (2.9).\n // Additive only: new session/node columns are nullable, `node_contributors` is new,\n // so a v7 (tenant-aware) DB upgrades in place with no data loss. `global_id`/\n // `content_hash` backfill lazily on the next upsertNode (no destructive backfill).\n const v7 = this.db.pragma('user_version', { simple: true }) as number;\n if (v7 < 8) {\n const sc = (this.db.prepare('PRAGMA table_info(sessions)').all() as Array<{ name: string }>).map((c) => c.name);\n if (sc.length > 0) {\n for (const col of ['hostname', 'user', 'machine_id', 'organization']) {\n if (!sc.includes(col)) this.db.exec(`ALTER TABLE sessions ADD COLUMN ${col} TEXT`);\n }\n }\n const nc = (this.db.prepare('PRAGMA table_info(nodes)').all() as Array<{ name: string }>).map((c) => c.name);\n if (nc.length > 0) {\n if (!nc.includes('global_id')) this.db.exec('ALTER TABLE nodes ADD COLUMN global_id TEXT');\n if (!nc.includes('content_hash')) this.db.exec('ALTER TABLE nodes ADD COLUMN content_hash TEXT');\n }\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS node_contributors (\n global_id TEXT NOT NULL,\n machine_id TEXT NOT NULL,\n hostname TEXT NOT NULL,\n user TEXT NOT NULL,\n organization TEXT,\n at TEXT NOT NULL,\n confidence REAL NOT NULL DEFAULT 0.5,\n PRIMARY KEY (global_id, machine_id)\n );\n CREATE INDEX IF NOT EXISTS idx_contrib_global ON node_contributors(global_id);\n `);\n // Node indexes only when `nodes` exists (guards partial fixtures, mirroring 2.8).\n if (nc.length > 0) {\n this.db.exec(`\n CREATE INDEX IF NOT EXISTS idx_nodes_global ON nodes(global_id);\n CREATE INDEX IF NOT EXISTS idx_nodes_content ON nodes(content_hash);\n `);\n }\n this.db.pragma('user_version = 8');\n }\n // v8 → v9: incremental-discovery observability (2.1). Adds nullable\n // `sessions.last_scanned_at` so an in-place rescan records when it last ran.\n // Additive/nullable: a v8 DB upgrades in place with no data loss and old rows\n // keep `last_scanned_at = NULL` until their next rescan.\n const v8 = this.db.pragma('user_version', { simple: true }) as number;\n if (v8 < 9) {\n const sc = (this.db.prepare('PRAGMA table_info(sessions)').all() as Array<{ name: string }>).map((c) => c.name);\n // PRAGMA table_info returns no rows for a non-existent table; skip those\n // (a real v8 catalog always has `sessions` — this guards partial fixtures).\n if (sc.length > 0 && !sc.includes('last_scanned_at')) {\n this.db.exec('ALTER TABLE sessions ADD COLUMN last_scanned_at TEXT');\n }\n this.db.pragma('user_version = 9');\n }\n // v9 → v10: per-run drift persistence for scheduled discovery (2.5). Additive\n // only — a new `drift_runs` table + two indexes; no existing table is altered,\n // so a v9 DB upgrades in place with no data loss. Idempotent via IF NOT EXISTS.\n const v9 = this.db.pragma('user_version', { simple: true }) as number;\n if (v9 < 10) {\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS drift_runs (\n id TEXT PRIMARY KEY,\n session_id TEXT NOT NULL REFERENCES sessions(id),\n base_session_id TEXT,\n ran_at TEXT NOT NULL,\n nodes_added INTEGER NOT NULL,\n nodes_removed INTEGER NOT NULL,\n nodes_changed INTEGER NOT NULL,\n edges_added INTEGER NOT NULL,\n edges_removed INTEGER NOT NULL,\n delta TEXT NOT NULL\n );\n CREATE INDEX IF NOT EXISTS idx_drift_runs_session ON drift_runs(session_id);\n CREATE INDEX IF NOT EXISTS idx_drift_runs_ran_at ON drift_runs(ran_at);\n `);\n this.db.pragma('user_version = 10');\n }\n // v10 → v11: persistent consent policy + admin-reversible anonymization (2.10).\n // Additive only — two new standalone tables (`sharing_policy`, `pseudonym_reversal`),\n // no existing table is altered, so a v10 DB upgrades in place with no data loss.\n // Idempotent via IF NOT EXISTS. The org key lives on disk (`~/.cartography/org-key`),\n // never in the DB; only the AES-256-GCM ciphertext of reversal plaintexts is stored.\n const v10 = this.db.pragma('user_version', { simple: true }) as number;\n if (v10 < 11) {\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS sharing_policy (\n pattern TEXT PRIMARY KEY,\n level TEXT NOT NULL CHECK (level IN ('none','anonymized','full')),\n created_at TEXT NOT NULL\n );\n CREATE TABLE IF NOT EXISTS pseudonym_reversal (\n token TEXT PRIMARY KEY,\n ciphertext TEXT NOT NULL,\n created_at TEXT NOT NULL\n );\n `);\n this.db.pragma('user_version = 11');\n }\n // v11 → v12: central-DB pending-review queue (2.11). Additive only — one new\n // standalone `pending_shares` table + two indexes; no existing table is altered,\n // so a v11 DB upgrades in place with no data loss. Idempotent via IF NOT EXISTS.\n // Nothing is ever pushed without an explicit approval/remembered rule, so an\n // unconfigured install never writes to this table.\n const v11 = this.db.pragma('user_version', { simple: true }) as number;\n if (v11 < 12) {\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS pending_shares (\n content_hash TEXT PRIMARY KEY,\n session_id TEXT NOT NULL REFERENCES sessions(id),\n node_id TEXT,\n kind TEXT NOT NULL CHECK (kind IN ('node','edge')),\n payload TEXT NOT NULL,\n status TEXT NOT NULL CHECK (status IN ('pending','approved','shared','withheld')),\n decided_by TEXT CHECK (decided_by IN ('user','rule')),\n created_at TEXT NOT NULL,\n decided_at TEXT,\n shared_at TEXT\n );\n CREATE INDEX IF NOT EXISTS idx_pending_status ON pending_shares(status);\n CREATE INDEX IF NOT EXISTS idx_pending_session ON pending_shares(session_id);\n `);\n this.db.pragma('user_version = 12');\n }\n // v12 → v13: central-collector ingest substrate (2.12). Additive only — three\n // new indexes that make the org-scoped merge lookups (by `(tenant, global_id)`\n // primary and `(tenant, content_hash)` secondary) and the org-wide contributor\n // count O(log n) at organization scale. No existing table is altered and the\n // `org`/`global_id`/`content_hash` columns + `node_contributors` table already\n // exist from 2.8/2.9, so a v12 DB upgrades in place with no data loss.\n // Idempotent via IF NOT EXISTS; the indexes only attach when `nodes` exists\n // (guards partial fixtures, mirroring the 2.8/2.9 blocks above).\n const v12 = this.db.pragma('user_version', { simple: true }) as number;\n if (v12 < 13) {\n const hasNodes = (this.db.prepare('PRAGMA table_info(nodes)').all() as unknown[]).length > 0;\n if (hasNodes) {\n this.db.exec(`\n CREATE INDEX IF NOT EXISTS idx_nodes_tenant_global ON nodes(tenant, global_id);\n CREATE INDEX IF NOT EXISTS idx_nodes_tenant_content ON nodes(tenant, content_hash);\n `);\n }\n const hasContrib = (this.db.prepare('PRAGMA table_info(node_contributors)').all() as unknown[]).length > 0;\n if (hasContrib) {\n this.db.exec('CREATE INDEX IF NOT EXISTS idx_contrib_org ON node_contributors(organization, global_id)');\n }\n this.db.pragma('user_version = 13');\n }\n // v13 → v14: cost-attribution columns on nodes (3.3). Additive, idempotent\n // `table_info`-guarded ALTERs + an owner index; old rows read back as\n // owner/cost = NULL → undefined. No data is rewritten.\n const v13 = this.db.pragma('user_version', { simple: true }) as number;\n if (v13 < 14) {\n const hasNodes = (this.db.prepare('PRAGMA table_info(nodes)').all() as Array<{ name: string }>);\n if (hasNodes.length > 0) {\n const cols = hasNodes.map((c) => c.name);\n if (!cols.includes('owner')) this.db.exec('ALTER TABLE nodes ADD COLUMN owner TEXT');\n if (!cols.includes('cost')) this.db.exec('ALTER TABLE nodes ADD COLUMN cost TEXT');\n this.db.exec('CREATE INDEX IF NOT EXISTS idx_nodes_owner ON nodes(session_id, owner)');\n }\n this.db.pragma('user_version = 14');\n }\n // v14 → v15: RBAC credential store (4.5) + actor columns on the audit trail.\n // Additive + idempotent; an unconfigured server never reads these (auth dormant).\n const v14 = this.db.pragma('user_version', { simple: true }) as number;\n if (v14 < 15) {\n this.db.exec(AUTH_SCHEMA);\n const ev = this.db.prepare('PRAGMA table_info(activity_events)').all() as Array<{ name: string }>;\n if (ev.length > 0) {\n const cols = ev.map((c) => c.name);\n if (!cols.includes('actor_subject')) this.db.exec('ALTER TABLE activity_events ADD COLUMN actor_subject TEXT');\n if (!cols.includes('actor_role')) this.db.exec('ALTER TABLE activity_events ADD COLUMN actor_role TEXT');\n if (!cols.includes('actor_tenant')) this.db.exec('ALTER TABLE activity_events ADD COLUMN actor_tenant TEXT');\n }\n this.db.pragma('user_version = 15');\n }\n }\n\n close(): void {\n this.db.pragma('optimize');\n this.db.close();\n }\n\n /**\n * Advanced: the underlying better-sqlite3 connection. Used by the optional\n * semantic-search layer to load the `sqlite-vec` extension and manage its\n * virtual table. Prefer the typed methods above for everything else.\n */\n rawConnection(): Database.Database {\n return this.db;\n }\n\n // ── Sessions ────────────────────────────\n\n /**\n * Create a discovery session, stamping its tenant from (in precedence order)\n * the explicit `tenantId` arg → `config.organization` → DEFAULT_TENANT. The\n * tenant is normalized once here; every child row written under this session\n * inherits it via {@link tenantOf}.\n */\n createSession(mode: 'discover', config: CartographyConfig, tenantId?: string): string {\n const id = crypto.randomUUID();\n const tenant = normalizeTenant(tenantId ?? config.organization);\n // 2.9 source attribution: stamp where/who ran the discovery. `organization` is\n // the raw provenance value; `tenant` (2.8) is its normalized partition form.\n this.db.prepare(\n `INSERT INTO sessions (id, mode, started_at, config, tenant, hostname, user, machine_id, organization)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`\n ).run(\n id, mode, new Date().toISOString(), JSON.stringify(config), tenant,\n hostname(), osUser(), machineId(), config.organization ?? null,\n );\n return id;\n }\n\n /** The tenant that owns a session (DEFAULT_TENANT if the session is unknown). */\n private tenantOf(sessionId: string): string {\n const r = this.db.prepare('SELECT tenant FROM sessions WHERE id = ?').get(sessionId) as { tenant?: string } | undefined;\n return r?.tenant ?? DEFAULT_TENANT;\n }\n\n endSession(id: string): void {\n this.db.prepare('UPDATE sessions SET completed_at = ? WHERE id = ?')\n .run(new Date().toISOString(), id);\n }\n\n getSession(id: string): SessionRow | undefined {\n const row = this.db.prepare('SELECT * FROM sessions WHERE id = ?').get(id) as Record<string, unknown> | undefined;\n return row ? this.mapSession(row) : undefined;\n }\n\n /**\n * Resolve the newest session, optionally constrained to a `mode` and/or\n * `tenantId`. Omitting `tenantId` preserves the original (unscoped) behavior;\n * passing one returns only that tenant's sessions, which is how the tenant\n * boundary is enforced at session resolution.\n */\n getLatestSession(mode?: string, tenantId?: string): SessionRow | undefined {\n const clauses: string[] = [];\n const params: unknown[] = [];\n if (mode) { clauses.push('mode = ?'); params.push(mode); }\n if (tenantId !== undefined) { clauses.push('tenant = ?'); params.push(tenantId); }\n const where = clauses.length ? `WHERE ${clauses.join(' AND ')} ` : '';\n const row = this.db.prepare(`SELECT * FROM sessions ${where}ORDER BY rowid DESC LIMIT 1`).get(...params) as Record<string, unknown> | undefined;\n return row ? this.mapSession(row) : undefined;\n }\n\n getSessions(tenantId?: string): SessionRow[] {\n const rows = tenantId !== undefined\n ? this.db.prepare('SELECT * FROM sessions WHERE tenant = ? ORDER BY rowid DESC').all(tenantId) as Record<string, unknown>[]\n : this.db.prepare('SELECT * FROM sessions ORDER BY rowid DESC').all() as Record<string, unknown>[];\n return rows.map(r => this.mapSession(r));\n }\n\n private mapSession(r: Record<string, unknown>): SessionRow {\n const v = SessionRowSchema.parse(r);\n return {\n id: v.id,\n mode: v.mode,\n startedAt: v.started_at,\n completedAt: v.completed_at ?? undefined,\n config: v.config,\n name: v.name ?? undefined,\n tenant: v.tenant,\n hostname: v.hostname ?? undefined,\n user: v.user ?? undefined,\n machineId: v.machine_id ?? undefined,\n organization: v.organization ?? undefined,\n lastScannedAt: v.last_scanned_at ?? undefined,\n };\n }\n\n /** Record that a session was (re-)scanned now (ISO 8601 UTC). */\n touchSession(id: string): void {\n this.db.prepare('UPDATE sessions SET last_scanned_at = ? WHERE id = ?')\n .run(new Date().toISOString(), id);\n }\n\n /** Set (or clear) a session's human-friendly name. */\n setSessionName(id: string, name: string): void {\n this.db.prepare('UPDATE sessions SET name = ? WHERE id = ?').run(name, id);\n }\n\n /**\n * Compare two discovery sessions and report drift (added/removed/changed nodes\n * and added/removed edges). Read-only; no schema changes. Throws if either\n * session id does not exist.\n */\n diffSessions(baseId: string, currentId: string): TopologyDiff {\n const base = this.getSession(baseId);\n if (!base) throw new Error(`Base session not found: ${baseId}`);\n const current = this.getSession(currentId);\n if (!current) throw new Error(`Current session not found: ${currentId}`);\n\n const baseData = { nodes: this.getNodes(baseId), edges: this.getEdges(baseId) };\n const curData = { nodes: this.getNodes(currentId), edges: this.getEdges(currentId) };\n const delta = diffTopology(baseData, curData);\n\n // 3.6: anomalies new in `current` vs `base` — the delta a scheduled sink (3.1)\n // alerts on. Reuses each session's summary degree computation (no extra query).\n const baseAnoms = this.getGraphSummary(baseId).anomalies;\n const curAnoms = this.getGraphSummary(currentId).anomalies;\n\n return {\n base: { sessionId: baseId, startedAt: base.startedAt, nodeCount: baseData.nodes.length, edgeCount: baseData.edges.length },\n current: { sessionId: currentId, startedAt: current.startedAt, nodeCount: curData.nodes.length, edgeCount: curData.edges.length },\n ...delta,\n anomalies: { base: baseAnoms, current: curAnoms, added: newAnomalies(baseAnoms, curAnoms) },\n };\n }\n\n /**\n * Score a session against a compliance ruleset (3.4) — a thin wrapper over the\n * pure `scoreTopology` engine (mirrors `diffSessions`). Throws only when the\n * session id is unknown; the engine never throws on data shape.\n */\n scoreSession(sessionId: string, ruleset: Ruleset, opts?: { now?: string }): ComplianceReport {\n if (!this.getSession(sessionId)) throw new Error(`Session not found: ${sessionId}`);\n return scoreTopology({ nodes: this.getNodes(sessionId), edges: this.getEdges(sessionId) }, ruleset, opts);\n }\n\n // ── Scheduled discovery: prior-session selection + drift runs (2.5) ──────────\n\n /**\n * The most recent session of `mode` (and optionally `tenantId`) other than\n * `excludeId`. Used by scheduled discovery to pick an unambiguous diff base\n * (newest-first by `rowid`), avoiding the `getLatestSession` just-created-session\n * ambiguity. Returns `undefined` when no such prior session exists.\n */\n getPreviousSession(excludeId: string, mode?: string, tenantId?: string): SessionRow | undefined {\n const clauses: string[] = ['id != ?'];\n const params: unknown[] = [excludeId];\n if (mode) { clauses.push('mode = ?'); params.push(mode); }\n if (tenantId !== undefined) { clauses.push('tenant = ?'); params.push(tenantId); }\n const row = this.db\n .prepare(`SELECT * FROM sessions WHERE ${clauses.join(' AND ')} ORDER BY rowid DESC LIMIT 1`)\n .get(...params) as Record<string, unknown> | undefined;\n return row ? this.mapSession(row) : undefined;\n }\n\n /**\n * Persist one scheduled-discovery drift run: the summary counts plus the full\n * {@link TopologyDelta} (for audit/replay). Returns the generated row id.\n * `ranAt` is stamped now (ISO 8601 UTC).\n */\n recordDriftRun(sessionId: string, baseSessionId: string | undefined, delta: TopologyDelta): string {\n const id = crypto.randomUUID();\n const s = delta.summary;\n this.db.prepare(`\n INSERT INTO drift_runs\n (id, session_id, base_session_id, ran_at,\n nodes_added, nodes_removed, nodes_changed, edges_added, edges_removed, delta)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `).run(\n id, sessionId, baseSessionId ?? null, new Date().toISOString(),\n s.nodesAdded, s.nodesRemoved, s.nodesChanged, s.edgesAdded, s.edgesRemoved,\n JSON.stringify(delta),\n );\n return id;\n }\n\n /** Recent drift runs, newest-first (`LIMIT`, default 50). */\n getDriftRuns(limit = 50): DriftRunRow[] {\n const rows = this.db\n .prepare('SELECT * FROM drift_runs ORDER BY rowid DESC LIMIT ?')\n .all(Math.max(1, Math.floor(limit))) as Record<string, unknown>[];\n return rows.map((r) => this.mapDriftRun(r));\n }\n\n /** The most recent drift run, or `undefined` when none has been recorded. */\n getLatestDriftRun(): DriftRunRow | undefined {\n const row = this.db\n .prepare('SELECT * FROM drift_runs ORDER BY rowid DESC LIMIT 1')\n .get() as Record<string, unknown> | undefined;\n return row ? this.mapDriftRun(row) : undefined;\n }\n\n private mapDriftRun(r: Record<string, unknown>): DriftRunRow {\n const v = DriftRunRowSchema.parse(r);\n const emptyDelta: TopologyDelta = {\n nodes: { added: [], removed: [], changed: [], unchanged: 0 },\n edges: { added: [], removed: [], unchanged: 0 },\n summary: { nodesAdded: 0, nodesRemoved: 0, nodesChanged: 0, edgesAdded: 0, edgesRemoved: 0 },\n };\n return {\n id: v.id,\n sessionId: v.session_id,\n baseSessionId: v.base_session_id ?? undefined,\n ranAt: v.ran_at,\n summary: {\n nodesAdded: v.nodes_added,\n nodesRemoved: v.nodes_removed,\n nodesChanged: v.nodes_changed,\n edgesAdded: v.edges_added,\n edgesRemoved: v.edges_removed,\n },\n delta: safeJsonParse<TopologyDelta>(v.delta, emptyDelta),\n };\n }\n\n // ── Nodes ───────────────────────────────\n\n upsertNode(sessionId: string, node: DiscoveryNode, depth = 0, attribution?: Contributor): void {\n // Sanitize untrusted free-text before it enters the catalog (and later an LLM\n // context): strip invisible/control characters that could hide prompt injection.\n const tenant = this.tenantOf(sessionId);\n\n // 2.9 global identity. The org-scope is the session's tenant (2.8's normalized\n // organization partition), so two organizations never collapse onto one node.\n const ch = contentHash(node.type, node.name, keyMetaOf(node.metadata ?? {}));\n // Catch id drift first: a different node.id from another machine that hashes to\n // the same content under the same tenant reuses that existing logical identity.\n const drift = this.db.prepare(\n `SELECT global_id FROM nodes\n WHERE content_hash = ? AND COALESCE(global_id, '') LIKE ?\n LIMIT 1`,\n ).get(ch, `${globalId(tenant, '')}%`) as { global_id: string } | undefined;\n const gid = drift?.global_id ?? globalId(tenant, node.id);\n\n this.db.prepare(`\n INSERT OR REPLACE INTO nodes\n (id, session_id, type, name, discovered_via, discovered_at, depth, confidence, metadata, tags,\n domain, sub_domain, quality_score, owner, cost, tenant, global_id, content_hash)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `).run(\n node.id, sessionId, node.type, sanitizeUntrusted(node.name), node.discoveredVia,\n new Date().toISOString(), depth, node.confidence,\n JSON.stringify(sanitizeValue(node.metadata ?? {})),\n JSON.stringify((node.tags ?? []).map(sanitizeUntrusted)),\n node.domain != null ? sanitizeUntrusted(node.domain) : null,\n node.subDomain != null ? sanitizeUntrusted(node.subDomain) : null,\n node.qualityScore ?? null,\n node.owner != null ? sanitizeUntrusted(node.owner) : null,\n node.cost ? JSON.stringify(CostEntrySchema.parse(node.cost)) : null,\n tenant, gid, ch,\n );\n\n if (attribution) {\n // (global_id, machine_id) conflict → keep highest confidence + latest observation.\n // Free-text attribution is sanitized like name/tags (it may enter an LLM context).\n this.db.prepare(`\n INSERT INTO node_contributors (global_id, machine_id, hostname, user, organization, at, confidence)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(global_id, machine_id) DO UPDATE SET\n confidence = MAX(confidence, excluded.confidence),\n at = excluded.at,\n hostname = excluded.hostname,\n user = excluded.user,\n organization = excluded.organization\n `).run(\n gid, attribution.machineId,\n sanitizeUntrusted(attribution.hostname), sanitizeUntrusted(attribution.user),\n attribution.organization != null ? sanitizeUntrusted(attribution.organization) : null,\n attribution.at, attribution.confidence,\n );\n }\n }\n\n /** All contributors that observed a logical node, ordered by observation time. */\n getContributors(globalId: string): Contributor[] {\n const rows = this.db.prepare(\n 'SELECT * FROM node_contributors WHERE global_id = ? ORDER BY at',\n ).all(globalId) as Record<string, unknown>[];\n return rows.map((r) => {\n const v = ContributorRowSchema.parse(r);\n return {\n machineId: v.machine_id,\n hostname: v.hostname,\n user: v.user,\n organization: v.organization ?? undefined,\n at: v.at,\n confidence: v.confidence,\n };\n });\n }\n\n getNodes(sessionId: string, opts?: { limit?: number; offset?: number }): NodeRow[] {\n let sql = 'SELECT * FROM nodes WHERE session_id = ?';\n if (opts?.limit) {\n sql += ` LIMIT ${opts.limit}`;\n if (opts.offset) sql += ` OFFSET ${opts.offset}`;\n }\n const rows = this.db.prepare(sql).all(sessionId) as Record<string, unknown>[];\n return rows.map(r => this.mapNode(r));\n }\n\n getNodeCount(sessionId: string): number {\n const row = this.db.prepare('SELECT COUNT(*) as cnt FROM nodes WHERE session_id = ?').get(sessionId) as { cnt: number };\n return row.cnt;\n }\n\n private mapNode(r: Record<string, unknown>): NodeRow {\n const v = NodeRowSchema.parse(r);\n return {\n id: v.id,\n sessionId: v.session_id,\n type: v.type,\n name: v.name,\n discoveredVia: v.discovered_via ?? '',\n discoveredAt: v.discovered_at,\n depth: v.depth,\n confidence: v.confidence,\n metadata: safeJsonParse<Record<string, unknown>>(v.metadata, {}),\n tags: safeJsonParse<string[]>(v.tags, []),\n pathId: v.path_id ?? undefined,\n domain: v.domain ?? undefined,\n subDomain: v.sub_domain ?? undefined,\n qualityScore: v.quality_score ?? undefined,\n owner: v.owner ?? undefined,\n cost: v.cost ? safeJsonParse<CostEntry | undefined>(v.cost, undefined) : undefined,\n globalId: v.global_id ?? undefined,\n contentHash: v.content_hash ?? undefined,\n };\n }\n\n /**\n * Update only the cost/owner of an existing node, without touching any other\n * field (unlike upsertNode's INSERT OR REPLACE) — the idempotent enrichment\n * primitive (3.3). `undefined` leaves a field unchanged; `null` clears it.\n * No-op (returns false) if the node is absent. Cost is re-validated before write.\n */\n enrichNodeAttribution(sessionId: string, nodeId: string, attr: NodeAttribution): boolean {\n const sets: string[] = [];\n const vals: unknown[] = [];\n if (attr.owner !== undefined) {\n sets.push('owner = ?');\n vals.push(attr.owner == null ? null : sanitizeUntrusted(attr.owner));\n }\n if (attr.cost !== undefined) {\n sets.push('cost = ?');\n vals.push(attr.cost == null ? null : JSON.stringify(CostEntrySchema.parse(attr.cost)));\n }\n if (sets.length === 0) return false;\n const info = this.db.prepare(\n `UPDATE nodes SET ${sets.join(', ')} WHERE session_id = ? AND id = ?`,\n ).run(...vals, sessionId, nodeId);\n return info.changes > 0;\n }\n\n deleteNode(sessionId: string, nodeId: string): void {\n this.db.prepare('DELETE FROM nodes WHERE session_id = ? AND id = ?').run(sessionId, nodeId);\n // Remove orphaned edges\n this.db.prepare(\n 'DELETE FROM edges WHERE session_id = ? AND (source_id = ? OR target_id = ?)'\n ).run(sessionId, nodeId, nodeId);\n }\n\n // ── Edges ───────────────────────────────\n\n insertEdge(sessionId: string, edge: DiscoveryEdge): void {\n const id = crypto.randomUUID();\n const tenant = this.tenantOf(sessionId);\n this.db.prepare(`\n INSERT OR IGNORE INTO edges\n (id, session_id, source_id, target_id, relationship, evidence, confidence, discovered_at, tenant)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)\n `).run(\n id, sessionId, edge.sourceId, edge.targetId,\n edge.relationship, sanitizeUntrusted(edge.evidence), edge.confidence,\n new Date().toISOString(), tenant,\n );\n }\n\n /**\n * Delete every edge matching the logical key (source, target, relationship)\n * within a session. `insertEdge` writes a random PK, so logical identity is the\n * only way to prune an edge that disappeared while both endpoints survived.\n */\n deleteEdgeByKey(sessionId: string, sourceId: string, targetId: string, relationship: string): void {\n this.db.prepare(\n 'DELETE FROM edges WHERE session_id = ? AND source_id = ? AND target_id = ? AND relationship = ?'\n ).run(sessionId, sourceId, targetId, relationship);\n }\n\n /**\n * Apply a precomputed {@link TopologyDelta} to one session in a single\n * transaction (2.1 incremental discovery): prune removed nodes (cascading their\n * edges via {@link deleteNode}), upsert added/changed nodes, delete removed edges\n * by logical key, insert added edges, and stamp `last_scanned_at`. Unchanged rows\n * are left untouched (stable `discovered_at`).\n *\n * `attribution` (2.9) is forwarded to every added/changed node's upsert so a\n * rescan keeps/appends the running machine's contributor instead of leaving it\n * out. Unchanged nodes are not re-upserted, so their existing contributors\n * survive the rescan. `NodeRow extends DiscoveryNode`, so the delta rows pass\n * straight into `upsertNode` with no mapper.\n */\n applyTopologyDelta(sessionId: string, delta: TopologyDelta, attribution?: Contributor): void {\n const apply = this.db.transaction(() => {\n for (const n of delta.nodes.removed) this.deleteNode(sessionId, n.id); // cascades orphan edges\n for (const n of delta.nodes.added) {\n this.upsertNode(sessionId, n, n.depth, attribution ? { ...attribution, confidence: n.confidence } : undefined);\n }\n for (const c of delta.nodes.changed) {\n this.upsertNode(sessionId, c.after, c.after.depth, attribution ? { ...attribution, confidence: c.after.confidence } : undefined);\n }\n for (const e of delta.edges.removed) this.deleteEdgeByKey(sessionId, e.sourceId, e.targetId, e.relationship);\n for (const e of delta.edges.added) this.insertEdge(sessionId, e);\n this.touchSession(sessionId);\n });\n apply();\n }\n\n getEdges(sessionId: string, opts?: { limit?: number; offset?: number }): EdgeRow[] {\n let sql = 'SELECT * FROM edges WHERE session_id = ?';\n if (opts?.limit) {\n sql += ` LIMIT ${opts.limit}`;\n if (opts.offset) sql += ` OFFSET ${opts.offset}`;\n }\n const rows = this.db.prepare(sql).all(sessionId) as Record<string, unknown>[];\n return rows.map(r => {\n const v = EdgeRowSchema.parse(r);\n return {\n id: v.id,\n sessionId: v.session_id,\n sourceId: v.source_id,\n targetId: v.target_id,\n relationship: v.relationship,\n evidence: v.evidence ?? '',\n confidence: v.confidence,\n discoveredAt: v.discovered_at,\n };\n });\n }\n\n // ── Events ──────────────────────────────\n\n insertEvent(\n sessionId: string,\n event: Pick<EventRow, 'eventType' | 'process' | 'pid' | 'target' | 'targetType' | 'port'>\n & Partial<Pick<EventRow, 'command' | 'resultBytes'>>,\n taskId?: string,\n /** Authenticated actor (4.5 RBAC) — stamped into the audit trail when present. */\n actor?: { subject: string; role: string; tenant: string },\n ): void {\n const id = crypto.randomUUID();\n const tenant = this.tenantOf(sessionId);\n this.db.prepare(`\n INSERT INTO activity_events\n (id, session_id, task_id, timestamp, event_type, process, pid, target, target_type, port, command, result_bytes, tenant, actor_subject, actor_role, actor_tenant)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `).run(\n id, sessionId, taskId ?? null, new Date().toISOString(),\n event.eventType, event.process, event.pid,\n event.target ?? null, event.targetType ?? null, event.port ?? null,\n event.command ?? null, event.resultBytes ?? null, tenant,\n actor?.subject ?? null, actor?.role ?? null, actor?.tenant ?? null,\n );\n }\n\n // ── RBAC credential store (4.5) ─────────────\n\n /** Number of stored credentials. `0` ⇒ no RBAC configured (fall back to shared/loopback). */\n countCredentials(): number {\n return (this.db.prepare('SELECT COUNT(*) AS n FROM auth_credentials').get() as { n: number }).n;\n }\n\n /** Look up a credential by its sha256 token hash. */\n findCredentialByHash(tokenHash: string): CredentialRecord | undefined {\n const r = this.db.prepare('SELECT * FROM auth_credentials WHERE token_hash = ?').get(tokenHash) as Record<string, unknown> | undefined;\n if (!r) return undefined;\n return {\n tokenHash: r['token_hash'] as string,\n subject: r['subject'] as string,\n tenant: r['tenant'] as string,\n role: r['role'] as Role,\n createdAt: r['created_at'] as string,\n };\n }\n\n /** Upsert a credential (idempotent on the token hash). Stores only the hash, never the raw token. */\n addCredential(rec: { tokenHash: string; subject: string; tenant: string; role: string }): void {\n this.db.prepare(`\n INSERT INTO auth_credentials (token_hash, subject, tenant, role, created_at)\n VALUES (?, ?, ?, ?, ?)\n ON CONFLICT(token_hash) DO UPDATE SET subject = excluded.subject, tenant = excluded.tenant, role = excluded.role\n `).run(rec.tokenHash, rec.subject, rec.tenant, rec.role, new Date().toISOString());\n }\n\n /** List all credentials (token hashes only — the raw token is unrecoverable). */\n listCredentials(): Array<CredentialRecord> {\n const rows = this.db.prepare('SELECT * FROM auth_credentials ORDER BY created_at').all() as Record<string, unknown>[];\n return rows.map((r) => ({\n tokenHash: r['token_hash'] as string,\n subject: r['subject'] as string,\n tenant: r['tenant'] as string,\n role: r['role'] as Role,\n createdAt: r['created_at'] as string,\n }));\n }\n\n /** Revoke every credential for a subject. Returns the number removed. */\n revokeCredentialsBySubject(subject: string): number {\n return this.db.prepare('DELETE FROM auth_credentials WHERE subject = ?').run(subject).changes;\n }\n\n getEvents(sessionId: string, since?: string): EventRow[] {\n const rows = since\n ? this.db.prepare('SELECT * FROM activity_events WHERE session_id = ? AND timestamp > ? ORDER BY timestamp').all(sessionId, since) as Record<string, unknown>[]\n : this.db.prepare('SELECT * FROM activity_events WHERE session_id = ? ORDER BY timestamp').all(sessionId) as Record<string, unknown>[];\n return rows.map(r => {\n const v = EventRowSchema.parse(r);\n return {\n id: v.id,\n sessionId: v.session_id,\n taskId: v.task_id ?? undefined,\n timestamp: v.timestamp,\n eventType: v.event_type,\n process: v.process,\n pid: v.pid,\n target: v.target ?? undefined,\n targetType: v.target_type ?? undefined,\n port: v.port ?? undefined,\n durationMs: v.duration_ms ?? undefined,\n command: v.command ?? undefined,\n resultBytes: v.result_bytes ?? undefined,\n };\n });\n }\n\n // ── Tasks ───────────────────────────────\n\n startTask(sessionId: string, description?: string): string {\n const id = crypto.randomUUID();\n const tenant = this.tenantOf(sessionId);\n this.db.prepare(`\n INSERT INTO tasks (id, session_id, description, started_at, steps, involved_services, status, tenant)\n VALUES (?, ?, ?, ?, '[]', '[]', 'active', ?)\n `).run(id, sessionId, description ?? null, new Date().toISOString(), tenant);\n return id;\n }\n\n endCurrentTask(sessionId: string): void {\n this.db.prepare(`\n UPDATE tasks SET status = 'completed', completed_at = ?\n WHERE session_id = ? AND status = 'active'\n `).run(new Date().toISOString(), sessionId);\n }\n\n updateTaskDescription(sessionId: string, description: string): void {\n this.db.prepare(`\n UPDATE tasks SET description = ?\n WHERE session_id = ? AND status = 'active'\n `).run(description, sessionId);\n }\n\n getActiveTask(sessionId: string): TaskRow | undefined {\n const row = this.db.prepare(\n \"SELECT * FROM tasks WHERE session_id = ? AND status = 'active' LIMIT 1\"\n ).get(sessionId) as Record<string, unknown> | undefined;\n return row ? this.mapTask(row) : undefined;\n }\n\n getTasks(sessionId: string): TaskRow[] {\n const rows = this.db.prepare('SELECT * FROM tasks WHERE session_id = ? ORDER BY started_at').all(sessionId) as Record<string, unknown>[];\n return rows.map(r => this.mapTask(r));\n }\n\n private mapTask(r: Record<string, unknown>): TaskRow {\n const v = TaskRowSchema.parse(r);\n return {\n id: v.id,\n sessionId: v.session_id,\n description: v.description ?? undefined,\n startedAt: v.started_at,\n completedAt: v.completed_at ?? undefined,\n steps: v.steps,\n involvedServices: v.involved_services,\n status: v.status,\n };\n }\n\n // ── Workflows ───────────────────────────\n\n insertWorkflow(sessionId: string, data: Omit<WorkflowRow, 'id'>): void {\n const id = crypto.randomUUID();\n const tenant = this.tenantOf(sessionId);\n this.db.prepare(`\n INSERT INTO workflows\n (id, session_id, name, pattern, task_ids, occurrences,\n first_seen, last_seen, avg_duration_ms, involved_services, tenant)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `).run(\n id, sessionId, data.name ?? null, data.pattern,\n data.taskIds, data.occurrences,\n data.firstSeen, data.lastSeen, data.avgDurationMs,\n data.involvedServices, tenant,\n );\n }\n\n getWorkflows(sessionId: string): WorkflowRow[] {\n const rows = this.db.prepare('SELECT * FROM workflows WHERE session_id = ?').all(sessionId) as Record<string, unknown>[];\n return rows.map(r => {\n const v = WorkflowRowSchema.parse(r);\n return {\n id: v.id,\n sessionId: v.session_id,\n name: v.name ?? undefined,\n pattern: v.pattern,\n taskIds: v.task_ids,\n occurrences: v.occurrences,\n firstSeen: v.first_seen,\n lastSeen: v.last_seen,\n avgDurationMs: v.avg_duration_ms ?? 0,\n involvedServices: v.involved_services,\n };\n });\n }\n\n // ── Connections (user-created hex map links) ─────────────────────────────\n\n upsertConnection(sessionId: string, conn: Omit<Connection, 'id'>): string {\n // Idempotent: same source+target+type = same connection\n const existing = this.db.prepare(\n 'SELECT id FROM connections WHERE session_id = ? AND source_asset_id = ? AND target_asset_id = ?'\n ).get(sessionId, conn.sourceAssetId, conn.targetAssetId) as { id: string } | undefined;\n if (existing) return existing.id;\n const id = crypto.randomUUID();\n const tenant = this.tenantOf(sessionId);\n this.db.prepare(`\n INSERT INTO connections (id, session_id, source_asset_id, target_asset_id, type, created_at, tenant)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n `).run(id, sessionId, conn.sourceAssetId, conn.targetAssetId, conn.type ?? null, new Date().toISOString(), tenant);\n return id;\n }\n\n getConnections(sessionId: string): ConnectionRow[] {\n const rows = this.db.prepare('SELECT * FROM connections WHERE session_id = ?').all(sessionId) as Record<string, unknown>[];\n return rows.map(r => {\n const v = ConnectionRowSchema.parse(r);\n return {\n id: v.id,\n sessionId: v.session_id,\n sourceAssetId: v.source_asset_id,\n targetAssetId: v.target_asset_id,\n type: v.type ?? undefined,\n createdAt: v.created_at,\n };\n });\n }\n\n deleteConnection(sessionId: string, connectionId: string): void {\n this.db.prepare('DELETE FROM connections WHERE session_id = ? AND id = ?').run(sessionId, connectionId);\n }\n\n // ── Approvals ───────────────────────────\n\n setApproval(pattern: string, action: 'save' | 'ignore' | 'auto'): void {\n this.db.prepare(`\n INSERT OR REPLACE INTO node_approvals (pattern, action, created_at) VALUES (?, ?, ?)\n `).run(pattern, action, new Date().toISOString());\n }\n\n getApproval(pattern: string): string | undefined {\n const row = this.db.prepare('SELECT action FROM node_approvals WHERE pattern = ?').get(pattern) as { action: string } | undefined;\n return row?.action;\n }\n\n // ── Sharing policy (2.10 consent) ───────────────────────────────────────────\n\n /**\n * Set (or replace) the sharing level for a pattern. The `'*'` pattern is the\n * global default; any other pattern is an override (glob over the node id).\n * Validated via {@link SharingLevelSchema} before write; `created_at` is ISO UTC.\n */\n setSharingLevel(pattern: string, level: SharingLevel): void {\n const valid = SharingLevelSchema.parse(level);\n this.db.prepare(\n 'INSERT OR REPLACE INTO sharing_policy (pattern, level, created_at) VALUES (?, ?, ?)',\n ).run(pattern, valid, new Date().toISOString());\n }\n\n /**\n * The full sharing policy: the `'*'` row resolves to `defaultLevel` (`'none'`\n * when absent — the opt-in floor), every other row becomes an override. The\n * glob-precedence resolution itself lives in `src/sharing.ts` so it is unit\n * testable in isolation; this returns the raw policy it consumes.\n */\n getSharingPolicy(): SharingPolicy {\n const rows = this.db.prepare('SELECT pattern, level FROM sharing_policy').all() as Array<{ pattern: string; level: string }>;\n let defaultLevel: SharingLevel = 'none';\n const overrides: { pattern: string; level: SharingLevel }[] = [];\n for (const r of rows) {\n const level = SharingLevelSchema.parse(r.level);\n if (r.pattern === '*') defaultLevel = level;\n else overrides.push({ pattern: r.pattern, level });\n }\n return { defaultLevel, overrides };\n }\n\n /** Remove a pattern override. The global default (`'*'`) cannot be cleared this way. */\n clearSharingOverride(pattern: string): void {\n this.db.prepare(\"DELETE FROM sharing_policy WHERE pattern = ? AND pattern != '*'\").run(pattern);\n }\n\n // ── Pseudonym reversal map (2.10 admin-reversible anonymization) ─────────────\n\n /**\n * Persist the encrypted plaintext behind a pseudonym token. Idempotent: the\n * token is deterministic, so repeated writes `INSERT OR REPLACE` and never grow\n * the table. `ciphertext` is base64(iv ‖ tag ‖ AES-256-GCM(plaintext)).\n */\n saveReversal(token: string, ciphertext: string): void {\n this.db.prepare(\n 'INSERT OR REPLACE INTO pseudonym_reversal (token, ciphertext, created_at) VALUES (?, ?, ?)',\n ).run(token, ciphertext, new Date().toISOString());\n }\n\n /** Read the stored ciphertext for a pseudonym token (admin reversal path). */\n getReversal(token: string): string | undefined {\n const row = this.db.prepare('SELECT ciphertext FROM pseudonym_reversal WHERE token = ?').get(token) as { ciphertext: string } | undefined;\n return row?.ciphertext;\n }\n\n // ── Pending-review share queue (2.11 central-DB sync) ────────────────────────\n\n /**\n * Enqueue one proposed share item. Idempotent via `INSERT OR IGNORE` on the\n * `content_hash` PK: re-classifying the same (transformed) item never duplicates\n * a row nor resets an existing decision. `payload` is the already-policy-\n * transformed projection (the exact bytes a push would send) — never raw node\n * data for `anonymized`/`none` items.\n */\n enqueuePending(item: {\n contentHash: string;\n sessionId: string;\n nodeId?: string;\n kind: 'node' | 'edge';\n payload: unknown;\n status: PendingStatus;\n decidedBy?: 'user' | 'rule';\n }): void {\n const now = new Date().toISOString();\n const decided = item.status === 'pending' ? null : now;\n this.db.prepare(`\n INSERT OR IGNORE INTO pending_shares\n (content_hash, session_id, node_id, kind, payload, status, decided_by, created_at, decided_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)\n `).run(\n item.contentHash, item.sessionId, item.nodeId ?? null, item.kind,\n JSON.stringify(item.payload), item.status, item.decidedBy ?? null, now, decided,\n );\n }\n\n /** Queued share items, optionally filtered by status and/or session. */\n getPendingShares(filter?: { status?: PendingStatus; sessionId?: string }): PendingShareRow[] {\n const clauses: string[] = [];\n const params: unknown[] = [];\n if (filter?.status) { clauses.push('status = ?'); params.push(filter.status); }\n if (filter?.sessionId) { clauses.push('session_id = ?'); params.push(filter.sessionId); }\n const where = clauses.length ? `WHERE ${clauses.join(' AND ')} ` : '';\n const rows = this.db.prepare(`SELECT * FROM pending_shares ${where}ORDER BY rowid`).all(...params) as Record<string, unknown>[];\n return rows.map((r) => this.mapPendingShare(r));\n }\n\n /** Queue size by status (every status key present, zero-filled). */\n countPendingByStatus(): Record<PendingStatus, number> {\n const out: Record<PendingStatus, number> = { pending: 0, approved: 0, shared: 0, withheld: 0 };\n const rows = this.db.prepare('SELECT status, COUNT(*) c FROM pending_shares GROUP BY status').all() as Array<{ status: string; c: number }>;\n for (const r of rows) {\n if (r.status in out) out[r.status as PendingStatus] = r.c;\n }\n return out;\n }\n\n /** `content_hash` values already pushed (status `shared`) — for re-share suppression. */\n getSharedHashes(): Set<string> {\n const rows = this.db.prepare(\"SELECT content_hash FROM pending_shares WHERE status = 'shared'\").all() as Array<{ content_hash: string }>;\n return new Set(rows.map((r) => r.content_hash));\n }\n\n /**\n * Transition one queued item. Stamps `decided_at` on any non-`pending` status and\n * `shared_at` when moving to `shared`. `decidedBy` records the actor (`'user'` or\n * `'rule'`) for the audit trail.\n */\n setPendingStatus(contentHash: string, status: PendingStatus, decidedBy?: 'user' | 'rule'): void {\n const now = new Date().toISOString();\n const sharedAt = status === 'shared' ? now : null;\n this.db.prepare(`\n UPDATE pending_shares\n SET status = ?, decided_by = COALESCE(?, decided_by), decided_at = ?, shared_at = COALESCE(?, shared_at)\n WHERE content_hash = ?\n `).run(status, decidedBy ?? null, now, sharedAt, contentHash);\n }\n\n /** Approved items cleared to push (FIFO), optionally capped by `limit`. */\n getApprovedShares(limit?: number): PendingShareRow[] {\n let sql = \"SELECT * FROM pending_shares WHERE status = 'approved' ORDER BY rowid\";\n if (limit) sql += ` LIMIT ${Math.max(1, Math.floor(limit))}`;\n const rows = this.db.prepare(sql).all() as Record<string, unknown>[];\n return rows.map((r) => this.mapPendingShare(r));\n }\n\n private mapPendingShare(r: Record<string, unknown>): PendingShareRow {\n const v = PendingShareRowSchema.parse(r);\n return {\n contentHash: v.content_hash,\n sessionId: v.session_id,\n nodeId: v.node_id ?? undefined,\n kind: v.kind,\n payload: safeJsonParse<unknown>(v.payload, null),\n status: v.status,\n decidedBy: v.decided_by ?? undefined,\n createdAt: v.created_at,\n decidedAt: v.decided_at ?? undefined,\n sharedAt: v.shared_at ?? undefined,\n };\n }\n\n // ── Pruning ──────────────────────────────\n\n /**\n * Delete a session and all its associated data (nodes, edges, events, tasks, workflows, connections).\n */\n deleteSession(sessionId: string): void {\n this.db.prepare('DELETE FROM connections WHERE session_id = ?').run(sessionId);\n this.db.prepare('DELETE FROM workflows WHERE session_id = ?').run(sessionId);\n this.db.prepare('DELETE FROM activity_events WHERE session_id = ?').run(sessionId);\n this.db.prepare('DELETE FROM tasks WHERE session_id = ?').run(sessionId);\n this.db.prepare('DELETE FROM edges WHERE session_id = ?').run(sessionId);\n this.db.prepare('DELETE FROM nodes WHERE session_id = ?').run(sessionId);\n this.db.prepare('DELETE FROM sessions WHERE id = ?').run(sessionId);\n }\n\n /**\n * Prune sessions older than the given ISO date string. Returns count of deleted sessions.\n */\n pruneSessions(olderThan: string): number {\n const rows = this.db.prepare(\n 'SELECT id FROM sessions WHERE started_at < ?'\n ).all(olderThan) as { id: string }[];\n for (const row of rows) {\n this.deleteSession(row.id);\n }\n return rows.length;\n }\n\n // ── Graph queries (read-only context layer) ─────────────────────────────────\n\n /** Fetch a single node by id within a session. */\n getNode(sessionId: string, nodeId: string): NodeRow | undefined {\n const row = this.db.prepare('SELECT * FROM nodes WHERE session_id = ? AND id = ?')\n .get(sessionId, nodeId) as Record<string, unknown> | undefined;\n return row ? this.mapNode(row) : undefined;\n }\n\n /** Batch-fetch nodes by id, keyed for O(1) lookup. Chunked to stay under SQLite's bind-variable limit. */\n getNodesByIds(sessionId: string, ids: readonly string[]): Map<string, NodeRow> {\n const out = new Map<string, NodeRow>();\n for (let i = 0; i < ids.length; i += 900) {\n const chunk = ids.slice(i, i + 900);\n const placeholders = chunk.map(() => '?').join(',');\n const rows = this.db.prepare(\n `SELECT * FROM nodes WHERE session_id = ? AND id IN (${placeholders})`,\n ).all(sessionId, ...chunk) as Record<string, unknown>[];\n for (const r of rows) { const n = this.mapNode(r); out.set(n.id, n); }\n }\n return out;\n }\n\n /** Fetch all nodes of one or more types. */\n getNodesByType(sessionId: string, types: readonly string[]): NodeRow[] {\n if (types.length === 0) return [];\n const placeholders = types.map(() => '?').join(',');\n const rows = this.db.prepare(\n `SELECT * FROM nodes WHERE session_id = ? AND type IN (${placeholders})`,\n ).all(sessionId, ...types) as Record<string, unknown>[];\n return rows.map(r => this.mapNode(r));\n }\n\n /**\n * Lexical search over node id, name, domain, sub-domain and tags.\n * Case-insensitive substring match — the deterministic fallback for semantic search.\n */\n searchNodes(sessionId: string, query: string, opts?: { types?: readonly string[]; limit?: number }): NodeRow[] {\n const q = `%${query.trim().toLowerCase()}%`;\n const params: unknown[] = [sessionId, q, q, q, q, q];\n let sql = `\n SELECT * FROM nodes\n WHERE session_id = ?\n AND (\n lower(id) LIKE ? OR lower(name) LIKE ?\n OR lower(COALESCE(domain, '')) LIKE ?\n OR lower(COALESCE(sub_domain, '')) LIKE ?\n OR lower(tags) LIKE ?\n )`;\n if (opts?.types && opts.types.length > 0) {\n sql += ` AND type IN (${opts.types.map(() => '?').join(',')})`;\n params.push(...opts.types);\n }\n sql += ' ORDER BY confidence DESC';\n if (opts?.limit) sql += ` LIMIT ${Math.max(1, Math.floor(opts.limit))}`;\n const rows = this.db.prepare(sql).all(...params) as Record<string, unknown>[];\n return rows.map(r => this.mapNode(r));\n }\n\n /**\n * Traverse the dependency graph from a node using a recursive CTE with a\n * path-based cycle guard. `downstream` follows source→target (what the node\n * depends on / points to); `upstream` follows target→source (what depends on it).\n */\n getDependencies(\n sessionId: string,\n nodeId: string,\n opts: { direction?: 'downstream' | 'upstream' | 'both'; maxDepth?: number } = {},\n ): TraversalResult {\n const direction = opts.direction ?? 'downstream';\n const maxDepth = Math.max(1, Math.min(opts.maxDepth ?? 8, 64));\n const root = this.getNode(sessionId, nodeId);\n\n const depthById = new Map<string, number>();\n const collect = (dir: 'downstream' | 'upstream'): void => {\n // SEP = newline; node ids never contain newlines, so the path guard is collision-free.\n const [from, to] = dir === 'downstream' ? ['source_id', 'target_id'] : ['target_id', 'source_id'];\n const sql = `\n WITH RECURSIVE walk(node_id, depth, path) AS (\n SELECT ?, 0, char(10) || ? || char(10)\n UNION ALL\n SELECT e.${to}, w.depth + 1, w.path || e.${to} || char(10)\n FROM edges e JOIN walk w ON e.${from} = w.node_id\n WHERE e.session_id = ?\n AND w.depth < ?\n AND instr(w.path, char(10) || e.${to} || char(10)) = 0\n )\n SELECT node_id, MIN(depth) AS depth FROM walk WHERE node_id != ? GROUP BY node_id`;\n const rows = this.db.prepare(sql).all(nodeId, nodeId, sessionId, maxDepth, nodeId) as Array<{ node_id: string; depth: number }>;\n for (const r of rows) {\n const prev = depthById.get(r.node_id);\n if (prev === undefined || r.depth < prev) depthById.set(r.node_id, r.depth);\n }\n };\n\n if (direction === 'both') { collect('downstream'); collect('upstream'); }\n else collect(direction);\n\n const byId = this.getNodesByIds(sessionId, [...depthById.keys()]);\n const nodes = [...depthById.entries()]\n .map(([id, depth]) => { const n = byId.get(id); return n ? { ...n, depth } : undefined; })\n .filter((n): n is NodeRow & { depth: number } => n !== undefined)\n .sort((a, b) => a.depth - b.depth);\n\n // Edges that lie within the reached subgraph (including the root).\n const reachable = new Set<string>([nodeId, ...depthById.keys()]);\n const edges = this.getEdges(sessionId).filter(e => reachable.has(e.sourceId) && reachable.has(e.targetId));\n\n return { root, direction, maxDepth, nodes, edges };\n }\n\n /** Lightweight aggregate index of the whole topology — the progressive-disclosure summary. */\n getGraphSummary(sessionId: string): GraphSummary {\n const totals = {\n nodes: (this.db.prepare('SELECT COUNT(*) c FROM nodes WHERE session_id = ?').get(sessionId) as { c: number }).c,\n edges: (this.db.prepare('SELECT COUNT(*) c FROM edges WHERE session_id = ?').get(sessionId) as { c: number }).c,\n };\n const byType: Record<string, number> = {};\n for (const r of this.db.prepare('SELECT type, COUNT(*) c FROM nodes WHERE session_id = ? GROUP BY type').all(sessionId) as Array<{ type: string; c: number }>) {\n byType[r.type] = r.c;\n }\n const byDomain: Record<string, number> = {};\n for (const r of this.db.prepare(\"SELECT COALESCE(domain, '(none)') d, COUNT(*) c FROM nodes WHERE session_id = ? GROUP BY d\").all(sessionId) as Array<{ d: string; c: number }>) {\n byDomain[r.d] = r.c;\n }\n const byRelationship: Record<string, number> = {};\n for (const r of this.db.prepare('SELECT relationship rel, COUNT(*) c FROM edges WHERE session_id = ? GROUP BY rel').all(sessionId) as Array<{ rel: string; c: number }>) {\n byRelationship[r.rel] = r.c;\n }\n // 3.6: degree for ALL nodes in one aggregate (the old top-10 query dropped the\n // LIMIT). The same rows feed `topConnected` and the per-node degree map the\n // anomaly engine consumes — no second scan.\n const degreeRows = this.db.prepare(`\n SELECT n.id, n.name, n.type, n.confidence, COUNT(e.id) AS degree\n FROM nodes n\n LEFT JOIN edges e ON e.session_id = n.session_id AND (e.source_id = n.id OR e.target_id = n.id)\n WHERE n.session_id = ?\n GROUP BY n.id, n.name, n.type\n `).all(sessionId) as Array<{ id: string; name: string; type: string; confidence: number; degree: number }>;\n\n const degree = new Map<string, number>();\n for (const r of degreeRows) degree.set(r.id, r.degree);\n\n // Reproduces the old `degree DESC, confidence DESC` order + a stable id tiebreaker.\n const topConnected = [...degreeRows]\n .sort((a, b) => b.degree - a.degree || b.confidence - a.confidence || a.id.localeCompare(b.id))\n .slice(0, 10)\n .map(({ id, name, type, degree }) => ({ id, name, type, degree }));\n\n const anomalies = this.anomalyEnabled\n ? detectAnomalies(this.getNodes(sessionId), degree, this.anomalyThresholds)\n : [];\n\n // 2.9: distinct machines that contributed to this session's nodes (by global_id).\n const contributors = (this.db.prepare(`\n SELECT COUNT(DISTINCT c.machine_id) AS n\n FROM node_contributors c\n JOIN nodes n ON n.global_id = c.global_id\n WHERE n.session_id = ?\n `).get(sessionId) as { n: number }).n;\n\n // 3.3 FinOps rollups — bucketed by (currency, period) so mixed currencies/periods\n // are never silently summed; only cost-bearing rows are scanned.\n const costByDomain = this.db.prepare(`\n SELECT COALESCE(domain, '(none)') domain,\n json_extract(cost, '$.currency') currency,\n json_extract(cost, '$.period') period,\n SUM(json_extract(cost, '$.amount')) total,\n COUNT(*) nodes\n FROM nodes\n WHERE session_id = ? AND cost IS NOT NULL\n GROUP BY domain, currency, period\n ORDER BY total DESC\n `).all(sessionId) as GraphSummary['costByDomain'];\n const costByOwner = this.db.prepare(`\n SELECT COALESCE(owner, '(unowned)') owner,\n json_extract(cost, '$.currency') currency,\n json_extract(cost, '$.period') period,\n SUM(json_extract(cost, '$.amount')) total,\n COUNT(*) nodes\n FROM nodes\n WHERE session_id = ? AND cost IS NOT NULL\n GROUP BY owner, currency, period\n ORDER BY total DESC\n `).all(sessionId) as GraphSummary['costByOwner'];\n const withCost = (this.db.prepare(\n 'SELECT COUNT(*) c FROM nodes WHERE session_id = ? AND cost IS NOT NULL',\n ).get(sessionId) as { c: number }).c;\n\n return {\n sessionId, totals, nodesByType: byType, nodesByDomain: byDomain,\n edgesByRelationship: byRelationship, topConnected, anomalies, contributors,\n costByDomain, costByOwner, costCoverage: { withCost, total: totals.nodes },\n };\n }\n\n // ── Central collector store (2.12) ──────────────────────────────────────────\n\n /**\n * Resolve (creating once) the synthetic collector session that owns every\n * central node/edge for a tenant. Central nodes are merged by `(org, global_id)`,\n * not by session, so they live under a single deterministic session id\n * (`central:{org}`) — this satisfies the existing `(id, session_id)` node PK and\n * the `session_id` foreign key without a destructive schema change. Idempotent.\n */\n ensureCentralSession(org: string): string {\n const tenant = normalizeTenant(org);\n const id = `central:${tenant}`;\n const existing = this.db.prepare('SELECT id FROM sessions WHERE id = ?').get(id) as { id: string } | undefined;\n if (existing) return id;\n this.db.prepare(\n `INSERT INTO sessions (id, mode, started_at, config, tenant, organization)\n VALUES (?, 'discover', ?, '{}', ?, ?)`\n ).run(id, new Date().toISOString(), tenant, org);\n return id;\n }\n\n /**\n * Find an existing central node within a tenant by its primary identity\n * (`global_id`), returning its stored `id` so a merge keeps a single row.\n */\n findCentralNodeIdByGlobalId(org: string, gid: string): string | undefined {\n const tenant = normalizeTenant(org);\n const row = this.db.prepare(\n 'SELECT id FROM nodes WHERE tenant = ? AND global_id = ? LIMIT 1',\n ).get(tenant, gid) as { id: string } | undefined;\n return row?.id;\n }\n\n /**\n * Secondary merge lookup: an existing central node in the tenant whose\n * `content_hash` matches (catches `id` drift between machines for the same\n * logical resource). Returns its stored `id` and `global_id`.\n */\n findCentralNodeByContentHash(org: string, ch: string): { id: string; globalId: string } | undefined {\n const tenant = normalizeTenant(org);\n const row = this.db.prepare(\n 'SELECT id, global_id FROM nodes WHERE tenant = ? AND content_hash = ? LIMIT 1',\n ).get(tenant, ch) as { id: string; global_id: string | null } | undefined;\n if (!row || row.global_id == null) return undefined;\n return { id: row.id, globalId: row.global_id };\n }\n\n /**\n * Merge one incoming node into the central store for a tenant and append the\n * contributor (2.12). Resolves identity by `(tenant, global_id)` primary, then\n * `(tenant, content_hash)` secondary; on a hit it keeps the existing row's id\n * (so the logical node stays a single row), unions tags, keeps the higher\n * confidence, and merges metadata (incoming values win on key conflict). The\n * incoming `globalId`/`contentHash` are precomputed by the merge core so they\n * are consistent with what the lookups used. Returns whether a new row was\n * created or an existing one was merged. Runs in one transaction.\n */\n upsertCentralNode(\n org: string,\n node: DiscoveryNode,\n identity: { globalId: string; contentHash: string },\n contributor: Contributor,\n ): 'created' | 'merged' {\n const tenant = normalizeTenant(org);\n const sessionId = this.ensureCentralSession(tenant);\n const txn = this.db.transaction((): 'created' | 'merged' => {\n // Resolve the merge target: primary by global_id, secondary by content_hash.\n let targetId = this.findCentralNodeIdByGlobalId(tenant, identity.globalId);\n let outcome: 'created' | 'merged' = 'merged';\n if (!targetId) {\n const byHash = this.findCentralNodeByContentHash(tenant, identity.contentHash);\n targetId = byHash?.id;\n }\n if (!targetId) { targetId = node.id; outcome = 'created'; }\n\n const existing = this.getCentralNode(tenant, sessionId, targetId);\n // Union tags + metadata and keep the higher confidence (mirrors local.ts:44).\n const mergedTags = Array.from(new Set([...(existing?.tags ?? []), ...(node.tags ?? [])]));\n const mergedMeta = { ...(existing?.metadata ?? {}), ...(node.metadata ?? {}) };\n const confidence = Math.max(existing?.confidence ?? 0, node.confidence);\n\n this.db.prepare(`\n INSERT OR REPLACE INTO nodes\n (id, session_id, type, name, discovered_via, discovered_at, depth, confidence, metadata, tags,\n domain, sub_domain, quality_score, tenant, global_id, content_hash)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `).run(\n targetId, sessionId, node.type, sanitizeUntrusted(node.name), node.discoveredVia,\n existing?.discoveredAt ?? new Date().toISOString(), 0, confidence,\n JSON.stringify(sanitizeValue(mergedMeta)),\n JSON.stringify(mergedTags.map(sanitizeUntrusted)),\n node.domain != null ? sanitizeUntrusted(node.domain) : (existing?.domain ?? null),\n node.subDomain != null ? sanitizeUntrusted(node.subDomain) : (existing?.subDomain ?? null),\n node.qualityScore ?? existing?.qualityScore ?? null,\n tenant, identity.globalId, identity.contentHash,\n );\n\n // Append/update the contributor (keyed by (global_id, machine_id)).\n this.db.prepare(`\n INSERT INTO node_contributors (global_id, machine_id, hostname, user, organization, at, confidence)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(global_id, machine_id) DO UPDATE SET\n confidence = MAX(confidence, excluded.confidence),\n at = excluded.at,\n hostname = excluded.hostname,\n user = excluded.user,\n organization = excluded.organization\n `).run(\n identity.globalId, contributor.machineId,\n sanitizeUntrusted(contributor.hostname), sanitizeUntrusted(contributor.user),\n contributor.organization != null ? sanitizeUntrusted(contributor.organization) : tenant,\n contributor.at, contributor.confidence,\n );\n return outcome;\n });\n return txn();\n }\n\n /** Insert an edge into the central store for a tenant (idempotent on logical key). */\n insertCentralEdge(org: string, edge: DiscoveryEdge): void {\n const tenant = normalizeTenant(org);\n const sessionId = this.ensureCentralSession(tenant);\n // Skip a logical duplicate (insertEdge writes a random PK, so INSERT OR IGNORE\n // never dedups by (source,target,relationship) on its own).\n const dup = this.db.prepare(\n 'SELECT 1 FROM edges WHERE tenant = ? AND source_id = ? AND target_id = ? AND relationship = ? LIMIT 1',\n ).get(tenant, edge.sourceId, edge.targetId, edge.relationship);\n if (dup) return;\n this.insertEdge(sessionId, edge);\n }\n\n /** A central node by tenant + stored id (the merge target after identity resolution). */\n getCentralNode(org: string, sessionId: string, nodeId: string): NodeRow | undefined {\n const tenant = normalizeTenant(org);\n const row = this.db.prepare(\n 'SELECT * FROM nodes WHERE tenant = ? AND session_id = ? AND id = ?',\n ).get(tenant, sessionId, nodeId) as Record<string, unknown> | undefined;\n return row ? this.mapNode(row) : undefined;\n }\n\n /** All contributors for a logical (global_id) node across an org. */\n getContributorsByGlobalId(gid: string): Contributor[] {\n return this.getContributors(gid);\n }\n\n /**\n * Org-wide aggregate summary (2.12) — the central analogue of\n * {@link getGraphSummary}, scoped `WHERE tenant = ?` so it merges every machine's\n * contribution into one organization-wide view. Cross-tenant isolation is\n * structural: org A's rows never appear in org B's counts.\n */\n getOrgSummary(org: string): OrgSummary {\n const tenant = normalizeTenant(org);\n const totals = {\n nodes: (this.db.prepare('SELECT COUNT(*) c FROM nodes WHERE tenant = ?').get(tenant) as { c: number }).c,\n edges: (this.db.prepare('SELECT COUNT(*) c FROM edges WHERE tenant = ?').get(tenant) as { c: number }).c,\n };\n const byType: Record<string, number> = {};\n for (const r of this.db.prepare('SELECT type, COUNT(*) c FROM nodes WHERE tenant = ? GROUP BY type').all(tenant) as Array<{ type: string; c: number }>) {\n byType[r.type] = r.c;\n }\n const byDomain: Record<string, number> = {};\n for (const r of this.db.prepare(\"SELECT COALESCE(domain, '(none)') d, COUNT(*) c FROM nodes WHERE tenant = ? GROUP BY d\").all(tenant) as Array<{ d: string; c: number }>) {\n byDomain[r.d] = r.c;\n }\n const byRelationship: Record<string, number> = {};\n for (const r of this.db.prepare('SELECT relationship rel, COUNT(*) c FROM edges WHERE tenant = ? GROUP BY rel').all(tenant) as Array<{ rel: string; c: number }>) {\n byRelationship[r.rel] = r.c;\n }\n const topConnected = (this.db.prepare(`\n SELECT n.id, n.name, n.type, COUNT(e.id) AS degree\n FROM nodes n\n LEFT JOIN edges e ON e.tenant = n.tenant AND (e.source_id = n.id OR e.target_id = n.id)\n WHERE n.tenant = ?\n GROUP BY n.id, n.name, n.type\n ORDER BY degree DESC, n.confidence DESC\n LIMIT 10\n `).all(tenant) as Array<{ id: string; name: string; type: string; degree: number }>);\n // Distinct machines that contributed to this org's nodes (org-wide attribution).\n const contributors = (this.db.prepare(`\n SELECT COUNT(DISTINCT c.machine_id) AS n\n FROM node_contributors c\n JOIN nodes n ON n.global_id = c.global_id\n WHERE n.tenant = ?\n `).get(tenant) as { n: number }).n;\n\n return { org: tenant, totals, nodesByType: byType, nodesByDomain: byDomain, edgesByRelationship: byRelationship, topConnected, contributors };\n }\n\n // ── Stats ───────────────────────────────\n\n getStats(sessionId: string): { nodes: number; edges: number; events: number; tasks: number } {\n const nodes = (this.db.prepare('SELECT COUNT(*) as c FROM nodes WHERE session_id = ?').get(sessionId) as { c: number }).c;\n const edges = (this.db.prepare('SELECT COUNT(*) as c FROM edges WHERE session_id = ?').get(sessionId) as { c: number }).c;\n const events = (this.db.prepare('SELECT COUNT(*) as c FROM activity_events WHERE session_id = ?').get(sessionId) as { c: number }).c;\n const tasks = (this.db.prepare('SELECT COUNT(*) as c FROM tasks WHERE session_id = ?').get(sessionId) as { c: number }).c;\n return { nodes, edges, events, tasks };\n }\n}\n","/**\n * Compliance scoring (3.4) — schemas + types.\n *\n * Rulesets are **declarative data** (a serializable `RuleCheck` expression tree)\n * interpreted by a single trusted engine — never executable predicate code. `field`\n * and `pattern` are closed enums, so a ruleset can neither reach arbitrary node\n * properties nor inject a ReDoS-prone regex. Every bundled ruleset is\n * `RulesetSchema.parse`d at module load (fail-fast on malformed data).\n */\n\nimport { z } from 'zod';\nimport { NODE_TYPES, NODE_TYPE_GROUPS } from '../types.js';\n\n/** Keys of NODE_TYPE_GROUPS — the semantic scopes a rule may target. */\nexport const NODE_TYPE_GROUP_KEYS = Object.keys(NODE_TYPE_GROUPS) as Array<keyof typeof NODE_TYPE_GROUPS>;\n\n/** Which nodes a rule applies to. Empty scope (no groups/types) = all nodes. */\nexport const RuleScopeSchema = z.object({\n groups: z.array(z.enum(NODE_TYPE_GROUP_KEYS as [string, ...string[]])).optional(),\n types: z.array(z.enum(NODE_TYPES)).optional(),\n});\nexport type RuleScope = z.infer<typeof RuleScopeSchema>;\n\n/** Closed set of node fields a condition may read (no prototype/field injection). */\nexport const FieldPathSchema = z.enum([\n 'type', 'name', 'domain', 'subDomain', 'confidence', 'qualityScore',\n 'owner', 'tags', 'metadataKeys', 'metadataValues',\n]);\nexport type FieldPath = z.infer<typeof FieldPathSchema>;\n\n/** Engine-owned named regexes — a ruleset can never supply a raw pattern. */\nexport const PATTERN_NAMES = ['dsn_with_credentials', 'owner_key', 'public_exposure'] as const;\n\nexport const ConditionSchema = z.object({\n field: FieldPathSchema,\n op: z.enum(['present', 'absent', 'lt', 'lte', 'gt', 'gte', 'eq', 'includes', 'matches']),\n value: z.union([z.string(), z.number()]).optional(),\n pattern: z.enum(PATTERN_NAMES).optional(),\n});\nexport type Condition = z.infer<typeof ConditionSchema>;\n\n/** A serializable check: a leaf Condition or an all/any/not combinator. */\nexport type RuleCheck =\n | Condition\n | { all: RuleCheck[] }\n | { any: RuleCheck[] }\n | { not: RuleCheck };\n\nexport const RuleCheckSchema: z.ZodType<RuleCheck> = z.lazy(() =>\n z.union([\n ConditionSchema,\n z.object({ all: z.array(RuleCheckSchema) }),\n z.object({ any: z.array(RuleCheckSchema) }),\n z.object({ not: RuleCheckSchema }),\n ]),\n);\n\nexport const SEVERITIES = ['critical', 'high', 'medium', 'low'] as const;\nexport const SeveritySchema = z.enum(SEVERITIES);\nexport type Severity = z.infer<typeof SeveritySchema>;\n\n/** Severity → score weight (decision #5). */\nexport const SEVERITY_WEIGHT: Record<Severity, number> = { critical: 4, high: 3, medium: 2, low: 1 };\n\nexport const ComplianceRuleSchema = z.object({\n id: z.string(),\n control: z.string().describe('External control id, e.g. \"CIS-1.4\"'),\n framework: z.enum(['CIS', 'SOC2', 'ISO27001', 'baseline']),\n title: z.string(),\n severity: SeveritySchema,\n rationale: z.string(),\n scope: RuleScopeSchema,\n /**\n * Optional applicability predicate (decision #7): a scoped node is only counted\n * when this is absent or evaluates true — so a rule needing a signal that's absent\n * everywhere becomes `not_applicable` rather than failing on sparse data.\n */\n applicableWhen: RuleCheckSchema.optional(),\n check: RuleCheckSchema,\n});\nexport type ComplianceRule = z.infer<typeof ComplianceRuleSchema>;\n\nexport const RulesetSchema = z.object({\n name: z.string(),\n version: z.string(),\n framework: z.string(),\n description: z.string(),\n rules: z.array(ComplianceRuleSchema).min(1),\n}).superRefine((rs, ctx) => {\n const seen = new Set<string>();\n for (const r of rs.rules) {\n if (seen.has(r.id)) ctx.addIssue({ code: 'custom', message: `duplicate rule id: ${r.id}`, path: ['rules'] });\n seen.add(r.id);\n }\n});\nexport type Ruleset = z.infer<typeof RulesetSchema>;\n\nexport const ControlResultSchema = z.object({\n ruleId: z.string(),\n control: z.string(),\n framework: z.string(),\n severity: SeveritySchema,\n status: z.enum(['pass', 'fail', 'not_applicable']),\n applicableCount: z.number().int(),\n passedCount: z.number().int(),\n failingNodeIds: z.array(z.string()),\n});\nexport type ControlResult = z.infer<typeof ControlResultSchema>;\n\nexport const ComplianceReportSchema = z.object({\n rulesetName: z.string(),\n rulesetVersion: z.string(),\n generatedAt: z.string(),\n score: z.number().min(0).max(100).nullable(),\n status: z.enum(['pass', 'fail', 'not_applicable']),\n totals: z.object({\n rules: z.number(), applicable: z.number(), passed: z.number(),\n failed: z.number(), notApplicable: z.number(),\n }),\n bySeverity: z.record(SeveritySchema, z.object({ passed: z.number(), failed: z.number() })),\n controls: z.array(ControlResultSchema),\n gaps: z.array(z.object({\n ruleId: z.string(), control: z.string(), severity: SeveritySchema,\n title: z.string(), nodeIds: z.array(z.string()),\n })),\n});\nexport type ComplianceReport = z.infer<typeof ComplianceReportSchema>;\n","/**\n * Compliance scoring engine (3.4) — pure, deterministic, DB-free.\n *\n * `scoreTopology({nodes, edges}, ruleset)` mirrors `diffTopology`'s shape: plain\n * arrays in, a structured `ComplianceReport` out. The engine is the only trusted\n * evaluator of the declarative `RuleCheck` DSL — no `eval`, no ruleset-supplied\n * code or regex. Iteration order is stabilised (nodes + rules sorted by id) so the\n * report is byte-stable for a fixed `now`.\n */\n\nimport type { NodeRow, EdgeRow } from '../types.js';\nimport { NODE_TYPE_GROUPS } from '../types.js';\nimport { redactSecrets } from '../tools.js';\nimport {\n RulesetSchema, SEVERITY_WEIGHT, SEVERITIES,\n} from './types.js';\nimport type {\n Ruleset, ComplianceRule, ComplianceReport, ControlResult, Condition, RuleCheck, Severity, FieldPath,\n} from './types.js';\n\nexport interface ComplianceInput { nodes: NodeRow[]; edges: EdgeRow[]; }\n\n/** Engine-owned named regexes (a ruleset references these by name, never supplies one). */\nconst OWNER_KEY_RE = /^(owner|team|maintainer|contact|owned[-_]?by)$/i;\nconst PUBLIC_EXPOSURE_RE = /(^|[^0-9])0\\.0\\.0\\.0(\\/0)?|public|internet|exposed/i;\n\n/** Read a closed-enum field off a node as a comparable value. */\nfunction readField(node: NodeRow, field: FieldPath): unknown {\n switch (field) {\n case 'type': return node.type;\n case 'name': return node.name;\n case 'domain': return node.domain;\n case 'subDomain': return node.subDomain;\n case 'confidence': return node.confidence;\n case 'qualityScore': return node.qualityScore;\n case 'owner': return node.owner;\n case 'tags': return node.tags;\n case 'metadataKeys': return Object.keys(node.metadata ?? {});\n case 'metadataValues': return Object.values(node.metadata ?? {}).map((v) => (typeof v === 'string' ? v : JSON.stringify(v)));\n }\n}\n\n/** True if `value` \"has content\": non-empty array/string, defined number/other. */\nfunction isPresent(value: unknown): boolean {\n if (value === undefined || value === null) return false;\n if (Array.isArray(value)) return value.length > 0;\n if (typeof value === 'string') return value.length > 0;\n return true;\n}\n\nfunction matchesPattern(value: unknown, pattern: NonNullable<Condition['pattern']>): boolean {\n const test = (s: string): boolean => {\n switch (pattern) {\n case 'dsn_with_credentials': return redactSecrets(s) !== s; // a credential was present\n case 'owner_key': return OWNER_KEY_RE.test(s);\n case 'public_exposure': return PUBLIC_EXPOSURE_RE.test(s);\n }\n };\n if (Array.isArray(value)) return value.some((v) => typeof v === 'string' && test(v));\n return typeof value === 'string' && test(value);\n}\n\nexport function evaluateCondition(node: NodeRow, cond: Condition): boolean {\n const v = readField(node, cond.field);\n switch (cond.op) {\n case 'present': return isPresent(v);\n case 'absent': return !isPresent(v);\n case 'lt': return typeof v === 'number' && typeof cond.value === 'number' && v < cond.value;\n case 'lte': return typeof v === 'number' && typeof cond.value === 'number' && v <= cond.value;\n case 'gt': return typeof v === 'number' && typeof cond.value === 'number' && v > cond.value;\n case 'gte': return typeof v === 'number' && typeof cond.value === 'number' && v >= cond.value;\n case 'eq': return v === cond.value;\n case 'includes': return Array.isArray(v) && cond.value !== undefined && v.includes(cond.value as never);\n case 'matches': return cond.pattern !== undefined && matchesPattern(v, cond.pattern);\n }\n}\n\nexport function evaluateCheck(node: NodeRow, check: RuleCheck): boolean {\n if ('all' in check) return check.all.every((c) => evaluateCheck(node, c));\n if ('any' in check) return check.any.some((c) => evaluateCheck(node, c));\n if ('not' in check) return !evaluateCheck(node, check.not);\n return evaluateCondition(node, check);\n}\n\n/** Expand a rule's scope to the set of node types it targets (empty = all types). */\nfunction scopedTypes(rule: ComplianceRule): Set<string> | null {\n const { groups, types } = rule.scope;\n if ((!groups || groups.length === 0) && (!types || types.length === 0)) return null; // all\n const out = new Set<string>();\n for (const g of groups ?? []) for (const t of NODE_TYPE_GROUPS[g as keyof typeof NODE_TYPE_GROUPS]) out.add(t);\n for (const t of types ?? []) out.add(t);\n return out;\n}\n\n/** Nodes a rule applies to: scope-matched AND (applicableWhen absent or true). */\nfunction applicableNodes(nodes: NodeRow[], rule: ComplianceRule): NodeRow[] {\n const types = scopedTypes(rule);\n return nodes.filter((n) =>\n (types === null || types.has(n.type)) &&\n (rule.applicableWhen === undefined || evaluateCheck(n, rule.applicableWhen)),\n );\n}\n\nexport function evaluateRule(input: ComplianceInput, rule: ComplianceRule): ControlResult {\n const applicable = applicableNodes(input.nodes, rule);\n const base = { ruleId: rule.id, control: rule.control, framework: rule.framework, severity: rule.severity };\n if (applicable.length === 0) {\n return { ...base, status: 'not_applicable', applicableCount: 0, passedCount: 0, failingNodeIds: [] };\n }\n const failingNodeIds = applicable.filter((n) => !evaluateCheck(n, rule.check)).map((n) => n.id).sort();\n return {\n ...base,\n status: failingNodeIds.length === 0 ? 'pass' : 'fail',\n applicableCount: applicable.length,\n passedCount: applicable.length - failingNodeIds.length,\n failingNodeIds,\n };\n}\n\n/**\n * Score a topology against a ruleset. `score = round(100 × Σweight(passed applicable) /\n * Σweight(applicable))`, weighted by severity, with not-applicable controls excluded\n * from the denominator. `score = null` / `status = 'not_applicable'` when nothing applies.\n */\nexport function scoreTopology(input: ComplianceInput, ruleset: Ruleset, opts?: { now?: string }): ComplianceReport {\n const nodes = [...input.nodes].sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));\n const rules = [...ruleset.rules].sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));\n const controls = rules.map((r) => evaluateRule({ nodes, edges: input.edges }, r));\n\n const bySeverity = Object.fromEntries(SEVERITIES.map((s) => [s, { passed: 0, failed: 0 }])) as ComplianceReport['bySeverity'];\n let applicable = 0, passed = 0, failed = 0, notApplicable = 0;\n let weightTotal = 0, weightPassed = 0;\n const gaps: ComplianceReport['gaps'] = [];\n\n for (const c of controls) {\n const w = SEVERITY_WEIGHT[c.severity as Severity];\n if (c.status === 'not_applicable') { notApplicable++; continue; }\n applicable++;\n weightTotal += w;\n if (c.status === 'pass') { passed++; weightPassed += w; bySeverity[c.severity].passed++; }\n else {\n failed++; bySeverity[c.severity].failed++;\n const rule = rules.find((r) => r.id === c.ruleId)!;\n gaps.push({ ruleId: c.ruleId, control: c.control, severity: c.severity, title: rule.title, nodeIds: c.failingNodeIds });\n }\n }\n\n const score = applicable === 0 ? null : Math.round((100 * weightPassed) / weightTotal);\n const status: ComplianceReport['status'] = applicable === 0 ? 'not_applicable' : failed === 0 ? 'pass' : 'fail';\n\n return {\n rulesetName: ruleset.name,\n rulesetVersion: ruleset.version,\n generatedAt: opts?.now ?? new Date().toISOString(),\n score,\n status,\n totals: { rules: rules.length, applicable, passed, failed, notApplicable },\n bySeverity,\n controls,\n gaps,\n };\n}\n\n/** Validate raw ruleset data (used by the registry and any future import path). */\nexport function loadRuleset(raw: unknown): Ruleset {\n return RulesetSchema.parse(raw);\n}\n","/**\n * Topology diffing — pure, deterministic comparison of two discovery snapshots.\n *\n * This module knows nothing about the database; it operates on plain\n * node/edge arrays so it can be unit-tested in isolation and reused by the\n * CLI, the MCP server, and the exporter. Drift is detected on a stable\n * projection of node fields (see DRIFT_FIELDS); `confidence` is reported but\n * never on its own marks a node as changed.\n */\n\nimport type { NodeRow, EdgeRow, NodeChange, DriftField } from './types.js';\nimport { DRIFT_FIELDS } from './types.js';\n\n/** Logical identity of an edge within a session: source + target + relationship. */\nconst edgeKey = (e: EdgeRow): string => `${e.sourceId}\u0000${e.targetId}\u0000${e.relationship}`;\n\n/** Deterministic JSON serialization with recursively sorted object keys. */\nexport function stableStringify(value: unknown): string {\n if (value === null || typeof value !== 'object') return JSON.stringify(value) ?? 'null';\n if (Array.isArray(value)) return `[${value.map(stableStringify).join(',')}]`;\n const keys = Object.keys(value as Record<string, unknown>).sort();\n return `{${keys.map((k) => `${JSON.stringify(k)}:${stableStringify((value as Record<string, unknown>)[k])}`).join(',')}}`;\n}\n\n/** Return the subset of DRIFT_FIELDS that differ between two nodes (order-independent). */\nfunction driftedFields(a: NodeRow, b: NodeRow): DriftField[] {\n const changed: DriftField[] = [];\n for (const f of DRIFT_FIELDS) {\n if (f === 'tags') {\n if ([...a.tags].sort().join('\u0000') !== [...b.tags].sort().join('\u0000')) changed.push(f);\n } else if (f === 'metadata' || f === 'cost') {\n // `cost` is an object (3.3); a reference compare via `!==` would always mis-fire.\n if (stableStringify(a[f]) !== stableStringify(b[f])) changed.push(f);\n } else if (a[f] !== b[f]) {\n changed.push(f);\n }\n }\n return changed;\n}\n\nexport interface TopologyInput {\n nodes: NodeRow[];\n edges: EdgeRow[];\n}\n\nexport interface TopologyDelta {\n nodes: { added: NodeRow[]; removed: NodeRow[]; changed: NodeChange[]; unchanged: number };\n edges: { added: EdgeRow[]; removed: EdgeRow[]; unchanged: number };\n summary: {\n nodesAdded: number; nodesRemoved: number; nodesChanged: number;\n edgesAdded: number; edgesRemoved: number;\n };\n}\n\n/**\n * Compute the delta from `base` to `current`. Nodes are keyed by `id`, edges by\n * (source, target, relationship). Pure: same inputs always yield the same output.\n */\nexport function diffTopology(base: TopologyInput, current: TopologyInput): TopologyDelta {\n const baseNodes = new Map(base.nodes.map((n) => [n.id, n]));\n const curNodes = new Map(current.nodes.map((n) => [n.id, n]));\n\n const added: NodeRow[] = [];\n const removed: NodeRow[] = [];\n const changed: NodeChange[] = [];\n let unchangedNodes = 0;\n\n for (const [id, after] of curNodes) {\n const before = baseNodes.get(id);\n if (!before) {\n added.push(after);\n continue;\n }\n const fields = driftedFields(before, after);\n if (fields.length > 0) {\n changed.push({ id, before, after, changedFields: fields, confidenceDelta: after.confidence - before.confidence });\n } else {\n unchangedNodes++;\n }\n }\n for (const [id, before] of baseNodes) {\n if (!curNodes.has(id)) removed.push(before);\n }\n\n const baseEdges = new Map(base.edges.map((e) => [edgeKey(e), e]));\n const curEdges = new Map(current.edges.map((e) => [edgeKey(e), e]));\n const edgesAdded: EdgeRow[] = [];\n const edgesRemoved: EdgeRow[] = [];\n let unchangedEdges = 0;\n for (const [k, e] of curEdges) {\n if (baseEdges.has(k)) unchangedEdges++;\n else edgesAdded.push(e);\n }\n for (const [k, e] of baseEdges) {\n if (!curEdges.has(k)) edgesRemoved.push(e);\n }\n\n return {\n nodes: { added, removed, changed, unchanged: unchangedNodes },\n edges: { added: edgesAdded, removed: edgesRemoved, unchanged: unchangedEdges },\n summary: {\n nodesAdded: added.length,\n nodesRemoved: removed.length,\n nodesChanged: changed.length,\n edgesAdded: edgesAdded.length,\n edgesRemoved: edgesRemoved.length,\n },\n };\n}\n","/**\n * Anomaly detection (3.6) — pure, deterministic flagging of standing structural risk.\n *\n * Knows nothing about the database; operates on plain `NodeRow[]` + an in-memory\n * degree map so it can be unit-tested in isolation and reused by the CLI, the MCP\n * server, and the scheduled-scan alert path (3.1). Same topology always yields the\n * same anomalies with identical `reason` strings (mirrors `deriveSessionName`). No\n * LLM, no I/O, no dependency. `reason` interpolates only the structured `nodeId` and\n * numeric scores — never raw node name/metadata (prompt-injection safe).\n */\n\nimport type { NodeRow, Anomaly, AnomalyThresholds, AnomalySeverity } from './types.js';\nimport { NODE_TYPE_GROUPS, DEFAULT_ANOMALY_THRESHOLDS } from './types.js';\n\nexport { DEFAULT_ANOMALY_THRESHOLDS };\n\n/** Node types in a managed family; anything else (incl. 'unknown') is shadow-IT-eligible. */\nconst MANAGED_TYPES: ReadonlySet<string> = new Set(Object.values(NODE_TYPE_GROUPS).flat());\n\n/** True when a node has no business domain ('(none)' is the summary's sentinel). */\nfunction hasNoDomain(n: NodeRow): boolean {\n return n.domain == null || n.domain === '' || n.domain === '(none)';\n}\n\n/** Flag zero/weak-degree nodes. degree 0 → high; 1..orphanWeakDegree → low. */\nexport function detectOrphans(\n nodes: NodeRow[],\n degree: ReadonlyMap<string, number>,\n thresholds: AnomalyThresholds = DEFAULT_ANOMALY_THRESHOLDS,\n): Anomaly[] {\n const out: Anomaly[] = [];\n for (const n of nodes) {\n const d = degree.get(n.id) ?? 0;\n if (d === 0) {\n out.push({ nodeId: n.id, kind: 'orphan', severity: 'high', reason: 'zero-degree node (no edges)' });\n } else if (d <= thresholds.orphanWeakDegree) {\n out.push({ nodeId: n.id, kind: 'orphan', severity: 'low', reason: `weakly-connected node (degree ${d})` });\n }\n }\n return out;\n}\n\n/** Flag unmanaged-type or undomained low-confidence/quality nodes. */\nexport function detectShadowIt(\n nodes: NodeRow[],\n thresholds: AnomalyThresholds = DEFAULT_ANOMALY_THRESHOLDS,\n): Anomaly[] {\n const out: Anomaly[] = [];\n for (const n of nodes) {\n if (!MANAGED_TYPES.has(n.type)) {\n out.push({ nodeId: n.id, kind: 'shadow-it', severity: 'medium', reason: `unmanaged node type \"${n.type}\"` });\n continue;\n }\n if (hasNoDomain(n)) {\n const lowConf = n.confidence < thresholds.shadowConfidence;\n const lowQual = n.qualityScore != null && n.qualityScore < thresholds.shadowQuality;\n if (lowConf || lowQual) {\n const sev: AnomalySeverity = lowConf && lowQual ? 'high' : 'medium';\n const parts: string[] = [];\n if (lowConf) parts.push(`low confidence ${n.confidence.toFixed(2)}`);\n if (lowQual) parts.push(`low quality ${n.qualityScore}`);\n out.push({ nodeId: n.id, kind: 'shadow-it', severity: sev, reason: `${parts.join(' and ')} with no business domain` });\n }\n }\n }\n return out;\n}\n\n/** Aggregate + stable sort (nodeId, then kind). Returns [] for an empty topology. */\nexport function detectAnomalies(\n nodes: NodeRow[],\n degree: ReadonlyMap<string, number>,\n thresholds: AnomalyThresholds = DEFAULT_ANOMALY_THRESHOLDS,\n): Anomaly[] {\n return [...detectOrphans(nodes, degree, thresholds), ...detectShadowIt(nodes, thresholds)]\n .sort((a, b) => a.nodeId.localeCompare(b.nodeId) || a.kind.localeCompare(b.kind));\n}\n\n/** Anomalies present in `current` but absent in `base`, keyed by (nodeId, kind). Pure & order-stable. */\nexport function newAnomalies(base: Anomaly[], current: Anomaly[]): Anomaly[] {\n const seen = new Set(base.map((a) => `${a.nodeId}|${a.kind}`));\n return current.filter((a) => !seen.has(`${a.nodeId}|${a.kind}`));\n}\n","/**\n * Identity resolution (4.5): turn a presented bearer token into a {@link Principal},\n * or `undefined` (→ 401). Three modes, in precedence order:\n * 1. **RBAC** — a populated SQLite {@link CredentialStore} is the source of truth; the\n * token is sha256-hashed and looked up (tokens are never stored or compared raw).\n * 2. **Shared token** — a single configured token resolves to one implicit `admin`\n * (today's behavior; constant-time compare).\n * 3. **Open/loopback dev** — no token configured → implicit `admin`, unless `required`.\n *\n * The hash-and-store design means a DB leak never exposes a usable credential, and the\n * OIDC seam is just another {@link CredentialStore}/resolver behind this one function.\n */\n\nimport { createHash } from 'node:crypto';\nimport { timingSafeEqual } from '../api/auth.js';\nimport { DEFAULT_TENANT } from '../db.js';\nimport type { CredentialStore, CredentialRecord, Principal } from './types.js';\n\n/** Stable sha256 hex of a token — the only form ever persisted or compared in the store. */\nexport function hashToken(token: string): string {\n return createHash('sha256').update(token, 'utf8').digest('hex');\n}\n\n/** Minimal DB surface the SQLite credential store needs (CartographyDB satisfies it structurally). */\nexport interface CredentialDb {\n countCredentials(): number;\n findCredentialByHash(tokenHash: string): CredentialRecord | undefined;\n}\n\n/** {@link CredentialStore} backed by `CartographyDB`'s `auth_credentials` table. */\nexport class SqliteCredentialStore implements CredentialStore {\n constructor(private readonly db: CredentialDb) {}\n count(): number {\n return this.db.countCredentials();\n }\n findByHash(tokenHash: string): CredentialRecord | undefined {\n return this.db.findCredentialByHash(tokenHash);\n }\n}\n\nexport interface ResolveOptions {\n /** Populated → RBAC mode (source of truth). */\n store?: CredentialStore;\n /** Single shared token (one implicit admin) when no store credentials exist. */\n sharedToken?: string;\n /** Tenant assigned to implicit (shared/loopback) admin principals. */\n defaultTenant?: string;\n /** Reject unauthenticated requests even when neither store nor shared token is set. */\n required?: boolean;\n}\n\n/**\n * Resolve an already-parsed bearer token to a {@link Principal}, or `undefined` (→ 401).\n * `presentedToken` is the value from `bearerToken(authorizationHeader)` (may be undefined).\n */\nexport function resolvePrincipal(presentedToken: string | undefined, opts: ResolveOptions): Principal | undefined {\n const tenant = opts.defaultTenant ?? DEFAULT_TENANT;\n\n // 1. RBAC mode — a populated store is authoritative.\n if (opts.store && opts.store.count() > 0) {\n if (!presentedToken) return undefined;\n const rec = opts.store.findByHash(hashToken(presentedToken));\n return rec ? { subject: rec.subject, tenant: rec.tenant, role: rec.role } : undefined;\n }\n\n // 2. Shared-token mode — one implicit admin.\n if (opts.sharedToken) {\n if (!presentedToken || !timingSafeEqual(presentedToken, opts.sharedToken)) return undefined;\n return { subject: 'shared-token', tenant, role: 'admin' };\n }\n\n // 3. Open/loopback dev — implicit admin unless explicitly required.\n if (opts.required) return undefined;\n return { subject: 'anonymous', tenant, role: 'admin' };\n}\n","/**\n * Shared HTTP auth + bind-hardening primitives.\n *\n * Extracted verbatim from `src/mcp/transports.ts` so the MCP transport, the REST/\n * GraphQL API server (4.2), and any future HTTP surface consume **one** provably-\n * identical implementation of the CVE-2025-66414 guards, the constant-time bearer\n * compare, and the default Host allowlist. The allowlist — not any one caller — is\n * the security boundary; centralizing it here keeps every networked surface on the\n * same posture and makes the behavior unit-testable in isolation.\n */\n\n/** Loopback hosts are safe to bind without an explicit Host allowlist. */\nexport const LOOPBACK_HOSTS: ReadonlySet<string> = new Set(['127.0.0.1', 'localhost', '::1', '[::1]']);\n\n/** True when `host` is a loopback address (safe to bind without an allowlist/token). */\nexport function isLoopbackHost(host: string): boolean {\n return LOOPBACK_HOSTS.has(host);\n}\n\n/** Constant-time comparison to avoid leaking the token via timing. */\nexport function timingSafeEqual(a: string, b: string): boolean {\n if (a.length !== b.length) return false;\n let diff = 0;\n for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);\n return diff === 0;\n}\n\n/**\n * Extract the bearer token from an Authorization header, if present. Parsed with\n * linear string ops (no regex) so a user-controlled header can never trigger\n * polynomial backtracking (ReDoS) — `^Bearer\\s+(.+)$` is ambiguous between `\\s+`\n * and `.+` on a long run of spaces.\n */\nexport function bearerToken(header: string | undefined): string | undefined {\n if (!header) return undefined;\n const trimmed = header.trim();\n // Case-insensitive \"Bearer\" scheme, then ≥1 whitespace, then the token.\n if (trimmed.length < 7 || trimmed.slice(0, 6).toLowerCase() !== 'bearer') return undefined;\n const rest = trimmed.slice(6);\n if (!/^\\s/.test(rest)) return undefined; // a single anchored test — no ambiguous quantifier\n const token = rest.trimStart();\n return token.length > 0 ? token : undefined;\n}\n\n/**\n * Returns true if the request is authenticated: a request is authenticated when no\n * token is configured (open loopback dev mode) OR the `Authorization: Bearer` value\n * is present and constant-time-equal to the configured token. The caller maps a\n * `false` to a 401.\n */\nexport function checkBearer(authorizationHeader: string | undefined, token: string | undefined): boolean {\n if (!token) return true;\n const provided = bearerToken(authorizationHeader);\n return provided !== undefined && timingSafeEqual(provided, token);\n}\n\nexport interface BindGuardOptions {\n host: string;\n port: number;\n allowedHosts?: string[];\n token?: string;\n}\n\n/**\n * Enforce the CVE-2025-66414 + mandatory-token guards before binding. Throws the\n * exact errors `runHttp` raised inline, so existing transport behavior is preserved:\n * a non-loopback bind requires BOTH an explicit `allowedHosts` allowlist AND a token.\n */\nexport function assertSafeBind(opts: BindGuardOptions): void {\n if (isLoopbackHost(opts.host)) return;\n\n // CVE-2025-66414: a server reachable beyond loopback must declare which Host\n // headers it trusts, or DNS-rebinding protection cannot do its job. Refuse to\n // start an exposed server with the permissive localhost defaults.\n if (opts.allowedHosts === undefined) {\n throw new Error(\n `Refusing to bind a non-loopback host (${opts.host}) without an explicit allowedHosts allowlist. ` +\n `Pass { allowedHosts: ['your.public.host:port'] } to opt in, or bind 127.0.0.1 for local-only use.`,\n );\n }\n\n // A network-reachable endpoint must be authenticated. Without a token, anyone\n // who can reach the host could invoke the scanning tools.\n if (!opts.token) {\n throw new Error(\n `Refusing to bind a non-loopback host (${opts.host}) without an auth token. ` +\n `Pass { token } (or --token / CARTOGRAPHY_HTTP_TOKEN) so requests must carry 'Authorization: Bearer <token>'.`,\n );\n }\n}\n\n/** Default Host allowlist: the bound host plus the localhost variants, all `:port`. */\nexport function defaultAllowedHosts(host: string, port: number): string[] {\n return [`${host}:${port}`, `localhost:${port}`, `127.0.0.1:${port}`];\n}\n","/**\n * The RBAC decision core (4.5): a pure, deny-by-default `can(role, action)` matrix\n * and the `authorize`/`assertSameTenant` guards the transports call. No I/O, no DB —\n * trivially unit-testable and identical for the MCP transport and the REST/GraphQL API.\n */\n\nimport type { Role, Action, Principal } from './types.js';\n\n/** Privilege rank — higher strictly subsumes lower (`admin ⊇ operator ⊇ viewer`). */\nconst ROLE_RANK: Record<Role, number> = { viewer: 1, operator: 2, admin: 3 };\n\n/** Minimum role each action requires. Deny-by-default: an unlisted action is never permitted. */\nconst ACTION_MIN_ROLE: Record<Action, Role> = { read: 'viewer', discovery: 'operator', admin: 'admin' };\n\n/** True iff `role` is at least the minimum role required for `action`. */\nexport function can(role: Role, action: Action): boolean {\n return ROLE_RANK[role] >= ROLE_RANK[ACTION_MIN_ROLE[action]];\n}\n\n/** Thrown when an authenticated principal lacks the role for an action → HTTP 403. */\nexport class AuthorizationError extends Error {\n constructor(public readonly action: Action, public readonly role: Role) {\n super(`forbidden: role '${role}' may not perform '${action}'`);\n this.name = 'AuthorizationError';\n }\n}\n\n/** Throw {@link AuthorizationError} unless `principal` may perform `action`. */\nexport function authorize(principal: Principal, action: Action): void {\n if (!can(principal.role, action)) throw new AuthorizationError(action, principal.role);\n}\n\n/** Thrown when a principal references a tenant other than its own → HTTP 403. */\nexport class TenantMismatchError extends Error {\n constructor() {\n super('forbidden: principal is not scoped to the requested tenant');\n this.name = 'TenantMismatchError';\n }\n}\n\n/**\n * The tenant a principal's reads are pinned to. Callers MUST use this rather than any\n * caller-supplied tenant header/param, so a principal can never read another tenant by\n * spoofing the request — isolation is structural, not advisory.\n */\nexport function scopeReads(principal: Principal): string {\n return principal.tenant;\n}\n\n/** Throw {@link TenantMismatchError} if `requestedTenant` differs from the principal's tenant. */\nexport function assertSameTenant(principal: Principal, requestedTenant: string): void {\n if (requestedTenant !== principal.tenant) throw new TenantMismatchError();\n}\n"],"mappings":"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","names":["run","z","z","z","z","globalId","contentHash","degree","createHash","createHash"]}