@datasynx/agentic-ai-cartography 2.7.0 → 2.8.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.
package/dist/index.d.cts CHANGED
@@ -1469,9 +1469,18 @@ interface NodeIdentity {
1469
1469
  /** Secondary merge key — content hash that catches `id` drift between machines. */
1470
1470
  contentHash: string;
1471
1471
  }
1472
+ /**
1473
+ * A value that may be produced synchronously (SQLite, `better-sqlite3`) or
1474
+ * asynchronously (a graph DB over the async Bolt driver, 4.3). The ingest core
1475
+ * `await`s every backend call, so a sync implementation incurs no overhead and the
1476
+ * SQLite path stays byte-for-byte synchronous.
1477
+ */
1478
+ type Awaitable<T> = T | Promise<T>;
1472
1479
  /**
1473
1480
  * A provider-agnostic central store. All operations are scoped to a single tenant
1474
- * (`org`); there is no cross-tenant read or write path.
1481
+ * (`org`); there is no cross-tenant read or write path. Methods are **async-capable**
1482
+ * (4.3): `SqliteStoreBackend` returns synchronously, `GraphStoreBackend` returns
1483
+ * Promises; consumers await either.
1475
1484
  */
1476
1485
  interface StoreBackend {
1477
1486
  /**
@@ -1480,15 +1489,15 @@ interface StoreBackend {
1480
1489
  * `'created'` when this is the first observation of the logical node, `'merged'`
1481
1490
  * when it collapsed onto an existing one.
1482
1491
  */
1483
- upsertNode(org: string, node: DiscoveryNode, identity: NodeIdentity, contributor: Contributor): 'created' | 'merged';
1492
+ upsertNode(org: string, node: DiscoveryNode, identity: NodeIdentity, contributor: Contributor): Awaitable<'created' | 'merged'>;
1484
1493
  /** Insert an edge under `org` (idempotent on the logical `(source, target, relationship)` key). */
1485
- insertEdge(org: string, edge: DiscoveryEdge): void;
1494
+ insertEdge(org: string, edge: DiscoveryEdge): Awaitable<void>;
1486
1495
  /** Org-wide aggregate summary (merged counts across all contributors). */
1487
- getSummary(org: string): OrgSummary;
1496
+ getSummary(org: string): Awaitable<OrgSummary>;
1488
1497
  /** Contributors for a merged logical node (test/audit helper). */
1489
- getContributors(globalId: string): Contributor[];
1498
+ getContributors(globalId: string): Awaitable<Contributor[]>;
1490
1499
  /** Release any underlying resources. */
1491
- close(): void;
1500
+ close(): Awaitable<void>;
1492
1501
  }
1493
1502
 
1494
1503
  /**
@@ -1519,6 +1528,76 @@ declare class SqliteStoreBackend implements StoreBackend {
1519
1528
  close(): void;
1520
1529
  }
1521
1530
 
1531
+ /**
1532
+ * `GraphStoreBackend` (4.3) — an opt-in central-store backend over a Bolt-speaking
1533
+ * graph database (Neo4j / Memgraph). Implements the async-capable {@link StoreBackend}
1534
+ * seam so the central collector's merge + org-summary path can scale to 10K+ nodes on a
1535
+ * native graph engine, while SQLite stays the zero-config default.
1536
+ *
1537
+ * `neo4j-driver` is an OPTIONAL dependency, dynamically imported by `openStoreBackend`
1538
+ * (`src/store/index.ts`); this module depends only on a minimal structural Bolt interface,
1539
+ * so it compiles and the package degrades gracefully when the driver is absent. The driver
1540
+ * is injected (constructor), which also lets tests drive it with a mock — no live DB in CI.
1541
+ *
1542
+ * Merge identity mirrors SQLite: a logical node is keyed by `(org, globalId)`; the
1543
+ * `contentHash` is stored + indexed for the id-drift secondary collapse (best-effort here,
1544
+ * tracked as a follow-up). Contributors are `(org, globalId, machineId)` with max-confidence.
1545
+ */
1546
+
1547
+ interface BoltRecord {
1548
+ get(key: string): unknown;
1549
+ }
1550
+ interface BoltResult {
1551
+ records: BoltRecord[];
1552
+ }
1553
+ interface BoltSession {
1554
+ run(cypher: string, params?: Record<string, unknown>): Promise<BoltResult>;
1555
+ close(): Promise<void>;
1556
+ }
1557
+ interface BoltDriver {
1558
+ session(): BoltSession;
1559
+ close(): Promise<void>;
1560
+ verifyConnectivity?(): Promise<unknown>;
1561
+ }
1562
+ declare class GraphStoreBackend implements StoreBackend {
1563
+ private readonly driver;
1564
+ constructor(driver: BoltDriver);
1565
+ private run;
1566
+ upsertNode(org: string, node: DiscoveryNode, identity: NodeIdentity, contributor: Contributor): Promise<'created' | 'merged'>;
1567
+ insertEdge(org: string, edge: DiscoveryEdge): Promise<void>;
1568
+ getSummary(org: string): Promise<OrgSummary>;
1569
+ getContributors(globalId: string): Promise<Contributor[]>;
1570
+ close(): Promise<void>;
1571
+ }
1572
+
1573
+ /**
1574
+ * Central-store backend factory (4.3).
1575
+ *
1576
+ * `openStoreBackend` returns a {@link GraphStoreBackend} only when a graph backend is
1577
+ * explicitly requested AND the optional `neo4j-driver` is installed AND the server is
1578
+ * reachable; otherwise it logs a structured WARN and returns the always-available
1579
+ * {@link SqliteStoreBackend}. A missing driver or an unreachable graph server therefore
1580
+ * NEVER breaks the collector — it degrades to SQLite (the "optional deps degrade" locked
1581
+ * constraint). SQLite stays the zero-config default.
1582
+ */
1583
+
1584
+ interface StoreBackendOptions {
1585
+ /** `'graph'` opts into the graph DB; anything else (default) uses SQLite. */
1586
+ backend?: 'sqlite' | 'graph';
1587
+ /** Bolt URL, e.g. `bolt://graph.internal:7687` or `neo4j+s://…`. */
1588
+ graphUrl?: string;
1589
+ graphUser?: string;
1590
+ graphPassword?: string;
1591
+ /** Injected driver factory (tests). Defaults to dynamically importing `neo4j-driver`. */
1592
+ driverFactory?: (url: string, user: string, password: string) => Promise<BoltDriver>;
1593
+ }
1594
+ /**
1595
+ * Resolve the central store backend. Graph when requested + available; else SQLite.
1596
+ * The returned backend is owned by the caller (call `close()` on shutdown for the graph
1597
+ * backend; the SQLite backend's `close()` is a no-op — the `CartographyDB` is shared).
1598
+ */
1599
+ declare function openStoreBackend(db: CartographyDB, opts?: StoreBackendOptions): Promise<StoreBackend>;
1600
+
1522
1601
  /**
1523
1602
  * `QueryBackend` — the **read-only** query seam for the API server (4.2).
1524
1603
  *
@@ -1770,7 +1849,7 @@ interface IngestOptions {
1770
1849
  * The caller (HTTP handler) wraps this in try/catch; the store's per-node upsert is
1771
1850
  * itself transactional, so a single bad node never half-writes a row.
1772
1851
  */
1773
- declare function ingestEnvelope(store: StoreBackend, envelope: IngestEnvelope, opts?: IngestOptions): IngestResult;
1852
+ declare function ingestEnvelope(store: StoreBackend, envelope: IngestEnvelope, opts?: IngestOptions): Promise<IngestResult>;
1774
1853
 
1775
1854
  /**
1776
1855
  * Ingest backpressure for the central collector (4.7).
@@ -1825,7 +1904,7 @@ interface IngestResponse {
1825
1904
  /** Extra response headers (e.g. `Retry-After` on a 429). */
1826
1905
  headers?: Record<string, string>;
1827
1906
  }
