@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/README.md +142 -2
- package/dist/api-bin.js +2 -2
- package/dist/{chunk-YVV6NIT2.js → chunk-LO6YFS6H.js} +2 -1
- package/dist/{chunk-ASCA3UFM.js → chunk-OIDAXUW5.js} +340 -204
- package/dist/chunk-OIDAXUW5.js.map +1 -0
- package/dist/{chunk-W4Q3TXHR.js → chunk-PD67MOKR.js} +2 -2
- package/dist/cli.js +97 -5
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +241 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +126 -3
- package/dist/index.d.ts +126 -3
- package/dist/index.js +217 -16
- package/dist/index.js.map +1 -1
- package/dist/mcp-bin.js +2 -2
- package/llms-full.txt +305 -25
- package/package.json +1 -1
- package/server.json +2 -2
- package/dist/chunk-ASCA3UFM.js.map +0 -1
- /package/dist/{chunk-YVV6NIT2.js.map → chunk-LO6YFS6H.js.map} +0 -0
- /package/dist/{chunk-W4Q3TXHR.js.map → chunk-PD67MOKR.js.map} +0 -0
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
defaultAllowedHosts,
|
|
11
11
|
normalizeTenant,
|
|
12
12
|
resolvePrincipal
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-LO6YFS6H.js";
|
|
14
14
|
import {
|
|
15
15
|
ANOMALY_KINDS,
|
|
16
16
|
ANOMALY_SEVERITIES,
|
|
@@ -1409,4 +1409,4 @@ export {
|
|
|
1409
1409
|
parseApiArgs,
|
|
1410
1410
|
startApi
|
|
1411
1411
|
};
|
|
1412
|
-
//# sourceMappingURL=chunk-
|
|
1412
|
+
//# sourceMappingURL=chunk-PD67MOKR.js.map
|
package/dist/cli.js
CHANGED
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
ScannerRegistry,
|
|
3
4
|
getRuleset,
|
|
4
5
|
isPersonalHost,
|
|
5
6
|
listRulesets,
|
|
6
7
|
loadOrgKey,
|
|
7
8
|
pseudonymize,
|
|
9
|
+
pseudonymizeId,
|
|
8
10
|
pseudonymizeString,
|
|
9
11
|
reversePseudonym,
|
|
10
12
|
rotateOrgKey,
|
|
11
13
|
runDrift,
|
|
12
14
|
runLocalDiscovery,
|
|
13
15
|
startMcp
|
|
14
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-OIDAXUW5.js";
|
|
15
17
|
import {
|
|
16
18
|
entitiesToYaml,
|
|
17
19
|
startApi,
|
|
18
20
|
toBackstageEntities
|
|
19
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-PD67MOKR.js";
|
|
20
22
|
import {
|
|
21
23
|
CartographyDB,
|
|
22
24
|
buildCartographyToolHandlers,
|
|
@@ -24,11 +26,12 @@ import {
|
|
|
24
26
|
deriveSessionName,
|
|
25
27
|
diffTopology,
|
|
26
28
|
hashToken,
|
|
29
|
+
k8sScanner,
|
|
27
30
|
normalizeTenant,
|
|
28
31
|
redactValue,
|
|
29
32
|
stableStringify,
|
|
30
33
|
stripSensitive
|
|
31
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-LO6YFS6H.js";
|
|
32
35
|
import {
|
|
33
36
|
ConfigFileSchema,
|
|
34
37
|
CostEntrySchema,
|
|
@@ -952,6 +955,63 @@ async function runOnce(cfg, db) {
|
|
|
952
955
|
}
|
|
953
956
|
}
|
|
954
957
|
|
|
958
|
+
// src/k8s/operator.ts
|
|
959
|
+
function k8sRegistry() {
|
|
960
|
+
return new ScannerRegistry().register(k8sScanner);
|
|
961
|
+
}
|
|
962
|
+
function isInCluster(env = process.env) {
|
|
963
|
+
return typeof env["KUBERNETES_SERVICE_HOST"] === "string" && env["KUBERNETES_SERVICE_HOST"].length > 0;
|
|
964
|
+
}
|
|
965
|
+
function pruneToRetention(db, keep, tenant) {
|
|
966
|
+
const stale = db.getSessions(tenant).slice(Math.max(1, keep));
|
|
967
|
+
for (const s of stale) db.deleteSession(s.id);
|
|
968
|
+
return stale.length;
|
|
969
|
+
}
|
|
970
|
+
async function runOperatorCycle(db, config, opts = {}) {
|
|
971
|
+
const sessionId = db.createSession("discover", config);
|
|
972
|
+
const discover = opts.discover ?? ((d, s) => runLocalDiscovery(d, s, { registry: k8sRegistry() }));
|
|
973
|
+
const res = await discover(db, sessionId);
|
|
974
|
+
const sess = db.getSession(sessionId);
|
|
975
|
+
if (sess && !sess.name) db.setSessionName(sessionId, deriveSessionName(db.getGraphSummary(sessionId), sess.startedAt));
|
|
976
|
+
const driftFn = opts.drift ?? ((d, c) => runDrift(d, c));
|
|
977
|
+
const drift = await driftFn(db, config);
|
|
978
|
+
pruneToRetention(db, opts.retain ?? 10, normalizeTenant(config.organization));
|
|
979
|
+
return { sessionId, nodes: res.nodes, edges: res.edges, drift };
|
|
980
|
+
}
|
|
981
|
+
async function runOperator(db, config, opts = {}) {
|
|
982
|
+
const log = opts.log ?? ((m) => process.stderr.write(m + "\n"));
|
|
983
|
+
const intervalMs = opts.intervalMs ?? 5 * 6e4;
|
|
984
|
+
const sleep = opts.sleep ?? ((ms) => new Promise((resolve3) => {
|
|
985
|
+
if (opts.signal?.aborted) {
|
|
986
|
+
resolve3();
|
|
987
|
+
return;
|
|
988
|
+
}
|
|
989
|
+
const t = setTimeout(() => {
|
|
990
|
+
opts.signal?.removeEventListener?.("abort", onAbort);
|
|
991
|
+
resolve3();
|
|
992
|
+
}, ms);
|
|
993
|
+
const onAbort = () => {
|
|
994
|
+
clearTimeout(t);
|
|
995
|
+
resolve3();
|
|
996
|
+
};
|
|
997
|
+
opts.signal?.addEventListener?.("abort", onAbort, { once: true });
|
|
998
|
+
}));
|
|
999
|
+
log(`Cartograph Kubernetes operator (in-cluster: ${isInCluster()}, interval: ${Math.round(intervalMs / 1e3)}s${opts.once ? ", single pass" : ""})`);
|
|
1000
|
+
for (; ; ) {
|
|
1001
|
+
try {
|
|
1002
|
+
const c = await runOperatorCycle(db, config, opts);
|
|
1003
|
+
log(
|
|
1004
|
+
`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")
|
|
1005
|
+
);
|
|
1006
|
+
} catch (err) {
|
|
1007
|
+
log(`reconcile failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1008
|
+
}
|
|
1009
|
+
if (opts.once || opts.signal?.aborted) return;
|
|
1010
|
+
await sleep(intervalMs);
|
|
1011
|
+
if (opts.signal?.aborted) return;
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
|
|
955
1015
|
// src/exporter.ts
|
|
956
1016
|
import { mkdirSync, writeFileSync } from "fs";
|
|
957
1017
|
import { join as join2 } from "path";
|
|
@@ -2792,9 +2852,10 @@ function resolveEffectiveLevel(node, policy) {
|
|
|
2792
2852
|
function applySharingLevel(node, level, orgKey, db) {
|
|
2793
2853
|
if (level === "none") return null;
|
|
2794
2854
|
if (level === "full") return { ...node, metadata: { ...node.metadata ?? {} }, tags: [...node.tags ?? []] };
|
|
2855
|
+
const { globalId: _g, contentHash: _h, ...rest } = node;
|
|
2795
2856
|
return {
|
|
2796
|
-
...
|
|
2797
|
-
id:
|
|
2857
|
+
...rest,
|
|
2858
|
+
id: pseudonymizeId(node.id, orgKey, db),
|
|
2798
2859
|
name: pseudonymizeString(node.name, orgKey, db),
|
|
2799
2860
|
metadata: pseudonymize(node.metadata ?? {}, orgKey, db),
|
|
2800
2861
|
tags: (node.tags ?? []).map((t) => pseudonymizeString(t, orgKey, db))
|
|
@@ -5052,6 +5113,37 @@ error: ${err instanceof Error ? err.message : String(err)}
|
|
|
5052
5113
|
process.exitCode = 1;
|
|
5053
5114
|
}
|
|
5054
5115
|
});
|
|
5116
|
+
program.command("operator").description("Run the Kubernetes operator: continuous in-cluster discovery + drift reporting (5.2)").option("--config <file>", "JSON config file (drift sinks, db path)").option("--interval <sec>", "Reconcile interval in seconds", "300").option("--once", "Run a single reconcile and exit (CronJob-driver friendly)", false).option("--db <path>", "DB path (overrides config)").action(async (opts) => {
|
|
5117
|
+
let db;
|
|
5118
|
+
try {
|
|
5119
|
+
const config = opts.config ? loadConfig(opts.config) : defaultConfig(opts.db ? { dbPath: opts.db } : {});
|
|
5120
|
+
const interval = parseInt(opts.interval, 10);
|
|
5121
|
+
if (Number.isNaN(interval) || interval < 1) {
|
|
5122
|
+
process.stderr.write(`
|
|
5123
|
+
error: --interval must be a positive number of seconds (got '${opts.interval}')
|
|
5124
|
+
`);
|
|
5125
|
+
process.exitCode = 1;
|
|
5126
|
+
return;
|
|
5127
|
+
}
|
|
5128
|
+
db = new CartographyDB(opts.db ?? config.dbPath);
|
|
5129
|
+
const controller = new AbortController();
|
|
5130
|
+
process.once("SIGINT", () => controller.abort());
|
|
5131
|
+
process.once("SIGTERM", () => controller.abort());
|
|
5132
|
+
process.stderr.write(`Cartograph operator: ${isInCluster() ? "in-cluster" : "out-of-cluster (dev)"}
|
|
5133
|
+
`);
|
|
5134
|
+
await runOperator(db, config, { intervalMs: interval * 1e3, once: opts.once === true, signal: controller.signal });
|
|
5135
|
+
} catch (err) {
|
|
5136
|
+
if (err instanceof ConfigError) process.stderr.write(`
|
|
5137
|
+
config error: ${err.message}
|
|
5138
|
+
`);
|
|
5139
|
+
else process.stderr.write(`
|
|
5140
|
+
error: ${err instanceof Error ? err.message : String(err)}
|
|
5141
|
+
`);
|
|
5142
|
+
process.exitCode = 1;
|
|
5143
|
+
} finally {
|
|
5144
|
+
db?.close();
|
|
5145
|
+
}
|
|
5146
|
+
});
|
|
5055
5147
|
const authCmd = program.command("auth").description("Manage RBAC credentials for the HTTP surfaces (MCP transport + API server, 4.5)");
|
|
5056
5148
|
authCmd.command("add <subject>").description("Create a credential and print its bearer token ONCE (only the hash is stored)").option("--role <role>", "viewer | operator | admin", "viewer").option("--tenant <id>", "Tenant the credential is scoped to (default: local)").option("--token <secret>", "Use this token instead of generating one").option("--db <path>", "DB path").action((subject, opts) => {
|
|
5057
5149
|
const role = RoleSchema.safeParse(opts.role);
|