@datasynx/agentic-ai-cartography 2.2.0 → 2.3.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.js CHANGED
@@ -130,6 +130,8 @@ var DOMAIN_PALETTE = [
130
130
  "#5eead4"
131
131
  ];
132
132
  var DRIFT_FIELDS = ["type", "name", "domain", "subDomain", "qualityScore", "metadata", "tags", "owner", "cost"];
133
+ var ANOMALY_KINDS = ["orphan", "shadow-it"];
134
+ var ANOMALY_SEVERITIES = ["low", "medium", "high"];
133
135
  var DEFAULT_ANOMALY_THRESHOLDS = {
134
136
  orphanWeakDegree: 1,
135
137
  shadowConfidence: 0.4,
@@ -1275,8 +1277,8 @@ var cloudGcpScanner = {
1275
1277
  allowedCommands: ["gcloud"],
1276
1278
  detect: (ctx) => Boolean((ctx.commandExists ?? commandExists)("gcloud")),
1277
1279
  async scan(ctx) {
1278
- const { project } = parseScanHint(ctx.hint);
1279
- const pf = project ? ` --project ${project}` : "";
1280
+ const { project: project2 } = parseScanHint(ctx.hint);
1281
+ const pf = project2 ? ` --project ${project2}` : "";
1280
1282
  const runG = createScanRunner((c) => ctx.run(c, { timeout: 2e4 }), { threshold: 3 });
1281
1283
  const nodes = [];
1282
1284
  const edges = [];
@@ -1964,9 +1966,9 @@ async function buildCartographyToolHandlers(db, sessionId, opts = {}) {
1964
1966
  tool("scan_gcp_resources", "Scan Google Cloud Platform via gcloud CLI \u2014 100% readonly (list, describe)", {
1965
1967
  project: z2.string().regex(SCAN_ARG_PATTERNS["gcp-project"], "invalid GCP project id").optional().describe("GCP Project ID \u2014 default: current gcloud project")
1966
1968
  }, async (args) => {
1967
- const project = args["project"];
1968
- if (project) assertSafeScanArg("gcp-project", project);
1969
- return runScannerTool(cloudGcpScanner, project ? `project=${project}` : "");
1969
+ const project2 = args["project"];
1970
+ if (project2) assertSafeScanArg("gcp-project", project2);
1971
+ return runScannerTool(cloudGcpScanner, project2 ? `project=${project2}` : "");
1970
1972
  }, { annotations: READ_SCAN }),
1971
1973
  tool("scan_azure_resources", "Scan Azure infrastructure via az CLI \u2014 100% readonly (list, show)", {
1972
1974
  subscription: z2.string().regex(SCAN_ARG_PATTERNS["azure-subscription"], "invalid Azure subscription id").optional().describe("Azure Subscription ID"),
@@ -2118,14 +2120,14 @@ async function buildCartographyToolHandlers(db, sessionId, opts = {}) {
2118
2120
  "neon"
2119
2121
  ];
2120
2122
  const found = [];
2121
- const notFound = [];
2123
+ const notFound2 = [];
2122
2124
  for (const t of knownTools) {
2123
2125
  const r = commandExists(t);
2124
2126
  if (r) found.push(`${t}: ${r}`);
2125
- else notFound.push(t);
2127
+ else notFound2.push(t);
2126
2128
  }
2127
2129
  results["TOOLS_FOUND"] = found.join("\n") || "(none found)";
2128
- results["TOOLS_NOT_FOUND"] = notFound.join(", ");
2130
+ results["TOOLS_NOT_FOUND"] = notFound2.join(", ");
2129
2131
  if (hint) {
2130
2132
  const terms = hint.split(/[\s,]+/).filter(Boolean);
2131
2133
  const hintResults = [];
@@ -4262,6 +4264,86 @@ var SqliteStoreBackend = class {
4262
4264
  }
4263
4265
  };
4264
4266
 
4267
+ // src/store/query.ts
4268
+ var NotFoundError = class extends Error {
4269
+ constructor(message) {
4270
+ super(message);
4271
+ this.name = "NotFoundError";
4272
+ }
4273
+ };
4274
+ var MAX_NODE_LIMIT = 1e3;
4275
+ var MAX_DEPTH = 64;
4276
+ function clamp(value, min, max) {
4277
+ return Math.floor(Math.max(min, Math.min(value, max)));
4278
+ }
4279
+ var SqliteQueryBackend = class {
4280
+ constructor(db, defaultSession = "latest") {
4281
+ this.db = db;
4282
+ this.defaultSession = defaultSession;
4283
+ }
4284
+ /**
4285
+ * Resolve the session id for a request, scoped to `ctx.tenant`. An explicit id must
4286
+ * belong to the tenant or it resolves to undefined (cross-tenant isolation); else the
4287
+ * newest `discover` session for the tenant. Mirrors `resolveSession` in the MCP server.
4288
+ */
4289
+ resolveSession(ctx, sessionId) {
4290
+ const requested = sessionId ?? (this.defaultSession === "latest" ? void 0 : this.defaultSession);
4291
+ if (requested) {
4292
+ const s = this.db.getSession(requested);
4293
+ if (s && s.tenant === ctx.tenant) return s.id;
4294
+ throw new NotFoundError(`session not found`);
4295
+ }
4296
+ const latest = this.db.getLatestSession("discover", ctx.tenant) ?? this.db.getLatestSession(void 0, ctx.tenant);
4297
+ if (!latest) throw new NotFoundError(`no session available`);
4298
+ return latest.id;
4299
+ }
4300
+ summary(ctx, sessionId) {
4301
+ return this.db.getGraphSummary(this.resolveSession(ctx, sessionId));
4302
+ }
4303
+ nodes(ctx, q, sessionId) {
4304
+ const sid = this.resolveSession(ctx, sessionId);
4305
+ const limit = clamp(q.limit ?? 100, 1, MAX_NODE_LIMIT);
4306
+ const offset = Math.floor(Math.max(0, q.offset ?? 0));
4307
+ const total = this.db.getNodeCount(sid);
4308
+ if (q.search) {
4309
+ const nodes2 = this.db.searchNodes(sid, q.search, { ...q.types ? { types: q.types } : {}, limit });
4310
+ return { nodes: nodes2, total: nodes2.length, limit, offset: 0 };
4311
+ }
4312
+ const nodes = this.db.getNodes(sid, { limit, offset });
4313
+ return { nodes, total, limit, offset };
4314
+ }
4315
+ node(ctx, id, sessionId) {
4316
+ return this.db.getNode(this.resolveSession(ctx, sessionId), id);
4317
+ }
4318
+ dependencies(ctx, id, q, sessionId) {
4319
+ const sid = this.resolveSession(ctx, sessionId);
4320
+ return this.db.getDependencies(sid, id, {
4321
+ direction: q.direction ?? "downstream",
4322
+ maxDepth: clamp(q.maxDepth ?? 8, 1, MAX_DEPTH)
4323
+ });
4324
+ }
4325
+ diff(ctx, base, current) {
4326
+ for (const id of [base, current]) {
4327
+ const s = this.db.getSession(id);
4328
+ if (!s || s.tenant !== ctx.tenant) throw new NotFoundError(`session not found`);
4329
+ }
4330
+ try {
4331
+ return this.db.diffSessions(base, current);
4332
+ } catch (err) {
4333
+ throw new NotFoundError(err instanceof Error ? err.message : "diff failed");
4334
+ }
4335
+ }
4336
+ sessions(ctx) {
4337
+ return this.db.getSessions(ctx.tenant);
4338
+ }
4339
+ health(ctx) {
4340
+ return { store: "sqlite", sessions: this.db.getSessions(ctx.tenant).length };
4341
+ }
4342
+ };
4343
+ function createSqliteQueryBackend(db, defaultSession = "latest") {
4344
+ return new SqliteQueryBackend(db, defaultSession);
4345
+ }
4346
+
4265
4347
  // src/central/merge.ts
4266
4348
  function computeIdentity(org, node) {
4267
4349
  return {
@@ -5292,7 +5374,7 @@ async function resolveNlQuery(db, sessionId, search, raw, opts) {
5292
5374
 
5293
5375
  // src/mcp/server.ts
5294
5376
  var SERVER_NAME = "cartography";
5295
- var SERVER_VERSION = "2.2.0";
5377
+ var SERVER_VERSION = "2.3.0";
5296
5378
  var SERVICE_TYPES = NODE_TYPE_GROUPS.web;
5297
5379
  var DATA_TYPES = NODE_TYPE_GROUPS.data;
5298
5380
  var lexicalSearch = async (db, sessionId, query, opts) => db.searchNodes(sessionId, query, { types: opts.types, limit: opts.limit }).map((node) => ({ node }));
@@ -5793,6 +5875,50 @@ import { randomUUID as randomUUID2 } from "crypto";
5793
5875
  import http from "http";
5794
5876
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5795
5877
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
5878
+
5879
+ // src/api/auth.ts
5880
+ var LOOPBACK_HOSTS2 = /* @__PURE__ */ new Set(["127.0.0.1", "localhost", "::1", "[::1]"]);
5881
+ function isLoopbackHost(host2) {
5882
+ return LOOPBACK_HOSTS2.has(host2);
5883
+ }
5884
+ function timingSafeEqual(a, b) {
5885
+ if (a.length !== b.length) return false;
5886
+ let diff = 0;
5887
+ for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
5888
+ return diff === 0;
5889
+ }
5890
+ function bearerToken(header) {
5891
+ if (!header) return void 0;
5892
+ const trimmed = header.trim();
5893
+ if (trimmed.length < 7 || trimmed.slice(0, 6).toLowerCase() !== "bearer") return void 0;
5894
+ const rest = trimmed.slice(6);
5895
+ if (!/^\s/.test(rest)) return void 0;
5896
+ const token = rest.trimStart();
5897
+ return token.length > 0 ? token : void 0;
5898
+ }
5899
+ function checkBearer(authorizationHeader, token) {
5900
+ if (!token) return true;
5901
+ const provided = bearerToken(authorizationHeader);
5902
+ return provided !== void 0 && timingSafeEqual(provided, token);
5903
+ }
5904
+ function assertSafeBind(opts) {
5905
+ if (isLoopbackHost(opts.host)) return;
5906
+ if (opts.allowedHosts === void 0) {
5907
+ throw new Error(
5908
+ `Refusing to bind a non-loopback host (${opts.host}) without an explicit allowedHosts allowlist. Pass { allowedHosts: ['your.public.host:port'] } to opt in, or bind 127.0.0.1 for local-only use.`
5909
+ );
5910
+ }
5911
+ if (!opts.token) {
5912
+ throw new Error(
5913
+ `Refusing to bind a non-loopback host (${opts.host}) without an auth token. Pass { token } (or --token / CARTOGRAPHY_HTTP_TOKEN) so requests must carry 'Authorization: Bearer <token>'.`
5914
+ );
5915
+ }
5916
+ }
5917
+ function defaultAllowedHosts(host2, port) {
5918
+ return [`${host2}:${port}`, `localhost:${port}`, `127.0.0.1:${port}`];
5919
+ }
5920
+
5921
+ // src/mcp/transports.ts
5796
5922
  async function runStdio(server) {
5797
5923
  const transport = new StdioServerTransport();
5798
5924
  await server.connect(transport);
@@ -5821,17 +5947,6 @@ async function readCappedBody(req, cap) {
5821
5947
  return { overflow: false, value: void 0 };
5822
5948
  }
5823
5949
  }
5824
- function timingSafeEqual(a, b) {
5825
- if (a.length !== b.length) return false;
5826
- let diff = 0;
5827
- for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
5828
- return diff === 0;
5829
- }
5830
- function bearerToken(header) {
5831
- if (!header) return void 0;
5832
- const m = /^Bearer\s+(.+)$/i.exec(header.trim());
5833
- return m ? m[1] : void 0;
5834
- }
5835
5950
  async function readJsonBody(req) {
5836
5951
  const chunks = [];
5837
5952
  for await (const chunk of req) chunks.push(chunk);
@@ -5842,22 +5957,11 @@ async function readJsonBody(req) {
5842
5957
  return void 0;
5843
5958
  }
5844
5959
  }
5845
- var LOOPBACK_HOSTS2 = /* @__PURE__ */ new Set(["127.0.0.1", "localhost", "::1", "[::1]"]);
5846
5960
  async function runHttp(factory, opts = {}) {
5847
5961
  const host2 = opts.host ?? "127.0.0.1";
5848
5962
  const port = opts.port ?? 3737;
5849
- const isLoopback = LOOPBACK_HOSTS2.has(host2);
5850
- if (!isLoopback && opts.allowedHosts === void 0) {
5851
- throw new Error(
5852
- `Refusing to bind a non-loopback host (${host2}) without an explicit allowedHosts allowlist. Pass { allowedHosts: ['your.public.host:port'] } to opt in, or bind 127.0.0.1 for local-only use.`
5853
- );
5854
- }
5855
- if (!isLoopback && !opts.token) {
5856
- throw new Error(
5857
- `Refusing to bind a non-loopback host (${host2}) without an auth token. Pass { token } (or --token / CARTOGRAPHY_HTTP_TOKEN) so requests must carry 'Authorization: Bearer <token>'.`
5858
- );
5859
- }
5860
- const allowedHosts = opts.allowedHosts ?? [`${host2}:${port}`, `localhost:${port}`, `127.0.0.1:${port}`];
5963
+ assertSafeBind({ host: host2, port, ...opts.allowedHosts ? { allowedHosts: opts.allowedHosts } : {}, ...opts.token ? { token: opts.token } : {} });
5964
+ const allowedHosts = opts.allowedHosts ?? defaultAllowedHosts(host2, port);
5861
5965
  const token = opts.token;
5862
5966
  const transports = /* @__PURE__ */ new Map();
5863
5967
  const httpServer = http.createServer(async (req, res) => {
@@ -5868,12 +5972,9 @@ async function runHttp(factory, opts = {}) {
5868
5972
  res.writeHead(404, { "content-type": "application/json" }).end('{"error":"not found"}');
5869
5973
  return;
5870
5974
  }
5871
- if (token) {
5872
- const provided = bearerToken(req.headers["authorization"]);
5873
- if (!provided || !timingSafeEqual(provided, token)) {
5874
- res.writeHead(401, { "content-type": "application/json", "www-authenticate": "Bearer" }).end('{"error":"unauthorized"}');
5875
- return;
5876
- }
5975
+ if (!checkBearer(req.headers["authorization"], token)) {
5976
+ res.writeHead(401, { "content-type": "application/json", "www-authenticate": "Bearer" }).end('{"error":"unauthorized"}');
5977
+ return;
5877
5978
  }
5878
5979
  if (isIngest) {
5879
5980
  const hostHeader = (req.headers["host"] ?? "").toLowerCase();
@@ -5927,7 +6028,7 @@ async function runHttp(factory, opts = {}) {
5927
6028
  if (!res.headersSent) res.writeHead(500, { "content-type": "application/json" }).end('{"error":"internal error"}');
5928
6029
  }
5929
6030
  });
5930
- await new Promise((resolve2) => httpServer.listen(port, host2, resolve2));
6031
+ await new Promise((resolve3) => httpServer.listen(port, host2, resolve3));
5931
6032
  return httpServer;
5932
6033
  }
5933
6034
 
@@ -6109,8 +6210,8 @@ async function createSemanticSearch(db, embedder, opts = {}) {
6109
6210
  return lexicalSearch2();
6110
6211
  }
6111
6212
  const store = new VectorStore(db, provider);
6112
- const ok = await store.init();
6113
- if (!ok) {
6213
+ const ok3 = await store.init();
6214
+ if (!ok3) {
6114
6215
  log2?.("semantic search: vector store unavailable (sqlite-vec not installed or failed to load) \u2014 using lexical search");
6115
6216
  return lexicalSearch2();
6116
6217
  }
@@ -6719,6 +6820,1037 @@ function localDiscoveryFn(registry, plugins) {
6719
6820
  };
6720
6821
  }
6721
6822
 
6823
+ // src/api/server.ts
6824
+ import http2 from "http";
6825
+
6826
+ // src/api/tenant.ts
6827
+ var TENANT_HEADER = "x-cartograph-tenant";
6828
+ var InvalidTenantError = class extends Error {
6829
+ constructor() {
6830
+ super("invalid tenant");
6831
+ this.name = "InvalidTenantError";
6832
+ }
6833
+ };
6834
+ function resolveTenant(req, url, opts = {}) {
6835
+ const headerName = (opts.header ?? TENANT_HEADER).toLowerCase();
6836
+ const raw = headerValue(req, headerName) ?? url.searchParams.get("tenant") ?? void 0;
6837
+ if (raw === void 0 || raw === "") {
6838
+ return { tenant: opts.defaultTenant ?? DEFAULT_TENANT };
6839
+ }
6840
+ if (raw.trim().length > 128) {
6841
+ throw new InvalidTenantError();
6842
+ }
6843
+ const normalized = normalizeTenant(raw);
6844
+ if (normalized === DEFAULT_TENANT && raw.trim() !== DEFAULT_TENANT) {
6845
+ throw new InvalidTenantError();
6846
+ }
6847
+ return { tenant: normalized };
6848
+ }
6849
+ function headerValue(req, name) {
6850
+ const v = req.headers[name];
6851
+ if (Array.isArray(v)) return v[0];
6852
+ return v;
6853
+ }
6854
+
6855
+ // src/api/schemas.ts
6856
+ import { z as z8 } from "zod";
6857
+ var DIRECTIONS = ["downstream", "upstream", "both"];
6858
+ var CostSchema = z8.object({
6859
+ amount: z8.number(),
6860
+ currency: z8.string(),
6861
+ period: z8.enum(COST_PERIODS),
6862
+ source: z8.string().optional()
6863
+ });
6864
+ var NodeSchema2 = z8.object({
6865
+ id: z8.string(),
6866
+ type: z8.string(),
6867
+ name: z8.string(),
6868
+ confidence: z8.number(),
6869
+ domain: z8.string().optional(),
6870
+ subDomain: z8.string().optional(),
6871
+ qualityScore: z8.number().optional(),
6872
+ owner: z8.string().optional(),
6873
+ cost: CostSchema.optional(),
6874
+ tags: z8.array(z8.string())
6875
+ });
6876
+ var EdgeSchema2 = z8.object({
6877
+ sourceId: z8.string(),
6878
+ targetId: z8.string(),
6879
+ relationship: z8.string(),
6880
+ confidence: z8.number(),
6881
+ evidence: z8.string()
6882
+ });
6883
+ var AnomalySchema = z8.object({
6884
+ nodeId: z8.string(),
6885
+ kind: z8.enum(ANOMALY_KINDS),
6886
+ severity: z8.enum(ANOMALY_SEVERITIES),
6887
+ reason: z8.string()
6888
+ });
6889
+ var TopConnectedSchema = z8.object({
6890
+ id: z8.string(),
6891
+ name: z8.string(),
6892
+ type: z8.string(),
6893
+ degree: z8.number().int()
6894
+ });
6895
+ var CostByDomainSchema = z8.object({
6896
+ domain: z8.string(),
6897
+ currency: z8.string(),
6898
+ period: z8.string(),
6899
+ total: z8.number(),
6900
+ nodes: z8.number().int()
6901
+ });
6902
+ var CostByOwnerSchema = z8.object({
6903
+ owner: z8.string(),
6904
+ currency: z8.string(),
6905
+ period: z8.string(),
6906
+ total: z8.number(),
6907
+ nodes: z8.number().int()
6908
+ });
6909
+ var SummaryResponse = z8.object({
6910
+ sessionId: z8.string(),
6911
+ totals: z8.object({ nodes: z8.number().int(), edges: z8.number().int() }),
6912
+ nodesByType: z8.record(z8.string(), z8.number().int()),
6913
+ nodesByDomain: z8.record(z8.string(), z8.number().int()),
6914
+ edgesByRelationship: z8.record(z8.string(), z8.number().int()),
6915
+ topConnected: z8.array(TopConnectedSchema),
6916
+ anomalies: z8.array(AnomalySchema),
6917
+ contributors: z8.number().int(),
6918
+ costByDomain: z8.array(CostByDomainSchema),
6919
+ costByOwner: z8.array(CostByOwnerSchema),
6920
+ costCoverage: z8.object({ withCost: z8.number().int(), total: z8.number().int() })
6921
+ });
6922
+ var NodesResponse = z8.object({
6923
+ nodes: z8.array(NodeSchema2),
6924
+ total: z8.number().int(),
6925
+ limit: z8.number().int(),
6926
+ offset: z8.number().int()
6927
+ });
6928
+ var DependencyNodeSchema = NodeSchema2.extend({ depth: z8.number().int() });
6929
+ var DependenciesResponse = z8.object({
6930
+ root: NodeSchema2.optional(),
6931
+ direction: z8.enum(DIRECTIONS),
6932
+ maxDepth: z8.number().int(),
6933
+ nodes: z8.array(DependencyNodeSchema),
6934
+ edges: z8.array(EdgeSchema2)
6935
+ });
6936
+ var SessionEndpointSchema = z8.object({
6937
+ sessionId: z8.string(),
6938
+ startedAt: z8.string(),
6939
+ nodeCount: z8.number().int(),
6940
+ edgeCount: z8.number().int()
6941
+ });
6942
+ var NodeChangeSchema = z8.object({
6943
+ id: z8.string(),
6944
+ changedFields: z8.array(z8.string()),
6945
+ confidenceDelta: z8.number()
6946
+ });
6947
+ var DiffResponse = z8.object({
6948
+ base: SessionEndpointSchema,
6949
+ current: SessionEndpointSchema,
6950
+ summary: z8.object({
6951
+ nodesAdded: z8.number().int(),
6952
+ nodesRemoved: z8.number().int(),
6953
+ nodesChanged: z8.number().int(),
6954
+ edgesAdded: z8.number().int(),
6955
+ edgesRemoved: z8.number().int()
6956
+ }),
6957
+ nodes: z8.object({
6958
+ added: z8.array(NodeSchema2),
6959
+ removed: z8.array(NodeSchema2),
6960
+ changed: z8.array(NodeChangeSchema),
6961
+ unchanged: z8.number().int()
6962
+ }),
6963
+ edges: z8.object({
6964
+ added: z8.array(EdgeSchema2),
6965
+ removed: z8.array(EdgeSchema2),
6966
+ unchanged: z8.number().int()
6967
+ }),
6968
+ anomalies: z8.object({ added: z8.array(AnomalySchema) })
6969
+ });
6970
+ var SessionSchema = z8.object({
6971
+ id: z8.string(),
6972
+ mode: z8.literal("discover"),
6973
+ startedAt: z8.string(),
6974
+ completedAt: z8.string().optional(),
6975
+ name: z8.string().optional(),
6976
+ tenant: z8.string(),
6977
+ lastScannedAt: z8.string().optional()
6978
+ });
6979
+ var SessionsResponse = z8.object({ sessions: z8.array(SessionSchema) });
6980
+ var HealthResponse = z8.object({
6981
+ status: z8.literal("ok"),
6982
+ version: z8.string(),
6983
+ store: z8.literal("sqlite"),
6984
+ sessions: z8.number().int()
6985
+ });
6986
+ var ErrorResponse = z8.object({
6987
+ error: z8.string(),
6988
+ code: z8.string().optional()
6989
+ });
6990
+ var API_SCHEMAS = {
6991
+ Node: NodeSchema2,
6992
+ Edge: EdgeSchema2,
6993
+ Anomaly: AnomalySchema,
6994
+ Summary: SummaryResponse,
6995
+ Nodes: NodesResponse,
6996
+ Dependencies: DependenciesResponse,
6997
+ Diff: DiffResponse,
6998
+ Session: SessionSchema,
6999
+ Sessions: SessionsResponse,
7000
+ Health: HealthResponse,
7001
+ Error: ErrorResponse
7002
+ };
7003
+
7004
+ // src/api/rest.ts
7005
+ function toApiNode(n) {
7006
+ const out = { id: n.id, type: n.type, name: n.name, confidence: n.confidence, tags: n.tags };
7007
+ if (n.domain !== void 0) out["domain"] = n.domain;
7008
+ if (n.subDomain !== void 0) out["subDomain"] = n.subDomain;
7009
+ if (n.qualityScore !== void 0) out["qualityScore"] = n.qualityScore;
7010
+ if (n.owner !== void 0) out["owner"] = n.owner;
7011
+ if (n.cost !== void 0) out["cost"] = n.cost;
7012
+ return out;
7013
+ }
7014
+ function toApiEdge(e) {
7015
+ return { sourceId: e.sourceId, targetId: e.targetId, relationship: e.relationship, confidence: e.confidence, evidence: e.evidence };
7016
+ }
7017
+ function toApiSession(s) {
7018
+ const out = { id: s.id, mode: s.mode, startedAt: s.startedAt, tenant: s.tenant };
7019
+ if (s.completedAt !== void 0) out["completedAt"] = s.completedAt;
7020
+ if (s.name !== void 0) out["name"] = s.name;
7021
+ if (s.lastScannedAt !== void 0) out["lastScannedAt"] = s.lastScannedAt;
7022
+ return out;
7023
+ }
7024
+ function toApiAnomaly(a) {
7025
+ return { nodeId: a.nodeId, kind: a.kind, severity: a.severity, reason: a.reason };
7026
+ }
7027
+ function projectDependencies(r) {
7028
+ return {
7029
+ ...r.root ? { root: toApiNode(r.root) } : {},
7030
+ direction: r.direction,
7031
+ maxDepth: r.maxDepth,
7032
+ nodes: r.nodes.map((n) => ({ ...toApiNode(n), depth: n.depth })),
7033
+ edges: r.edges.map(toApiEdge)
7034
+ };
7035
+ }
7036
+ function projectDiff(diff) {
7037
+ return {
7038
+ base: { sessionId: diff.base.sessionId, startedAt: diff.base.startedAt, nodeCount: diff.base.nodeCount, edgeCount: diff.base.edgeCount },
7039
+ current: { sessionId: diff.current.sessionId, startedAt: diff.current.startedAt, nodeCount: diff.current.nodeCount, edgeCount: diff.current.edgeCount },
7040
+ summary: diff.summary,
7041
+ nodes: {
7042
+ added: diff.nodes.added.map(toApiNode),
7043
+ removed: diff.nodes.removed.map(toApiNode),
7044
+ changed: diff.nodes.changed.map((c) => ({ id: c.id, changedFields: c.changedFields, confidenceDelta: c.confidenceDelta })),
7045
+ unchanged: diff.nodes.unchanged
7046
+ },
7047
+ edges: {
7048
+ added: diff.edges.added.map(toApiEdge),
7049
+ removed: diff.edges.removed.map(toApiEdge),
7050
+ unchanged: diff.edges.unchanged
7051
+ },
7052
+ anomalies: { added: diff.anomalies.added.map(toApiAnomaly) }
7053
+ };
7054
+ }
7055
+ function ok(body) {
7056
+ return { status: 200, body };
7057
+ }
7058
+ function badRequest(error) {
7059
+ return { status: 400, body: { error } };
7060
+ }
7061
+ function notFound(error = "not found") {
7062
+ return { status: 404, body: { error } };
7063
+ }
7064
+ function guard(fn) {
7065
+ try {
7066
+ return fn();
7067
+ } catch (err) {
7068
+ if (err instanceof NotFoundError) return notFound(err.message);
7069
+ throw err;
7070
+ }
7071
+ }
7072
+ function validateOut(schema, body) {
7073
+ if (process.env["NODE_ENV"] !== "production") {
7074
+ const r = schema.safeParse(body);
7075
+ if (!r.success) throw new Error(`API response failed its own schema contract: ${r.error.message}`);
7076
+ }
7077
+ return body;
7078
+ }
7079
+ function intParam(url, name) {
7080
+ const raw = url.searchParams.get(name);
7081
+ if (raw === null || raw.trim() === "") return void 0;
7082
+ const n = Number(raw);
7083
+ return Number.isInteger(n) ? n : void 0;
7084
+ }
7085
+ function sessionParam(url) {
7086
+ return url.searchParams.get("session") ?? void 0;
7087
+ }
7088
+ function handleSummary(ctx, url, d) {
7089
+ return guard(() => ok(validateOut(SummaryResponse, d.backend.summary(ctx, sessionParam(url)))));
7090
+ }
7091
+ function handleNodes(ctx, url, d) {
7092
+ return guard(() => {
7093
+ const search = url.searchParams.get("search") ?? void 0;
7094
+ const typesRaw = url.searchParams.get("types");
7095
+ const types = typesRaw ? typesRaw.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
7096
+ const limit = intParam(url, "limit");
7097
+ const offset = intParam(url, "offset");
7098
+ const r = d.backend.nodes(
7099
+ ctx,
7100
+ { ...search ? { search } : {}, ...types ? { types } : {}, ...limit !== void 0 ? { limit } : {}, ...offset !== void 0 ? { offset } : {} },
7101
+ sessionParam(url)
7102
+ );
7103
+ return ok(validateOut(NodesResponse, { nodes: r.nodes.map(toApiNode), total: r.total, limit: r.limit, offset: r.offset }));
7104
+ });
7105
+ }
7106
+ function handleDependencies(ctx, id, url, d) {
7107
+ const directionRaw = url.searchParams.get("direction");
7108
+ if (directionRaw !== null && !DIRECTIONS.includes(directionRaw)) {
7109
+ return badRequest(`direction must be one of ${DIRECTIONS.join(", ")}`);
7110
+ }
7111
+ return guard(() => {
7112
+ const direction = directionRaw ?? void 0;
7113
+ const maxDepth = intParam(url, "maxDepth");
7114
+ const r = d.backend.dependencies(
7115
+ ctx,
7116
+ id,
7117
+ { ...direction ? { direction } : {}, ...maxDepth !== void 0 ? { maxDepth } : {} },
7118
+ sessionParam(url)
7119
+ );
7120
+ return ok(validateOut(DependenciesResponse, projectDependencies(r)));
7121
+ });
7122
+ }
7123
+ function handleDiff(ctx, url, d) {
7124
+ const base = url.searchParams.get("base");
7125
+ const current = url.searchParams.get("current");
7126
+ if (!base || !current) return badRequest("both `base` and `current` query params are required");
7127
+ return guard(() => {
7128
+ const diff = d.backend.diff(ctx, base, current);
7129
+ return ok(validateOut(DiffResponse, projectDiff(diff)));
7130
+ });
7131
+ }
7132
+ function handleSessions(ctx, d) {
7133
+ return guard(() => ok(validateOut(SessionsResponse, { sessions: d.backend.sessions(ctx).map(toApiSession) })));
7134
+ }
7135
+ function handleHealth(ctx, d) {
7136
+ const h = d.backend.health(ctx);
7137
+ return ok(validateOut(HealthResponse, { status: "ok", version: d.version, store: h.store, sessions: h.sessions }));
7138
+ }
7139
+
7140
+ // src/api/openapi.ts
7141
+ function defOf(schema) {
7142
+ return schema.def ?? {};
7143
+ }
7144
+ function unwrapOptional(schema) {
7145
+ const def = defOf(schema);
7146
+ if ((def.type === "optional" || def.type === "nullable") && def.innerType) {
7147
+ return { inner: def.innerType, optional: true };
7148
+ }
7149
+ return { inner: schema, optional: false };
7150
+ }
7151
+ function zodToJsonSchema(schema) {
7152
+ const def = defOf(schema);
7153
+ switch (def.type) {
7154
+ case "string":
7155
+ return { type: "string" };
7156
+ case "number": {
7157
+ const isInt = (def.checks ?? []).some((c) => c._zod?.def?.check === "number_format");
7158
+ return { type: isInt ? "integer" : "number" };
7159
+ }
7160
+ case "boolean":
7161
+ return { type: "boolean" };
7162
+ case "literal": {
7163
+ const values = def.values ?? [];
7164
+ return values.length === 1 ? { const: values[0] } : { enum: values };
7165
+ }
7166
+ case "enum":
7167
+ return { type: "string", enum: Object.values(def.entries ?? {}) };
7168
+ case "array":
7169
+ return { type: "array", items: def.element ? zodToJsonSchema(def.element) : {} };
7170
+ case "record":
7171
+ return { type: "object", additionalProperties: def.valueType ? zodToJsonSchema(def.valueType) : true };
7172
+ case "optional":
7173
+ case "nullable":
7174
+ return def.innerType ? zodToJsonSchema(def.innerType) : {};
7175
+ case "object": {
7176
+ const shape = def.shape ?? {};
7177
+ const properties = {};
7178
+ const required = [];
7179
+ for (const key of Object.keys(shape)) {
7180
+ const { inner, optional } = unwrapOptional(shape[key]);
7181
+ properties[key] = zodToJsonSchema(inner);
7182
+ if (!optional) required.push(key);
7183
+ }
7184
+ return { type: "object", properties, required, additionalProperties: false };
7185
+ }
7186
+ default:
7187
+ throw new Error(`zodToJsonSchema: unsupported zod construct "${def.type ?? "unknown"}". Extend src/api/openapi.ts.`);
7188
+ }
7189
+ }
7190
+ var TENANT_PARAM = {
7191
+ name: "tenant",
7192
+ in: "query",
7193
+ required: false,
7194
+ description: 'Tenant/org scope (also accepted via the X-Cartograph-Tenant header). Defaults to "local".',
7195
+ schema: { type: "string" }
7196
+ };
7197
+ var SESSION_PARAM = {
7198
+ name: "session",
7199
+ in: "query",
7200
+ required: false,
7201
+ description: "Session id to query, or omit for the latest discovery session.",
7202
+ schema: { type: "string" }
7203
+ };
7204
+ function errorResponses() {
7205
+ const err = { description: "Error", content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } } } };
7206
+ return { "400": { ...err, description: "Bad request" }, "401": { ...err, description: "Unauthorized" }, "404": { ...err, description: "Not found" } };
7207
+ }
7208
+ function ok2(ref, description) {
7209
+ return { description, content: { "application/json": { schema: { $ref: `#/components/schemas/${ref}` } } } };
7210
+ }
7211
+ function buildOpenApiDocument(opts) {
7212
+ const schemas = {};
7213
+ for (const [name, schema] of Object.entries(API_SCHEMAS)) {
7214
+ schemas[name] = zodToJsonSchema(schema);
7215
+ }
7216
+ return {
7217
+ openapi: "3.1.0",
7218
+ info: {
7219
+ title: "Cartograph API",
7220
+ version: opts.version,
7221
+ description: "Read-only REST API over the discovered infrastructure/agentic-AI topology. Every endpoint is tenant-scoped and bearer-authenticated."
7222
+ },
7223
+ servers: [{ url: "/" }],
7224
+ security: [{ bearerAuth: [] }],
7225
+ components: {
7226
+ securitySchemes: { bearerAuth: { type: "http", scheme: "bearer" } },
7227
+ schemas
7228
+ },
7229
+ paths: {
7230
+ "/v1/health": {
7231
+ get: {
7232
+ summary: "Liveness + store/coverage probe",
7233
+ security: [],
7234
+ responses: { "200": ok2("Health", "Service health") }
7235
+ }
7236
+ },
7237
+ "/v1/openapi.json": {
7238
+ get: {
7239
+ summary: "This OpenAPI document",
7240
+ security: [],
7241
+ responses: { "200": { description: "OpenAPI 3.1 document", content: { "application/json": { schema: { type: "object" } } } } }
7242
+ }
7243
+ },
7244
+ "/v1/summary": {
7245
+ get: {
7246
+ summary: "Low-token topology aggregate for the resolved session",
7247
+ parameters: [SESSION_PARAM, TENANT_PARAM],
7248
+ responses: { "200": ok2("Summary", "Topology summary"), ...errorResponses() }
7249
+ }
7250
+ },
7251
+ "/v1/nodes": {
7252
+ get: {
7253
+ summary: "List/search/paginate nodes",
7254
+ parameters: [
7255
+ { name: "search", in: "query", required: false, description: "Lexical/semantic search anchor.", schema: { type: "string" } },
7256
+ { name: "types", in: "query", required: false, description: "Comma-separated node-type filter.", schema: { type: "string" } },
7257
+ { name: "limit", in: "query", required: false, description: "Page size (default 100, max 1000).", schema: { type: "integer" } },
7258
+ { name: "offset", in: "query", required: false, description: "Page offset (ignored for search).", schema: { type: "integer" } },
7259
+ SESSION_PARAM,
7260
+ TENANT_PARAM
7261
+ ],
7262
+ responses: { "200": ok2("Nodes", "A page of nodes"), ...errorResponses() }
7263
+ }
7264
+ },
7265
+ "/v1/nodes/{id}/dependencies": {
7266
+ get: {
7267
+ summary: "Dependency traversal from a node",
7268
+ parameters: [
7269
+ { name: "id", in: "path", required: true, description: 'Node id ("{type}:{id}").', schema: { type: "string" } },
7270
+ { name: "direction", in: "query", required: false, description: "downstream | upstream | both (default downstream).", schema: { type: "string", enum: ["downstream", "upstream", "both"] } },
7271
+ { name: "maxDepth", in: "query", required: false, description: "Traversal depth (default 8, max 64).", schema: { type: "integer" } },
7272
+ SESSION_PARAM,
7273
+ TENANT_PARAM
7274
+ ],
7275
+ responses: { "200": ok2("Dependencies", "Traversal result"), ...errorResponses() }
7276
+ }
7277
+ },
7278
+ "/v1/diff": {
7279
+ get: {
7280
+ summary: "Compare two sessions (drift)",
7281
+ parameters: [
7282
+ { name: "base", in: "query", required: true, description: "Base session id.", schema: { type: "string" } },
7283
+ { name: "current", in: "query", required: true, description: "Current session id.", schema: { type: "string" } },
7284
+ TENANT_PARAM
7285
+ ],
7286
+ responses: { "200": ok2("Diff", "Topology delta"), ...errorResponses() }
7287
+ }
7288
+ },
7289
+ "/v1/sessions": {
7290
+ get: {
7291
+ summary: "List discovery sessions for the tenant",
7292
+ parameters: [TENANT_PARAM],
7293
+ responses: { "200": ok2("Sessions", "Sessions"), ...errorResponses() }
7294
+ }
7295
+ }
7296
+ }
7297
+ };
7298
+ }
7299
+
7300
+ // src/api/graphql.ts
7301
+ var SDL = `# Cartograph read-only GraphQL API (4.2). Mirrors the REST surface.
7302
+ schema { query: Query }
7303
+
7304
+ type Query {
7305
+ summary(session: String): Summary
7306
+ nodes(search: String, types: [String!], limit: Int, offset: Int, session: String): NodeConnection
7307
+ node(id: String!, session: String): Node
7308
+ dependencies(id: String!, direction: Direction, maxDepth: Int, session: String): Dependencies
7309
+ diff(base: String!, current: String!): Diff
7310
+ sessions: [Session!]!
7311
+ }
7312
+
7313
+ enum Direction { downstream upstream both }
7314
+
7315
+ type Totals { nodes: Int! edges: Int! }
7316
+ type Count { key: String! value: Int! }
7317
+ type TopConnected { id: String! name: String! type: String! degree: Int! }
7318
+ type Anomaly { nodeId: String! kind: String! severity: String! reason: String! }
7319
+ type Cost { amount: Float! currency: String! period: String! source: String }
7320
+ type CostRollup { key: String! currency: String! period: String! total: Float! nodes: Int! }
7321
+ type CostCoverage { withCost: Int! total: Int! }
7322
+
7323
+ type Node {
7324
+ id: String! type: String! name: String! confidence: Float!
7325
+ domain: String subDomain: String qualityScore: Float owner: String cost: Cost tags: [String!]!
7326
+ }
7327
+ type DependencyNode {
7328
+ id: String! type: String! name: String! confidence: Float!
7329
+ domain: String subDomain: String qualityScore: Float owner: String cost: Cost tags: [String!]! depth: Int!
7330
+ }
7331
+ type Edge { sourceId: String! targetId: String! relationship: String! confidence: Float! evidence: String! }
7332
+
7333
+ type Summary {
7334
+ sessionId: String!
7335
+ totals: Totals!
7336
+ topConnected: [TopConnected!]!
7337
+ anomalies: [Anomaly!]!
7338
+ contributors: Int!
7339
+ costByDomain: [CostRollup!]!
7340
+ costByOwner: [CostRollup!]!
7341
+ costCoverage: CostCoverage!
7342
+ }
7343
+
7344
+ type NodeConnection { nodes: [Node!]! total: Int! limit: Int! offset: Int! }
7345
+ type Dependencies { root: Node direction: Direction! maxDepth: Int! nodes: [DependencyNode!]! edges: [Edge!]! }
7346
+
7347
+ type SessionEndpoint { sessionId: String! startedAt: String! nodeCount: Int! edgeCount: Int! }
7348
+ type DiffSummary { nodesAdded: Int! nodesRemoved: Int! nodesChanged: Int! edgesAdded: Int! edgesRemoved: Int! }
7349
+ type NodeChange { id: String! changedFields: [String!]! confidenceDelta: Float! }
7350
+ type DiffNodes { added: [Node!]! removed: [Node!]! changed: [NodeChange!]! unchanged: Int! }
7351
+ type DiffEdges { added: [Edge!]! removed: [Edge!]! unchanged: Int! }
7352
+ type DiffAnomalies { added: [Anomaly!]! }
7353
+ type Diff {
7354
+ base: SessionEndpoint! current: SessionEndpoint! summary: DiffSummary!
7355
+ nodes: DiffNodes! edges: DiffEdges! anomalies: DiffAnomalies!
7356
+ }
7357
+
7358
+ type Session { id: String! mode: String! startedAt: String! completedAt: String name: String tenant: String! lastScannedAt: String }
7359
+ `;
7360
+ var resolvers = {
7361
+ summary: (ctx, args, backend) => backend.summary(ctx, str(args["session"])),
7362
+ nodes: (ctx, args, backend) => {
7363
+ const r = backend.nodes(
7364
+ ctx,
7365
+ {
7366
+ ...str(args["search"]) ? { search: str(args["search"]) } : {},
7367
+ ...Array.isArray(args["types"]) ? { types: args["types"].map(String) } : {},
7368
+ ...num(args["limit"]) !== void 0 ? { limit: num(args["limit"]) } : {},
7369
+ ...num(args["offset"]) !== void 0 ? { offset: num(args["offset"]) } : {}
7370
+ },
7371
+ str(args["session"])
7372
+ );
7373
+ return { nodes: r.nodes.map(toApiNode), total: r.total, limit: r.limit, offset: r.offset };
7374
+ },
7375
+ node: (ctx, args, backend) => {
7376
+ const n = backend.node(ctx, String(args["id"]), str(args["session"]));
7377
+ return n ? toApiNode(n) : null;
7378
+ },
7379
+ dependencies: (ctx, args, backend) => {
7380
+ const r = backend.dependencies(
7381
+ ctx,
7382
+ String(args["id"]),
7383
+ {
7384
+ ...str(args["direction"]) ? { direction: str(args["direction"]) } : {},
7385
+ ...num(args["maxDepth"]) !== void 0 ? { maxDepth: num(args["maxDepth"]) } : {}
7386
+ },
7387
+ str(args["session"])
7388
+ );
7389
+ return projectDependencies(r);
7390
+ },
7391
+ diff: (ctx, args, backend) => projectDiff(backend.diff(ctx, String(args["base"]), String(args["current"]))),
7392
+ sessions: (ctx, _args, backend) => backend.sessions(ctx).map(toApiSession)
7393
+ };
7394
+ function str(v) {
7395
+ return typeof v === "string" ? v : void 0;
7396
+ }
7397
+ function num(v) {
7398
+ return typeof v === "number" && Number.isInteger(v) ? v : void 0;
7399
+ }
7400
+ var NAME_RE = /[_A-Za-z][_0-9A-Za-z]*/y;
7401
+ function tokenize2(src) {
7402
+ const tokens = [];
7403
+ let i = 0;
7404
+ while (i < src.length) {
7405
+ const c = src[i];
7406
+ if (/\s|,/.test(c)) {
7407
+ i++;
7408
+ continue;
7409
+ }
7410
+ if (c === "#") {
7411
+ while (i < src.length && src[i] !== "\n") i++;
7412
+ continue;
7413
+ }
7414
+ if ("{}()[]:!$".includes(c)) {
7415
+ tokens.push(c);
7416
+ i++;
7417
+ continue;
7418
+ }
7419
+ if (c === '"') {
7420
+ let j = i + 1;
7421
+ let s = "";
7422
+ while (j < src.length && src[j] !== '"') {
7423
+ s += src[j];
7424
+ j++;
7425
+ }
7426
+ tokens.push(JSON.stringify(s));
7427
+ i = j + 1;
7428
+ continue;
7429
+ }
7430
+ NAME_RE.lastIndex = i;
7431
+ const m = NAME_RE.exec(src);
7432
+ if (m && m.index === i) {
7433
+ tokens.push(m[0]);
7434
+ i = NAME_RE.lastIndex;
7435
+ continue;
7436
+ }
7437
+ const numMatch = /-?\d+(\.\d+)?/y;
7438
+ numMatch.lastIndex = i;
7439
+ const nm = numMatch.exec(src);
7440
+ if (nm && nm.index === i) {
7441
+ tokens.push(nm[0]);
7442
+ i = numMatch.lastIndex;
7443
+ continue;
7444
+ }
7445
+ throw new Error(`unexpected character '${c}'`);
7446
+ }
7447
+ return tokens;
7448
+ }
7449
+ var MAX_SELECTION_DEPTH = 32;
7450
+ var Parser = class {
7451
+ constructor(tokens, variables) {
7452
+ this.tokens = tokens;
7453
+ this.variables = variables;
7454
+ }
7455
+ pos = 0;
7456
+ depth = 0;
7457
+ peek() {
7458
+ return this.tokens[this.pos];
7459
+ }
7460
+ next() {
7461
+ return this.tokens[this.pos++];
7462
+ }
7463
+ expect(tok) {
7464
+ if (this.tokens[this.pos] !== tok) throw new Error(`expected '${tok}', got '${this.tokens[this.pos] ?? "<eof>"}'`);
7465
+ this.pos++;
7466
+ }
7467
+ parseDocument() {
7468
+ if (this.peek() === "mutation" || this.peek() === "subscription") {
7469
+ throw new Error("only query operations are supported (read-only API)");
7470
+ }
7471
+ if (this.peek() === "query") {
7472
+ this.next();
7473
+ if (this.peek() && this.peek() !== "{" && this.peek() !== "(") this.next();
7474
+ if (this.peek() === "(") this.skipBalanced("(", ")");
7475
+ }
7476
+ this.expect("{");
7477
+ const selections = this.parseSelectionSet();
7478
+ return selections;
7479
+ }
7480
+ skipBalanced(open, close) {
7481
+ this.expect(open);
7482
+ let depth = 1;
7483
+ while (depth > 0) {
7484
+ const t = this.next();
7485
+ if (t === void 0) throw new Error("unbalanced");
7486
+ if (t === open) depth++;
7487
+ else if (t === close) depth--;
7488
+ }
7489
+ }
7490
+ parseSelectionSet() {
7491
+ if (++this.depth > MAX_SELECTION_DEPTH) throw new Error(`selection set nested deeper than ${MAX_SELECTION_DEPTH}`);
7492
+ const out = [];
7493
+ while (this.peek() !== "}") {
7494
+ if (this.peek() === void 0) throw new Error("unexpected end of selection set");
7495
+ out.push(this.parseSelection());
7496
+ }
7497
+ this.expect("}");
7498
+ this.depth--;
7499
+ return out;
7500
+ }
7501
+ parseSelection() {
7502
+ let name = this.next();
7503
+ const alias = name;
7504
+ if (this.peek() === ":") {
7505
+ this.next();
7506
+ name = this.next();
7507
+ }
7508
+ const args = {};
7509
+ if (this.peek() === "(") {
7510
+ this.next();
7511
+ while (this.peek() !== ")") {
7512
+ const argName = this.next();
7513
+ this.expect(":");
7514
+ args[argName] = this.parseValue();
7515
+ }
7516
+ this.expect(")");
7517
+ }
7518
+ let selections = [];
7519
+ if (this.peek() === "{") {
7520
+ this.next();
7521
+ selections = this.parseSelectionSet();
7522
+ }
7523
+ return { name, alias, args, selections };
7524
+ }
7525
+ parseValue() {
7526
+ const t = this.next();
7527
+ if (t === "$") {
7528
+ const v = this.next();
7529
+ return this.variables[v];
7530
+ }
7531
+ if (t === "[") {
7532
+ const arr = [];
7533
+ while (this.peek() !== "]") arr.push(this.parseValue());
7534
+ this.expect("]");
7535
+ return arr;
7536
+ }
7537
+ if (t.startsWith('"')) return JSON.parse(t);
7538
+ if (t === "true") return true;
7539
+ if (t === "false") return false;
7540
+ if (t === "null") return null;
7541
+ if (/^-?\d+(\.\d+)?$/.test(t)) return Number(t);
7542
+ return t;
7543
+ }
7544
+ };
7545
+ function project(value, selections) {
7546
+ if (value === null || value === void 0) return null;
7547
+ if (selections.length === 0) return value;
7548
+ if (Array.isArray(value)) return value.map((v) => project(v, selections));
7549
+ if (typeof value !== "object") return value;
7550
+ const obj = value;
7551
+ const out = {};
7552
+ for (const sel of selections) {
7553
+ if (sel.name === "__typename") {
7554
+ out[sel.alias] = void 0;
7555
+ continue;
7556
+ }
7557
+ out[sel.alias] = project(obj[sel.name], sel.selections);
7558
+ }
7559
+ return out;
7560
+ }
7561
+ function introspectionSchema() {
7562
+ const names = [...SDL.matchAll(/^(?:type|enum)\s+([_A-Za-z][_0-9A-Za-z]*)/gm)].map((m) => m[1]);
7563
+ const types = names.map((name) => ({ name, kind: /^[A-Z]/.test(name) ? "OBJECT" : "SCALAR" }));
7564
+ return {
7565
+ __schema: {
7566
+ queryType: { name: "Query" },
7567
+ mutationType: null,
7568
+ subscriptionType: null,
7569
+ types,
7570
+ directives: []
7571
+ }
7572
+ };
7573
+ }
7574
+ async function executeGraphql(ctx, body, deps) {
7575
+ const req = body ?? {};
7576
+ if (typeof req.query !== "string" || req.query.trim() === "") {
7577
+ return { errors: [{ message: "missing query" }] };
7578
+ }
7579
+ const variables = typeof req.variables === "object" && req.variables !== null ? req.variables : {};
7580
+ let selections;
7581
+ try {
7582
+ selections = new Parser(tokenize2(req.query), variables).parseDocument();
7583
+ } catch (err) {
7584
+ return { errors: [{ message: `syntax error: ${err instanceof Error ? err.message : String(err)}` }] };
7585
+ }
7586
+ const data = {};
7587
+ const errors = [];
7588
+ for (const sel of selections) {
7589
+ try {
7590
+ if (sel.name === "__schema") {
7591
+ data[sel.alias] = project(introspectionSchema()["__schema"], sel.selections);
7592
+ continue;
7593
+ }
7594
+ if (sel.name === "__typename") {
7595
+ data[sel.alias] = "Query";
7596
+ continue;
7597
+ }
7598
+ const resolver = resolvers[sel.name];
7599
+ if (!resolver) {
7600
+ errors.push({ message: `Cannot query field "${sel.name}" on type "Query"` });
7601
+ continue;
7602
+ }
7603
+ const resolved = resolver(ctx, sel.args, deps.backend);
7604
+ data[sel.alias] = project(resolved, sel.selections);
7605
+ } catch (err) {
7606
+ errors.push({ message: err instanceof Error ? err.message : String(err) });
7607
+ }
7608
+ }
7609
+ return errors.length > 0 ? { data, errors } : { data };
7610
+ }
7611
+ function handleGraphqlGet() {
7612
+ return { status: 200, body: SDL };
7613
+ }
7614
+
7615
+ // src/api/server.ts
7616
+ var DEPENDENCIES_RE = /^\/v1\/nodes\/(.+)\/dependencies$/;
7617
+ var MAX_GRAPHQL_BYTES = 1024 * 1024;
7618
+ function send(res, status, body, headers = {}) {
7619
+ res.writeHead(status, { "content-type": "application/json", ...headers }).end(JSON.stringify(body));
7620
+ }
7621
+ async function readBody(req, cap) {
7622
+ const chunks = [];
7623
+ let total = 0;
7624
+ let overflow = false;
7625
+ for await (const chunk of req) {
7626
+ if (overflow) continue;
7627
+ const buf = chunk;
7628
+ total += buf.length;
7629
+ if (total > cap) {
7630
+ overflow = true;
7631
+ chunks.length = 0;
7632
+ continue;
7633
+ }
7634
+ chunks.push(buf);
7635
+ }
7636
+ if (overflow) return { overflow: true, value: void 0 };
7637
+ if (chunks.length === 0) return { overflow: false, value: void 0 };
7638
+ try {
7639
+ return { overflow: false, value: JSON.parse(Buffer.concat(chunks).toString("utf8")) };
7640
+ } catch {
7641
+ return { overflow: false, value: void 0 };
7642
+ }
7643
+ }
7644
+ async function runApi(opts) {
7645
+ const host2 = opts.host ?? "127.0.0.1";
7646
+ const requestedPort = opts.port ?? 3737;
7647
+ const token = opts.token;
7648
+ const graphqlEnabled = opts.graphql !== false;
7649
+ const defaultTenant = opts.tenant?.defaultTenant ?? DEFAULT_TENANT;
7650
+ const log2 = opts.log ?? (() => {
7651
+ });
7652
+ const restDeps = { backend: opts.backend, version: opts.version };
7653
+ const openApiDoc = buildOpenApiDocument({ version: opts.version });
7654
+ const allowedOrigins = opts.allowedOrigins ?? [];
7655
+ assertSafeBind({ host: host2, port: requestedPort, ...opts.allowedHosts ? { allowedHosts: opts.allowedHosts } : {}, ...token ? { token } : {} });
7656
+ let allowedHosts = opts.allowedHosts ?? [];
7657
+ const corsHeaders = (req) => {
7658
+ const origin = req.headers["origin"];
7659
+ if (typeof origin === "string" && allowedOrigins.includes(origin)) {
7660
+ return {
7661
+ "access-control-allow-origin": origin,
7662
+ "vary": "Origin",
7663
+ "access-control-allow-methods": "GET, POST, OPTIONS",
7664
+ "access-control-allow-headers": "authorization, content-type, x-cartograph-tenant"
7665
+ };
7666
+ }
7667
+ return {};
7668
+ };
7669
+ const server = http2.createServer((req, res) => {
7670
+ const started = Date.now();
7671
+ let tenantLabel = "-";
7672
+ const finish = (status) => {
7673
+ log2(`[cartography-api] ${req.method ?? "-"} ${req.url ?? "-"} ${status} ${Date.now() - started}ms tenant=${tenantLabel}`);
7674
+ };
7675
+ void (async () => {
7676
+ try {
7677
+ const url = new URL(req.url ?? "/", `http://${req.headers["host"] ?? host2}`);
7678
+ const path = url.pathname;
7679
+ const cors = corsHeaders(req);
7680
+ if (req.method === "OPTIONS") {
7681
+ res.writeHead(204, cors).end();
7682
+ finish(204);
7683
+ return;
7684
+ }
7685
+ const hostHeader = (req.headers["host"] ?? "").toLowerCase();
7686
+ if (!allowedHosts.some((h) => h.toLowerCase() === hostHeader)) {
7687
+ send(res, 403, { error: "host not allowed" }, cors);
7688
+ finish(403);
7689
+ return;
7690
+ }
7691
+ if (path === "/v1/openapi.json" && req.method === "GET") {
7692
+ send(res, 200, openApiDoc, cors);
7693
+ finish(200);
7694
+ return;
7695
+ }
7696
+ if (path === "/v1/health") {
7697
+ if (req.method !== "GET") {
7698
+ send(res, 405, { error: "method not allowed" }, { allow: "GET", ...cors });
7699
+ finish(405);
7700
+ return;
7701
+ }
7702
+ tenantLabel = defaultTenant;
7703
+ const r = handleHealth({ tenant: defaultTenant }, restDeps);
7704
+ send(res, r.status, r.body, cors);
7705
+ finish(r.status);
7706
+ return;
7707
+ }
7708
+ if (!checkBearer(req.headers["authorization"], token)) {
7709
+ send(res, 401, { error: "unauthorized" }, { "www-authenticate": "Bearer", ...cors });
7710
+ finish(401);
7711
+ return;
7712
+ }
7713
+ let ctx;
7714
+ try {
7715
+ ctx = resolveTenant(req, url, opts.tenant ?? {});
7716
+ tenantLabel = ctx.tenant;
7717
+ } catch (err) {
7718
+ if (err instanceof InvalidTenantError) {
7719
+ send(res, 400, { error: "invalid tenant" }, cors);
7720
+ finish(400);
7721
+ return;
7722
+ }
7723
+ throw err;
7724
+ }
7725
+ if (graphqlEnabled && path === "/graphql") {
7726
+ if (req.method === "GET") {
7727
+ const g = handleGraphqlGet();
7728
+ res.writeHead(g.status, { "content-type": "text/plain; charset=utf-8", ...cors }).end(g.body);
7729
+ finish(g.status);
7730
+ return;
7731
+ }
7732
+ if (req.method === "POST") {
7733
+ const { overflow, value } = await readBody(req, MAX_GRAPHQL_BYTES);
7734
+ if (overflow) {
7735
+ send(res, 413, { error: "payload too large" }, cors);
7736
+ finish(413);
7737
+ return;
7738
+ }
7739
+ const result = await executeGraphql(ctx, value, { backend: opts.backend });
7740
+ send(res, 200, result, cors);
7741
+ finish(200);
7742
+ return;
7743
+ }
7744
+ send(res, 405, { error: "method not allowed" }, { allow: "GET, POST", ...cors });
7745
+ finish(405);
7746
+ return;
7747
+ }
7748
+ if (path.startsWith("/v1/")) {
7749
+ if (req.method !== "GET") {
7750
+ send(res, 405, { error: "method not allowed" }, { allow: "GET", ...cors });
7751
+ finish(405);
7752
+ return;
7753
+ }
7754
+ const result = dispatchRest(ctx, path, url, restDeps);
7755
+ if (result) {
7756
+ send(res, result.status, result.body, cors);
7757
+ finish(result.status);
7758
+ return;
7759
+ }
7760
+ }
7761
+ send(res, 404, { error: "not found" }, cors);
7762
+ finish(404);
7763
+ } catch (err) {
7764
+ process.stderr.write(`[cartography-api] request failed: ${err instanceof Error ? err.message : String(err)}
7765
+ `);
7766
+ if (!res.headersSent) send(res, 500, { error: "internal error" });
7767
+ finish(500);
7768
+ }
7769
+ })();
7770
+ });
7771
+ await new Promise((resolve3) => server.listen(requestedPort, host2, resolve3));
7772
+ const actualPort = server.address().port;
7773
+ if (allowedHosts.length === 0) allowedHosts = defaultAllowedHosts(host2, actualPort);
7774
+ return server;
7775
+ }
7776
+ function dispatchRest(ctx, path, url, deps) {
7777
+ switch (path) {
7778
+ case "/v1/summary":
7779
+ return handleSummary(ctx, url, deps);
7780
+ case "/v1/nodes":
7781
+ return handleNodes(ctx, url, deps);
7782
+ case "/v1/diff":
7783
+ return handleDiff(ctx, url, deps);
7784
+ case "/v1/sessions":
7785
+ return handleSessions(ctx, deps);
7786
+ default: {
7787
+ const m = DEPENDENCIES_RE.exec(path);
7788
+ if (m) return handleDependencies(ctx, decodeURIComponent(m[1]), url, deps);
7789
+ return void 0;
7790
+ }
7791
+ }
7792
+ }
7793
+
7794
+ // src/api/start.ts
7795
+ import { readFileSync as readFileSync4 } from "fs";
7796
+ import { dirname as dirname3, resolve } from "path";
7797
+ import { fileURLToPath } from "url";
7798
+ function readVersion() {
7799
+ try {
7800
+ const dir = import.meta.dirname ?? dirname3(fileURLToPath(import.meta.url));
7801
+ return JSON.parse(readFileSync4(resolve(dir, "..", "package.json"), "utf-8")).version ?? "0.0.0";
7802
+ } catch {
7803
+ return "0.0.0";
7804
+ }
7805
+ }
7806
+ function parseApiArgs(argv) {
7807
+ const opts = {};
7808
+ for (let i = 0; i < argv.length; i++) {
7809
+ const a = argv[i];
7810
+ if (a === "--http") continue;
7811
+ else if (a === "--no-graphql") opts.graphql = false;
7812
+ else if (a === "--port") opts.port = Number(argv[++i]);
7813
+ else if (a === "--host") opts.host = argv[++i];
7814
+ else if (a === "--allowed-hosts") opts.allowedHosts = splitList(argv[++i]);
7815
+ else if (a === "--allowed-origins") opts.allowedOrigins = splitList(argv[++i]);
7816
+ else if (a === "--token") opts.token = argv[++i];
7817
+ else if (a === "--db") opts.dbPath = argv[++i];
7818
+ else if (a === "--session") opts.session = argv[++i];
7819
+ else if (a === "--tenant" || a === "--org") opts.tenant = argv[++i];
7820
+ else if (a === "--help" || a === "-h") opts.help = true;
7821
+ }
7822
+ return opts;
7823
+ }
7824
+ function splitList(raw) {
7825
+ return (raw ?? "").split(",").map((s) => s.trim()).filter(Boolean);
7826
+ }
7827
+ async function startApi(opts = {}) {
7828
+ const log2 = opts.log ?? ((m) => process.stderr.write(m + "\n"));
7829
+ const db = new CartographyDB(opts.dbPath ?? defaultConfig().dbPath);
7830
+ const backend = createSqliteQueryBackend(db, opts.session ?? "latest");
7831
+ const token = opts.token ?? process.env["CARTOGRAPHY_HTTP_TOKEN"];
7832
+ const host2 = opts.host ?? "127.0.0.1";
7833
+ const port = opts.port ?? 3737;
7834
+ const version = readVersion();
7835
+ const server = await runApi({
7836
+ host: host2,
7837
+ port,
7838
+ backend,
7839
+ version,
7840
+ ...opts.allowedHosts ? { allowedHosts: opts.allowedHosts } : {},
7841
+ ...opts.allowedOrigins ? { allowedOrigins: opts.allowedOrigins } : {},
7842
+ ...token ? { token } : {},
7843
+ ...opts.graphql === false ? { graphql: false } : {},
7844
+ ...opts.tenant ? { tenant: { defaultTenant: normalizeTenant(opts.tenant) } } : {},
7845
+ log: log2
7846
+ });
7847
+ const graphqlNote = opts.graphql === false ? " [REST only]" : " + /graphql";
7848
+ log2(
7849
+ `Cartograph API (REST${graphqlNote}) on http://${host2}:${port}/v1${token ? " (auth: bearer token required)" : ""} (tenant: ${normalizeTenant(opts.tenant)})`
7850
+ );
7851
+ return server;
7852
+ }
7853
+
6722
7854
  // src/installer/format.ts
6723
7855
  import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
6724
7856
  import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
@@ -6791,8 +7923,8 @@ function defaultServerEntry(opts = {}) {
6791
7923
  }
6792
7924
 
6793
7925
  // src/installer/install.ts
6794
- import { mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync4 } from "fs";
6795
- import { dirname as dirname3 } from "path";
7926
+ import { mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync4 } from "fs";
7927
+ import { dirname as dirname4 } from "path";
6796
7928
  import { homedir as homedir3 } from "os";
6797
7929
  function currentOs() {
6798
7930
  if (process.platform === "win32") return "win";
@@ -6808,7 +7940,7 @@ function planInstall(spec, ctx, opts) {
6808
7940
  throw new Error(`${spec.label} does not support the "${ctx.scope}" scope.`);
6809
7941
  }
6810
7942
  const fileExists = existsSync4(path);
6811
- const before = fileExists ? readFileSync4(path, "utf8") : "";
7943
+ const before = fileExists ? readFileSync5(path, "utf8") : "";
6812
7944
  const existing = parseConfig(before, spec.format);
6813
7945
  const merged = spec.apply(existing, opts.serverName ?? DEFAULT_SERVER_NAME, opts.entry);
6814
7946
  const after = serializeConfig(merged, spec.format);
@@ -6825,7 +7957,7 @@ function planInstall(spec, ctx, opts) {
6825
7957
  };
6826
7958
  }
6827
7959
  function applyInstall(plan) {
6828
- mkdirSync4(dirname3(plan.path), { recursive: true });
7960
+ mkdirSync4(dirname4(plan.path), { recursive: true });
6829
7961
  writeFileSync3(plan.path, plan.after, "utf8");
6830
7962
  }
6831
7963
  function renderDiff(before, after) {
@@ -7199,13 +8331,13 @@ function createClaudeProvider() {
7199
8331
  }
7200
8332
 
7201
8333
  // src/providers/shell.ts
7202
- import { z as z8 } from "zod";
8334
+ import { z as z9 } from "zod";
7203
8335
  function createBashTool() {
7204
8336
  const shell = IS_WIN ? "powershell" : "posix";
7205
8337
  return {
7206
8338
  name: "Bash",
7207
8339
  description: "Run a read-only shell command (inspect ports, processes, config). Mutating or destructive commands are blocked by the read-only allowlist.",
7208
- inputShape: { command: z8.string().describe("The read-only shell command to run") },
8340
+ inputShape: { command: z9.string().describe("The read-only shell command to run") },
7209
8341
  annotations: { readOnlyHint: true, openWorldHint: true },
7210
8342
  handler: async (args) => {
7211
8343
  const command = String(args["command"] ?? "").trim();
@@ -7674,8 +8806,8 @@ Use ask_user when you need context from the user.`;
7674
8806
  }
7675
8807
 
7676
8808
  // src/cost.ts
7677
- import { readFileSync as readFileSync5 } from "fs";
7678
- import { resolve } from "path";
8809
+ import { readFileSync as readFileSync6 } from "fs";
8810
+ import { resolve as resolve2 } from "path";
7679
8811
  function splitCsvLine(line) {
7680
8812
  const out = [];
7681
8813
  let cur = "";
@@ -7753,7 +8885,7 @@ var CsvCostSource = class {
7753
8885
  }
7754
8886
  id;
7755
8887
  async fetch() {
7756
- const text = readFileSync5(resolve(this.opts.filePath), "utf-8");
8888
+ const text = readFileSync6(resolve2(this.opts.filePath), "utf-8");
7757
8889
  const records = parseCostCsv(text);
7758
8890
  const match = this.opts.match ?? "nodeId";
7759
8891
  const out = /* @__PURE__ */ new Map();
@@ -7784,11 +8916,11 @@ async function enrichCosts(db, sessionId, source) {
7784
8916
  let matched = 0;
7785
8917
  const unmatchedIds = [];
7786
8918
  for (const [nodeId, rec] of records) {
7787
- const ok = db.enrichNodeAttribution(sessionId, nodeId, {
8919
+ const ok3 = db.enrichNodeAttribution(sessionId, nodeId, {
7788
8920
  owner: rec.owner ?? void 0,
7789
8921
  cost: rec.cost ?? void 0
7790
8922
  });
7791
- if (ok) matched++;
8923
+ if (ok3) matched++;
7792
8924
  else unmatchedIds.push(nodeId);
7793
8925
  }
7794
8926
  return { source: source.id, total: records.size, matched, unmatched: unmatchedIds.length, unmatchedIds };
@@ -7895,10 +9027,10 @@ function assignColors(domains) {
7895
9027
  return result;
7896
9028
  }
7897
9029
  function shadeVariant(hex, amount) {
7898
- const num = parseInt(hex.replace("#", ""), 16);
7899
- const r = Math.min(255, (num >> 16) + amount);
7900
- const g = Math.min(255, (num >> 8 & 255) + amount);
7901
- const b = Math.min(255, (num & 255) + amount);
9030
+ const num2 = parseInt(hex.replace("#", ""), 16);
9031
+ const r = Math.min(255, (num2 >> 16) + amount);
9032
+ const g = Math.min(255, (num2 >> 8 & 255) + amount);
9033
+ const b = Math.min(255, (num2 & 255) + amount);
7902
9034
  return `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
7903
9035
  }
7904
9036
  function groupByDomain(assets) {
@@ -9541,7 +10673,7 @@ function formatComplianceText(report) {
9541
10673
  }
9542
10674
 
9543
10675
  // src/config.ts
9544
- import { readFileSync as readFileSync6 } from "fs";
10676
+ import { readFileSync as readFileSync7 } from "fs";
9545
10677
  var ConfigError = class extends Error {
9546
10678
  constructor(message) {
9547
10679
  super(message);
@@ -9566,7 +10698,7 @@ function loadConfig(path) {
9566
10698
  function readConfigFile(path) {
9567
10699
  let raw;
9568
10700
  try {
9569
- raw = readFileSync6(path, "utf-8");
10701
+ raw = readFileSync7(path, "utf-8");
9570
10702
  } catch (err) {
9571
10703
  throw new ConfigError(
9572
10704
  `Cannot read config file ${path}: ${err instanceof Error ? err.message : String(err)}`
@@ -9827,7 +10959,7 @@ async function pushDeltas(config, items, opts = {}) {
9827
10959
  sentHashes.push(...batch.map((b) => b.contentHash));
9828
10960
  continue;
9829
10961
  }
9830
- let ok = false;
10962
+ let ok3 = false;
9831
10963
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
9832
10964
  const controller = new AbortController();
9833
10965
  const timer = setTimeout(() => controller.abort(), timeoutMs);
@@ -9846,7 +10978,7 @@ async function pushDeltas(config, items, opts = {}) {
9846
10978
  const elapsed = Date.now() - startedAt;
9847
10979
  if (res.ok) {
9848
10980
  log2(`pushed ${batch.length} item(s) \u2192 ${safeUrl} [${res.status}] ${elapsed}ms (attempt ${attempt + 1})`);
9849
- ok = true;
10981
+ ok3 = true;
9850
10982
  break;
9851
10983
  }
9852
10984
  if (res.status >= 400 && res.status < 500) {
@@ -9865,7 +10997,7 @@ async function pushDeltas(config, items, opts = {}) {
9865
10997
  await sleep(base + Math.floor(Math.random() * 100));
9866
10998
  }
9867
10999
  }
9868
- if (ok) {
11000
+ if (ok3) {
9869
11001
  sent += batch.length;
9870
11002
  sentHashes.push(...batch.map((b) => b.contentHash));
9871
11003
  } else {
@@ -9924,14 +11056,14 @@ function runSyncClassify(db, sessionId, config, opts = {}) {
9924
11056
 
9925
11057
  // src/preflight.ts
9926
11058
  import { execSync as execSync2 } from "child_process";
9927
- import { existsSync as existsSync5, readFileSync as readFileSync7 } from "fs";
11059
+ import { existsSync as existsSync5, readFileSync as readFileSync8 } from "fs";
9928
11060
  import { join as join6 } from "path";
9929
11061
  function isOAuthLoggedIn() {
9930
11062
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "/tmp";
9931
11063
  const credFile = join6(home, ".claude", ".credentials.json");
9932
11064
  if (!existsSync5(credFile)) return false;
9933
11065
  try {
9934
- const creds = JSON.parse(readFileSync7(credFile, "utf8"));
11066
+ const creds = JSON.parse(readFileSync8(credFile, "utf8"));
9935
11067
  const oauth = creds["claudeAiOauth"];
9936
11068
  return typeof oauth?.["accessToken"] === "string" && oauth["accessToken"].length > 0;
9937
11069
  } catch {
@@ -9997,7 +11129,10 @@ export {
9997
11129
  DriftConfigSchema,
9998
11130
  INGEST_SCHEMA_VERSION,
9999
11131
  IngestEnvelopeSchema,
11132
+ InvalidTenantError,
11133
+ LOOPBACK_HOSTS2 as LOOPBACK_HOSTS,
10000
11134
  MCP_BIN,
11135
+ NotFoundError,
10001
11136
  PACKAGE_NAME,
10002
11137
  PERSONAL,
10003
11138
  PORT_MAP,
@@ -10008,26 +11143,33 @@ export {
10008
11143
  RuleCheckSchema,
10009
11144
  RulesetSchema,
10010
11145
  SCAN_ARG_PATTERNS,
11146
+ SDL,
10011
11147
  SEVERITY_WEIGHT,
10012
11148
  SHARING_LEVELS,
10013
11149
  ScannerRegistry,
10014
11150
  ScannerShape,
10015
11151
  SharingLevelSchema,
11152
+ SqliteQueryBackend,
10016
11153
  SqliteStoreBackend,
10017
11154
  StdoutSink,
11155
+ TENANT_HEADER,
10018
11156
  VectorStore,
10019
11157
  WebhookSink,
10020
11158
  applyInstall,
10021
11159
  applySharingLevel,
10022
11160
  assertReadOnly,
11161
+ assertSafeBind,
10023
11162
  assertSafeScanArg,
10024
11163
  assignColors,
11164
+ bearerToken,
10025
11165
  bookmarksScanner,
10026
11166
  buildCartographyToolHandlers,
10027
11167
  buildMapData,
11168
+ buildOpenApiDocument,
10028
11169
  buildReport,
10029
11170
  buildSinks,
10030
11171
  centralDbFromEnv,
11172
+ checkBearer,
10031
11173
  checkPrerequisites,
10032
11174
  checkReadOnly,
10033
11175
  clampText,
@@ -10055,10 +11197,12 @@ export {
10055
11197
  createOpenAIProvider,
10056
11198
  createScanRunner,
10057
11199
  createSemanticSearch,
11200
+ createSqliteQueryBackend,
10058
11201
  currentOs,
10059
11202
  cursorDeeplink,
10060
11203
  databasesScanner,
10061
11204
  deepMerge,
11205
+ defaultAllowedHosts,
10062
11206
  defaultConfig,
10063
11207
  defaultContext,
10064
11208
  defaultProviderRegistry,
@@ -10075,6 +11219,7 @@ export {
10075
11219
  evaluateCheck,
10076
11220
  evaluateRule,
10077
11221
  evidenceLine,
11222
+ executeGraphql,
10078
11223
  executeNlQuery,
10079
11224
  exportAll,
10080
11225
  exportBackstageYAML,
@@ -10095,6 +11240,7 @@ export {
10095
11240
  getRuleset,
10096
11241
  globalId,
10097
11242
  groupByDomain,
11243
+ handleGraphqlGet,
10098
11244
  hexCorners,
10099
11245
  hexDistance,
10100
11246
  hexNeighbors,
@@ -10105,6 +11251,7 @@ export {
10105
11251
  hostname,
10106
11252
  ingestEnvelope,
10107
11253
  installedAppsScanner,
11254
+ isLoopbackHost,
10108
11255
  isPersonalHost,
10109
11256
  isReadOnlyCommand,
10110
11257
  isRemembered,
@@ -10133,6 +11280,7 @@ export {
10133
11280
  normalizeTenant,
10134
11281
  orgKeyPath,
10135
11282
  osUser,
11283
+ parseApiArgs,
10136
11284
  parseComposeDeps,
10137
11285
  parseConfig,
10138
11286
  parseConnectionString,
@@ -10158,10 +11306,12 @@ export {
10158
11306
  resolveEffectiveLevel,
10159
11307
  resolveNlQuery,
10160
11308
  resolveSharingLevel,
11309
+ resolveTenant,
10161
11310
  revalidateAnonymized,
10162
11311
  reversalKey,
10163
11312
  reversePseudonym,
10164
11313
  rotateOrgKey,
11314
+ runApi,
10165
11315
  runDiscovery,
10166
11316
  runDrift,
10167
11317
  runHttp,
@@ -10184,8 +11334,11 @@ export {
10184
11334
  shareHash,
10185
11335
  splitSegments,
10186
11336
  stableStringify,
11337
+ startApi,
10187
11338
  stripSensitive,
11339
+ timingSafeEqual,
10188
11340
  validateScanner,
10189
- vscodeDeeplink
11341
+ vscodeDeeplink,
11342
+ zodToJsonSchema
10190
11343
  };
10191
11344
  //# sourceMappingURL=index.js.map