1828
- type IngestHandler = (body: unknown) => IngestResponse;
1907
+ type IngestHandler = (body: unknown) => Promise<IngestResponse>;
1829
1908
  interface IngestHandlerOptions extends IngestOptions {
1830
1909
  /** Per-org ingest rate limiter (4.7 backpressure). Over-quota → 429 + Retry-After. */
1831
1910
  quota?: RateLimiter;
@@ -2186,6 +2265,12 @@ interface CreateMcpServerOptions {
2186
2265
  * behaviour exactly. The org is normalized to a tenant.
2187
2266
  */
2188
2267
  org?: string;
2268
+ /**
2269
+ * Org-wide summary source (4.3). When set (server-mode with a graph backend), the
2270
+ * org `get_summary` reads from here instead of `db.getOrgSummary` — so a graph-DB
2271
+ * collector serves its own merged aggregate. Defaults to the SQLite central store.
2272
+ */
2273
+ orgSummary?: (org: string) => OrgSummary | Promise<OrgSummary>;
2189
2274
  /**
2190
2275
  * The authenticated principal (4.5 RBAC). When set, mutating tools (`run_discovery`)
2191
2276
  * are gated by role: a `viewer` is refused with a forbidden error. Read tools are
@@ -2229,7 +2314,11 @@ interface HttpOptions {
2229
2314
  * caps the body, parses JSON, and returns the hook's `{ status, body }`. When unset,
2230
2315
  * `/ingest` 404s exactly like any other path — the collector stays dark by default.
2231
2316
  */
2232
- onIngest?: (body: unknown) => {
2317
+ onIngest?: (body: unknown) => Promise<{
2318
+ status: number;
2319
+ body: unknown;
2320
+ headers?: Record<string, string>;
2321
+ }> | {
2233
2322
  status: number;
2234
2323
  body: unknown;
2235
2324
  headers?: Record<string, string>;
@@ -4186,4 +4275,4 @@ declare function logInfo(message: string, context?: Record<string, unknown>): vo
4186
4275
  declare function logWarn(message: string, context?: Record<string, unknown>): void;
4187
4276
  declare function logError(message: string, context?: Record<string, unknown>): void;
4188
4277
 
4189
- export { ACTIONS, ANOMALY_KINDS, ANOMALY_SEVERITIES, type Action, ActionSchema, type AgentProvider, type AgentRunContext, type AgentTool, type Anomaly, type AnomalyConfig, type AnomalyKind, type AnomalySeverity, type AnomalyThresholds, type AnonViolation, type AnonymizationLevel, type ApiServerOptions, type AskUserFn, type AuthConfig, AuthConfigSchema, AuthorizationError, type BackstageEntity, type BackstageMapOptions, type BindGuardOptions, CLIENTS, CONFIDENCE, COST_PERIODS, type CartographyConfig, CartographyDB, type CartographyMapData, type CentralDbConfig, CentralDbConfigSchema, type ClassifiedItem, type ClassifyInput, type ClassifyResult, type ClientSpec, type Cluster, ClusterSchema, type ComplianceInput, type ComplianceReport, ComplianceReportSchema, type ComplianceRule, ComplianceRuleSchema, type Condition, ConditionSchema, ConfigError, type ConfigFile, ConfigFileSchema, type ConfigFormat, type Connection, ConnectionSchema, type Contributor, type ControlResult, ControlResultSchema, type CostEntry, CostEntrySchema, type CostPeriod, type CostRecord, type CostSource, type CreateMcpServerOptions, type CredentialConfig, CredentialConfigSchema, type CredentialDb, type CredentialRecord, type CredentialStore, type CronFields, CsvCostSource, type CsvCostSourceOptions, DEFAULT_ANOMALY_THRESHOLDS, DEFAULT_FAST_MODEL, DEFAULT_INGEST_QUOTA, DEFAULT_LEAD_MODEL, DEFAULT_SERVER_NAME, DEFAULT_TENANT, DOMAIN_COLORS, DOMAIN_PALETTE, DRIFT_FIELDS, type DataAsset, DataAssetSchema, type DependencyQuery, type DiscoveryEdge, type DiscoveryEvent, type DiscoveryFn, type DiscoveryNode, type DriftAlert, type DriftAlertItem, type DriftConfig, DriftConfigSchema, type DriftField, type DriftItemKind, type DriftRunRow, type DriftSink, type DriftSinkConfig, EDGE_RELATIONSHIPS, type EdgeRelationship, type EdgeRow, EdgeSchema, type EmbeddingProvider, type EnrichResult, type EntryOptions, type EstablishedConn, type EvidenceKind, type FetchLike, type FragmentKind, type GraphSummary, type HealthResult, type HttpOptions, INGEST_SCHEMA_VERSION, type IngestEnvelope, IngestEnvelopeSchema, type IngestHandler, type IngestHandlerOptions, type IngestOptions, type IngestResponse, type IngestResult, type InstallPlan, InvalidTenantError, type JiraIssue, type JiraOptions, JiraSink, type JiraSinkOptions, LOOPBACK_HOSTS, type LocalDiscoveryOptions, type LocalDiscoveryResult, type LogEntry, type LogLevel, MCP_BIN, type MatchStrategy, NODE_TYPES, NODE_TYPE_GROUPS, type NlIntent, type NlQueryOptions, type NlQueryResult, type NlRelation, type NodeAttribution, type NodeChange, type NodeIdentity, type NodeQuery, type NodeRow, NodeSchema, type NodeType, type NodesResult, NotFoundError, OUTPUT_FORMATS, type OrgKeyOptions, type OrgSummary, type OsKind, type OutputFormat, PACKAGE_NAME, PAGERDUTY_ENQUEUE_URL, PENDING_STATUSES, PERSONAL, PORT_MAP, PRIVATE_IP, PUSH_SCHEMA_VERSION, type PagerDutyEvent, PagerDutySink, type PagerDutySinkOptions, type ParsedApiArgs, type PendingShareRow, type PendingStatus, type PlanOptions, type PolicyResult, type PostJsonOptions, type Principal, PrincipalSchema, type ProviderFactory, type ProviderName, ProviderRegistry, type PushItem, type PushOptions, type PushResult, type QueryBackend, type QuotaConfig, type QuotaDecision, RELATION_TO_DIRECTION, ROLES, RateLimiter, type ResolveContext, type ResolveOptions, type Role, RoleSchema, type RuleCheck, RuleCheckSchema, type RuleScope, type Ruleset, RulesetSchema, type RunDriftOptions, SCAN_ARG_PATTERNS, SCHEMA_VERSION, SDL, SECURITY_METADATA_KEYS, SEVERITIES, SEVERITY_WEIGHT, SHARING_LEVELS, type ScanArgKind, type ScanContext, type ScanHintParams, type ScanResult, type Scanner, type ScannerPlugin, type ScannerPluginApi, ScannerRegistry, ScannerShape, type ScheduleConfig, ScheduleConfigSchema, type ScheduledRunResult, type Scope, type SearchFn, type SemanticSearchOptions, type ServerEntry, type SessionRow, type Severity, type SharePreview, type SharePreviewEntry, type SharingLevel, SharingLevelSchema, type SharingPolicy, type ShellKind, type SlackMessage, SlackSink, SqliteCredentialStore, SqliteQueryBackend, SqliteStoreBackend, type StartApiOptions, StdoutSink, type StoreBackend, type SyncClassifyOptions, type SyncClassifyResult, TENANT_HEADER, type TenantContext, TenantMismatchError, type TenantOptions, type ToolResult, type TopologyDelta, type TopologyDiff, type TopologyInput, type TraversalResult, VectorStore, WebhookSink, type WebhookSinkOptions, applyInstall, applySharingLevel, assertReadOnly, assertSafeBind, assertSafeScanArg, assertSameTenant, assignColors, authorize, bearerToken, bookmarksScanner, buildCartographyToolHandlers, buildMapData, buildOpenApiDocument, buildReport, buildSinks, can, centralDbFromEnv, checkBearer, checkPrerequisites, checkReadOnly, clampText, classify, classifyDrift, cleanupTempFiles, cloudAwsScanner, cloudAzureScanner, cloudGcpScanner, codeAddMcpCommand, computeCentroid, computeClusterBounds, computeIdentity, connectionsScanner, contentHash, createBashTool, createCartographyTools, createClaudeProvider, createDefaultRegistry, createHashEmbedder, createIngestHandler, createLocalEmbedder, createMcpServer, createOllamaProvider, createOpenAIProvider, createScanRunner, createSemanticSearch, createSqliteQueryBackend, currentOs, cursorDeeplink, databasesScanner, deepMerge, defaultAllowedHosts, defaultConfig, defaultContext, defaultProviderRegistry, defaultRegistry, defaultServerEntry, definePlugin, deriveSessionName, detectAnomalies, detectOrphans, detectShadowIt, diffTopology, edgesToConnections, enrichCosts, entitiesToYaml, evaluateCheck, evaluateRule, evidenceLine, executeGraphql, executeNlQuery, exportAll, exportBackstageYAML, exportComplianceReport, exportCostCSV, exportCostSummary, exportDiscoveryApp, exportJGF, exportJSON, extractListeningPorts, filterBySeverity, findAnonViolations, formatComplianceText, formatJira, formatPagerDuty, formatSlack, generateDependencyMermaid, generateDiffMermaid, generateTopologyMermaid, getClient, getRuleset, globalId, groupByDomain, handleGraphqlGet, hashToken, hexCorners, hexDistance, hexNeighbors, hexRing, hexSpiral, hexToPixel, hmacKey, hostname, ingestEnvelope, installedAppsScanner, isLoopbackHost, isPersonalHost, isReadOnlyCommand, isRemembered, isSecureWebhookUrl, k8sScanner, keyMetaOf, layoutClusters, listClients, listRulesets, loadConfig, loadOrgKey, loadPlugins, loadRuleset, localDiscoveryFn, log, logDebug, logError, logInfo, logWarn, machineId, maxSeverity, mcpServerObject, newAnomalies, nextRun, nodesToAssets, normalizeId, normalizeTenant, orgKeyPath, osUser, parseApiArgs, parseComposeDeps, parseConfig, parseConnectionString, parseCostCsv, parseCron, parseEstablished, parseNginxUpstreams, parseNlQuery, parseScanHint, pixelToHex, planInstall, portsScanner, postJson, previewShare, pseudonymize, pseudonymizeFragment, pseudonymizeString, pushDeltas, readConfigFile, redactConnectionString, redactSecrets, redactValue, renderDiff, resolveEffectiveLevel, resolveNlQuery, resolvePrincipal, resolveSharingLevel, resolveTenant, revalidateAnonymized, reversalKey, reversePseudonym, rotateOrgKey, runApi, runDiscovery, runDrift, runHttp, runLocalDiscovery, runOnce, runStdio, runSyncClassify, safeEnv, safeJson, safetyHook, sanitizeUntrusted, sanitizeValue, scopeReads, scoreTopology, securityRelevantChange, serializeConfig, serviceConfigScanner, setVerbose, shadeVariant, shapeToJsonSchema, shareHash, splitSegments, stableStringify, startApi, stripSensitive, timingSafeEqual, toBackstageEntities, validateScanner, vscodeDeeplink, zodToJsonSchema };
4278
+ export { ACTIONS, ANOMALY_KINDS, ANOMALY_SEVERITIES, type Action, ActionSchema, type AgentProvider, type AgentRunContext, type AgentTool, type Anomaly, type AnomalyConfig, type AnomalyKind, type AnomalySeverity, type AnomalyThresholds, type AnonViolation, type AnonymizationLevel, type ApiServerOptions, type AskUserFn, type AuthConfig, AuthConfigSchema, AuthorizationError, type Awaitable, type BackstageEntity, type BackstageMapOptions, type BindGuardOptions, type BoltDriver, type BoltRecord, type BoltResult, type BoltSession, CLIENTS, CONFIDENCE, COST_PERIODS, type CartographyConfig, CartographyDB, type CartographyMapData, type CentralDbConfig, CentralDbConfigSchema, type ClassifiedItem, type ClassifyInput, type ClassifyResult, type ClientSpec, type Cluster, ClusterSchema, type ComplianceInput, type ComplianceReport, ComplianceReportSchema, type ComplianceRule, ComplianceRuleSchema, type Condition, ConditionSchema, ConfigError, type ConfigFile, ConfigFileSchema, type ConfigFormat, type Connection, ConnectionSchema, type Contributor, type ControlResult, ControlResultSchema, type CostEntry, CostEntrySchema, type CostPeriod, type CostRecord, type CostSource, type CreateMcpServerOptions, type CredentialConfig, CredentialConfigSchema, type CredentialDb, type CredentialRecord, type CredentialStore, type CronFields, CsvCostSource, type CsvCostSourceOptions, DEFAULT_ANOMALY_THRESHOLDS, DEFAULT_FAST_MODEL, DEFAULT_INGEST_QUOTA, DEFAULT_LEAD_MODEL, DEFAULT_SERVER_NAME, DEFAULT_TENANT, DOMAIN_COLORS, DOMAIN_PALETTE, DRIFT_FIELDS, type DataAsset, DataAssetSchema, type DependencyQuery, type DiscoveryEdge, type DiscoveryEvent, type DiscoveryFn, type DiscoveryNode, type DriftAlert, type DriftAlertItem, type DriftConfig, DriftConfigSchema, type DriftField, type DriftItemKind, type DriftRunRow, type DriftSink, type DriftSinkConfig, EDGE_RELATIONSHIPS, type EdgeRelationship, type EdgeRow, EdgeSchema, type EmbeddingProvider, type EnrichResult, type EntryOptions, type EstablishedConn, type EvidenceKind, type FetchLike, type FragmentKind, GraphStoreBackend, type GraphSummary, type HealthResult, type HttpOptions, INGEST_SCHEMA_VERSION, type IngestEnvelope, IngestEnvelopeSchema, type IngestHandler, type IngestHandlerOptions, type IngestOptions, type IngestResponse, type IngestResult, type InstallPlan, InvalidTenantError, type JiraIssue, type JiraOptions, JiraSink, type JiraSinkOptions, LOOPBACK_HOSTS, type LocalDiscoveryOptions, type LocalDiscoveryResult, type LogEntry, type LogLevel, MCP_BIN, type MatchStrategy, NODE_TYPES, NODE_TYPE_GROUPS, type NlIntent, type NlQueryOptions, type NlQueryResult, type NlRelation, type NodeAttribution, type NodeChange, type NodeIdentity, type NodeQuery, type NodeRow, NodeSchema, type NodeType, type NodesResult, NotFoundError, OUTPUT_FORMATS, type OrgKeyOptions, type OrgSummary, type OsKind, type OutputFormat, PACKAGE_NAME, PAGERDUTY_ENQUEUE_URL, PENDING_STATUSES, PERSONAL, PORT_MAP, PRIVATE_IP, PUSH_SCHEMA_VERSION, type PagerDutyEvent, PagerDutySink, type PagerDutySinkOptions, type ParsedApiArgs, type PendingShareRow, type PendingStatus, type PlanOptions, type PolicyResult, type PostJsonOptions, type Principal, PrincipalSchema, type ProviderFactory, type ProviderName, ProviderRegistry, type PushItem, type PushOptions, type PushResult, type QueryBackend, type QuotaConfig, type QuotaDecision, RELATION_TO_DIRECTION, ROLES, RateLimiter, type ResolveContext, type ResolveOptions, type Role, RoleSchema, type RuleCheck, RuleCheckSchema, type RuleScope, type Ruleset, RulesetSchema, type RunDriftOptions, SCAN_ARG_PATTERNS, SCHEMA_VERSION, SDL, SECURITY_METADATA_KEYS, SEVERITIES, SEVERITY_WEIGHT, SHARING_LEVELS, type ScanArgKind, type ScanContext, type ScanHintParams, type ScanResult, type Scanner, type ScannerPlugin, type ScannerPluginApi, ScannerRegistry, ScannerShape, type ScheduleConfig, ScheduleConfigSchema, type ScheduledRunResult, type Scope, type SearchFn, type SemanticSearchOptions, type ServerEntry, type SessionRow, type Severity, type SharePreview, type SharePreviewEntry, type SharingLevel, SharingLevelSchema, type SharingPolicy, type ShellKind, type SlackMessage, SlackSink, SqliteCredentialStore, SqliteQueryBackend, SqliteStoreBackend, type StartApiOptions, StdoutSink, type StoreBackend, type StoreBackendOptions, type SyncClassifyOptions, type SyncClassifyResult, TENANT_HEADER, type TenantContext, TenantMismatchError, type TenantOptions, type ToolResult, type TopologyDelta, type TopologyDiff, type TopologyInput, type TraversalResult, VectorStore, WebhookSink, type WebhookSinkOptions, applyInstall, applySharingLevel, assertReadOnly, assertSafeBind, assertSafeScanArg, assertSameTenant, assignColors, authorize, bearerToken, bookmarksScanner, buildCartographyToolHandlers, buildMapData, buildOpenApiDocument, buildReport, buildSinks, can, centralDbFromEnv, checkBearer, checkPrerequisites, checkReadOnly, clampText, classify, classifyDrift, cleanupTempFiles, cloudAwsScanner, cloudAzureScanner, cloudGcpScanner, codeAddMcpCommand, computeCentroid, computeClusterBounds, computeIdentity, connectionsScanner, contentHash, createBashTool, createCartographyTools, createClaudeProvider, createDefaultRegistry, createHashEmbedder, createIngestHandler, createLocalEmbedder, createMcpServer, createOllamaProvider, createOpenAIProvider, createScanRunner, createSemanticSearch, createSqliteQueryBackend, currentOs, cursorDeeplink, databasesScanner, deepMerge, defaultAllowedHosts, defaultConfig, defaultContext, defaultProviderRegistry, defaultRegistry, defaultServerEntry, definePlugin, deriveSessionName, detectAnomalies, detectOrphans, detectShadowIt, diffTopology, edgesToConnections, enrichCosts, entitiesToYaml, evaluateCheck, evaluateRule, evidenceLine, executeGraphql, executeNlQuery, exportAll, exportBackstageYAML, exportComplianceReport, exportCostCSV, exportCostSummary, exportDiscoveryApp, exportJGF, exportJSON, extractListeningPorts, filterBySeverity, findAnonViolations, formatComplianceText, formatJira, formatPagerDuty, formatSlack, generateDependencyMermaid, generateDiffMermaid, generateTopologyMermaid, getClient, getRuleset, globalId, groupByDomain, handleGraphqlGet, hashToken, hexCorners, hexDistance, hexNeighbors, hexRing, hexSpiral, hexToPixel, hmacKey, hostname, ingestEnvelope, installedAppsScanner, isLoopbackHost, isPersonalHost, isReadOnlyCommand, isRemembered, isSecureWebhookUrl, k8sScanner, keyMetaOf, layoutClusters, listClients, listRulesets, loadConfig, loadOrgKey, loadPlugins, loadRuleset, localDiscoveryFn, log, logDebug, logError, logInfo, logWarn, machineId, maxSeverity, mcpServerObject, newAnomalies, nextRun, nodesToAssets, normalizeId, normalizeTenant, openStoreBackend, orgKeyPath, osUser, parseApiArgs, parseComposeDeps, parseConfig, parseConnectionString, parseCostCsv, parseCron, parseEstablished, parseNginxUpstreams, parseNlQuery, parseScanHint, pixelToHex, planInstall, portsScanner, postJson, previewShare, pseudonymize, pseudonymizeFragment, pseudonymizeString, pushDeltas, readConfigFile, redactConnectionString, redactSecrets, redactValue, renderDiff, resolveEffectiveLevel, resolveNlQuery, resolvePrincipal, resolveSharingLevel, resolveTenant, revalidateAnonymized, reversalKey, reversePseudonym, rotateOrgKey, runApi, runDiscovery, runDrift, runHttp, runLocalDiscovery, runOnce, runStdio, runSyncClassify, safeEnv, safeJson, safetyHook, sanitizeUntrusted, sanitizeValue, scopeReads, scoreTopology, securityRelevantChange, serializeConfig, serviceConfigScanner, setVerbose, shadeVariant, shapeToJsonSchema, shareHash, splitSegments, stableStringify, startApi, stripSensitive, timingSafeEqual, toBackstageEntities, validateScanner, vscodeDeeplink, zodToJsonSchema };
package/dist/index.d.ts CHANGED
@@ -1469,9 +1469,18 @@ interface NodeIdentity {
1469
1469
  /** Secondary merge key — content hash that catches `id` drift between machines. */
1470
1470
  contentHash: string;
1471
1471
  }
1472
+ /**
1473
+ * A value that may be produced synchronously (SQLite, `better-sqlite3`) or
1474
+ * asynchronously (a graph DB over the async Bolt driver, 4.3). The ingest core
1475
+ * `await`s every backend call, so a sync implementation incurs no overhead and the
1476
+ * SQLite path stays byte-for-byte synchronous.
1477
+ */
1478
+ type Awaitable<T> = T | Promise<T>;
1472
1479
  /**
1473
1480
  * A provider-agnostic central store. All operations are scoped to a single tenant
1474
- * (`org`); there is no cross-tenant read or write path.
1481
+ * (`org`); there is no cross-tenant read or write path. Methods are **async-capable**
1482
+ * (4.3): `SqliteStoreBackend` returns synchronously, `GraphStoreBackend` returns
1483
+ * Promises; consumers await either.
1475
1484
  */
1476
1485
  interface StoreBackend {
1477
1486
  /**
@@ -1480,15 +1489,15 @@ interface StoreBackend {
1480
1489
  * `'created'` when this is the first observation of the logical node, `'merged'`
1481
1490
  * when it collapsed onto an existing one.
1482
1491
  */
1483
- upsertNode(org: string, node: DiscoveryNode, identity: NodeIdentity, contributor: Contributor): 'created' | 'merged';
1492
+ upsertNode(org: string, node: DiscoveryNode, identity: NodeIdentity, contributor: Contributor): Awaitable<'created' | 'merged'>;
1484
1493
  /** Insert an edge under `org` (idempotent on the logical `(source, target, relationship)` key). */
1485
- insertEdge(org: string, edge: DiscoveryEdge): void;
1494
+ insertEdge(org: string, edge: DiscoveryEdge): Awaitable<void>;
1486
1495
  /** Org-wide aggregate summary (merged counts across all contributors). */
1487
- getSummary(org: string): OrgSummary;
1496
+ getSummary(org: string): Awaitable<OrgSummary>;
1488
1497
  /** Contributors for a merged logical node (test/audit helper). */
1489
- getContributors(globalId: string): Contributor[];
1498
+ getContributors(globalId: string): Awaitable<Contributor[]>;
1490
1499
  /** Release any underlying resources. */
1491
- close(): void;
1500
+ close(): Awaitable<void>;
1492
1501
  }
1493
1502
 
1494
1503
  /**
@@ -1519,6 +1528,76 @@ declare class SqliteStoreBackend implements StoreBackend {
1519
1528
  close(): void;
1520
1529
  }
1521
1530
 
1531
+ /**
1532
+ * `GraphStoreBackend` (4.3) — an opt-in central-store backend over a Bolt-speaking
1533
+ * graph database (Neo4j / Memgraph). Implements the async-capable {@link StoreBackend}
1534
+ * seam so the central collector's merge + org-summary path can scale to 10K+ nodes on a
1535
+ * native graph engine, while SQLite stays the zero-config default.
1536
+ *
1537
+ * `neo4j-driver` is an OPTIONAL dependency, dynamically imported by `openStoreBackend`
1538
+ * (`src/store/index.ts`); this module depends only on a minimal structural Bolt interface,
1539
+ * so it compiles and the package degrades gracefully when the driver is absent. The driver
1540
+ * is injected (constructor), which also lets tests drive it with a mock — no live DB in CI.
1541
+ *
1542
+ * Merge identity mirrors SQLite: a logical node is keyed by `(org, globalId)`; the
1543
+ * `contentHash` is stored + indexed for the id-drift secondary collapse (best-effort here,
1544
+ * tracked as a follow-up). Contributors are `(org, globalId, machineId)` with max-confidence.
1545
+ */
1546
+
1547
+ interface BoltRecord {
1548
+ get(key: string): unknown;
1549
+ }
1550
+ interface BoltResult {
1551
+ records: BoltRecord[];
1552
+ }
1553
+ interface BoltSession {
1554
+ run(cypher: string, params?: Record<string, unknown>): Promise<BoltResult>;
1555
+ close(): Promise<void>;
1556
+ }
1557
+ interface BoltDriver {
1558
+ session(): BoltSession;
1559
+ close(): Promise<void>;
1560
+ verifyConnectivity?(): Promise<unknown>;
1561
+ }
1562
+ declare class GraphStoreBackend implements StoreBackend {
1563
+ private readonly driver;
1564
+ constructor(driver: BoltDriver);
1565
+ private run;
1566
+ upsertNode(org: string, node: DiscoveryNode, identity: NodeIdentity, contributor: Contributor): Promise<'created' | 'merged'>;
1567
+ insertEdge(org: string, edge: DiscoveryEdge): Promise<void>;
1568
+ getSummary(org: string): Promise<OrgSummary>;
1569
+ getContributors(globalId: string): Promise<Contributor[]>;
1570
+ close(): Promise<void>;
1571
+ }
1572
+
1573
+ /**
1574
+ * Central-store backend factory (4.3).
1575
+ *
1576
+ * `openStoreBackend` returns a {@link GraphStoreBackend} only when a graph backend is
1577
+ * explicitly requested AND the optional `neo4j-driver` is installed AND the server is
1578
+ * reachable; otherwise it logs a structured WARN and returns the always-available
1579
+ * {@link SqliteStoreBackend}. A missing driver or an unreachable graph server therefore
1580
+ * NEVER breaks the collector — it degrades to SQLite (the "optional deps degrade" locked
1581
+ * constraint). SQLite stays the zero-config default.
1582
+ */
1583
+
1584
+ interface StoreBackendOptions {
1585
+ /** `'graph'` opts into the graph DB; anything else (default) uses SQLite. */
1586
+ backend?: 'sqlite' | 'graph';
1587
+ /** Bolt URL, e.g. `bolt://graph.internal:7687` or `neo4j+s://…`. */
1588
+ graphUrl?: string;
1589
+ graphUser?: string;
1590
+ graphPassword?: string;
1591
+ /** Injected driver factory (tests). Defaults to dynamically importing `neo4j-driver`. */
1592
+ driverFactory?: (url: string, user: string, password: string) => Promise<BoltDriver>;
1593
+ }
1594
+ /**
1595
+ * Resolve the central store backend. Graph when requested + available; else SQLite.
1596
+ * The returned backend is owned by the caller (call `close()` on shutdown for the graph
1597
+ * backend; the SQLite backend's `close()` is a no-op — the `CartographyDB` is shared).
1598
+ */
1599
+ declare function openStoreBackend(db: CartographyDB, opts?: StoreBackendOptions): Promise<StoreBackend>;
1600
+
1522
1601
  /**
1523
1602
  * `QueryBackend` — the **read-only** query seam for the API server (4.2).
1524
1603
  *
@@ -1770,7 +1849,7 @@ interface IngestOptions {
1770
1849
  * The caller (HTTP handler) wraps this in try/catch; the store's per-node upsert is
1771
1850
  * itself transactional, so a single bad node never half-writes a row.
1772
1851
  */
1773
- declare function ingestEnvelope(store: StoreBackend, envelope: IngestEnvelope, opts?: IngestOptions): IngestResult;
1852
+ declare function ingestEnvelope(store: StoreBackend, envelope: IngestEnvelope, opts?: IngestOptions): Promise<IngestResult>;
1774
1853
 
1775
1854
  /**
1776
1855
  * Ingest backpressure for the central collector (4.7).
@@ -1825,7 +1904,7 @@ interface IngestResponse {
1825
1904
  /** Extra response headers (e.g. `Retry-After` on a 429). */
1826
1905
  headers?: Record<string, string>;
1827
1906
  }
1828
- type IngestHandler = (body: unknown) => IngestResponse;
1907
+ type IngestHandler = (body: unknown) => Promise<IngestResponse>;
1829
1908
  interface IngestHandlerOptions extends IngestOptions {
1830
1909
  /** Per-org ingest rate limiter (4.7 backpressure). Over-quota → 429 + Retry-After. */
1831
1910
  quota?: RateLimiter;
@@ -2186,6 +2265,12 @@ interface CreateMcpServerOptions {
2186
2265
  * behaviour exactly. The org is normalized to a tenant.
2187
2266
  */
2188
2267
  org?: string;
2268
+ /**
2269
+ * Org-wide summary source (4.3). When set (server-mode with a graph backend), the
2270
+ * org `get_summary` reads from here instead of `db.getOrgSummary` — so a graph-DB
2271
+ * collector serves its own merged aggregate. Defaults to the SQLite central store.
2272
+ */
2273
+ orgSummary?: (org: string) => OrgSummary | Promise<OrgSummary>;
2189
2274
  /**
2190
2275
  * The authenticated principal (4.5 RBAC). When set, mutating tools (`run_discovery`)
2191
2276
  * are gated by role: a `viewer` is refused with a forbidden error. Read tools are
@@ -2229,7 +2314,11 @@ interface HttpOptions {
2229
2314
  * caps the body, parses JSON, and returns the hook's `{ status, body }`. When unset,
2230
2315
  * `/ingest` 404s exactly like any other path — the collector stays dark by default.
2231
2316
  */
2232
- onIngest?: (body: unknown) => {
2317
+ onIngest?: (body: unknown) => Promise<{
2318
+ status: number;
2319
+ body: unknown;
2320
+ headers?: Record<string, string>;
2321
+ }> | {
2233
2322
  status: number;
2234
2323
  body: unknown;
2235
2324
  headers?: Record<string, string>;
@@ -4186,4 +4275,4 @@ declare function logInfo(message: string, context?: Record<string, unknown>): vo
4186
4275
  declare function logWarn(message: string, context?: Record<string, unknown>): void;
4187
4276
  declare function logError(message: string, context?: Record<string, unknown>): void;
4188
4277
 
4189
- export { ACTIONS, ANOMALY_KINDS, ANOMALY_SEVERITIES, type Action, ActionSchema, type AgentProvider, type AgentRunContext, type AgentTool, type Anomaly, type AnomalyConfig, type AnomalyKind, type AnomalySeverity, type AnomalyThresholds, type AnonViolation, type AnonymizationLevel, type ApiServerOptions, type AskUserFn, type AuthConfig, AuthConfigSchema, AuthorizationError, type BackstageEntity, type BackstageMapOptions, type BindGuardOptions, CLIENTS, CONFIDENCE, COST_PERIODS, type CartographyConfig, CartographyDB, type CartographyMapData, type CentralDbConfig, CentralDbConfigSchema, type ClassifiedItem, type ClassifyInput, type ClassifyResult, type ClientSpec, type Cluster, ClusterSchema, type ComplianceInput, type ComplianceReport, ComplianceReportSchema, type ComplianceRule, ComplianceRuleSchema, type Condition, ConditionSchema, ConfigError, type ConfigFile, ConfigFileSchema, type ConfigFormat, type Connection, ConnectionSchema, type Contributor, type ControlResult, ControlResultSchema, type CostEntry, CostEntrySchema, type CostPeriod, type CostRecord, type CostSource, type CreateMcpServerOptions, type CredentialConfig, CredentialConfigSchema, type CredentialDb, type CredentialRecord, type CredentialStore, type CronFields, CsvCostSource, type CsvCostSourceOptions, DEFAULT_ANOMALY_THRESHOLDS, DEFAULT_FAST_MODEL, DEFAULT_INGEST_QUOTA, DEFAULT_LEAD_MODEL, DEFAULT_SERVER_NAME, DEFAULT_TENANT, DOMAIN_COLORS, DOMAIN_PALETTE, DRIFT_FIELDS, type DataAsset, DataAssetSchema, type DependencyQuery, type DiscoveryEdge, type DiscoveryEvent, type DiscoveryFn, type DiscoveryNode, type DriftAlert, type DriftAlertItem, type DriftConfig, DriftConfigSchema, type DriftField, type DriftItemKind, type DriftRunRow, type DriftSink, type DriftSinkConfig, EDGE_RELATIONSHIPS, type EdgeRelationship, type EdgeRow, EdgeSchema, type EmbeddingProvider, type EnrichResult, type EntryOptions, type EstablishedConn, type EvidenceKind, type FetchLike, type FragmentKind, type GraphSummary, type HealthResult, type HttpOptions, INGEST_SCHEMA_VERSION, type IngestEnvelope, IngestEnvelopeSchema, type IngestHandler, type IngestHandlerOptions, type IngestOptions, type IngestResponse, type IngestResult, type InstallPlan, InvalidTenantError, type JiraIssue, type JiraOptions, JiraSink, type JiraSinkOptions, LOOPBACK_HOSTS, type LocalDiscoveryOptions, type LocalDiscoveryResult, type LogEntry, type LogLevel, MCP_BIN, type MatchStrategy, NODE_TYPES, NODE_TYPE_GROUPS, type NlIntent, type NlQueryOptions, type NlQueryResult, type NlRelation, type NodeAttribution, type NodeChange, type NodeIdentity, type NodeQuery, type NodeRow, NodeSchema, type NodeType, type NodesResult, NotFoundError, OUTPUT_FORMATS, type OrgKeyOptions, type OrgSummary, type OsKind, type OutputFormat, PACKAGE_NAME, PAGERDUTY_ENQUEUE_URL, PENDING_STATUSES, PERSONAL, PORT_MAP, PRIVATE_IP, PUSH_SCHEMA_VERSION, type PagerDutyEvent, PagerDutySink, type PagerDutySinkOptions, type ParsedApiArgs, type PendingShareRow, type PendingStatus, type PlanOptions, type PolicyResult, type PostJsonOptions, type Principal, PrincipalSchema, type ProviderFactory, type ProviderName, ProviderRegistry, type PushItem, type PushOptions, type PushResult, type QueryBackend, type QuotaConfig, type QuotaDecision, RELATION_TO_DIRECTION, ROLES, RateLimiter, type ResolveContext, type ResolveOptions, type Role, RoleSchema, type RuleCheck, RuleCheckSchema, type RuleScope, type Ruleset, RulesetSchema, type RunDriftOptions, SCAN_ARG_PATTERNS, SCHEMA_VERSION, SDL, SECURITY_METADATA_KEYS, SEVERITIES, SEVERITY_WEIGHT, SHARING_LEVELS, type ScanArgKind, type ScanContext, type ScanHintParams, type ScanResult, type Scanner, type ScannerPlugin, type ScannerPluginApi, ScannerRegistry, ScannerShape, type ScheduleConfig, ScheduleConfigSchema, type ScheduledRunResult, type Scope, type SearchFn, type SemanticSearchOptions, type ServerEntry, type SessionRow, type Severity, type SharePreview, type SharePreviewEntry, type SharingLevel, SharingLevelSchema, type SharingPolicy, type ShellKind, type SlackMessage, SlackSink, SqliteCredentialStore, SqliteQueryBackend, SqliteStoreBackend, type StartApiOptions, StdoutSink, type StoreBackend, type SyncClassifyOptions, type SyncClassifyResult, TENANT_HEADER, type TenantContext, TenantMismatchError, type TenantOptions, type ToolResult, type TopologyDelta, type TopologyDiff, type TopologyInput, type TraversalResult, VectorStore, WebhookSink, type WebhookSinkOptions, applyInstall, applySharingLevel, assertReadOnly, assertSafeBind, assertSafeScanArg, assertSameTenant, assignColors, authorize, bearerToken, bookmarksScanner, buildCartographyToolHandlers, buildMapData, buildOpenApiDocument, buildReport, buildSinks, can, centralDbFromEnv, checkBearer, checkPrerequisites, checkReadOnly, clampText, classify, classifyDrift, cleanupTempFiles, cloudAwsScanner, cloudAzureScanner, cloudGcpScanner, codeAddMcpCommand, computeCentroid, computeClusterBounds, computeIdentity, connectionsScanner, contentHash, createBashTool, createCartographyTools, createClaudeProvider, createDefaultRegistry, createHashEmbedder, createIngestHandler, createLocalEmbedder, createMcpServer, createOllamaProvider, createOpenAIProvider, createScanRunner, createSemanticSearch, createSqliteQueryBackend, currentOs, cursorDeeplink, databasesScanner, deepMerge, defaultAllowedHosts, defaultConfig, defaultContext, defaultProviderRegistry, defaultRegistry, defaultServerEntry, definePlugin, deriveSessionName, detectAnomalies, detectOrphans, detectShadowIt, diffTopology, edgesToConnections, enrichCosts, entitiesToYaml, evaluateCheck, evaluateRule, evidenceLine, executeGraphql, executeNlQuery, exportAll, exportBackstageYAML, exportComplianceReport, exportCostCSV, exportCostSummary, exportDiscoveryApp, exportJGF, exportJSON, extractListeningPorts, filterBySeverity, findAnonViolations, formatComplianceText, formatJira, formatPagerDuty, formatSlack, generateDependencyMermaid, generateDiffMermaid, generateTopologyMermaid, getClient, getRuleset, globalId, groupByDomain, handleGraphqlGet, hashToken, hexCorners, hexDistance, hexNeighbors, hexRing, hexSpiral, hexToPixel, hmacKey, hostname, ingestEnvelope, installedAppsScanner, isLoopbackHost, isPersonalHost, isReadOnlyCommand, isRemembered, isSecureWebhookUrl, k8sScanner, keyMetaOf, layoutClusters, listClients, listRulesets, loadConfig, loadOrgKey, loadPlugins, loadRuleset, localDiscoveryFn, log, logDebug, logError, logInfo, logWarn, machineId, maxSeverity, mcpServerObject, newAnomalies, nextRun, nodesToAssets, normalizeId, normalizeTenant, orgKeyPath, osUser, parseApiArgs, parseComposeDeps, parseConfig, parseConnectionString, parseCostCsv, parseCron, parseEstablished, parseNginxUpstreams, parseNlQuery, parseScanHint, pixelToHex, planInstall, portsScanner, postJson, previewShare, pseudonymize, pseudonymizeFragment, pseudonymizeString, pushDeltas, readConfigFile, redactConnectionString, redactSecrets, redactValue, renderDiff, resolveEffectiveLevel, resolveNlQuery, resolvePrincipal, resolveSharingLevel, resolveTenant, revalidateAnonymized, reversalKey, reversePseudonym, rotateOrgKey, runApi, runDiscovery, runDrift, runHttp, runLocalDiscovery, runOnce, runStdio, runSyncClassify, safeEnv, safeJson, safetyHook, sanitizeUntrusted, sanitizeValue, scopeReads, scoreTopology, securityRelevantChange, serializeConfig, serviceConfigScanner, setVerbose, shadeVariant, shapeToJsonSchema, shareHash, splitSegments, stableStringify, startApi, stripSensitive, timingSafeEqual, toBackstageEntities, validateScanner, vscodeDeeplink, zodToJsonSchema };
4278
+ export { ACTIONS, ANOMALY_KINDS, ANOMALY_SEVERITIES, type Action, ActionSchema, type AgentProvider, type AgentRunContext, type AgentTool, type Anomaly, type AnomalyConfig, type AnomalyKind, type AnomalySeverity, type AnomalyThresholds, type AnonViolation, type AnonymizationLevel, type ApiServerOptions, type AskUserFn, type AuthConfig, AuthConfigSchema, AuthorizationError, type Awaitable, type BackstageEntity, type BackstageMapOptions, type BindGuardOptions, type BoltDriver, type BoltRecord, type BoltResult, type BoltSession, CLIENTS, CONFIDENCE, COST_PERIODS, type CartographyConfig, CartographyDB, type CartographyMapData, type CentralDbConfig, CentralDbConfigSchema, type ClassifiedItem, type ClassifyInput, type ClassifyResult, type ClientSpec, type Cluster, ClusterSchema, type ComplianceInput, type ComplianceReport, ComplianceReportSchema, type ComplianceRule, ComplianceRuleSchema, type Condition, ConditionSchema, ConfigError, type ConfigFile, ConfigFileSchema, type ConfigFormat, type Connection, ConnectionSchema, type Contributor, type ControlResult, ControlResultSchema, type CostEntry, CostEntrySchema, type CostPeriod, type CostRecord, type CostSource, type CreateMcpServerOptions, type CredentialConfig, CredentialConfigSchema, type CredentialDb, type CredentialRecord, type CredentialStore, type CronFields, CsvCostSource, type CsvCostSourceOptions, DEFAULT_ANOMALY_THRESHOLDS, DEFAULT_FAST_MODEL, DEFAULT_INGEST_QUOTA, DEFAULT_LEAD_MODEL, DEFAULT_SERVER_NAME, DEFAULT_TENANT, DOMAIN_COLORS, DOMAIN_PALETTE, DRIFT_FIELDS, type DataAsset, DataAssetSchema, type DependencyQuery, type DiscoveryEdge, type DiscoveryEvent, type DiscoveryFn, type DiscoveryNode, type DriftAlert, type DriftAlertItem, type DriftConfig, DriftConfigSchema, type DriftField, type DriftItemKind, type DriftRunRow, type DriftSink, type DriftSinkConfig, EDGE_RELATIONSHIPS, type EdgeRelationship, type EdgeRow, EdgeSchema, type EmbeddingProvider, type EnrichResult, type EntryOptions, type EstablishedConn, type EvidenceKind, type FetchLike, type FragmentKind, GraphStoreBackend, type GraphSummary, type HealthResult, type HttpOptions, INGEST_SCHEMA_VERSION, type IngestEnvelope, IngestEnvelopeSchema, type IngestHandler, type IngestHandlerOptions, type IngestOptions, type IngestResponse, type IngestResult, type InstallPlan, InvalidTenantError, type JiraIssue, type JiraOptions, JiraSink, type JiraSinkOptions, LOOPBACK_HOSTS, type LocalDiscoveryOptions, type LocalDiscoveryResult, type LogEntry, type LogLevel, MCP_BIN, type MatchStrategy, NODE_TYPES, NODE_TYPE_GROUPS, type NlIntent, type NlQueryOptions, type NlQueryResult, type NlRelation, type NodeAttribution, type NodeChange, type NodeIdentity, type NodeQuery, type NodeRow, NodeSchema, type NodeType, type NodesResult, NotFoundError, OUTPUT_FORMATS, type OrgKeyOptions, type OrgSummary, type OsKind, type OutputFormat, PACKAGE_NAME, PAGERDUTY_ENQUEUE_URL, PENDING_STATUSES, PERSONAL, PORT_MAP, PRIVATE_IP, PUSH_SCHEMA_VERSION, type PagerDutyEvent, PagerDutySink, type PagerDutySinkOptions, type ParsedApiArgs, type PendingShareRow, type PendingStatus, type PlanOptions, type PolicyResult, type PostJsonOptions, type Principal, PrincipalSchema, type ProviderFactory, type ProviderName, ProviderRegistry, type PushItem, type PushOptions, type PushResult, type QueryBackend, type QuotaConfig, type QuotaDecision, RELATION_TO_DIRECTION, ROLES, RateLimiter, type ResolveContext, type ResolveOptions, type Role, RoleSchema, type RuleCheck, RuleCheckSchema, type RuleScope, type Ruleset, RulesetSchema, type RunDriftOptions, SCAN_ARG_PATTERNS, SCHEMA_VERSION, SDL, SECURITY_METADATA_KEYS, SEVERITIES, SEVERITY_WEIGHT, SHARING_LEVELS, type ScanArgKind, type ScanContext, type ScanHintParams, type ScanResult, type Scanner, type ScannerPlugin, type ScannerPluginApi, ScannerRegistry, ScannerShape, type ScheduleConfig, ScheduleConfigSchema, type ScheduledRunResult, type Scope, type SearchFn, type SemanticSearchOptions, type ServerEntry, type SessionRow, type Severity, type SharePreview, type SharePreviewEntry, type SharingLevel, SharingLevelSchema, type SharingPolicy, type ShellKind, type SlackMessage, SlackSink, SqliteCredentialStore, SqliteQueryBackend, SqliteStoreBackend, type StartApiOptions, StdoutSink, type StoreBackend, type StoreBackendOptions, type SyncClassifyOptions, type SyncClassifyResult, TENANT_HEADER, type TenantContext, TenantMismatchError, type TenantOptions, type ToolResult, type TopologyDelta, type TopologyDiff, type TopologyInput, type TraversalResult, VectorStore, WebhookSink, type WebhookSinkOptions, applyInstall, applySharingLevel, assertReadOnly, assertSafeBind, assertSafeScanArg, assertSameTenant, assignColors, authorize, bearerToken, bookmarksScanner, buildCartographyToolHandlers, buildMapData, buildOpenApiDocument, buildReport, buildSinks, can, centralDbFromEnv, checkBearer, checkPrerequisites, checkReadOnly, clampText, classify, classifyDrift, cleanupTempFiles, cloudAwsScanner, cloudAzureScanner, cloudGcpScanner, codeAddMcpCommand, computeCentroid, computeClusterBounds, computeIdentity, connectionsScanner, contentHash, createBashTool, createCartographyTools, createClaudeProvider, createDefaultRegistry, createHashEmbedder, createIngestHandler, createLocalEmbedder, createMcpServer, createOllamaProvider, createOpenAIProvider, createScanRunner, createSemanticSearch, createSqliteQueryBackend, currentOs, cursorDeeplink, databasesScanner, deepMerge, defaultAllowedHosts, defaultConfig, defaultContext, defaultProviderRegistry, defaultRegistry, defaultServerEntry, definePlugin, deriveSessionName, detectAnomalies, detectOrphans, detectShadowIt, diffTopology, edgesToConnections, enrichCosts, entitiesToYaml, evaluateCheck, evaluateRule, evidenceLine, executeGraphql, executeNlQuery, exportAll, exportBackstageYAML, exportComplianceReport, exportCostCSV, exportCostSummary, exportDiscoveryApp, exportJGF, exportJSON, extractListeningPorts, filterBySeverity, findAnonViolations, formatComplianceText, formatJira, formatPagerDuty, formatSlack, generateDependencyMermaid, generateDiffMermaid, generateTopologyMermaid, getClient, getRuleset, globalId, groupByDomain, handleGraphqlGet, hashToken, hexCorners, hexDistance, hexNeighbors, hexRing, hexSpiral, hexToPixel, hmacKey, hostname, ingestEnvelope, installedAppsScanner, isLoopbackHost, isPersonalHost, isReadOnlyCommand, isRemembered, isSecureWebhookUrl, k8sScanner, keyMetaOf, layoutClusters, listClients, listRulesets, loadConfig, loadOrgKey, loadPlugins, loadRuleset, localDiscoveryFn, log, logDebug, logError, logInfo, logWarn, machineId, maxSeverity, mcpServerObject, newAnomalies, nextRun, nodesToAssets, normalizeId, normalizeTenant, openStoreBackend, orgKeyPath, osUser, parseApiArgs, parseComposeDeps, parseConfig, parseConnectionString, parseCostCsv, parseCron, parseEstablished, parseNginxUpstreams, parseNlQuery, parseScanHint, pixelToHex, planInstall, portsScanner, postJson, previewShare, pseudonymize, pseudonymizeFragment, pseudonymizeString, pushDeltas, readConfigFile, redactConnectionString, redactSecrets, redactValue, renderDiff, resolveEffectiveLevel, resolveNlQuery, resolvePrincipal, resolveSharingLevel, resolveTenant, revalidateAnonymized, reversalKey, reversePseudonym, rotateOrgKey, runApi, runDiscovery, runDrift, runHttp, runLocalDiscovery, runOnce, runStdio, runSyncClassify, safeEnv, safeJson, safetyHook, sanitizeUntrusted, sanitizeValue, scopeReads, scoreTopology, securityRelevantChange, serializeConfig, serviceConfigScanner, setVerbose, shadeVariant, shapeToJsonSchema, shareHash, splitSegments, stableStringify, startApi, stripSensitive, timingSafeEqual, toBackstageEntities, validateScanner, vscodeDeeplink, zodToJsonSchema };
package/dist/index.js CHANGED
@@ -4362,6 +4362,148 @@ var SqliteStoreBackend = class {
4362
4362
  }
4363
4363
  };
4364
4364
 
4365
+ // src/store/graph.ts
4366
+ function toNum(v) {
4367
+ if (typeof v === "number") return v;
4368
+ if (v && typeof v === "object" && "toNumber" in v && typeof v.toNumber === "function") {
4369
+ return v.toNumber();
4370
+ }
4371
+ return Number(v ?? 0);
4372
+ }
4373
+ var GraphStoreBackend = class {
4374
+ constructor(driver) {
4375
+ this.driver = driver;
4376
+ }
4377
+ async run(cypher, params) {
4378
+ const session = this.driver.session();
4379
+ try {
4380
+ return await session.run(cypher, params);
4381
+ } finally {
4382
+ await session.close();
4383
+ }
4384
+ }
4385
+ async upsertNode(org, node, identity, contributor) {
4386
+ const res = await this.run(
4387
+ `MERGE (n:Node {org: $org, globalId: $globalId})
4388
+ ON CREATE SET n._created = true
4389
+ ON MATCH SET n._created = false
4390
+ SET n.id = $id, n.contentHash = $contentHash, n.type = $type, n.name = $name,
4391
+ n.domain = $domain, n.owner = $owner,
4392
+ n.confidence = CASE WHEN n.confidence IS NULL OR $confidence > n.confidence THEN $confidence ELSE n.confidence END
4393
+ MERGE (c:Contributor {org: $org, globalId: $globalId, machineId: $machineId})
4394
+ SET c.hostname = $hostname, c.user = $user, c.at = $at,
4395
+ c.confidence = CASE WHEN c.confidence IS NULL OR $contribConfidence > c.confidence THEN $contribConfidence ELSE c.confidence END
4396
+ RETURN n._created AS created`,
4397
+ {
4398
+ org,
4399
+ globalId: identity.globalId,
4400
+ contentHash: identity.contentHash,
4401
+ id: node.id,
4402
+ type: node.type,
4403
+ name: node.name,
4404
+ domain: node.domain ?? null,
4405
+ owner: node.owner ?? null,
4406
+ confidence: node.confidence,
4407
+ machineId: contributor.machineId,
4408
+ hostname: contributor.hostname,
4409
+ user: contributor.user,
4410
+ at: contributor.at,
4411
+ contribConfidence: contributor.confidence
4412
+ }
4413
+ );
4414
+ return res.records[0]?.get("created") === true ? "created" : "merged";
4415
+ }
4416
+ async insertEdge(org, edge) {
4417
+ await this.run(
4418
+ `MATCH (s:Node {org: $org, id: $source})
4419
+ MATCH (t:Node {org: $org, id: $target})
4420
+ MERGE (s)-[r:DEPENDS {relationship: $rel}]->(t)
4421
+ SET r.evidence = $evidence, r.confidence = $confidence`,
4422
+ { org, source: edge.sourceId, target: edge.targetId, rel: edge.relationship, evidence: edge.evidence, confidence: edge.confidence }
4423
+ );
4424
+ }
4425
+ async getSummary(org) {
4426
+ const totals = await this.run(
4427
+ `MATCH (n:Node {org: $org})
4428
+ OPTIONAL MATCH (n)-[r:DEPENDS]->(:Node {org: $org})
4429
+ RETURN count(DISTINCT n) AS nodes, count(r) AS edges`,
4430
+ { org }
4431
+ );
4432
+ const byType = await this.run(`MATCH (n:Node {org: $org}) RETURN n.type AS k, count(*) AS c`, { org });
4433
+ const byDomain = await this.run(`MATCH (n:Node {org: $org}) RETURN coalesce(n.domain, '(none)') AS k, count(*) AS c`, { org });
4434
+ const byRel = await this.run(`MATCH (:Node {org: $org})-[r:DEPENDS]->(:Node {org: $org}) RETURN r.relationship AS k, count(*) AS c`, { org });
4435
+ const top = await this.run(
4436
+ `MATCH (n:Node {org: $org})
4437
+ OPTIONAL MATCH (n)-[r:DEPENDS]-(:Node {org: $org})
4438
+ RETURN n.id AS id, n.name AS name, n.type AS type, count(r) AS degree
4439
+ ORDER BY degree DESC, id ASC LIMIT 10`,
4440
+ { org }
4441
+ );
4442
+ const contrib = await this.run(`MATCH (c:Contributor {org: $org}) RETURN count(DISTINCT c.machineId) AS contributors`, { org });
4443
+ const counts = (r) => {
4444
+ const out = {};
4445
+ for (const rec of r.records) out[String(rec.get("k"))] = toNum(rec.get("c"));
4446
+ return out;
4447
+ };
4448
+ return {
4449
+ org,
4450
+ totals: { nodes: toNum(totals.records[0]?.get("nodes")), edges: toNum(totals.records[0]?.get("edges")) },
4451
+ nodesByType: counts(byType),
4452
+ nodesByDomain: counts(byDomain),
4453
+ edgesByRelationship: counts(byRel),
4454
+ topConnected: top.records.map((rec) => ({
4455
+ id: String(rec.get("id")),
4456
+ name: String(rec.get("name")),
4457
+ type: String(rec.get("type")),
4458
+ degree: toNum(rec.get("degree"))
4459
+ })),
4460
+ contributors: toNum(contrib.records[0]?.get("contributors"))
4461
+ };
4462
+ }
4463
+ async getContributors(globalId2) {
4464
+ const res = await this.run(
4465
+ `MATCH (c:Contributor {globalId: $globalId})
4466
+ RETURN c.machineId AS machineId, c.hostname AS hostname, c.user AS user, c.org AS org, c.at AS at, c.confidence AS confidence`,
4467
+ { globalId: globalId2 }
4468
+ );
4469
+ return res.records.map((rec) => ({
4470
+ machineId: String(rec.get("machineId")),
4471
+ hostname: String(rec.get("hostname")),
4472
+ user: String(rec.get("user")),
4473
+ organization: rec.get("org") != null ? String(rec.get("org")) : void 0,
4474
+ at: String(rec.get("at")),
4475
+ confidence: toNum(rec.get("confidence"))
4476
+ }));
4477
+ }
4478
+ async close() {
4479
+ await this.driver.close();
4480
+ }
4481
+ };
4482
+
4483
+ // src/store/index.ts
4484
+ async function defaultNeo4jDriver(url, user, password) {
4485
+ const mod = await import("neo4j-driver");
4486
+ return mod.default.driver(url, mod.default.auth.basic(user, password));
4487
+ }
4488
+ async function openStoreBackend(db, opts = {}) {
4489
+ if (opts.backend === "graph" && opts.graphUrl) {
4490
+ try {
4491
+ const make = opts.driverFactory ?? defaultNeo4jDriver;
4492
+ const driver = await make(opts.graphUrl, opts.graphUser ?? "neo4j", opts.graphPassword ?? "");
4493
+ if (driver.verifyConnectivity) await driver.verifyConnectivity();
4494
+ logInfo("central store: graph backend active", { host: stripSensitive(opts.graphUrl) });
4495
+ return new GraphStoreBackend(driver);
4496
+ } catch (err) {
4497
+ logWarn("central store: graph backend unavailable \u2014 falling back to SQLite", {
4498
+ host: stripSensitive(opts.graphUrl),
4499
+ reason: err instanceof Error ? err.message : String(err)
4500
+ });
4501
+ return new SqliteStoreBackend(db);
4502
+ }
4503
+ }
4504
+ return new SqliteStoreBackend(db);
4505
+ }
4506
+
4365
4507
  // src/store/query.ts
4366
4508
  var NotFoundError = class extends Error {
4367
4509
  constructor(message) {
@@ -4676,9 +4818,9 @@ var IngestEnvelopeSchema = z5.object({
4676
4818
  contributor: ContributorSchema.optional(),
4677
4819
  anonymizationLevel: z5.enum(["none", "anonymized", "full"]).optional()
4678
4820
  });
4679
- function ingestEnvelope(store, envelope, opts = {}) {
4821
+ async function ingestEnvelope(store, envelope, opts = {}) {
4680
4822
  const anonMode = opts.anonMode ?? "reject";
4681
- const org = envelope.org ?? opts.defaultOrg ?? "local";
4823
+ const org = normalizeTenant(envelope.org ?? opts.defaultOrg);
4682
4824
  const level = envelope.anonymizationLevel ?? "anonymized";
4683
4825
  const at = (/* @__PURE__ */ new Date()).toISOString();
4684
4826
  const contributor = {
@@ -4729,7 +4871,7 @@ function ingestEnvelope(store, envelope, opts = {}) {
4729
4871
  }
4730
4872
  const safe = check.node;
4731
4873
  const identity = computeIdentity(org, safe);
4732
- const outcome = store.upsertNode(org, safe, identity, { ...contributor, confidence: safe.confidence });
4874
+ const outcome = await store.upsertNode(org, safe, identity, { ...contributor, confidence: safe.confidence });
4733
4875
  accepted += 1;
4734
4876
  if (outcome === "merged") merged += 1;
4735
4877
  acceptedNodeIds.add(safe.id);
@@ -4745,7 +4887,7 @@ function ingestEnvelope(store, envelope, opts = {}) {
4745
4887
  if (acceptedNodeIds.size > 0 && (!acceptedNodeIds.has(edge.sourceId) || !acceptedNodeIds.has(edge.targetId))) {
4746
4888
  continue;
4747
4889
  }
4748
- store.insertEdge(org, edge);
4890
+ await store.insertEdge(org, edge);
4749
4891
  edges += 1;
4750
4892
  }
4751
4893
  logInfo("ingest", { org, accepted, merged, rejected, edges, violations, level, anonMode });
@@ -4755,7 +4897,7 @@ function ingestEnvelope(store, envelope, opts = {}) {
4755
4897
  // src/central/server.ts
4756
4898
  function createIngestHandler(store, opts = {}) {
4757
4899
  const quota = opts.quota;
4758
- return (body) => {
4900
+ return async (body) => {
4759
4901
  const parsed = IngestEnvelopeSchema.safeParse(body);
4760
4902
  if (!parsed.success) {
4761
4903
  const issues = parsed.error.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`);
@@ -4771,7 +4913,7 @@ function createIngestHandler(store, opts = {}) {
4771
4913
  }
4772
4914
  }
4773
4915
  try {
4774
- const result = ingestEnvelope(store, parsed.data, opts);
4916
+ const result = await ingestEnvelope(store, parsed.data, opts);
4775
4917
  return { status: 200, body: result };
4776
4918
  } catch (err) {
4777
4919
  logWarn("ingest: failed", { error: err instanceof Error ? err.message : String(err) });
@@ -5700,7 +5842,7 @@ async function resolveNlQuery(db, sessionId, search, raw, opts) {
5700
5842
 
5701
5843
  // src/mcp/server.ts
5702
5844
  var SERVER_NAME = "cartography";
5703
- var SERVER_VERSION = "2.7.0";
5845
+ var SERVER_VERSION = "2.8.0";
5704
5846
  var SERVICE_TYPES = NODE_TYPE_GROUPS.web;
5705
5847
  var DATA_TYPES = NODE_TYPE_GROUPS.data;
5706
5848
  var lexicalSearch = async (db, sessionId, query, opts) => db.searchNodes(sessionId, query, { types: opts.types, limit: opts.limit }).map((node) => ({ node }));
@@ -5775,9 +5917,10 @@ function createMcpServer(opts = {}) {
5775
5917
  "graph-summary",
5776
5918
  "cartography://graph/summary",
5777
5919
  { title: "Topology summary", description: "Low-token aggregate index of the whole landscape \u2014 read this first.", mimeType: "text/markdown" },
5778
- (uri) => {
5920
+ async (uri) => {
5779
5921
  if (org !== void 0) {
5780
- return { contents: [{ uri: uri.href, mimeType: "text/markdown", text: summaryText(db.getOrgSummary(org)) }] };
5922
+ const s = opts.orgSummary ? await opts.orgSummary(org) : db.getOrgSummary(org);
5923
+ return { contents: [{ uri: uri.href, mimeType: "text/markdown", text: summaryText(s) }] };
5781
5924
  }
5782
5925
  const sid = resolveSession();
5783
5926
  if (!sid) return { contents: [{ uri: uri.href, mimeType: "text/markdown", text: "No discovery session found. Run discovery first." }] };
@@ -5852,8 +5995,8 @@ function createMcpServer(opts = {}) {
5852
5995
  server.registerTool(
5853
5996
  "get_summary",
5854
5997
  { title: "Get topology summary", description: "Low-token overview of the whole landscape (counts, types, domains, most-connected, anomalies).", inputSchema: {}, annotations: readOnly },
5855
- () => {
5856
- if (org !== void 0) return json(db.getOrgSummary(org));
5998
+ async () => {
5999
+ if (org !== void 0) return json(opts.orgSummary ? await opts.orgSummary(org) : db.getOrgSummary(org));
5857
6000
  const sid = resolveSession();
5858
6001
  if (!sid) return json({ error: "No discovery session found." });
5859
6002
  return json(db.getGraphSummary(sid));
@@ -6379,7 +6522,7 @@ async function runHttp(factory, opts = {}) {
6379
6522
  res.writeHead(413, { "content-type": "application/json" }).end('{"error":"payload too large"}');
6380
6523
  return;
6381
6524
  }
6382
- const out = onIngest(value);
6525
+ const out = await onIngest(value);
6383
6526
  res.writeHead(out.status, { "content-type": "application/json", ...out.headers ?? {} }).end(JSON.stringify(out.body));
6384
6527
  return;
6385
6528
  }
@@ -11634,6 +11777,7 @@ export {
11634
11777
  DEFAULT_SERVER_NAME,
11635
11778
  DEFAULT_TENANT,
11636
11779
  DriftConfigSchema,
11780
+ GraphStoreBackend,
11637
11781
  INGEST_SCHEMA_VERSION,
11638
11782
  IngestEnvelopeSchema,
11639
11783
  InvalidTenantError,
@@ -11805,6 +11949,7 @@ export {
11805
11949
  nodesToAssets,
11806
11950
  normalizeId,
11807
11951
  normalizeTenant,
11952
+ openStoreBackend,
11808
11953
  orgKeyPath,
11809
11954
  osUser,
11810
11955
  parseApiArgs,