@datasynx/agentic-ai-cartography 2.10.0 → 2.12.1

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.cjs CHANGED
@@ -31,9 +31,11 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var src_exports = {};
32
32
  __export(src_exports, {
33
33
  ACTIONS: () => ACTIONS,
34
+ ANON_TOKEN: () => ANON_TOKEN,
34
35
  ActionSchema: () => ActionSchema,
35
36
  AuthConfigSchema: () => AuthConfigSchema,
36
37
  AuthorizationError: () => AuthorizationError,
38
+ BARE_INTERNAL_HOST: () => BARE_INTERNAL_HOST,
37
39
  CLIENTS: () => CLIENTS,
38
40
  CONFIDENCE: () => CONFIDENCE,
39
41
  CORRELATION_CONFIDENCE: () => CORRELATION_CONFIDENCE,
@@ -197,11 +199,13 @@ __export(src_exports, {
197
199
  hostname: () => hostname,
198
200
  ingestEnvelope: () => ingestEnvelope,
199
201
  installedAppsScanner: () => installedAppsScanner,
202
+ isInCluster: () => isInCluster,
200
203
  isLoopbackHost: () => isLoopbackHost,
201
204
  isPersonalHost: () => isPersonalHost,
202
205
  isReadOnlyCommand: () => isReadOnlyCommand,
203
206
  isRemembered: () => isRemembered,
204
207
  isSecureWebhookUrl: () => isSecureWebhookUrl,
208
+ k8sRegistry: () => k8sRegistry,
205
209
  k8sScanner: () => k8sScanner,
206
210
  keyMetaOf: () => keyMetaOf,
207
211
  layoutClusters: () => layoutClusters,
@@ -238,6 +242,7 @@ __export(src_exports, {
238
242
  parseNginxUpstreams: () => parseNginxUpstreams,
239
243
  parseNlQuery: () => parseNlQuery,
240
244
  parseScanHint: () => parseScanHint,
245
+ parseTerraformState: () => parseTerraformState,
241
246
  pixelToHex: () => pixelToHex,
242
247
  planInstall: () => planInstall,
243
248
  portsScanner: () => portsScanner,
@@ -245,6 +250,7 @@ __export(src_exports, {
245
250
  previewShare: () => previewShare,
246
251
  pseudonymize: () => pseudonymize,
247
252
  pseudonymizeFragment: () => pseudonymizeFragment,
253
+ pseudonymizeId: () => pseudonymizeId,
248
254
  pseudonymizeString: () => pseudonymizeString,
249
255
  pushDeltas: () => pushDeltas,
250
256
  readConfigFile: () => readConfigFile,
@@ -267,6 +273,8 @@ __export(src_exports, {
267
273
  runHttp: () => runHttp,
268
274
  runLocalDiscovery: () => runLocalDiscovery,
269
275
  runOnce: () => runOnce,
276
+ runOperator: () => runOperator,
277
+ runOperatorCycle: () => runOperatorCycle,
270
278
  runStdio: () => runStdio,
271
279
  runSyncClassify: () => runSyncClassify,
272
280
  safeEnv: () => safeEnv,
@@ -287,6 +295,8 @@ __export(src_exports, {
287
295
  stableStringify: () => stableStringify,
288
296
  startApi: () => startApi,
289
297
  stripSensitive: () => stripSensitive,
298
+ terraformScanner: () => terraformScanner,
299
+ terraformTypeToNode: () => terraformTypeToNode,
290
300
  timingSafeEqual: () => timingSafeEqual,
291
301
  toBackstageEntities: () => toBackstageEntities,
292
302
  validateScanner: () => validateScanner,
@@ -4949,6 +4959,8 @@ function reversalKey(orgKey) {
4949
4959
  // src/anonymize.ts
4950
4960
  var PRIVATE_IP = /\b(?:10(?:\.\d{1,3}){3}|192\.168(?:\.\d{1,3}){2}|172\.(?:1[6-9]|2\d|3[01])(?:\.\d{1,3}){2})\b/g;
4951
4961
  var HOSTNAME = /\b(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,}\b/gi;
4962
+ var BARE_INTERNAL_HOST = /^[a-z0-9]+(?:-[a-z0-9]+)+$|^[a-z]+\d+$|^\d+[a-z]+$/i;
4963
+ var ANON_TOKEN = /^anon:(?:host|user|path|ip):[a-z2-7]+$/;
4952
4964
  var POSIX_PATH = /(?:^|(?<=\s|=|:|"|'|\())(\/[A-Za-z0-9._-]+(?:\/[A-Za-z0-9._-]+)+)/g;
4953
4965
  var WIN_PATH = /\b[A-Za-z]:\\[A-Za-z0-9._\\-]+/g;
4954
4966
  var B32_ALPHABET = "abcdefghijklmnopqrstuvwxyz234567";
@@ -5003,8 +5015,18 @@ function pseudonymizeString(s, orgKey, db) {
5003
5015
  (_m, user, host2) => `${pseudonymizeFragment(user, "user", orgKey, db)}@${pseudonymizeFragment(host2, "host", orgKey, db)}`
5004
5016
  );
5005
5017
  out = out.replace(HOSTNAME, (m) => pseudonymizeFragment(m, "host", orgKey, db));
5018
+ const trimmed = out.trim();
5019
+ if (out === s && !ANON_TOKEN.test(trimmed) && BARE_INTERNAL_HOST.test(trimmed)) {
5020
+ out = pseudonymizeFragment(trimmed, "host", orgKey, db);
5021
+ }
5006
5022
  return out;
5007
5023
  }
5024
+ function pseudonymizeId(id, orgKey, db) {
5025
+ const segments = id.split(":");
5026
+ if (segments.length <= 1) return pseudonymizeString(id, orgKey, db);
5027
+ const [type, ...rest] = segments;
5028
+ return [type, ...rest.map((seg) => pseudonymizeString(seg, orgKey, db))].join(":");
5029
+ }
5008
5030
  function pseudonymize(value, orgKey, db) {
5009
5031
  if (typeof value === "string") return pseudonymizeString(value, orgKey, db);
5010
5032
  if (Array.isArray(value)) return value.map((v) => pseudonymize(v, orgKey, db));
@@ -5031,8 +5053,6 @@ var FQDN = /\b(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,}\b/gi;
5031
5053
  var POSIX_PATH2 = /(?:^|(?<=\s|=|:|"|'|\())(\/[A-Za-z0-9._-]+(?:\/[A-Za-z0-9._-]+)+)/g;
5032
5054
  var WIN_PATH2 = /\b[A-Za-z]:\\[A-Za-z0-9._\\-]+/g;
5033
5055
  var HOME_USER = /(?:\/home\/|\/Users\/|[A-Za-z]:\\Users\\)([A-Za-z0-9._-]+)/g;
5034
- var BARE_INTERNAL_HOST = /^[a-z0-9]+(?:-[a-z0-9]+)+$|^[a-z]+\d+$|^\d+[a-z]+$/i;
5035
- var ANON_TOKEN = /^anon:(?:host|user|path|ip):[a-z2-7]+$/;
5036
5056
  function violationsInString(s, path) {
5037
5057
  const out = [];
5038
5058
  const trimmed = s.trim();
@@ -5422,9 +5442,10 @@ function resolveEffectiveLevel(node, policy) {
5422
5442
  function applySharingLevel(node, level, orgKey, db) {
5423
5443
  if (level === "none") return null;
5424
5444
  if (level === "full") return { ...node, metadata: { ...node.metadata ?? {} }, tags: [...node.tags ?? []] };
5445
+ const { globalId: _g, contentHash: _h, ...rest } = node;
5425
5446
  return {
5426
- ...node,
5427
- id: pseudonymizeString(node.id, orgKey, db),
5447
+ ...rest,
5448
+ id: pseudonymizeId(node.id, orgKey, db),
5428
5449
  name: pseudonymizeString(node.name, orgKey, db),
5429
5450
  metadata: pseudonymize(node.metadata ?? {}, orgKey, db),
5430
5451
  tags: (node.tags ?? []).map((t) => pseudonymizeString(t, orgKey, db))
@@ -6276,7 +6297,7 @@ function correlateTopology(nodes, _edges = []) {
6276
6297
 
6277
6298
  // src/mcp/server.ts
6278
6299
  var SERVER_NAME = "cartography";
6279
- var SERVER_VERSION = "2.10.0";
6300
+ var SERVER_VERSION = "2.12.1";
6280
6301
  var SERVICE_TYPES = NODE_TYPE_GROUPS.web;
6281
6302
  var DATA_TYPES = NODE_TYPE_GROUPS.data;
6282
6303
  var lexicalSearch = async (db, sessionId, query, opts) => db.searchNodes(sessionId, query, { types: opts.types, limit: opts.limit }).map((node) => ({ node }));
@@ -7686,9 +7707,132 @@ var serviceConfigScanner = {
7686
7707
  }
7687
7708
  };
7688
7709
 
7710
+ // src/scanners/terraform.ts
7711
+ var import_node_fs5 = require("fs");
7712
+ var TYPE_RULES = [
7713
+ [/(db_instance|_rds|sql_database|sql_instance|database_instance|cosmosdb|dynamodb|spanner|bigtable|documentdb|redshift)/, "database_server"],
7714
+ [/(elasticache|_redis|memcached|memorystore)/, "cache_server"],
7715
+ [/(s3_bucket|storage_bucket|gcs_bucket|storage_account|_blob)/, "database"],
7716
+ [/(_sqs|_queue|servicebus_queue)/, "queue"],
7717
+ [/(_sns|_topic|pubsub_topic|servicebus_topic)/, "topic"],
7718
+ [/(kafka|_msk|event_hub|kinesis)/, "message_broker"],
7719
+ [/(_eks|_gke|_aks|kubernetes_cluster|container_cluster)/, "k8s_cluster"],
7720
+ [/(ecs_|_container|fargate)/, "container"],
7721
+ [/(lambda|cloud_function|cloudfunctions|function_app|cloud_run)/, "web_service"],
7722
+ [/(_lb$|load_balancer|_alb|_elb|application_gateway)/, "web_service"],
7723
+ [/(api_gateway|apigateway)/, "api_endpoint"],
7724
+ [/(_instance|virtual_machine|_vm$|compute_instance)/, "host"]
7725
+ ];
7726
+ function terraformTypeToNode(tfType) {
7727
+ const t = tfType.toLowerCase();
7728
+ for (const [re, nt] of TYPE_RULES) if (re.test(t)) return nt;
7729
+ return "unknown";
7730
+ }
7731
+ var IDENTITY_ATTRS = ["id", "arn", "region", "location", "instance_type", "engine", "machine_type"];
7732
+ var OWNER_TAGS = ["Owner", "owner", "Team", "team"];
7733
+ var SAFE_TAG_KEYS = /* @__PURE__ */ new Set(["Name", "name", "Owner", "owner", "Team", "team", "Env", "env", "Environment", "environment", "Service", "service", "Component", "component", "App", "app", "Project", "project", "Tier", "tier", "Role", "role"]);
7734
+ var SECRET_KEY = /pass|secret|token|key|pwd|cred|private/i;
7735
+ function attrTags(tags) {
7736
+ if (!tags || typeof tags !== "object") return [];
7737
+ return Object.entries(tags).filter(([k]) => SAFE_TAG_KEYS.has(k) && !SECRET_KEY.test(k)).map(([k, v]) => `${k}:${redactSecrets(String(v))}`);
7738
+ }
7739
+ function parseTerraformState(json2) {
7740
+ let state;
7741
+ try {
7742
+ state = JSON.parse(json2);
7743
+ } catch {
7744
+ return { nodes: [], edges: [] };
7745
+ }
7746
+ const resources = Array.isArray(state?.resources) ? state.resources : [];
7747
+ const nodes = [];
7748
+ const edges = [];
7749
+ const addrToId = /* @__PURE__ */ new Map();
7750
+ for (const raw of resources) {
7751
+ const r = raw;
7752
+ if (r.mode && r.mode !== "managed") continue;
7753
+ if (typeof r.type !== "string" || typeof r.name !== "string") continue;
7754
+ const address = `${r.type}.${r.name}`;
7755
+ const nt = terraformTypeToNode(r.type);
7756
+ const id = `${nt}:terraform:${address}`;
7757
+ if (addrToId.has(address)) continue;
7758
+ addrToId.set(address, id);
7759
+ const inst = Array.isArray(r.instances) ? r.instances[0] : void 0;
7760
+ const attrs = inst?.attributes ?? {};
7761
+ const identity = { source: "terraform", tfType: r.type };
7762
+ for (const k of IDENTITY_ATTRS) if (attrs[k] !== void 0) identity[k] = attrs[k];
7763
+ const owner = OWNER_TAGS.map((k) => attrs.tags?.[k]).find((v) => typeof v === "string");
7764
+ nodes.push({
7765
+ id,
7766
+ type: nt,
7767
+ name: address,
7768
+ discoveredVia: "terraform-state",
7769
+ confidence: 0.9,
7770
+ // IaC is authoritative declared intent.
7771
+ metadata: redactValue(identity),
7772
+ tags: attrTags(attrs.tags),
7773
+ ...owner ? { owner } : {}
7774
+ });
7775
+ }
7776
+ for (const raw of resources) {
7777
+ const r = raw;
7778
+ if (r.mode && r.mode !== "managed") continue;
7779
+ if (typeof r.type !== "string" || typeof r.name !== "string") continue;
7780
+ const srcId = addrToId.get(`${r.type}.${r.name}`);
7781
+ if (!srcId) continue;
7782
+ const inst = Array.isArray(r.instances) ? r.instances[0] : void 0;
7783
+ const deps = Array.isArray(inst?.dependencies) ? inst.dependencies : [];
7784
+ for (const dep of deps) {
7785
+ if (typeof dep !== "string") continue;
7786
+ const tgtId = addrToId.get(dep) ?? addrToId.get(dep.split("[")[0]);
7787
+ if (!tgtId || tgtId === srcId) continue;
7788
+ edges.push({ sourceId: srcId, targetId: tgtId, relationship: "depends_on", evidence: evidenceLine("config-declared", `terraform depends_on ${dep}`), confidence: 0.85 });
7789
+ }
7790
+ }
7791
+ return { nodes, edges };
7792
+ }
7793
+ function stateDirs() {
7794
+ return [".", "./terraform", "./infra", "./infrastructure", "./deploy", "./terraform/environments"];
7795
+ }
7796
+ function hintPath(hint) {
7797
+ if (!hint) return void 0;
7798
+ const m = /(?:^|[\s,])tfstate=([^\s,]+)/.exec(hint);
7799
+ return m ? m[1] : void 0;
7800
+ }
7801
+ function resolveStatePath(ctx) {
7802
+ const explicit = hintPath(ctx.hint);
7803
+ if (explicit) return explicit;
7804
+ const found = (ctx.findFiles ?? findFiles)(stateDirs(), ["*.tfstate"], 4, 20).split(/\r?\n/).map((s) => s.trim()).filter(Boolean);
7805
+ return found[0];
7806
+ }
7807
+ function readStateFile(path) {
7808
+ try {
7809
+ return (0, import_node_fs5.readFileSync)(path, "utf8");
7810
+ } catch {
7811
+ return "";
7812
+ }
7813
+ }
7814
+ var terraformScanner = {
7815
+ id: "terraform-state",
7816
+ title: "Terraform state (IaC)",
7817
+ platforms: "all",
7818
+ // No shell commands — the state file is read directly via node:fs, so an
7819
+ // operator-supplied path can never inject a command (no `cat "${path}"` interpolation).
7820
+ detect(ctx) {
7821
+ return resolveStatePath(ctx) !== void 0;
7822
+ },
7823
+ async scan(ctx) {
7824
+ const path = resolveStatePath(ctx);
7825
+ if (!path) return { nodes: [], edges: [] };
7826
+ const json2 = (ctx.readFile ?? readStateFile)(path);
7827
+ if (!json2) return { nodes: [], edges: [] };
7828
+ const result = parseTerraformState(json2);
7829
+ return { ...result, report: `terraform-state: ${result.nodes.length} resources, ${result.edges.length} dependencies from ${path}` };
7830
+ }
7831
+ };
7832
+
7689
7833
  // src/scanners/registry.ts
7690
7834
  function defaultRegistry() {
7691
- return new ScannerRegistry().register(bookmarksScanner).register(installedAppsScanner).register(portsScanner).register(cloudAwsScanner).register(cloudGcpScanner).register(cloudAzureScanner).register(k8sScanner).register(databasesScanner).register(connectionsScanner).register(serviceConfigScanner);
7835
+ return new ScannerRegistry().register(bookmarksScanner).register(installedAppsScanner).register(portsScanner).register(cloudAwsScanner).register(cloudGcpScanner).register(cloudAzureScanner).register(k8sScanner).register(databasesScanner).register(connectionsScanner).register(serviceConfigScanner).register(terraformScanner);
7692
7836
  }
7693
7837
 
7694
7838
  // src/scanners/loader.ts
@@ -9056,14 +9200,14 @@ var AuthConfigSchema = import_zod9.z.object({
9056
9200
  });
9057
9201
 
9058
9202
  // src/api/start.ts
9059
- var import_node_fs5 = require("fs");
9203
+ var import_node_fs6 = require("fs");
9060
9204
  var import_node_path5 = require("path");
9061
9205
  var import_node_url = require("url");
9062
9206
  var import_meta = {};
9063
9207
  function readVersion() {
9064
9208
  try {
9065
9209
  const dir = import_meta.dirname ?? (0, import_node_path5.dirname)((0, import_node_url.fileURLToPath)(import_meta.url));
9066
- return JSON.parse((0, import_node_fs5.readFileSync)((0, import_node_path5.resolve)(dir, "..", "package.json"), "utf-8")).version ?? "0.0.0";
9210
+ return JSON.parse((0, import_node_fs6.readFileSync)((0, import_node_path5.resolve)(dir, "..", "package.json"), "utf-8")).version ?? "0.0.0";
9067
9211
  } catch {
9068
9212
  return "0.0.0";
9069
9213
  }
@@ -9194,7 +9338,7 @@ function defaultServerEntry(opts = {}) {
9194
9338
  }
9195
9339
 
9196
9340
  // src/installer/install.ts
9197
- var import_node_fs6 = require("fs");
9341
+ var import_node_fs7 = require("fs");
9198
9342
  var import_node_path6 = require("path");
9199
9343
  var import_node_os4 = require("os");
9200
9344
  function currentOs() {
@@ -9210,8 +9354,8 @@ function planInstall(spec, ctx, opts) {
9210
9354
  if (!path) {
9211
9355
  throw new Error(`${spec.label} does not support the "${ctx.scope}" scope.`);
9212
9356
  }
9213
- const fileExists = (0, import_node_fs6.existsSync)(path);
9214
- const before = fileExists ? (0, import_node_fs6.readFileSync)(path, "utf8") : "";
9357
+ const fileExists = (0, import_node_fs7.existsSync)(path);
9358
+ const before = fileExists ? (0, import_node_fs7.readFileSync)(path, "utf8") : "";
9215
9359
  const existing = parseConfig(before, spec.format);
9216
9360
  const merged = spec.apply(existing, opts.serverName ?? DEFAULT_SERVER_NAME, opts.entry);
9217
9361
  const after = serializeConfig(merged, spec.format);
@@ -9228,8 +9372,8 @@ function planInstall(spec, ctx, opts) {
9228
9372
  };
9229
9373
  }
9230
9374
  function applyInstall(plan) {
9231
- (0, import_node_fs6.mkdirSync)((0, import_node_path6.dirname)(plan.path), { recursive: true });
9232
- (0, import_node_fs6.writeFileSync)(plan.path, plan.after, "utf8");
9375
+ (0, import_node_fs7.mkdirSync)((0, import_node_path6.dirname)(plan.path), { recursive: true });
9376
+ (0, import_node_fs7.writeFileSync)(plan.path, plan.after, "utf8");
9233
9377
  }
9234
9378
  function renderDiff(before, after) {
9235
9379
  if (before === after) return " (no changes)";
@@ -10076,8 +10220,65 @@ Use ask_user when you need context from the user.`;
10076
10220
  }
10077
10221
  }
10078
10222
 
10223
+ // src/k8s/operator.ts
10224
+ function k8sRegistry() {
10225
+ return new ScannerRegistry().register(k8sScanner);
10226
+ }
10227
+ function isInCluster(env = process.env) {
10228
+ return typeof env["KUBERNETES_SERVICE_HOST"] === "string" && env["KUBERNETES_SERVICE_HOST"].length > 0;
10229
+ }
10230
+ function pruneToRetention(db, keep, tenant) {
10231
+ const stale = db.getSessions(tenant).slice(Math.max(1, keep));
10232
+ for (const s of stale) db.deleteSession(s.id);
10233
+ return stale.length;
10234
+ }
10235
+ async function runOperatorCycle(db, config, opts = {}) {
10236
+ const sessionId = db.createSession("discover", config);
10237
+ const discover = opts.discover ?? ((d, s) => runLocalDiscovery(d, s, { registry: k8sRegistry() }));
10238
+ const res = await discover(db, sessionId);
10239
+ const sess = db.getSession(sessionId);
10240
+ if (sess && !sess.name) db.setSessionName(sessionId, deriveSessionName(db.getGraphSummary(sessionId), sess.startedAt));
10241
+ const driftFn = opts.drift ?? ((d, c) => runDrift(d, c));
10242
+ const drift = await driftFn(db, config);
10243
+ pruneToRetention(db, opts.retain ?? 10, normalizeTenant(config.organization));
10244
+ return { sessionId, nodes: res.nodes, edges: res.edges, drift };
10245
+ }
10246
+ async function runOperator(db, config, opts = {}) {
10247
+ const log2 = opts.log ?? ((m) => process.stderr.write(m + "\n"));
10248
+ const intervalMs = opts.intervalMs ?? 5 * 6e4;
10249
+ const sleep = opts.sleep ?? ((ms) => new Promise((resolve3) => {
10250
+ if (opts.signal?.aborted) {
10251
+ resolve3();
10252
+ return;
10253
+ }
10254
+ const t = setTimeout(() => {
10255
+ opts.signal?.removeEventListener?.("abort", onAbort);
10256
+ resolve3();
10257
+ }, ms);
10258
+ const onAbort = () => {
10259
+ clearTimeout(t);
10260
+ resolve3();
10261
+ };
10262
+ opts.signal?.addEventListener?.("abort", onAbort, { once: true });
10263
+ }));
10264
+ log2(`Cartograph Kubernetes operator (in-cluster: ${isInCluster()}, interval: ${Math.round(intervalMs / 1e3)}s${opts.once ? ", single pass" : ""})`);
10265
+ for (; ; ) {
10266
+ try {
10267
+ const c = await runOperatorCycle(db, config, opts);
10268
+ log2(
10269
+ `reconcile: session ${c.sessionId} \u2014 ${c.nodes} nodes, ${c.edges} edges` + (c.drift ? `, drift ${c.drift.severity} (${c.drift.items.length} change${c.drift.items.length === 1 ? "" : "s"})` : ", no drift")
10270
+ );
10271
+ } catch (err) {
10272
+ log2(`reconcile failed: ${err instanceof Error ? err.message : String(err)}`);
10273
+ }
10274
+ if (opts.once || opts.signal?.aborted) return;
10275
+ await sleep(intervalMs);
10276
+ if (opts.signal?.aborted) return;
10277
+ }
10278
+ }
10279
+
10079
10280
  // src/cost.ts
10080
- var import_node_fs7 = require("fs");
10281
+ var import_node_fs8 = require("fs");
10081
10282
  var import_node_path8 = require("path");
10082
10283
  function splitCsvLine(line) {
10083
10284
  const out = [];
@@ -10156,7 +10357,7 @@ var CsvCostSource = class {
10156
10357
  }
10157
10358
  id;
10158
10359
  async fetch() {
10159
- const text = (0, import_node_fs7.readFileSync)((0, import_node_path8.resolve)(this.opts.filePath), "utf-8");
10360
+ const text = (0, import_node_fs8.readFileSync)((0, import_node_path8.resolve)(this.opts.filePath), "utf-8");
10160
10361
  const records = parseCostCsv(text);
10161
10362
  const match = this.opts.match ?? "nodeId";
10162
10363
  const out = /* @__PURE__ */ new Map();
@@ -10198,7 +10399,7 @@ async function enrichCosts(db, sessionId, source) {
10198
10399
  }
10199
10400
 
10200
10401
  // src/exporter.ts
10201
- var import_node_fs8 = require("fs");
10402
+ var import_node_fs9 = require("fs");
10202
10403
  var import_node_path9 = require("path");
10203
10404
 
10204
10405
  // src/hex.ts
@@ -11868,28 +12069,28 @@ function exportComplianceReport(report, format) {
11868
12069
  return lines.join("\n");
11869
12070
  }
11870
12071
  function exportAll(db, sessionId, outputDir, formats = ["mermaid", "json", "yaml", "html", "map", "discovery"]) {
11871
- (0, import_node_fs8.mkdirSync)(outputDir, { recursive: true });
12072
+ (0, import_node_fs9.mkdirSync)(outputDir, { recursive: true });
11872
12073
  const nodes = db.getNodes(sessionId);
11873
12074
  const edges = db.getEdges(sessionId);
11874
12075
  const jgfPath = (0, import_node_path9.join)(outputDir, "cartography-graph.jgf.json");
11875
- (0, import_node_fs8.writeFileSync)(jgfPath, exportJGF(nodes, edges));
12076
+ (0, import_node_fs9.writeFileSync)(jgfPath, exportJGF(nodes, edges));
11876
12077
  if (formats.includes("mermaid")) {
11877
- (0, import_node_fs8.writeFileSync)((0, import_node_path9.join)(outputDir, "topology.mermaid"), generateTopologyMermaid(nodes, edges));
11878
- (0, import_node_fs8.writeFileSync)((0, import_node_path9.join)(outputDir, "dependencies.mermaid"), generateDependencyMermaid(nodes, edges));
12078
+ (0, import_node_fs9.writeFileSync)((0, import_node_path9.join)(outputDir, "topology.mermaid"), generateTopologyMermaid(nodes, edges));
12079
+ (0, import_node_fs9.writeFileSync)((0, import_node_path9.join)(outputDir, "dependencies.mermaid"), generateDependencyMermaid(nodes, edges));
11879
12080
  }
11880
12081
  if (formats.includes("json")) {
11881
- (0, import_node_fs8.writeFileSync)((0, import_node_path9.join)(outputDir, "catalog.json"), exportJSON(db, sessionId));
12082
+ (0, import_node_fs9.writeFileSync)((0, import_node_path9.join)(outputDir, "catalog.json"), exportJSON(db, sessionId));
11882
12083
  }
11883
12084
  if (formats.includes("yaml")) {
11884
- (0, import_node_fs8.writeFileSync)((0, import_node_path9.join)(outputDir, "catalog-info.yaml"), exportBackstageYAML(nodes, edges));
12085
+ (0, import_node_fs9.writeFileSync)((0, import_node_path9.join)(outputDir, "catalog-info.yaml"), exportBackstageYAML(nodes, edges));
11885
12086
  }
11886
12087
  if (formats.includes("html") || formats.includes("map") || formats.includes("discovery")) {
11887
- (0, import_node_fs8.writeFileSync)((0, import_node_path9.join)(outputDir, "discovery.html"), exportDiscoveryApp(nodes, edges));
12088
+ (0, import_node_fs9.writeFileSync)((0, import_node_path9.join)(outputDir, "discovery.html"), exportDiscoveryApp(nodes, edges));
11888
12089
  }
11889
12090
  if (formats.includes("cost")) {
11890
12091
  const summary = db.getGraphSummary(sessionId);
11891
- (0, import_node_fs8.writeFileSync)((0, import_node_path9.join)(outputDir, "cost-by-domain.csv"), exportCostCSV(summary));
11892
- (0, import_node_fs8.writeFileSync)((0, import_node_path9.join)(outputDir, "cost-summary.json"), exportCostSummary(summary));
12092
+ (0, import_node_fs9.writeFileSync)((0, import_node_path9.join)(outputDir, "cost-by-domain.csv"), exportCostCSV(summary));
12093
+ (0, import_node_fs9.writeFileSync)((0, import_node_path9.join)(outputDir, "cost-summary.json"), exportCostSummary(summary));
11893
12094
  }
11894
12095
  }
11895
12096
 
@@ -11921,7 +12122,7 @@ function formatComplianceText(report) {
11921
12122
  }
11922
12123
 
11923
12124
  // src/config.ts
11924
- var import_node_fs9 = require("fs");
12125
+ var import_node_fs10 = require("fs");
11925
12126
  var ConfigError = class extends Error {
11926
12127
  constructor(message) {
11927
12128
  super(message);
@@ -11946,7 +12147,7 @@ function loadConfig(path) {
11946
12147
  function readConfigFile(path) {
11947
12148
  let raw;
11948
12149
  try {
11949
- raw = (0, import_node_fs9.readFileSync)(path, "utf-8");
12150
+ raw = (0, import_node_fs10.readFileSync)(path, "utf-8");
11950
12151
  } catch (err) {
11951
12152
  throw new ConfigError(
11952
12153
  `Cannot read config file ${path}: ${err instanceof Error ? err.message : String(err)}`
@@ -12304,14 +12505,14 @@ function runSyncClassify(db, sessionId, config, opts = {}) {
12304
12505
 
12305
12506
  // src/preflight.ts
12306
12507
  var import_node_child_process2 = require("child_process");
12307
- var import_node_fs10 = require("fs");
12508
+ var import_node_fs11 = require("fs");
12308
12509
  var import_node_path10 = require("path");
12309
12510
  function isOAuthLoggedIn() {
12310
12511
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "/tmp";
12311
12512
  const credFile = (0, import_node_path10.join)(home, ".claude", ".credentials.json");
12312
- if (!(0, import_node_fs10.existsSync)(credFile)) return false;
12513
+ if (!(0, import_node_fs11.existsSync)(credFile)) return false;
12313
12514
  try {
12314
- const creds = JSON.parse((0, import_node_fs10.readFileSync)(credFile, "utf8"));
12515
+ const creds = JSON.parse((0, import_node_fs11.readFileSync)(credFile, "utf8"));
12315
12516
  const oauth = creds["claudeAiOauth"];
12316
12517
  return typeof oauth?.["accessToken"] === "string" && oauth["accessToken"].length > 0;
12317
12518
  } catch {
@@ -12364,9 +12565,11 @@ function checkClaudePrerequisites() {
12364
12565
  // Annotate the CommonJS export names for ESM import in node:
12365
12566
  0 && (module.exports = {
12366
12567
  ACTIONS,
12568
+ ANON_TOKEN,
12367
12569
  ActionSchema,
12368
12570
  AuthConfigSchema,
12369
12571
  AuthorizationError,
12572
+ BARE_INTERNAL_HOST,
12370
12573
  CLIENTS,
12371
12574
  CONFIDENCE,
12372
12575
  CORRELATION_CONFIDENCE,
@@ -12530,11 +12733,13 @@ function checkClaudePrerequisites() {
12530
12733
  hostname,
12531
12734
  ingestEnvelope,
12532
12735
  installedAppsScanner,
12736
+ isInCluster,
12533
12737
  isLoopbackHost,
12534
12738
  isPersonalHost,
12535
12739
  isReadOnlyCommand,
12536
12740
  isRemembered,
12537
12741
  isSecureWebhookUrl,
12742
+ k8sRegistry,
12538
12743
  k8sScanner,
12539
12744
  keyMetaOf,
12540
12745
  layoutClusters,
@@ -12571,6 +12776,7 @@ function checkClaudePrerequisites() {
12571
12776
  parseNginxUpstreams,
12572
12777
  parseNlQuery,
12573
12778
  parseScanHint,
12779
+ parseTerraformState,
12574
12780
  pixelToHex,
12575
12781
  planInstall,
12576
12782
  portsScanner,
@@ -12578,6 +12784,7 @@ function checkClaudePrerequisites() {
12578
12784
  previewShare,
12579
12785
  pseudonymize,
12580
12786
  pseudonymizeFragment,
12787
+ pseudonymizeId,
12581
12788
  pseudonymizeString,
12582
12789
  pushDeltas,
12583
12790
  readConfigFile,
@@ -12600,6 +12807,8 @@ function checkClaudePrerequisites() {
12600
12807
  runHttp,
12601
12808
  runLocalDiscovery,
12602
12809
  runOnce,
12810
+ runOperator,
12811
+ runOperatorCycle,
12603
12812
  runStdio,
12604
12813
  runSyncClassify,
12605
12814
  safeEnv,
@@ -12620,6 +12829,8 @@ function checkClaudePrerequisites() {
12620
12829
  stableStringify,
12621
12830
  startApi,
12622
12831
  stripSensitive,
12832
+ terraformScanner,
12833
+ terraformTypeToNode,
12623
12834
  timingSafeEqual,
12624
12835
  toBackstageEntities,
12625
12836
  validateScanner,