@datasynx/agentic-ai-cartography 2.3.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.
@@ -1,18 +1,22 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ AuthorizationError,
3
4
  CartographyDB,
4
5
  DEFAULT_TENANT,
6
+ SqliteCredentialStore,
5
7
  assertSafeBind,
6
- checkBearer,
8
+ authorize,
9
+ bearerToken,
7
10
  defaultAllowedHosts,
8
- normalizeTenant
9
- } from "./chunk-7QEBFMN4.js";
11
+ normalizeTenant,
12
+ resolvePrincipal
13
+ } from "./chunk-GA4427LB.js";
10
14
  import {
11
15
  ANOMALY_KINDS,
12
16
  ANOMALY_SEVERITIES,
13
17
  COST_PERIODS,
14
18
  defaultConfig
15
- } from "./chunk-WCR47QA2.js";
19
+ } from "./chunk-QQOQBE2A.js";
16
20
 
17
21
  // src/api/start.ts
18
22
  import { readFileSync } from "fs";
@@ -926,6 +930,8 @@ async function runApi(opts) {
926
930
  const token = opts.token;
927
931
  const graphqlEnabled = opts.graphql !== false;
928
932
  const defaultTenant = opts.tenant?.defaultTenant ?? DEFAULT_TENANT;
933
+ const authStore = opts.auth?.store;
934
+ const rbacMode = !!(authStore && authStore.count() > 0);
929
935
  const log = opts.log ?? (() => {
930
936
  });
931
937
  const restDeps = { backend: opts.backend, version: opts.version };
@@ -984,23 +990,44 @@ async function runApi(opts) {
984
990
  finish(r.status);
985
991
  return;
986
992
  }
987
- if (!checkBearer(req.headers["authorization"], token)) {
993
+ const principal = resolvePrincipal(bearerToken(req.headers["authorization"]), {
994
+ ...authStore ? { store: authStore } : {},
995
+ ...token ? { sharedToken: token } : {},
996
+ defaultTenant,
997
+ ...opts.auth?.required ? { required: true } : {}
998
+ });
999
+ if (!principal) {
988
1000
  send(res, 401, { error: "unauthorized" }, { "www-authenticate": "Bearer", ...cors });
989
1001
  finish(401);
990
1002
  return;
991
1003
  }
992
- let ctx;
993
1004
  try {
994
- ctx = resolveTenant(req, url, opts.tenant ?? {});
995
- tenantLabel = ctx.tenant;
1005
+ authorize(principal, "read");
996
1006
  } catch (err) {
997
- if (err instanceof InvalidTenantError) {
998
- send(res, 400, { error: "invalid tenant" }, cors);
999
- finish(400);
1007
+ if (err instanceof AuthorizationError) {
1008
+ send(res, 403, { error: "forbidden" }, cors);
1009
+ finish(403);
1000
1010
  return;
1001
1011
  }
1002
1012
  throw err;
1003
1013
  }
1014
+ let ctx;
1015
+ if (rbacMode) {
1016
+ ctx = { tenant: principal.tenant };
1017
+ tenantLabel = principal.tenant;
1018
+ } else {
1019
+ try {
1020
+ ctx = resolveTenant(req, url, opts.tenant ?? {});
1021
+ tenantLabel = ctx.tenant;
1022
+ } catch (err) {
1023
+ if (err instanceof InvalidTenantError) {
1024
+ send(res, 400, { error: "invalid tenant" }, cors);
1025
+ finish(400);
1026
+ return;
1027
+ }
1028
+ throw err;
1029
+ }
1030
+ }
1004
1031
  if (graphqlEnabled && path === "/graphql") {
1005
1032
  if (req.method === "GET") {
1006
1033
  const g = handleGraphqlGet();
@@ -1093,6 +1120,7 @@ function parseApiArgs(argv) {
1093
1120
  else if (a === "--db") opts.dbPath = argv[++i];
1094
1121
  else if (a === "--session") opts.session = argv[++i];
1095
1122
  else if (a === "--tenant" || a === "--org") opts.tenant = argv[++i];
1123
+ else if (a === "--auth-required") opts.authRequired = true;
1096
1124
  else if (a === "--help" || a === "-h") opts.help = true;
1097
1125
  }
1098
1126
  return opts;
@@ -1108,11 +1136,13 @@ async function startApi(opts = {}) {
1108
1136
  const host = opts.host ?? "127.0.0.1";
1109
1137
  const port = opts.port ?? 3737;
1110
1138
  const version = readVersion();
1139
+ const authStore = new SqliteCredentialStore(db);
1111
1140
  const server = await runApi({
1112
1141
  host,
1113
1142
  port,
1114
1143
  backend,
1115
1144
  version,
1145
+ auth: { store: authStore, ...opts.authRequired ? { required: true } : {} },
1116
1146
  ...opts.allowedHosts ? { allowedHosts: opts.allowedHosts } : {},
1117
1147
  ...opts.allowedOrigins ? { allowedOrigins: opts.allowedOrigins } : {},
1118
1148
  ...token ? { token } : {},
@@ -1131,4 +1161,4 @@ export {
1131
1161
  parseApiArgs,
1132
1162
  startApi
1133
1163
  };
1134
- //# sourceMappingURL=chunk-7VZH5PFV.js.map
1164
+ //# sourceMappingURL=chunk-NQXZUWOI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/api/start.ts","../src/store/query.ts","../src/api/server.ts","../src/api/tenant.ts","../src/api/schemas.ts","../src/api/rest.ts","../src/api/openapi.ts","../src/api/graphql.ts"],"sourcesContent":["/**\n * Shared entry logic for the read-only API server (4.2), used by both the dedicated\n * `cartography-api` binary and the `api` CLI sub-command. Mirrors `src/mcp/start.ts`:\n * opens the catalog, builds the SQLite query backend, resolves the bearer token from\n * `--token`/`CARTOGRAPHY_HTTP_TOKEN`, and starts `runApi`. All logging is to stderr;\n * the token value is never logged (only whether one is set).\n */\n\nimport type { Server } from 'node:http';\nimport { readFileSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { CartographyDB, normalizeTenant } from '../db.js';\nimport { defaultConfig } from '../types.js';\nimport { createSqliteQueryBackend } from '../store/query.js';\nimport { SqliteCredentialStore } from '../auth/identity.js';\nimport { runApi } from './server.js';\n\nexport interface StartApiOptions {\n dbPath?: string;\n session?: string | 'latest';\n port?: number;\n host?: string;\n allowedHosts?: string[];\n allowedOrigins?: string[];\n token?: string;\n /** Expose `/graphql` (default true). */\n graphql?: boolean;\n /** Default tenant served when a request names none. */\n tenant?: string;\n /** Reject unauthenticated requests even on loopback (RBAC `required` mode). */\n authRequired?: boolean;\n log?: (msg: string) => void;\n}\n\nexport interface ParsedApiArgs extends StartApiOptions {\n /** `--help`/`-h` was passed; the caller should print usage and exit 0. */\n help?: boolean;\n}\n\nfunction readVersion(): string {\n try {\n const dir = import.meta.dirname ?? dirname(fileURLToPath(import.meta.url));\n return (JSON.parse(readFileSync(resolve(dir, '..', 'package.json'), 'utf-8')).version as string) ?? '0.0.0';\n } catch {\n return '0.0.0';\n }\n}\n\n/** Parse `cartography-api` argv into StartApiOptions (unit-testable, no side effects). */\nexport function parseApiArgs(argv: string[]): ParsedApiArgs {\n const opts: ParsedApiArgs = {};\n for (let i = 0; i < argv.length; i++) {\n const a = argv[i];\n if (a === '--http') continue; // symmetry with `mcp`; HTTP is the only API transport\n else if (a === '--no-graphql') opts.graphql = false;\n else if (a === '--port') opts.port = Number(argv[++i]);\n else if (a === '--host') opts.host = argv[++i];\n else if (a === '--allowed-hosts') opts.allowedHosts = splitList(argv[++i]);\n else if (a === '--allowed-origins') opts.allowedOrigins = splitList(argv[++i]);\n else if (a === '--token') opts.token = argv[++i];\n else if (a === '--db') opts.dbPath = argv[++i];\n else if (a === '--session') opts.session = argv[++i];\n else if (a === '--tenant' || a === '--org') opts.tenant = argv[++i];\n else if (a === '--auth-required') opts.authRequired = true;\n else if (a === '--help' || a === '-h') opts.help = true;\n }\n return opts;\n}\n\nfunction splitList(raw: string | undefined): string[] {\n return (raw ?? '').split(',').map((s) => s.trim()).filter(Boolean);\n}\n\n/** Open the catalog, build the read backend, and start the API server. Returns the server. */\nexport async function startApi(opts: StartApiOptions = {}): Promise<Server> {\n const log = opts.log ?? ((m: string) => process.stderr.write(m + '\\n'));\n const db = new CartographyDB(opts.dbPath ?? defaultConfig().dbPath);\n const backend = createSqliteQueryBackend(db, opts.session ?? 'latest');\n const token = opts.token ?? process.env['CARTOGRAPHY_HTTP_TOKEN'];\n const host = opts.host ?? '127.0.0.1';\n const port = opts.port ?? 3737;\n const version = readVersion();\n\n // RBAC (4.5): the credential store is always wired; it only enforces once an admin\n // has added credentials (`cartography auth add`). Empty store → legacy shared/open behavior.\n const authStore = new SqliteCredentialStore(db);\n\n const server = await runApi({\n host,\n port,\n backend,\n version,\n auth: { store: authStore, ...(opts.authRequired ? { required: true } : {}) },\n ...(opts.allowedHosts ? { allowedHosts: opts.allowedHosts } : {}),\n ...(opts.allowedOrigins ? { allowedOrigins: opts.allowedOrigins } : {}),\n ...(token ? { token } : {}),\n ...(opts.graphql === false ? { graphql: false } : {}),\n ...(opts.tenant ? { tenant: { defaultTenant: normalizeTenant(opts.tenant) } } : {}),\n log,\n });\n\n const graphqlNote = opts.graphql === false ? ' [REST only]' : ' + /graphql';\n log(\n `Cartograph API (REST${graphqlNote}) on http://${host}:${port}/v1` +\n `${token ? ' (auth: bearer token required)' : ''} (tenant: ${normalizeTenant(opts.tenant)})`,\n );\n return server;\n}\n","/**\n * `QueryBackend` — the **read-only** query seam for the API server (4.2).\n *\n * This is deliberately distinct from {@link StoreBackend} (`src/store/backend.ts`),\n * which is the central-collector **write/ingest** seam. The two seams have opposite\n * shapes: ingest merges incoming deltas; this one answers topology questions. A\n * non-SQLite backend (4.3) implements both. Keeping them separate means the API\n * never gains a write path and the ingest core never gains a query path.\n *\n * Every method takes a {@link TenantContext}. Session resolution is tenant-scoped, so\n * a caller bound to tenant A can never read tenant B's topology — even by naming a\n * session id that belongs to B (it resolves to \"not found\", never B's data). This\n * mirrors the MCP server's `resolveSession` tenant guard exactly.\n */\n\nimport type { CartographyDB, GraphSummary, TraversalResult } from '../db.js';\nimport type { NodeRow, SessionRow, TopologyDiff } from '../types.js';\n\n/** The tenant (org-scope) a request is bound to. `'local'` (DEFAULT_TENANT) until a real org is supplied. */\nexport interface TenantContext {\n tenant: string;\n}\n\nexport interface NodeQuery {\n search?: string;\n types?: readonly string[];\n limit?: number;\n offset?: number;\n}\n\nexport interface DependencyQuery {\n direction?: 'downstream' | 'upstream' | 'both';\n maxDepth?: number;\n}\n\nexport interface NodesResult {\n nodes: NodeRow[];\n total: number;\n limit: number;\n offset: number;\n}\n\nexport interface HealthResult {\n store: 'sqlite';\n sessions: number;\n}\n\n/** A requested resource (session / diff endpoint) does not exist for this tenant → REST 404. */\nexport class NotFoundError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'NotFoundError';\n }\n}\n\n/** Narrow, read-only view of the topology store. Tenant is required on every call. */\nexport interface QueryBackend {\n /** Aggregate, low-token index of the resolved session. Throws {@link NotFoundError} if no session resolves. */\n summary(ctx: TenantContext, sessionId?: string): GraphSummary;\n /** Page/search nodes of the resolved session. Throws {@link NotFoundError} if no session resolves. */\n nodes(ctx: TenantContext, q: NodeQuery, sessionId?: string): NodesResult;\n /** One node by id (or `undefined` if absent). Throws {@link NotFoundError} if no session resolves. */\n node(ctx: TenantContext, id: string, sessionId?: string): NodeRow | undefined;\n /** Dependency traversal from a node. Throws {@link NotFoundError} if no session resolves. */\n dependencies(ctx: TenantContext, id: string, q: DependencyQuery, sessionId?: string): TraversalResult;\n /** Compare two sessions (both must belong to the tenant). Throws {@link NotFoundError} on an unknown/foreign id. */\n diff(ctx: TenantContext, base: string, current: string): TopologyDiff;\n /** All sessions for this tenant, newest first. */\n sessions(ctx: TenantContext): SessionRow[];\n /** Liveness/coverage probe (never resolves a session). */\n health(ctx: TenantContext): HealthResult;\n}\n\n/** Hard cap on a single page of nodes; mirrors the API edge clamp. */\nconst MAX_NODE_LIMIT = 1000;\n/** Hard cap on traversal depth; mirrors `getDependencies` (src/db.ts) and the API edge clamp. */\nconst MAX_DEPTH = 64;\n\n/** Clamp to [min, max] and floor — a fractional limit/offset/depth would break the SQL/contract. */\nfunction clamp(value: number, min: number, max: number): number {\n return Math.floor(Math.max(min, Math.min(value, max)));\n}\n\n/**\n * `QueryBackend` over the local `CartographyDB`. A thin read adapter: the schema,\n * migrations, and SQL all live in `db.ts`; this only resolves the tenant-scoped\n * session and forwards. Constructing it adds no state and no schema.\n */\nexport class SqliteQueryBackend implements QueryBackend {\n constructor(\n private readonly db: CartographyDB,\n private readonly defaultSession: string | 'latest' = 'latest',\n ) {}\n\n /**\n * Resolve the session id for a request, scoped to `ctx.tenant`. An explicit id must\n * belong to the tenant or it resolves to undefined (cross-tenant isolation); else the\n * newest `discover` session for the tenant. Mirrors `resolveSession` in the MCP server.\n */\n private resolveSession(ctx: TenantContext, sessionId?: string): string {\n const requested = sessionId ?? (this.defaultSession === 'latest' ? undefined : this.defaultSession);\n if (requested) {\n const s = this.db.getSession(requested);\n if (s && s.tenant === ctx.tenant) return s.id;\n throw new NotFoundError(`session not found`);\n }\n const latest = this.db.getLatestSession('discover', ctx.tenant) ?? this.db.getLatestSession(undefined, ctx.tenant);\n if (!latest) throw new NotFoundError(`no session available`);\n return latest.id;\n }\n\n summary(ctx: TenantContext, sessionId?: string): GraphSummary {\n return this.db.getGraphSummary(this.resolveSession(ctx, sessionId));\n }\n\n nodes(ctx: TenantContext, q: NodeQuery, sessionId?: string): NodesResult {\n const sid = this.resolveSession(ctx, sessionId);\n const limit = clamp(q.limit ?? 100, 1, MAX_NODE_LIMIT);\n const offset = Math.floor(Math.max(0, q.offset ?? 0));\n const total = this.db.getNodeCount(sid);\n if (q.search) {\n const nodes = this.db.searchNodes(sid, q.search, { ...(q.types ? { types: q.types } : {}), limit });\n return { nodes, total: nodes.length, limit, offset: 0 };\n }\n const nodes = this.db.getNodes(sid, { limit, offset });\n return { nodes, total, limit, offset };\n }\n\n node(ctx: TenantContext, id: string, sessionId?: string): NodeRow | undefined {\n return this.db.getNode(this.resolveSession(ctx, sessionId), id);\n }\n\n dependencies(ctx: TenantContext, id: string, q: DependencyQuery, sessionId?: string): TraversalResult {\n const sid = this.resolveSession(ctx, sessionId);\n return this.db.getDependencies(sid, id, {\n direction: q.direction ?? 'downstream',\n maxDepth: clamp(q.maxDepth ?? 8, 1, MAX_DEPTH),\n });\n }\n\n diff(ctx: TenantContext, base: string, current: string): TopologyDiff {\n // Tenant-isolate both endpoints before touching the diff SQL: a foreign session id is 404, not data.\n for (const id of [base, current]) {\n const s = this.db.getSession(id);\n if (!s || s.tenant !== ctx.tenant) throw new NotFoundError(`session not found`);\n }\n try {\n return this.db.diffSessions(base, current);\n } catch (err) {\n throw new NotFoundError(err instanceof Error ? err.message : 'diff failed');\n }\n }\n\n sessions(ctx: TenantContext): SessionRow[] {\n return this.db.getSessions(ctx.tenant);\n }\n\n health(ctx: TenantContext): HealthResult {\n return { store: 'sqlite', sessions: this.db.getSessions(ctx.tenant).length };\n }\n}\n\n/** Construct the default SQLite-backed read query backend. */\nexport function createSqliteQueryBackend(db: CartographyDB, defaultSession: string | 'latest' = 'latest'): QueryBackend {\n return new SqliteQueryBackend(db, defaultSession);\n}\n","/**\n * The read-only API HTTP server (4.2), on Node's built-in `http` (zero new runtime dep).\n *\n * Request flow mirrors the MCP transport (`src/mcp/transports.ts`): the CVE-2025-66414\n * bind guards run at startup (shared `assertSafeBind`); per request the Host header is\n * checked against the allowlist (DNS-rebinding), then the bearer token is verified\n * **before any backend access**, then the tenant is resolved, then the route dispatches.\n * REST handlers are pure (`rest.ts`); GraphQL is wired when enabled (`graphql.ts`). One\n * structured stderr access line per request — never the token, never query values.\n */\n\nimport http, { type IncomingMessage, type ServerResponse } from 'node:http';\nimport type { AddressInfo } from 'node:net';\nimport { DEFAULT_TENANT } from '../db.js';\nimport { assertSafeBind, bearerToken, defaultAllowedHosts, type BindGuardOptions } from './auth.js';\nimport { resolveTenant, InvalidTenantError, type TenantOptions } from './tenant.js';\nimport { resolvePrincipal } from '../auth/identity.js';\nimport { authorize, AuthorizationError } from '../auth/rbac.js';\nimport type { CredentialStore } from '../auth/types.js';\nimport type { QueryBackend } from '../store/query.js';\nimport type { RestDeps } from './rest.js';\nimport { handleSummary, handleNodes, handleDependencies, handleDiff, handleSessions, handleHealth } from './rest.js';\nimport { buildOpenApiDocument } from './openapi.js';\nimport { executeGraphql, handleGraphqlGet } from './graphql.js';\n\nexport interface ApiServerOptions extends BindGuardOptions {\n backend: QueryBackend;\n version: string;\n /** CORS Origin allowlist. Default: none (same-origin only). */\n allowedOrigins?: string[];\n /** Tenant resolution options (header name / default tenant). */\n tenant?: TenantOptions;\n /** Expose `/graphql` (default true). */\n graphql?: boolean;\n /**\n * RBAC (4.5). When `store` holds credentials, the API runs in RBAC mode: a request's\n * bearer token must resolve to a {@link Principal} (else 401), the principal's role must\n * permit `read` (else 403), and reads are **pinned to the principal's tenant** (any\n * caller-supplied tenant header/param is ignored). Without a populated store the legacy\n * behavior is preserved: the configured shared `token` (or open loopback) is one implicit\n * admin that may still select a tenant via header/param.\n */\n auth?: { store?: CredentialStore; required?: boolean };\n /** Access logger (stderr). */\n log?: (msg: string) => void;\n}\n\nconst DEPENDENCIES_RE = /^\\/v1\\/nodes\\/(.+)\\/dependencies$/;\nconst MAX_GRAPHQL_BYTES = 1024 * 1024; // 1 MB query body cap\n\nfunction send(res: ServerResponse, status: number, body: unknown, headers: Record<string, string> = {}): void {\n res.writeHead(status, { 'content-type': 'application/json', ...headers }).end(JSON.stringify(body));\n}\n\nasync function readBody(req: IncomingMessage, cap: number): Promise<{ overflow: boolean; value: unknown }> {\n const chunks: Buffer[] = [];\n let total = 0;\n let overflow = false;\n for await (const chunk of req) {\n if (overflow) continue;\n const buf = chunk as Buffer;\n total += buf.length;\n if (total > cap) { overflow = true; chunks.length = 0; continue; }\n chunks.push(buf);\n }\n if (overflow) return { overflow: true, value: undefined };\n if (chunks.length === 0) return { overflow: false, value: undefined };\n try { return { overflow: false, value: JSON.parse(Buffer.concat(chunks).toString('utf8')) }; }\n catch { return { overflow: false, value: undefined }; }\n}\n\n/** Start the read-only API server. Resolves once it is listening. */\nexport async function runApi(opts: ApiServerOptions): Promise<http.Server> {\n const host = opts.host ?? '127.0.0.1';\n const requestedPort = opts.port ?? 3737;\n const token = opts.token;\n const graphqlEnabled = opts.graphql !== false;\n const defaultTenant = opts.tenant?.defaultTenant ?? DEFAULT_TENANT;\n const authStore = opts.auth?.store;\n // RBAC mode is active only when real credentials exist; otherwise the legacy\n // shared-token / open-dev behavior (and tenant-by-header) is preserved.\n const rbacMode = !!(authStore && authStore.count() > 0);\n const log = opts.log ?? (() => {});\n const restDeps: RestDeps = { backend: opts.backend, version: opts.version };\n const openApiDoc = buildOpenApiDocument({ version: opts.version });\n const allowedOrigins = opts.allowedOrigins ?? [];\n\n // CVE-2025-66414 + mandatory-token guards (shared with the MCP transport).\n assertSafeBind({ host, port: requestedPort, ...(opts.allowedHosts ? { allowedHosts: opts.allowedHosts } : {}), ...(token ? { token } : {}) });\n\n // Filled in after listen() so port:0 (ephemeral) still produces a correct Host allowlist.\n let allowedHosts: string[] = opts.allowedHosts ?? [];\n\n const corsHeaders = (req: IncomingMessage): Record<string, string> => {\n const origin = req.headers['origin'];\n if (typeof origin === 'string' && allowedOrigins.includes(origin)) {\n return {\n 'access-control-allow-origin': origin,\n 'vary': 'Origin',\n 'access-control-allow-methods': 'GET, POST, OPTIONS',\n 'access-control-allow-headers': 'authorization, content-type, x-cartograph-tenant',\n };\n }\n return {};\n };\n\n const server = http.createServer((req, res) => {\n const started = Date.now();\n let tenantLabel = '-';\n const finish = (status: number): void => {\n log(`[cartography-api] ${req.method ?? '-'} ${req.url ?? '-'} ${status} ${Date.now() - started}ms tenant=${tenantLabel}`);\n };\n void (async () => {\n try {\n const url = new URL(req.url ?? '/', `http://${req.headers['host'] ?? host}`);\n const path = url.pathname;\n const cors = corsHeaders(req);\n\n // CORS preflight.\n if (req.method === 'OPTIONS') { res.writeHead(204, cors).end(); finish(204); return; }\n\n // DNS-rebinding protection: the Host header must be on the allowlist.\n const hostHeader = (req.headers['host'] ?? '').toLowerCase();\n if (!allowedHosts.some((h) => h.toLowerCase() === hostHeader)) { send(res, 403, { error: 'host not allowed' }, cors); finish(403); return; }\n\n // Public endpoints — no auth and, crucially, NO caller-supplied tenant: health uses the\n // server's default tenant so an unauthenticated probe can't enumerate other tenants' session\n // counts (`?tenant=acme` oracle). They are also handled before tenant resolution so a probe\n // with a malformed tenant header still succeeds.\n if (path === '/v1/openapi.json' && req.method === 'GET') { send(res, 200, openApiDoc, cors); finish(200); return; }\n if (path === '/v1/health') {\n if (req.method !== 'GET') { send(res, 405, { error: 'method not allowed' }, { allow: 'GET', ...cors }); finish(405); return; }\n tenantLabel = defaultTenant;\n const r = handleHealth({ tenant: defaultTenant }, restDeps);\n send(res, r.status, r.body, cors);\n finish(r.status);\n return;\n }\n\n // Authenticate (RBAC 4.5) before touching any backend state. Resolve the bearer\n // token to a principal; 401 if it doesn't resolve (RBAC unknown token / wrong\n // shared token / required-but-absent).\n const principal = resolvePrincipal(bearerToken(req.headers['authorization']), {\n ...(authStore ? { store: authStore } : {}),\n ...(token ? { sharedToken: token } : {}),\n defaultTenant,\n ...(opts.auth?.required ? { required: true } : {}),\n });\n if (!principal) {\n send(res, 401, { error: 'unauthorized' }, { 'www-authenticate': 'Bearer', ...cors });\n finish(401);\n return;\n }\n // Every API endpoint is a read; a principal below `viewer` cannot exist, so this\n // only ever fails if a future write endpoint demands a higher role.\n try {\n authorize(principal, 'read');\n } catch (err) {\n if (err instanceof AuthorizationError) { send(res, 403, { error: 'forbidden' }, cors); finish(403); return; }\n throw err;\n }\n\n // Tenant: in RBAC mode pin to the principal's tenant (ignore any caller-supplied\n // tenant — no cross-tenant read by header spoofing). Otherwise (shared-token / open\n // dev) preserve the legacy behavior: the implicit admin may select a tenant.\n let ctx;\n if (rbacMode) {\n ctx = { tenant: principal.tenant };\n tenantLabel = principal.tenant;\n } else {\n try {\n ctx = resolveTenant(req, url, opts.tenant ?? {});\n tenantLabel = ctx.tenant;\n } catch (err) {\n if (err instanceof InvalidTenantError) { send(res, 400, { error: 'invalid tenant' }, cors); finish(400); return; }\n throw err;\n }\n }\n\n // ── GraphQL ──\n if (graphqlEnabled && path === '/graphql') {\n if (req.method === 'GET') { const g = handleGraphqlGet(); res.writeHead(g.status, { 'content-type': 'text/plain; charset=utf-8', ...cors }).end(g.body); finish(g.status); return; }\n if (req.method === 'POST') {\n const { overflow, value } = await readBody(req, MAX_GRAPHQL_BYTES);\n if (overflow) { send(res, 413, { error: 'payload too large' }, cors); finish(413); return; }\n const result = await executeGraphql(ctx, value, { backend: opts.backend });\n send(res, 200, result, cors);\n finish(200);\n return;\n }\n send(res, 405, { error: 'method not allowed' }, { allow: 'GET, POST', ...cors });\n finish(405);\n return;\n }\n\n // ── REST (GET only; /v1/health + /v1/openapi.json already handled above) ──\n if (path.startsWith('/v1/')) {\n if (req.method !== 'GET') { send(res, 405, { error: 'method not allowed' }, { allow: 'GET', ...cors }); finish(405); return; }\n const result = dispatchRest(ctx, path, url, restDeps);\n if (result) { send(res, result.status, result.body, cors); finish(result.status); return; }\n }\n\n send(res, 404, { error: 'not found' }, cors);\n finish(404);\n } catch (err) {\n process.stderr.write(`[cartography-api] request failed: ${err instanceof Error ? err.message : String(err)}\\n`);\n if (!res.headersSent) send(res, 500, { error: 'internal error' });\n finish(500);\n }\n })();\n });\n\n await new Promise<void>((resolve) => server.listen(requestedPort, host, resolve));\n const actualPort = (server.address() as AddressInfo).port;\n if (allowedHosts.length === 0) allowedHosts = defaultAllowedHosts(host, actualPort);\n return server;\n}\n\n/** Route a `/v1/...` GET to its REST handler. Returns `undefined` for an unknown path (→ 404). */\nfunction dispatchRest(ctx: { tenant: string }, path: string, url: URL, deps: RestDeps): { status: number; body: unknown } | undefined {\n switch (path) {\n case '/v1/summary': return handleSummary(ctx, url, deps);\n case '/v1/nodes': return handleNodes(ctx, url, deps);\n case '/v1/diff': return handleDiff(ctx, url, deps);\n case '/v1/sessions': return handleSessions(ctx, deps);\n default: {\n const m = DEPENDENCIES_RE.exec(path);\n if (m) return handleDependencies(ctx, decodeURIComponent(m[1]), url, deps);\n return undefined;\n }\n }\n}\n","/**\n * Per-request tenant resolution for the API server (4.2).\n *\n * The tenant (org-scope) is a first-class request property: it is resolved once,\n * up front, and threaded into every {@link QueryBackend} call so isolation is\n * structural, not bolted on. A request may name a tenant via the\n * `X-Cartograph-Tenant` header or a `?tenant=` query param; absent either, it\n * defaults to the server's configured default (normally `DEFAULT_TENANT='local'`).\n *\n * Validation reuses `normalizeTenant` (the single charset-allowlisted validator,\n * `^[\\w.@:+-]{1,128}$`) — but here we **reject** a malformed value with a typed\n * error (→ HTTP 400) rather than silently falling back, so a client never believes\n * it is scoped to one tenant while being served another. The raw input is never\n * reflected into a response.\n */\n\nimport type { IncomingMessage } from 'node:http';\nimport { normalizeTenant, DEFAULT_TENANT } from '../db.js';\nimport type { TenantContext } from '../store/query.js';\n\nexport const TENANT_HEADER = 'x-cartograph-tenant';\n\n/** The supplied tenant value did not pass the charset/length allowlist → HTTP 400. */\nexport class InvalidTenantError extends Error {\n constructor() {\n super('invalid tenant');\n this.name = 'InvalidTenantError';\n }\n}\n\nexport interface TenantOptions {\n /** Default tenant when the request names none. Defaults to `DEFAULT_TENANT` ('local'). */\n defaultTenant?: string;\n /** Header to read the tenant from. Defaults to `x-cartograph-tenant`. */\n header?: string;\n}\n\n/**\n * Resolve the tenant from the request header or `?tenant=` query param, else the\n * configured default. A supplied-but-malformed value throws {@link InvalidTenantError}\n * (the caller maps it to a 400) instead of silently defaulting.\n */\nexport function resolveTenant(req: IncomingMessage, url: URL, opts: TenantOptions = {}): TenantContext {\n const headerName = (opts.header ?? TENANT_HEADER).toLowerCase();\n const raw = headerValue(req, headerName) ?? url.searchParams.get('tenant') ?? undefined;\n\n if (raw === undefined || raw === '') {\n return { tenant: opts.defaultTenant ?? DEFAULT_TENANT };\n }\n\n // Reject over-length up front: normalizeTenant *truncates* to 128 chars before validating,\n // so without this two distinct long tenant names would collide on a shared 128-char prefix\n // and be silently served the same partition. The doc promise is \"never silently re-scoped\".\n if (raw.trim().length > 128) {\n throw new InvalidTenantError();\n }\n\n // normalizeTenant returns DEFAULT_TENANT for anything that fails the allowlist. We\n // must distinguish \"client legitimately asked for the default tenant\" from \"client\n // sent garbage\", so we re-validate: a non-default raw value that normalizes to the\n // default was malformed → 400.\n const normalized = normalizeTenant(raw);\n if (normalized === DEFAULT_TENANT && raw.trim() !== DEFAULT_TENANT) {\n throw new InvalidTenantError();\n }\n return { tenant: normalized };\n}\n\nfunction headerValue(req: IncomingMessage, name: string): string | undefined {\n const v = req.headers[name];\n if (Array.isArray(v)) return v[0];\n return v;\n}\n","/**\n * Response contracts for the read-only API (4.2), as zod schemas.\n *\n * These are the **single source of truth**: the OpenAPI document and the GraphQL SDL\n * are generated from them (`openapi.ts`/`graphql.ts`), and the REST handlers validate\n * their outgoing bodies against them in non-production (a cheap correctness guard). The\n * projections are deliberately narrower than the internal row types — they exclude\n * session attribution (`hostname`/`user`/`machineId`/`organization`/`config`) and node\n * `metadata`/`globalId`/`contentHash`, so the API never leaks raw identifying data\n * (consent posture, SPEC §6). The node model itself is already host:port-level and\n * redacted at ingest.\n */\n\nimport { z } from 'zod';\nimport { COST_PERIODS, ANOMALY_KINDS, ANOMALY_SEVERITIES } from '../types.js';\n\nexport const DIRECTIONS = ['downstream', 'upstream', 'both'] as const;\n\nconst CostSchema = z.object({\n amount: z.number(),\n currency: z.string(),\n period: z.enum(COST_PERIODS),\n source: z.string().optional(),\n});\n\n/** Public node projection (no metadata/globalId/contentHash/session internals). */\nexport const NodeSchema = z.object({\n id: z.string(),\n type: z.string(),\n name: z.string(),\n confidence: z.number(),\n domain: z.string().optional(),\n subDomain: z.string().optional(),\n qualityScore: z.number().optional(),\n owner: z.string().optional(),\n cost: CostSchema.optional(),\n tags: z.array(z.string()),\n});\n\nexport const EdgeSchema = z.object({\n sourceId: z.string(),\n targetId: z.string(),\n relationship: z.string(),\n confidence: z.number(),\n evidence: z.string(),\n});\n\nexport const AnomalySchema = z.object({\n nodeId: z.string(),\n kind: z.enum(ANOMALY_KINDS),\n severity: z.enum(ANOMALY_SEVERITIES),\n reason: z.string(),\n});\n\nconst TopConnectedSchema = z.object({\n id: z.string(),\n name: z.string(),\n type: z.string(),\n degree: z.number().int(),\n});\n\nconst CostByDomainSchema = z.object({\n domain: z.string(),\n currency: z.string(),\n period: z.string(),\n total: z.number(),\n nodes: z.number().int(),\n});\n\nconst CostByOwnerSchema = z.object({\n owner: z.string(),\n currency: z.string(),\n period: z.string(),\n total: z.number(),\n nodes: z.number().int(),\n});\n\nexport const SummaryResponse = z.object({\n sessionId: z.string(),\n totals: z.object({ nodes: z.number().int(), edges: z.number().int() }),\n nodesByType: z.record(z.string(), z.number().int()),\n nodesByDomain: z.record(z.string(), z.number().int()),\n edgesByRelationship: z.record(z.string(), z.number().int()),\n topConnected: z.array(TopConnectedSchema),\n anomalies: z.array(AnomalySchema),\n contributors: z.number().int(),\n costByDomain: z.array(CostByDomainSchema),\n costByOwner: z.array(CostByOwnerSchema),\n costCoverage: z.object({ withCost: z.number().int(), total: z.number().int() }),\n});\n\nexport const NodesResponse = z.object({\n nodes: z.array(NodeSchema),\n total: z.number().int(),\n limit: z.number().int(),\n offset: z.number().int(),\n});\n\nconst DependencyNodeSchema = NodeSchema.extend({ depth: z.number().int() });\n\nexport const DependenciesResponse = z.object({\n root: NodeSchema.optional(),\n direction: z.enum(DIRECTIONS),\n maxDepth: z.number().int(),\n nodes: z.array(DependencyNodeSchema),\n edges: z.array(EdgeSchema),\n});\n\nconst SessionEndpointSchema = z.object({\n sessionId: z.string(),\n startedAt: z.string(),\n nodeCount: z.number().int(),\n edgeCount: z.number().int(),\n});\n\nconst NodeChangeSchema = z.object({\n id: z.string(),\n changedFields: z.array(z.string()),\n confidenceDelta: z.number(),\n});\n\nexport const DiffResponse = z.object({\n base: SessionEndpointSchema,\n current: SessionEndpointSchema,\n summary: z.object({\n nodesAdded: z.number().int(),\n nodesRemoved: z.number().int(),\n nodesChanged: z.number().int(),\n edgesAdded: z.number().int(),\n edgesRemoved: z.number().int(),\n }),\n nodes: z.object({\n added: z.array(NodeSchema),\n removed: z.array(NodeSchema),\n changed: z.array(NodeChangeSchema),\n unchanged: z.number().int(),\n }),\n edges: z.object({\n added: z.array(EdgeSchema),\n removed: z.array(EdgeSchema),\n unchanged: z.number().int(),\n }),\n anomalies: z.object({ added: z.array(AnomalySchema) }),\n});\n\nexport const SessionSchema = z.object({\n id: z.string(),\n mode: z.literal('discover'),\n startedAt: z.string(),\n completedAt: z.string().optional(),\n name: z.string().optional(),\n tenant: z.string(),\n lastScannedAt: z.string().optional(),\n});\n\nexport const SessionsResponse = z.object({ sessions: z.array(SessionSchema) });\n\nexport const HealthResponse = z.object({\n status: z.literal('ok'),\n version: z.string(),\n store: z.literal('sqlite'),\n sessions: z.number().int(),\n});\n\nexport const ErrorResponse = z.object({\n error: z.string(),\n code: z.string().optional(),\n});\n\n/** Named registry of every response schema — drives the OpenAPI `components.schemas`. */\nexport const API_SCHEMAS = {\n Node: NodeSchema,\n Edge: EdgeSchema,\n Anomaly: AnomalySchema,\n Summary: SummaryResponse,\n Nodes: NodesResponse,\n Dependencies: DependenciesResponse,\n Diff: DiffResponse,\n Session: SessionSchema,\n Sessions: SessionsResponse,\n Health: HealthResponse,\n Error: ErrorResponse,\n} as const;\n","/**\n * REST handlers for the read-only API (4.2).\n *\n * Pure functions of `(ctx, url, deps)` → `{ status, body }` with **no `http` coupling**,\n * so they are unit-testable without a socket and reusable by the GraphQL resolvers. Each\n * projects the internal row/summary types down to the public `schemas.ts` shape (dropping\n * session attribution + node metadata — consent posture), clamps pagination/depth at the\n * edge, and validates its own outgoing body against the matching zod schema in\n * non-production (a cheap contract guard that surfaces drift loudly in tests).\n */\n\nimport { z } from 'zod';\nimport type { NodeRow, EdgeRow, SessionRow, TopologyDiff } from '../types.js';\nimport type { Anomaly } from '../types.js';\nimport type { QueryBackend, TenantContext } from '../store/query.js';\nimport { NotFoundError } from '../store/query.js';\nimport {\n SummaryResponse, NodesResponse, DependenciesResponse, DiffResponse, SessionsResponse, HealthResponse, DIRECTIONS,\n} from './schemas.js';\n\nexport interface RestDeps {\n backend: QueryBackend;\n version: string;\n}\n\nexport interface RestResult {\n status: number;\n body: unknown;\n}\n\n// ── projections (internal row → public API shape) ──────────────────────────────\n// Exported so the GraphQL resolvers (graphql.ts) reuse the exact same projection,\n// keeping REST and GraphQL byte-identical and the consent posture in one place.\n\nexport function toApiNode(n: NodeRow): Record<string, unknown> {\n const out: Record<string, unknown> = { id: n.id, type: n.type, name: n.name, confidence: n.confidence, tags: n.tags };\n if (n.domain !== undefined) out['domain'] = n.domain;\n if (n.subDomain !== undefined) out['subDomain'] = n.subDomain;\n if (n.qualityScore !== undefined) out['qualityScore'] = n.qualityScore;\n if (n.owner !== undefined) out['owner'] = n.owner;\n if (n.cost !== undefined) out['cost'] = n.cost;\n return out;\n}\n\nexport function toApiEdge(e: EdgeRow): Record<string, unknown> {\n return { sourceId: e.sourceId, targetId: e.targetId, relationship: e.relationship, confidence: e.confidence, evidence: e.evidence };\n}\n\nexport function toApiSession(s: SessionRow): Record<string, unknown> {\n const out: Record<string, unknown> = { id: s.id, mode: s.mode, startedAt: s.startedAt, tenant: s.tenant };\n if (s.completedAt !== undefined) out['completedAt'] = s.completedAt;\n if (s.name !== undefined) out['name'] = s.name;\n if (s.lastScannedAt !== undefined) out['lastScannedAt'] = s.lastScannedAt;\n return out;\n}\n\nexport function toApiAnomaly(a: Anomaly): Record<string, unknown> {\n return { nodeId: a.nodeId, kind: a.kind, severity: a.severity, reason: a.reason };\n}\n\n/** Project a `TraversalResult` to the public Dependencies shape (shared by REST + GraphQL). */\nexport function projectDependencies(r: { root?: NodeRow; direction: string; maxDepth: number; nodes: Array<NodeRow & { depth: number }>; edges: EdgeRow[] }): Record<string, unknown> {\n return {\n ...(r.root ? { root: toApiNode(r.root) } : {}),\n direction: r.direction,\n maxDepth: r.maxDepth,\n nodes: r.nodes.map((n) => ({ ...toApiNode(n), depth: n.depth })),\n edges: r.edges.map(toApiEdge),\n };\n}\n\n/** Project a `TopologyDiff` to the public Diff shape (shared by REST + GraphQL). */\nexport function projectDiff(diff: TopologyDiff): Record<string, unknown> {\n return {\n base: { sessionId: diff.base.sessionId, startedAt: diff.base.startedAt, nodeCount: diff.base.nodeCount, edgeCount: diff.base.edgeCount },\n current: { sessionId: diff.current.sessionId, startedAt: diff.current.startedAt, nodeCount: diff.current.nodeCount, edgeCount: diff.current.edgeCount },\n summary: diff.summary,\n nodes: {\n added: diff.nodes.added.map(toApiNode),\n removed: diff.nodes.removed.map(toApiNode),\n changed: diff.nodes.changed.map((c) => ({ id: c.id, changedFields: c.changedFields, confidenceDelta: c.confidenceDelta })),\n unchanged: diff.nodes.unchanged,\n },\n edges: {\n added: diff.edges.added.map(toApiEdge),\n removed: diff.edges.removed.map(toApiEdge),\n unchanged: diff.edges.unchanged,\n },\n anomalies: { added: diff.anomalies.added.map(toApiAnomaly) },\n };\n}\n\n// ── helpers ────────────────────────────────────────────────────────────────────\n\nfunction ok(body: unknown): RestResult {\n return { status: 200, body };\n}\nfunction badRequest(error: string): RestResult {\n return { status: 400, body: { error } };\n}\nfunction notFound(error = 'not found'): RestResult {\n return { status: 404, body: { error } };\n}\n\n/** Run a handler body, mapping a thrown {@link NotFoundError} to a 404. */\nfunction guard(fn: () => RestResult): RestResult {\n try {\n return fn();\n } catch (err) {\n if (err instanceof NotFoundError) return notFound(err.message);\n throw err;\n }\n}\n\n/** In non-production, assert the outgoing body matches its declared schema (contract guard). */\nfunction validateOut(schema: z.ZodType, body: unknown): unknown {\n if (process.env['NODE_ENV'] !== 'production') {\n const r = schema.safeParse(body);\n if (!r.success) throw new Error(`API response failed its own schema contract: ${r.error.message}`);\n }\n return body;\n}\n\n/** Parse an integer query param. A non-integer (float, NaN, junk) yields undefined → the\n * backend default applies, never a fractional value that would 500 in SQLite (`LIMIT 2.5`). */\nfunction intParam(url: URL, name: string): number | undefined {\n const raw = url.searchParams.get(name);\n if (raw === null || raw.trim() === '') return undefined;\n const n = Number(raw);\n return Number.isInteger(n) ? n : undefined;\n}\n\nfunction sessionParam(url: URL): string | undefined {\n return url.searchParams.get('session') ?? undefined;\n}\n\n// ── handlers ─────────────────────────────────────────────────────────────────\n\nexport function handleSummary(ctx: TenantContext, url: URL, d: RestDeps): RestResult {\n return guard(() => ok(validateOut(SummaryResponse, d.backend.summary(ctx, sessionParam(url)))));\n}\n\nexport function handleNodes(ctx: TenantContext, url: URL, d: RestDeps): RestResult {\n return guard(() => {\n const search = url.searchParams.get('search') ?? undefined;\n const typesRaw = url.searchParams.get('types');\n const types = typesRaw ? typesRaw.split(',').map((s) => s.trim()).filter(Boolean) : undefined;\n const limit = intParam(url, 'limit');\n const offset = intParam(url, 'offset');\n const r = d.backend.nodes(\n ctx,\n { ...(search ? { search } : {}), ...(types ? { types } : {}), ...(limit !== undefined ? { limit } : {}), ...(offset !== undefined ? { offset } : {}) },\n sessionParam(url),\n );\n return ok(validateOut(NodesResponse, { nodes: r.nodes.map(toApiNode), total: r.total, limit: r.limit, offset: r.offset }));\n });\n}\n\nexport function handleDependencies(ctx: TenantContext, id: string, url: URL, d: RestDeps): RestResult {\n const directionRaw = url.searchParams.get('direction');\n if (directionRaw !== null && !(DIRECTIONS as readonly string[]).includes(directionRaw)) {\n return badRequest(`direction must be one of ${DIRECTIONS.join(', ')}`);\n }\n return guard(() => {\n const direction = (directionRaw ?? undefined) as 'downstream' | 'upstream' | 'both' | undefined;\n const maxDepth = intParam(url, 'maxDepth');\n const r = d.backend.dependencies(\n ctx,\n id,\n { ...(direction ? { direction } : {}), ...(maxDepth !== undefined ? { maxDepth } : {}) },\n sessionParam(url),\n );\n return ok(validateOut(DependenciesResponse, projectDependencies(r)));\n });\n}\n\nexport function handleDiff(ctx: TenantContext, url: URL, d: RestDeps): RestResult {\n const base = url.searchParams.get('base');\n const current = url.searchParams.get('current');\n if (!base || !current) return badRequest('both `base` and `current` query params are required');\n return guard(() => {\n const diff: TopologyDiff = d.backend.diff(ctx, base, current);\n return ok(validateOut(DiffResponse, projectDiff(diff)));\n });\n}\n\nexport function handleSessions(ctx: TenantContext, d: RestDeps): RestResult {\n return guard(() => ok(validateOut(SessionsResponse, { sessions: d.backend.sessions(ctx).map(toApiSession) })));\n}\n\nexport function handleHealth(ctx: TenantContext, d: RestDeps): RestResult {\n const h = d.backend.health(ctx);\n return ok(validateOut(HealthResponse, { status: 'ok', version: d.version, store: h.store, sessions: h.sessions }));\n}\n","/**\n * OpenAPI 3.1 document generation for the read-only API (4.2).\n *\n * The document is **generated from the zod response schemas** (`schemas.ts`), never\n * hand-maintained, so it cannot drift from what the server actually returns. A\n * committed copy lives at `docs/api/openapi.json`; a test asserts the built document\n * deep-equals it (drift guard) and validates under `ajv`.\n *\n * `zodToJsonSchema` is a small, fail-closed projection covering exactly the zod\n * constructs `schemas.ts` uses (object/array/string/number/integer/boolean/enum/\n * literal/record/optional). An unsupported construct throws, so a future schema\n * change can't be silently mis-projected. (The provider tool layer has its own flat\n * converter in `src/providers/zod-schema.ts`; this one is recursive and serves the\n * API's nested response shapes.)\n */\n\nimport { z } from 'zod';\nimport { API_SCHEMAS } from './schemas.js';\n\n/** zod-internal view (v4): every schema exposes its definition under `.def`. */\ninterface ZodDef {\n type?: string;\n shape?: Record<string, z.ZodTypeAny>;\n element?: z.ZodTypeAny;\n valueType?: z.ZodTypeAny;\n innerType?: z.ZodTypeAny;\n values?: unknown[];\n entries?: Record<string, string>;\n checks?: { _zod?: { def?: { check?: string } } }[];\n}\n\nfunction defOf(schema: z.ZodTypeAny): ZodDef {\n return (schema as unknown as { def?: ZodDef }).def ?? {};\n}\n\n/** Peel an optional/nullable wrapper, reporting whether the field is required. */\nfunction unwrapOptional(schema: z.ZodTypeAny): { inner: z.ZodTypeAny; optional: boolean } {\n const def = defOf(schema);\n if ((def.type === 'optional' || def.type === 'nullable') && def.innerType) {\n return { inner: def.innerType, optional: true };\n }\n return { inner: schema, optional: false };\n}\n\n/** Project a zod schema to a JSON-Schema (2020-12) fragment. Fail-closed on the unknown. */\nexport function zodToJsonSchema(schema: z.ZodTypeAny): Record<string, unknown> {\n const def = defOf(schema);\n switch (def.type) {\n case 'string':\n return { type: 'string' };\n case 'number': {\n const isInt = (def.checks ?? []).some((c) => c._zod?.def?.check === 'number_format');\n return { type: isInt ? 'integer' : 'number' };\n }\n case 'boolean':\n return { type: 'boolean' };\n case 'literal': {\n const values = def.values ?? [];\n return values.length === 1 ? { const: values[0] } : { enum: values };\n }\n case 'enum':\n return { type: 'string', enum: Object.values(def.entries ?? {}) };\n case 'array':\n return { type: 'array', items: def.element ? zodToJsonSchema(def.element) : {} };\n case 'record':\n return { type: 'object', additionalProperties: def.valueType ? zodToJsonSchema(def.valueType) : true };\n case 'optional':\n case 'nullable':\n return def.innerType ? zodToJsonSchema(def.innerType) : {};\n case 'object': {\n const shape = def.shape ?? {};\n const properties: Record<string, unknown> = {};\n const required: string[] = [];\n for (const key of Object.keys(shape)) {\n const { inner, optional } = unwrapOptional(shape[key]);\n properties[key] = zodToJsonSchema(inner);\n if (!optional) required.push(key);\n }\n return { type: 'object', properties, required, additionalProperties: false };\n }\n default:\n throw new Error(`zodToJsonSchema: unsupported zod construct \"${def.type ?? 'unknown'}\". Extend src/api/openapi.ts.`);\n }\n}\n\nexport interface OpenApiOptions {\n version: string;\n}\n\nconst TENANT_PARAM = {\n name: 'tenant',\n in: 'query',\n required: false,\n description: 'Tenant/org scope (also accepted via the X-Cartograph-Tenant header). Defaults to \"local\".',\n schema: { type: 'string' },\n};\nconst SESSION_PARAM = {\n name: 'session',\n in: 'query',\n required: false,\n description: 'Session id to query, or omit for the latest discovery session.',\n schema: { type: 'string' },\n};\n\nfunction errorResponses(): Record<string, unknown> {\n const err = { description: 'Error', content: { 'application/json': { schema: { $ref: '#/components/schemas/Error' } } } };\n return { '400': { ...err, description: 'Bad request' }, '401': { ...err, description: 'Unauthorized' }, '404': { ...err, description: 'Not found' } };\n}\n\nfunction ok(ref: string, description: string): Record<string, unknown> {\n return { description, content: { 'application/json': { schema: { $ref: `#/components/schemas/${ref}` } } } };\n}\n\n/** Build the OpenAPI 3.1 document from the zod schemas + the static route table. Deterministic. */\nexport function buildOpenApiDocument(opts: OpenApiOptions): Record<string, unknown> {\n const schemas: Record<string, unknown> = {};\n for (const [name, schema] of Object.entries(API_SCHEMAS)) {\n schemas[name] = zodToJsonSchema(schema as z.ZodTypeAny);\n }\n\n return {\n openapi: '3.1.0',\n info: {\n title: 'Cartograph API',\n version: opts.version,\n description: 'Read-only REST API over the discovered infrastructure/agentic-AI topology. ' +\n 'Every endpoint is tenant-scoped and bearer-authenticated.',\n },\n servers: [{ url: '/' }],\n security: [{ bearerAuth: [] }],\n components: {\n securitySchemes: { bearerAuth: { type: 'http', scheme: 'bearer' } },\n schemas,\n },\n paths: {\n '/v1/health': {\n get: {\n summary: 'Liveness + store/coverage probe',\n security: [],\n responses: { '200': ok('Health', 'Service health') },\n },\n },\n '/v1/openapi.json': {\n get: {\n summary: 'This OpenAPI document',\n security: [],\n responses: { '200': { description: 'OpenAPI 3.1 document', content: { 'application/json': { schema: { type: 'object' } } } } },\n },\n },\n '/v1/summary': {\n get: {\n summary: 'Low-token topology aggregate for the resolved session',\n parameters: [SESSION_PARAM, TENANT_PARAM],\n responses: { '200': ok('Summary', 'Topology summary'), ...errorResponses() },\n },\n },\n '/v1/nodes': {\n get: {\n summary: 'List/search/paginate nodes',\n parameters: [\n { name: 'search', in: 'query', required: false, description: 'Lexical/semantic search anchor.', schema: { type: 'string' } },\n { name: 'types', in: 'query', required: false, description: 'Comma-separated node-type filter.', schema: { type: 'string' } },\n { name: 'limit', in: 'query', required: false, description: 'Page size (default 100, max 1000).', schema: { type: 'integer' } },\n { name: 'offset', in: 'query', required: false, description: 'Page offset (ignored for search).', schema: { type: 'integer' } },\n SESSION_PARAM, TENANT_PARAM,\n ],\n responses: { '200': ok('Nodes', 'A page of nodes'), ...errorResponses() },\n },\n },\n '/v1/nodes/{id}/dependencies': {\n get: {\n summary: 'Dependency traversal from a node',\n parameters: [\n { name: 'id', in: 'path', required: true, description: 'Node id (\"{type}:{id}\").', schema: { type: 'string' } },\n { name: 'direction', in: 'query', required: false, description: 'downstream | upstream | both (default downstream).', schema: { type: 'string', enum: ['downstream', 'upstream', 'both'] } },\n { name: 'maxDepth', in: 'query', required: false, description: 'Traversal depth (default 8, max 64).', schema: { type: 'integer' } },\n SESSION_PARAM, TENANT_PARAM,\n ],\n responses: { '200': ok('Dependencies', 'Traversal result'), ...errorResponses() },\n },\n },\n '/v1/diff': {\n get: {\n summary: 'Compare two sessions (drift)',\n parameters: [\n { name: 'base', in: 'query', required: true, description: 'Base session id.', schema: { type: 'string' } },\n { name: 'current', in: 'query', required: true, description: 'Current session id.', schema: { type: 'string' } },\n TENANT_PARAM,\n ],\n responses: { '200': ok('Diff', 'Topology delta'), ...errorResponses() },\n },\n },\n '/v1/sessions': {\n get: {\n summary: 'List discovery sessions for the tenant',\n parameters: [TENANT_PARAM],\n responses: { '200': ok('Sessions', 'Sessions'), ...errorResponses() },\n },\n },\n },\n };\n}\n","/**\n * Hand-rolled, zero-dependency GraphQL layer for the read-only API (4.2).\n *\n * Mirrors REST over `POST /graphql` (and serves the SDL on `GET /graphql`) without\n * adding a `graphql`/`apollo` runtime dependency. Resolvers delegate to the same\n * {@link QueryBackend} and reuse the REST projections (`rest.ts`), so REST and GraphQL\n * return byte-identical shapes and the consent posture stays in one place. It is\n * strictly **read-only**: there is no `Mutation` type and a `mutation` document is\n * rejected. A small tokenizer/parser handles the query subset the schema needs\n * (fields, arguments, variables, nested selections) and a minimal `__schema`\n * introspection response keeps GraphiQL-style clients working.\n */\n\nimport type { QueryBackend, TenantContext } from '../store/query.js';\nimport { toApiNode, toApiSession, projectDependencies, projectDiff } from './rest.js';\n\nexport interface GraphqlDeps {\n backend: QueryBackend;\n}\n\nexport interface GraphqlResult {\n data?: unknown;\n errors?: Array<{ message: string }>;\n}\n\nexport const SDL = `# Cartograph read-only GraphQL API (4.2). Mirrors the REST surface.\nschema { query: Query }\n\ntype Query {\n summary(session: String): Summary\n nodes(search: String, types: [String!], limit: Int, offset: Int, session: String): NodeConnection\n node(id: String!, session: String): Node\n dependencies(id: String!, direction: Direction, maxDepth: Int, session: String): Dependencies\n diff(base: String!, current: String!): Diff\n sessions: [Session!]!\n}\n\nenum Direction { downstream upstream both }\n\ntype Totals { nodes: Int! edges: Int! }\ntype Count { key: String! value: Int! }\ntype TopConnected { id: String! name: String! type: String! degree: Int! }\ntype Anomaly { nodeId: String! kind: String! severity: String! reason: String! }\ntype Cost { amount: Float! currency: String! period: String! source: String }\ntype CostRollup { key: String! currency: String! period: String! total: Float! nodes: Int! }\ntype CostCoverage { withCost: Int! total: Int! }\n\ntype Node {\n id: String! type: String! name: String! confidence: Float!\n domain: String subDomain: String qualityScore: Float owner: String cost: Cost tags: [String!]!\n}\ntype DependencyNode {\n id: String! type: String! name: String! confidence: Float!\n domain: String subDomain: String qualityScore: Float owner: String cost: Cost tags: [String!]! depth: Int!\n}\ntype Edge { sourceId: String! targetId: String! relationship: String! confidence: Float! evidence: String! }\n\ntype Summary {\n sessionId: String!\n totals: Totals!\n topConnected: [TopConnected!]!\n anomalies: [Anomaly!]!\n contributors: Int!\n costByDomain: [CostRollup!]!\n costByOwner: [CostRollup!]!\n costCoverage: CostCoverage!\n}\n\ntype NodeConnection { nodes: [Node!]! total: Int! limit: Int! offset: Int! }\ntype Dependencies { root: Node direction: Direction! maxDepth: Int! nodes: [DependencyNode!]! edges: [Edge!]! }\n\ntype SessionEndpoint { sessionId: String! startedAt: String! nodeCount: Int! edgeCount: Int! }\ntype DiffSummary { nodesAdded: Int! nodesRemoved: Int! nodesChanged: Int! edgesAdded: Int! edgesRemoved: Int! }\ntype NodeChange { id: String! changedFields: [String!]! confidenceDelta: Float! }\ntype DiffNodes { added: [Node!]! removed: [Node!]! changed: [NodeChange!]! unchanged: Int! }\ntype DiffEdges { added: [Edge!]! removed: [Edge!]! unchanged: Int! }\ntype DiffAnomalies { added: [Anomaly!]! }\ntype Diff {\n base: SessionEndpoint! current: SessionEndpoint! summary: DiffSummary!\n nodes: DiffNodes! edges: DiffEdges! anomalies: DiffAnomalies!\n}\n\ntype Session { id: String! mode: String! startedAt: String! completedAt: String name: String tenant: String! lastScannedAt: String }\n`;\n\n// ── resolvers (delegate to the backend, reuse the REST projections) ──────────────\n\ntype Args = Record<string, unknown>;\n\nconst resolvers: Record<string, (ctx: TenantContext, args: Args, backend: QueryBackend) => unknown> = {\n summary: (ctx, args, backend) => backend.summary(ctx, str(args['session'])),\n nodes: (ctx, args, backend) => {\n const r = backend.nodes(\n ctx,\n {\n ...(str(args['search']) ? { search: str(args['search']) } : {}),\n ...(Array.isArray(args['types']) ? { types: (args['types'] as unknown[]).map(String) } : {}),\n ...(num(args['limit']) !== undefined ? { limit: num(args['limit']) } : {}),\n ...(num(args['offset']) !== undefined ? { offset: num(args['offset']) } : {}),\n },\n str(args['session']),\n );\n return { nodes: r.nodes.map(toApiNode), total: r.total, limit: r.limit, offset: r.offset };\n },\n node: (ctx, args, backend) => {\n const n = backend.node(ctx, String(args['id']), str(args['session']));\n return n ? toApiNode(n) : null;\n },\n dependencies: (ctx, args, backend) => {\n const r = backend.dependencies(\n ctx,\n String(args['id']),\n {\n ...(str(args['direction']) ? { direction: str(args['direction']) as 'downstream' | 'upstream' | 'both' } : {}),\n ...(num(args['maxDepth']) !== undefined ? { maxDepth: num(args['maxDepth']) } : {}),\n },\n str(args['session']),\n );\n return projectDependencies(r);\n },\n diff: (ctx, args, backend) => projectDiff(backend.diff(ctx, String(args['base']), String(args['current']))),\n sessions: (ctx, _args, backend) => backend.sessions(ctx).map(toApiSession),\n};\n\nfunction str(v: unknown): string | undefined {\n return typeof v === 'string' ? v : undefined;\n}\nfunction num(v: unknown): number | undefined {\n // Integer-only: a fractional limit/offset/maxDepth would 500 in SQLite or break the contract.\n return typeof v === 'number' && Number.isInteger(v) ? v : undefined;\n}\n\n// ── tiny GraphQL query parser (subset: fields, args, variables, nested selections) ──\n\ninterface Selection {\n name: string;\n alias: string;\n args: Args;\n selections: Selection[];\n}\n\nconst NAME_RE = /[_A-Za-z][_0-9A-Za-z]*/y;\n\nfunction tokenize(src: string): string[] {\n const tokens: string[] = [];\n let i = 0;\n while (i < src.length) {\n const c = src[i];\n if (/\\s|,/.test(c)) { i++; continue; }\n if (c === '#') { while (i < src.length && src[i] !== '\\n') i++; continue; }\n if ('{}()[]:!$'.includes(c)) { tokens.push(c); i++; continue; }\n if (c === '\"') {\n let j = i + 1;\n let s = '';\n while (j < src.length && src[j] !== '\"') { s += src[j]; j++; }\n tokens.push(JSON.stringify(s)); // keep quoted to mark as string literal\n i = j + 1;\n continue;\n }\n NAME_RE.lastIndex = i;\n const m = NAME_RE.exec(src);\n if (m && m.index === i) { tokens.push(m[0]); i = NAME_RE.lastIndex; continue; }\n // number (incl. negative / float)\n const numMatch = /-?\\d+(\\.\\d+)?/y;\n numMatch.lastIndex = i;\n const nm = numMatch.exec(src);\n if (nm && nm.index === i) { tokens.push(nm[0]); i = numMatch.lastIndex; continue; }\n throw new Error(`unexpected character '${c}'`);\n }\n return tokens;\n}\n\n/** Explicit selection-set nesting cap (defense-in-depth; our schema nests ≤4 deep). */\nconst MAX_SELECTION_DEPTH = 32;\n\nclass Parser {\n private pos = 0;\n private depth = 0;\n constructor(private readonly tokens: string[], private readonly variables: Args) {}\n\n private peek(): string | undefined { return this.tokens[this.pos]; }\n private next(): string { return this.tokens[this.pos++]; }\n private expect(tok: string): void {\n if (this.tokens[this.pos] !== tok) throw new Error(`expected '${tok}', got '${this.tokens[this.pos] ?? '<eof>'}'`);\n this.pos++;\n }\n\n parseDocument(): Selection[] {\n // Optional `query`/`mutation`/`subscription` Name (varDefs)\n if (this.peek() === 'mutation' || this.peek() === 'subscription') {\n throw new Error('only query operations are supported (read-only API)');\n }\n if (this.peek() === 'query') {\n this.next();\n if (this.peek() && this.peek() !== '{' && this.peek() !== '(') this.next(); // operation name\n if (this.peek() === '(') this.skipBalanced('(', ')'); // variable definitions\n }\n this.expect('{');\n const selections = this.parseSelectionSet();\n return selections;\n }\n\n private skipBalanced(open: string, close: string): void {\n this.expect(open);\n let depth = 1;\n while (depth > 0) {\n const t = this.next();\n if (t === undefined) throw new Error('unbalanced');\n if (t === open) depth++;\n else if (t === close) depth--;\n }\n }\n\n private parseSelectionSet(): Selection[] {\n if (++this.depth > MAX_SELECTION_DEPTH) throw new Error(`selection set nested deeper than ${MAX_SELECTION_DEPTH}`);\n const out: Selection[] = [];\n while (this.peek() !== '}') {\n if (this.peek() === undefined) throw new Error('unexpected end of selection set');\n out.push(this.parseSelection());\n }\n this.expect('}');\n this.depth--;\n return out;\n }\n\n private parseSelection(): Selection {\n let name = this.next();\n const alias = name;\n if (this.peek() === ':') { this.next(); name = this.next(); } // `alias: field` — keep alias, take field name\n const args: Args = {};\n if (this.peek() === '(') {\n this.next();\n while (this.peek() !== ')') {\n const argName = this.next();\n this.expect(':');\n args[argName] = this.parseValue();\n }\n this.expect(')');\n }\n let selections: Selection[] = [];\n if (this.peek() === '{') { this.next(); selections = this.parseSelectionSet(); }\n return { name, alias, args, selections };\n }\n\n private parseValue(): unknown {\n const t = this.next();\n if (t === '$') { const v = this.next(); return this.variables[v]; }\n if (t === '[') {\n const arr: unknown[] = [];\n while (this.peek() !== ']') arr.push(this.parseValue());\n this.expect(']');\n return arr;\n }\n if (t.startsWith('\"')) return JSON.parse(t); // string literal\n if (t === 'true') return true;\n if (t === 'false') return false;\n if (t === 'null') return null;\n if (/^-?\\d+(\\.\\d+)?$/.test(t)) return Number(t);\n return t; // enum / bare identifier\n }\n}\n\n// ── projection of resolved values against the requested selection set ────────────\n\nfunction project(value: unknown, selections: Selection[]): unknown {\n if (value === null || value === undefined) return null;\n if (selections.length === 0) return value;\n if (Array.isArray(value)) return value.map((v) => project(v, selections));\n if (typeof value !== 'object') return value;\n const obj = value as Record<string, unknown>;\n const out: Record<string, unknown> = {};\n for (const sel of selections) {\n if (sel.name === '__typename') { out[sel.alias] = undefined; continue; }\n out[sel.alias] = project(obj[sel.name], sel.selections);\n }\n return out;\n}\n\n// ── minimal introspection ────────────────────────────────────────────────────\n\nfunction introspectionSchema(): Record<string, unknown> {\n const names = [...SDL.matchAll(/^(?:type|enum)\\s+([_A-Za-z][_0-9A-Za-z]*)/gm)].map((m) => m[1]);\n const types = names.map((name) => ({ name, kind: /^[A-Z]/.test(name) ? 'OBJECT' : 'SCALAR' }));\n return {\n __schema: {\n queryType: { name: 'Query' },\n mutationType: null,\n subscriptionType: null,\n types,\n directives: [],\n },\n };\n}\n\n/** Execute a `{ query, variables, operationName }` request. Read-only; rejects mutations. */\nexport async function executeGraphql(ctx: TenantContext, body: unknown, deps: GraphqlDeps): Promise<GraphqlResult> {\n const req = (body ?? {}) as { query?: unknown; variables?: unknown };\n if (typeof req.query !== 'string' || req.query.trim() === '') {\n return { errors: [{ message: 'missing query' }] };\n }\n const variables = (typeof req.variables === 'object' && req.variables !== null ? req.variables : {}) as Args;\n\n let selections: Selection[];\n try {\n selections = new Parser(tokenize(req.query), variables).parseDocument();\n } catch (err) {\n return { errors: [{ message: `syntax error: ${err instanceof Error ? err.message : String(err)}` }] };\n }\n\n const data: Record<string, unknown> = {};\n const errors: Array<{ message: string }> = [];\n for (const sel of selections) {\n try {\n if (sel.name === '__schema') {\n data[sel.alias] = project(introspectionSchema()['__schema'], sel.selections);\n continue;\n }\n if (sel.name === '__typename') { data[sel.alias] = 'Query'; continue; }\n const resolver = resolvers[sel.name];\n if (!resolver) { errors.push({ message: `Cannot query field \"${sel.name}\" on type \"Query\"` }); continue; }\n const resolved = resolver(ctx, sel.args, deps.backend);\n data[sel.alias] = project(resolved, sel.selections);\n } catch (err) {\n errors.push({ message: err instanceof Error ? err.message : String(err) });\n }\n }\n\n return errors.length > 0 ? { data, errors } : { data };\n}\n\n/** `GET /graphql` → the SDL as text/plain. */\nexport function handleGraphqlGet(): { status: number; body: string } {\n return { status: 200, body: SDL };\n}\n"],"mappings":"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","names":["nodes","ok","resolve"]}
@@ -151,15 +151,26 @@ var SECURITY_METADATA_KEYS = [
151
151
  var DriftConfigSchema = z.object({
152
152
  minSeverity: z.enum(SEVERITIES).default("info"),
153
153
  sinks: z.array(z.object({
154
- type: z.enum(["stdout", "webhook"]),
154
+ type: z.enum(["stdout", "webhook", "slack", "pagerduty", "jira"]),
155
155
  url: z.string().url().optional(),
156
156
  token: z.string().optional(),
157
- timeoutMs: z.number().int().positive().optional()
157
+ timeoutMs: z.number().int().positive().optional(),
158
+ routingKey: z.string().optional(),
159
+ email: z.string().optional(),
160
+ project: z.string().optional(),
161
+ issueType: z.string().optional()
158
162
  })).default([{ type: "stdout" }])
159
163
  }).superRefine((cfg, ctx) => {
160
164
  for (const [i, s] of cfg.sinks.entries()) {
161
- if (s.type === "webhook" && !s.url) {
162
- ctx.addIssue({ code: "custom", path: ["sinks", i, "url"], message: "webhook sink requires a url" });
165
+ const requireUrl = (msg) => {
166
+ if (!s.url) ctx.addIssue({ code: "custom", path: ["sinks", i, "url"], message: msg });
167
+ };
168
+ if (s.type === "webhook") requireUrl("webhook sink requires a url");
169
+ if (s.type === "slack") requireUrl("slack sink requires a webhook url");
170
+ if (s.type === "jira") {
171
+ requireUrl("jira sink requires a base url");
172
+ if (!s.email) ctx.addIssue({ code: "custom", path: ["sinks", i, "email"], message: "jira sink requires an email" });
173
+ if (!s.project) ctx.addIssue({ code: "custom", path: ["sinks", i, "project"], message: "jira sink requires a project key" });
163
174
  }
164
175
  }
165
176
  });
@@ -274,4 +285,4 @@ export {
274
285
  DEFAULT_FAST_MODEL,
275
286
  defaultConfig
276
287
  };
277
- //# sourceMappingURL=chunk-WCR47QA2.js.map
288
+ //# sourceMappingURL=chunk-QQOQBE2A.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/types.ts"],"sourcesContent":["import { z } from 'zod';\n// Type-only import (erased at runtime — no value cycle with diff.ts, which\n// imports its node/edge types from this module).\nimport type { TopologyDelta } from './diff.js';\n\n// ── Enums ────────────────────────────────\n\nexport const NODE_TYPES = [\n 'host', 'database_server', 'database', 'table',\n 'web_service', 'api_endpoint', 'cache_server',\n 'message_broker', 'queue', 'topic',\n 'container', 'pod', 'k8s_cluster',\n 'config_file', 'saas_tool', 'unknown',\n] as const;\nexport type NodeType = typeof NODE_TYPES[number];\n\n/**\n * Semantic groupings of node types — the single source of truth shared by the MCP\n * resource layer (services/databases) and the exporters (layer assignment). Each\n * node type belongs to at most one group; anything ungrouped is treated as \"other\".\n */\nexport const NODE_TYPE_GROUPS = {\n saas: ['saas_tool'],\n web: ['web_service', 'api_endpoint'],\n data: ['database_server', 'database', 'table', 'cache_server'],\n messaging: ['message_broker', 'queue', 'topic'],\n infra: ['host', 'container', 'pod', 'k8s_cluster'],\n config: ['config_file'],\n} as const satisfies Record<string, readonly NodeType[]>;\n\nexport const EDGE_RELATIONSHIPS = [\n 'connects_to', 'reads_from', 'writes_to',\n 'calls', 'contains', 'depends_on',\n] as const;\nexport type EdgeRelationship = typeof EDGE_RELATIONSHIPS[number];\n\n// ── Zod Schemas ──────────────────────────\n\n/** ISO-4217-ish currency code (3 uppercase letters). */\nconst CurrencyCode = z.string().regex(/^[A-Z]{3}$/, 'ISO-4217 currency code, e.g. \"USD\"');\n\n/** Billing period the amount covers. Cross-period rollup is bucketed, never normalized (5.1). */\nexport const COST_PERIODS = ['hourly', 'daily', 'monthly', 'yearly'] as const;\nexport type CostPeriod = typeof COST_PERIODS[number];\n\n/** Attributed cost for one billing period (3.3). Amount is in `currency` per `period`. */\nexport const CostEntrySchema = z.object({\n amount: z.number().nonnegative().describe('Spend for one `period`, in `currency`'),\n currency: CurrencyCode,\n period: z.enum(COST_PERIODS),\n source: z.string().optional().describe('Provenance, e.g. \"csv:billing-2026-06\", \"aws-ce\"'),\n});\nexport type CostEntry = z.infer<typeof CostEntrySchema>;\n\nexport const NodeSchema = z.object({\n id: z.string().describe('Format: \"{type}:{host}:{port}\" or \"{type}:{name}\"'),\n type: z.enum(NODE_TYPES),\n name: z.string(),\n discoveredVia: z.string(),\n confidence: z.number().min(0).max(1).default(0.5),\n metadata: z.record(z.string(), z.unknown()).default({}),\n tags: z.array(z.string()).default([]),\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 owner: z.string().optional().describe('Owning team/person, e.g. from an `Owner`/`Team` tag (3.3)'),\n cost: CostEntrySchema.optional().describe('Attributed cost for one billing period (3.3)'),\n});\nexport type DiscoveryNode = z.infer<typeof NodeSchema>;\n\nexport const EdgeSchema = z.object({\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).default(0.5),\n});\nexport type DiscoveryEdge = z.infer<typeof EdgeSchema>;\n\n// ── Sharing policy (2.10 consent + anonymization) ─────────────────────────────\n\n/**\n * Per-employee sharing levels, ordered most-private → least-private:\n * - `none` — share nothing (the opt-in default; nothing leaves the machine).\n * - `anonymized` — pseudonymize identifying fields (host/user/path/private-IP) via\n * an org-keyed, admin-reversible HMAC while preserving topology shape.\n * - `full` — share the raw record verbatim.\n */\nexport const SHARING_LEVELS = ['none', 'anonymized', 'full'] as const;\nexport const SharingLevelSchema = z.enum(SHARING_LEVELS);\nexport type SharingLevel = typeof SHARING_LEVELS[number];\n\n/**\n * A resolved sharing policy: the global `defaultLevel` (the `'*'` row) plus\n * remembered pattern overrides (glob over the node id). The most-specific\n * matching override wins; the default applies when nothing matches.\n */\nexport interface SharingPolicy {\n defaultLevel: SharingLevel;\n overrides: { pattern: string; level: SharingLevel }[];\n}\n\n// ── Cartography Map Types ────────────────\n\nexport const DataAssetSchema = z.object({\n id: z.string(),\n name: z.string(),\n domain: z.string(),\n subDomain: z.string().optional(),\n qualityScore: z.number().min(0).max(100).optional(),\n metadata: z.record(z.string(), z.unknown()).default({}),\n position: z.object({ q: z.number(), r: z.number() }),\n});\nexport type DataAsset = z.infer<typeof DataAssetSchema>;\n\nexport const ClusterSchema = z.object({\n id: z.string(),\n label: z.string(),\n domain: z.string(),\n color: z.string(),\n assetIds: z.array(z.string()),\n centroid: z.object({ x: z.number(), y: z.number() }),\n});\nexport type Cluster = z.infer<typeof ClusterSchema>;\n\nexport const ConnectionSchema = z.object({\n id: z.string(),\n sourceAssetId: z.string(),\n targetAssetId: z.string(),\n type: z.string().optional(),\n});\nexport type Connection = z.infer<typeof ConnectionSchema>;\n\nexport interface CartographyMapData {\n assets: DataAsset[];\n clusters: Cluster[];\n connections: Connection[];\n meta: { exportedAt: string; theme: 'light' | 'dark' };\n}\n\n/** Navy → medium blue → periwinkle → teal/cyan palette */\nexport const DOMAIN_COLORS: Record<string, string> = {\n 'Quality Control': '#1a2744',\n 'Supply Chain': '#1e3a6e',\n 'Marketing': '#6a7fb5',\n 'Finance': '#3a8a8a',\n 'HR': '#2a5a9a',\n 'Logistics': '#0e7490',\n 'Sales': '#1d4ed8',\n 'Engineering': '#4338ca',\n 'Operations': '#0891b2',\n 'Data Layer': '#1e3352',\n 'Web / API': '#1a3a1a',\n 'Messaging': '#2a1a3a',\n 'Infrastructure': '#0f2a40',\n 'Other': '#374151',\n};\n\n/** Ordered palette for dynamic domain assignment */\nexport const DOMAIN_PALETTE = [\n '#1a2e5a', '#1e3a8a', '#1d4ed8', '#2563eb', '#3b82f6',\n '#6366f1', '#818cf8', '#7c9fc3', '#0e7490', '#0891b2',\n '#06b6d4', '#22d3ee', '#0d9488', '#14b8a6', '#2dd4bf', '#5eead4',\n] as const;\n\n// ── DB Row Types ─────────────────────────\n\nexport interface NodeRow extends DiscoveryNode {\n sessionId: string;\n discoveredAt: string;\n depth: number;\n pathId?: string;\n /**\n * Org-scoped human-readable global identity (`{tenant}:{normalizedId}`); the\n * same logical resource collapses to one `globalId` across machines (2.9).\n */\n globalId?: string;\n /** Secondary dedup key (sha256 over type + name + key-meta) that catches `id` drift between machines (2.9). */\n contentHash?: string;\n}\n\nexport interface EdgeRow extends DiscoveryEdge {\n id: string;\n sessionId: string;\n discoveredAt: string;\n pathId?: string;\n}\n\nexport interface SessionRow {\n id: string;\n mode: 'discover';\n startedAt: string;\n completedAt?: string;\n config: string;\n /** Human-friendly, deterministically-derived label (e.g. \"infra+data · 42 nodes · 2026-06-11\"). */\n name?: string;\n /** Tenant/organization partition this session belongs to. Defaults to `'local'`. */\n tenant: string;\n /**\n * Source attribution captured at session creation (2.9). Local-only — these\n * identifying fields never leave the machine; off-machine sharing (2.11) and\n * anonymization/consent (2.10) are deferred.\n */\n hostname?: string;\n user?: string;\n machineId?: string;\n /**\n * Raw `--org` / `config.organization` value as supplied (provenance). The\n * normalized form is {@link tenant} — the org-scope partition introduced by 2.8.\n */\n organization?: string;\n /**\n * ISO 8601 UTC timestamp of the last in-place rescan of this session (2.1).\n * `undefined`/NULL until the session is rescanned via incremental discovery —\n * the freshness signal for scheduled discovery (2.5) to build on.\n */\n lastScannedAt?: string;\n}\n\n/**\n * One observation of a logical node from a single machine. Accumulated in the\n * `node_contributors` table (keyed by `(global_id, machine_id)`); never anonymized\n * in 2.9 (that is 2.10) and never transmitted off-machine in 2.9 (that is 2.11).\n */\nexport interface Contributor {\n machineId: string;\n hostname: string;\n user: string;\n /** Effective org-scope of the contribution (the session's tenant). */\n organization?: string;\n /** ISO 8601 UTC timestamp of the contributing observation. */\n at: string;\n /** Confidence of the observation that produced this contribution (0–1). */\n confidence: number;\n}\n\n// ── Diff / Drift ─────────────────────────\n\n/**\n * Node fields whose change marks a node as `changed` in a topology diff.\n * `confidence` is deliberately excluded — it fluctuates between scans (noise)\n * and is reported separately as `confidenceDelta` rather than triggering drift.\n */\nexport const DRIFT_FIELDS = ['type', 'name', 'domain', 'subDomain', 'qualityScore', 'metadata', 'tags', 'owner', 'cost'] as const;\nexport type DriftField = typeof DRIFT_FIELDS[number];\n\nexport interface NodeChange {\n id: string;\n before: NodeRow;\n after: NodeRow;\n /** Which of DRIFT_FIELDS differ between `before` and `after`. */\n changedFields: DriftField[];\n /** Informational confidence delta (after − before); does not itself trigger drift. */\n confidenceDelta: number;\n}\n\n// ── Anomalies (3.6) ──────────────────────\n\nexport const ANOMALY_KINDS = ['orphan', 'shadow-it'] as const;\nexport type AnomalyKind = typeof ANOMALY_KINDS[number];\n\nexport const ANOMALY_SEVERITIES = ['low', 'medium', 'high'] as const;\nexport type AnomalySeverity = typeof ANOMALY_SEVERITIES[number];\n\n/** A standing structural anomaly within a single topology snapshot. Deterministic. */\nexport interface Anomaly {\n /** The flagged node, structured id \"{type}:{id}\" — never raw free-text. */\n nodeId: string;\n kind: AnomalyKind;\n severity: AnomalySeverity;\n /** Stable, human-readable explanation built only from nodeId + numeric scores. */\n reason: string;\n}\n\n/** Resolved anomaly thresholds (defaults in `DEFAULT_ANOMALY_THRESHOLDS` unless overridden by config). */\nexport interface AnomalyThresholds {\n /** Degree at or below which a node is a weak-link orphan candidate (0 = isolated). */\n orphanWeakDegree: number;\n /** Confidence (0–1) below which an undomained node is shadow-IT. */\n shadowConfidence: number;\n /** qualityScore (0–100) below which an undomained node is shadow-IT. */\n shadowQuality: number;\n}\n\nexport interface AnomalyConfig extends AnomalyThresholds {\n /** When false, the engine short-circuits to an empty array (rollback flag). */\n enabled: boolean;\n}\n\n/**\n * Default anomaly thresholds. Defined here (not in `anomaly.ts`) so `defaultConfig`\n * can reference them without a runtime cycle; `anomaly.ts` re-exports this constant.\n */\nexport const DEFAULT_ANOMALY_THRESHOLDS: AnomalyThresholds = {\n orphanWeakDegree: 1,\n shadowConfidence: 0.4,\n shadowQuality: 40,\n};\n\nexport interface TopologyDiff {\n base: { sessionId: string; startedAt: string; nodeCount: number; edgeCount: number };\n current: { sessionId: string; startedAt: string; nodeCount: number; edgeCount: number };\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 /** Standing anomalies in base vs current, plus those newly appearing in current (3.6). */\n anomalies: { base: Anomaly[]; current: Anomaly[]; added: Anomaly[] };\n}\n\n// ── Drift alerts (3.1) ───────────────────\n\n/** Severity rank, ascending. `maxSeverity` and threshold filtering rely on this order. */\nexport const SEVERITIES = ['info', 'warning', 'critical'] as const;\nexport type Severity = typeof SEVERITIES[number];\n\n/**\n * Free-form metadata keys (case-insensitive) whose change escalates a node-changed\n * item to `critical`. Security-/exposure-relevant signals live only in the\n * free-form `metadata` blob (there are no first-class security node fields).\n */\nexport const SECURITY_METADATA_KEYS = [\n 'publicexposure', 'public', 'exposed', 'iamrole', 'role', 'encryption',\n 'encrypted', 'tls', 'tlsenabled', 'ports', 'openports', 'auth', 'authentication',\n] as const;\n\nexport type DriftItemKind =\n | 'node-added' | 'node-removed' | 'node-changed' | 'edge-added' | 'edge-removed';\n\nexport interface DriftAlertItem {\n kind: DriftItemKind;\n /** Node id, or \"source -rel-> target\" for edges. */\n ref: string;\n /** Human-readable node/edge name for display. */\n label: string;\n nodeType?: NodeType;\n severity: Severity;\n /** Present for node-changed; subset of DRIFT_FIELDS that differ. */\n changedFields?: DriftField[];\n /** Present for node-changed; metadata keys that triggered escalation. */\n securityFields?: string[];\n}\n\nexport interface DriftAlert {\n base: TopologyDiff['base'];\n current: TopologyDiff['current'];\n summary: TopologyDiff['summary'];\n /** Overall severity = max severity across items (info when no items). */\n severity: Severity;\n items: DriftAlertItem[];\n /** ISO-8601 UTC generation time. */\n generatedAt: string;\n}\n\n/** One configured drift sink. `url` is required for every type except `stdout`. */\nexport interface DriftSinkConfig {\n type: 'stdout' | 'webhook' | 'slack' | 'pagerduty' | 'jira';\n /** Required for `webhook`/`slack`/`jira`; optional for `pagerduty` (defaults to the Events API). */\n url?: string;\n /** Bearer token (`webhook`) / Jira API token (`jira`); falls back to CARTOGRAPHY_DRIFT_TOKEN. */\n token?: string;\n timeoutMs?: number;\n /** PagerDuty Events API v2 routing key (rides in the body). Falls back to `token`/CARTOGRAPHY_DRIFT_TOKEN. */\n routingKey?: string;\n /** Jira account email (basic-auth user). */\n email?: string;\n /** Jira target project key, e.g. \"OPS\". */\n project?: string;\n /** Jira issue type name (default \"Task\"). */\n issueType?: string;\n}\n\n/**\n * Opt-in drift-alerting block on {@link CartographyConfig}. Absent → the runner\n * defaults to a single `stdout` sink at `minSeverity: 'info'` (everything stays\n * local; no outbound traffic unless a `webhook` sink is explicitly configured).\n */\nexport interface DriftConfig {\n /** Items below this severity are dropped before dispatch. Default 'info'. */\n minSeverity: Severity;\n sinks: DriftSinkConfig[];\n}\n\n/** Validate an externally-supplied drift block (CLI/env/future file loader). */\nexport const DriftConfigSchema = z.object({\n minSeverity: z.enum(SEVERITIES).default('info'),\n sinks: z.array(z.object({\n type: z.enum(['stdout', 'webhook', 'slack', 'pagerduty', 'jira']),\n url: z.string().url().optional(),\n token: z.string().optional(),\n timeoutMs: z.number().int().positive().optional(),\n routingKey: z.string().optional(),\n email: z.string().optional(),\n project: z.string().optional(),\n issueType: z.string().optional(),\n })).default([{ type: 'stdout' }]),\n}).superRefine((cfg, ctx) => {\n for (const [i, s] of cfg.sinks.entries()) {\n const requireUrl = (msg: string) => { if (!s.url) ctx.addIssue({ code: 'custom', path: ['sinks', i, 'url'], message: msg }); };\n if (s.type === 'webhook') requireUrl('webhook sink requires a url');\n if (s.type === 'slack') requireUrl('slack sink requires a webhook url');\n if (s.type === 'jira') {\n requireUrl('jira sink requires a base url');\n if (!s.email) ctx.addIssue({ code: 'custom', path: ['sinks', i, 'email'], message: 'jira sink requires an email' });\n if (!s.project) ctx.addIssue({ code: 'custom', path: ['sinks', i, 'project'], message: 'jira sink requires a project key' });\n }\n // pagerduty: url is optional (defaults to the Events API); routingKey falls back to token/env at build time.\n }\n});\n\n// ── Schedule / config file (2.5) ─────────\n\n/** Machine-readable result formats shared by `discover` (#67) and `schedule`. */\nexport const OUTPUT_FORMATS = ['text', 'json', 'stream-json'] as const;\nexport type OutputFormat = typeof OUTPUT_FORMATS[number];\n\n/**\n * A recurring-discovery schedule, read from a JSON config file. The `cron`\n * string is a 5-field expression (min hour dom month dow) validated by\n * `parseCron` in `schedule.ts`; the Zod schema only enforces non-emptiness so\n * the config layer stays decoupled from the cron grammar.\n */\nexport const ScheduleConfigSchema = z\n .object({\n /** 5-field cron expression (min hour dom month dow). Validated by schedule.ts. */\n cron: z.string().min(1),\n /** Discovery entry points for the scheduled scan (falls back to the file-level / default). */\n entryPoints: z.array(z.string().min(1)).nonempty().optional(),\n /** Machine-readable result format for `--watch`/`--once` output. */\n outputFormat: z.enum(OUTPUT_FORMATS).default('json'),\n /** Catalog path the scheduled runs read/write (falls back to the file-level / default). */\n dbPath: z.string().min(1).optional(),\n })\n .strict();\nexport type ScheduleConfig = z.infer<typeof ScheduleConfigSchema>;\n\n// ── Central-DB sync (2.11) ───────────────\n\n/**\n * Outbound central-DB connection (2.11). The first egress path Cartograph has:\n * after a scan, consented, policy-transformed deltas are pushed to this ingest\n * endpoint over bearer-auth HTTPS. Presence of `url` *is* the feature flag — when\n * absent the entire sync pipeline short-circuits and nothing ever networks.\n *\n * `.strict()` so a typo'd key in `config.json` fails loudly. The `token` is an\n * opaque secret (never logged, never serialized into a payload); `org` is forwarded\n * as a header so the central side (2.12) can scope ingest by tenant.\n */\nexport const CentralDbConfigSchema = z\n .object({\n /** Ingest endpoint. Must be `https:` for any non-loopback host (see push.ts). */\n url: z.string().url(),\n /** Opaque bearer token sent as `Authorization: Bearer <token>`. Never logged. */\n token: z.string().min(1),\n /** Org/tenant routing hint forwarded to the ingest API. */\n org: z.string().min(1).optional(),\n /** Items per push batch (default 100). */\n batchSize: z.number().int().positive().max(1000).optional(),\n })\n .strict();\nexport type CentralDbConfig = z.infer<typeof CentralDbConfigSchema>;\n\n/**\n * Read a {@link CentralDbConfig} from environment variables\n * (`CARTOGRAPHY_CENTRAL_URL`/`_TOKEN`/`_ORG`), letting CI / secret-managers inject\n * the token without a file. Returns a partial — only the keys actually present —\n * so it composes field-wise over a `config.json` block. Never throws.\n */\nexport function centralDbFromEnv(env: NodeJS.ProcessEnv = process.env): Partial<CentralDbConfig> {\n const out: Partial<CentralDbConfig> = {};\n if (env.CARTOGRAPHY_CENTRAL_URL) out.url = env.CARTOGRAPHY_CENTRAL_URL;\n if (env.CARTOGRAPHY_CENTRAL_TOKEN) out.token = env.CARTOGRAPHY_CENTRAL_TOKEN;\n if (env.CARTOGRAPHY_CENTRAL_ORG) out.org = env.CARTOGRAPHY_CENTRAL_ORG;\n return out;\n}\n\n/**\n * Lifecycle status of one queued share item (2.11):\n * - `pending` — new/unmatched, awaiting the employee's explicit review.\n * - `approved` — cleared to leave (by `sync review`, or auto by a remembered rule).\n * - `shared` — successfully pushed to the central ingest endpoint.\n * - `withheld` — explicitly suppressed; never leaves.\n *\n * The load-bearing privacy invariant: only `approved` rows are ever pushed.\n */\nexport const PENDING_STATUSES = ['pending', 'approved', 'shared', 'withheld'] as const;\nexport type PendingStatus = typeof PENDING_STATUSES[number];\n\n/**\n * One row of the `pending_shares` review queue (2.11). `payload` is the *already\n * policy-transformed* (anonymized/dropped) projection from `previewShare` — never\n * raw node data for `anonymized`/`none` items — so what is queued is exactly what\n * may leave. Keyed by `contentHash` (a hash of that transformed payload).\n */\nexport interface PendingShareRow {\n contentHash: string;\n sessionId: string;\n nodeId?: string;\n kind: 'node' | 'edge';\n /** Policy-transformed payload (the exact bytes a push would send). */\n payload: unknown;\n status: PendingStatus;\n /** Who decided: `'user'` (interactive review) or `'rule'` (remembered policy). */\n decidedBy?: 'user' | 'rule';\n createdAt: string;\n decidedAt?: string;\n sharedAt?: string;\n}\n\n/**\n * Top-level shape of a `cartography.config.json` file. `.strict()` rejects\n * unknown keys so typos fail loudly rather than being silently ignored. WS 2.11\n * (central-org sync) extends this same schema with a `centralDb` block.\n */\nexport const ConfigFileSchema = z\n .object({\n schedule: ScheduleConfigSchema.optional(),\n entryPoints: z.array(z.string().min(1)).nonempty().optional(),\n dbPath: z.string().min(1).optional(),\n organization: z.string().min(1).optional(),\n centralDb: CentralDbConfigSchema.optional(),\n })\n .strict();\nexport type ConfigFile = z.infer<typeof ConfigFileSchema>;\n\n/**\n * One persisted scheduled-discovery run (2.5). Records what changed between this\n * run's session and the prior one, with the full {@link TopologyDelta} for audit\n * and the summary counts for fast querying. `baseSessionId` is `undefined` on the\n * very first run (no prior topology — everything is `added`).\n */\nexport interface DriftRunRow {\n id: string;\n sessionId: string;\n baseSessionId?: string;\n /** ISO 8601 UTC timestamp this run was recorded. */\n ranAt: string;\n summary: {\n nodesAdded: number;\n nodesRemoved: number;\n nodesChanged: number;\n edgesAdded: number;\n edgesRemoved: number;\n };\n delta: TopologyDelta;\n}\n\n// ── Config ───────────────────────────────\n\n/**\n * Agent backend selectable via `--provider` / `CARTOGRAPHY_PROVIDER`. Defined here\n * (in the dependency-free types module) and re-exported from `providers/types.ts`\n * so `defaultConfig` can reference it without a runtime cycle.\n */\nexport type ProviderName = 'claude' | 'openai' | 'ollama';\n\nexport interface CartographyConfig {\n maxDepth: number;\n maxTurns: number;\n entryPoints: string[];\n /** Agent backend. Defaults to `'claude'`; selected by `--provider` / `CARTOGRAPHY_PROVIDER`. */\n provider: ProviderName;\n /** Lead/discovery model. Back-compat alias for `models.lead` (kept in sync by defaultConfig). */\n agentModel: string;\n /** Model roles: `lead` drives discovery, `fast` powers cheaper helper tasks (e.g. chat). */\n models: { lead: string; fast: string };\n organization?: string;\n outputDir: string;\n dbPath: string;\n verbose: boolean;\n /** Max characters of a single scan-tool response returned to the agent (guards the context window). */\n maxToolResponseBytes: number;\n /** Explicit allowlist of scanner plugin package names to load (opt-in / consent-first). Default `[]`. */\n plugins: string[];\n /**\n * Optional recurring-discovery schedule (2.5), populated from a config file by\n * `loadConfig`. `undefined` for every existing/CLI caller — additive only.\n */\n schedule?: ScheduleConfig;\n /**\n * Optional central-DB outbound sync target (2.11). `undefined` for every caller\n * unless configured via `config.json` (`centralDb` block), the\n * `CARTOGRAPHY_CENTRAL_*` env vars, or an explicit override. Absent = the sync\n * pipeline is fully inert (no classify, no queue, no push).\n */\n centralDb?: CentralDbConfig;\n /**\n * Optional anomaly-detection thresholds (3.6). `undefined` for every existing\n * caller — `defaultConfig` populates it from `DEFAULT_ANOMALY_THRESHOLDS`, and the\n * engine falls back to those defaults when absent (optional-deps-degrade).\n */\n anomaly?: AnomalyConfig;\n /**\n * Optional drift-alerting block (3.1). `undefined` for every existing/CLI caller\n * (additive only); when absent the drift runner defaults to a local `stdout` sink.\n * No outbound traffic unless an operator configures a `webhook` sink.\n */\n drift?: DriftConfig;\n}\n\n/** Default lead (discovery) model. */\nexport const DEFAULT_LEAD_MODEL = 'claude-sonnet-4-5-20250929';\n/** Default fast model for helper tasks (chat, summaries). */\nexport const DEFAULT_FAST_MODEL = 'claude-haiku-4-5-20251001';\n\nexport function defaultConfig(overrides: Partial<CartographyConfig> = {}): CartographyConfig {\n const home = process.env.HOME ?? process.env.USERPROFILE ?? '/tmp';\n const base: CartographyConfig = {\n maxDepth: 8,\n maxTurns: 50,\n entryPoints: ['localhost'],\n provider: 'claude',\n agentModel: DEFAULT_LEAD_MODEL,\n models: { lead: DEFAULT_LEAD_MODEL, fast: DEFAULT_FAST_MODEL },\n outputDir: './cartography-output',\n dbPath: `${home}/.cartography/cartography.db`,\n verbose: false,\n maxToolResponseBytes: 100_000,\n plugins: [],\n anomaly: { enabled: true, ...DEFAULT_ANOMALY_THRESHOLDS },\n };\n const merged = { ...base, ...overrides };\n // Keep the invariant agentModel === models.lead so existing agentModel readers\n // and the new role config stay consistent. An explicit `models` override wins;\n // otherwise a legacy `agentModel` override flows into the lead role.\n const lead = overrides.models?.lead ?? merged.agentModel;\n const fast = overrides.models?.fast ?? merged.models.fast;\n\n // 2.11 central-DB sync: assemble the optional `centralDb` block field-wise from\n // (low→high) the explicit override < env. A field set in `overrides.centralDb`\n // (e.g. file-derived in loadConfig) wins; env fills the remaining fields, so a\n // file `url` + env `token` compose. Validate the result with `CentralDbConfigSchema`;\n // on failure (or no `url`) drop it and warn — the feature stays inert, never crashes.\n const centralDb = resolveCentralDb(overrides.centralDb);\n\n const out: CartographyConfig = { ...merged, agentModel: lead, models: { lead, fast } };\n // Only a validated block lands; a dropped/invalid one is removed entirely so the\n // feature stays inert rather than carrying through a malformed override.\n if (centralDb) out.centralDb = centralDb;\n else delete out.centralDb;\n return out;\n}\n\n/**\n * Resolve the effective `centralDb` from an explicit (override/file) partial layered\n * over `CARTOGRAPHY_CENTRAL_*` env. Field-wise merge (override wins per field), then\n * `safeParse`. Returns `undefined` (and warns to stderr) when no `url` is present or\n * the assembled block fails validation — so a missing/invalid config degrades to an\n * inert feature rather than a crash. The token is never written to the warning.\n */\nfunction resolveCentralDb(explicit?: Partial<CentralDbConfig>): CentralDbConfig | undefined {\n const env = centralDbFromEnv();\n const assembled: Partial<CentralDbConfig> = { ...env, ...explicit };\n if (assembled.url === undefined && assembled.token === undefined && assembled.org === undefined && assembled.batchSize === undefined) {\n return undefined; // nothing configured anywhere — feature off\n }\n const parsed = CentralDbConfigSchema.safeParse(assembled);\n if (!parsed.success) {\n const detail = parsed.error.issues.map((i) => `${i.path.join('.') || '(root)'}: ${i.message}`).join('; ');\n process.stderr.write(`[cartography] ignoring invalid centralDb config: ${detail}\\n`);\n return undefined;\n }\n return parsed.data;\n}\n"],"mappings":"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","names":[]}