@datasynx/agentic-ai-cartography 2.9.0 → 2.10.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/{chunk-LRUWWHMQ.js → chunk-ASCA3UFM.js} +148 -2
- package/dist/chunk-ASCA3UFM.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/index.cjs +153 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +80 -1
- package/dist/index.d.ts +80 -1
- package/dist/index.js +150 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp-bin.js +1 -1
- package/llms-full.txt +1 -0
- package/package.json +1 -1
- package/server.json +2 -2
- package/dist/chunk-LRUWWHMQ.js.map +0 -1
|
@@ -1537,9 +1537,146 @@ async function executeNlQuery(db, sessionId, search, intent, opts = {}) {
|
|
|
1537
1537
|
return { intent, anchors, nodes, paths: trav.edges };
|
|
1538
1538
|
}
|
|
1539
1539
|
|
|
1540
|
+
// src/correlation/signals.ts
|
|
1541
|
+
var IPV4 = /\b(?:(?:25[0-5]|2[0-4]\d|1?\d?\d)\.){3}(?:25[0-5]|2[0-4]\d|1?\d?\d)\b/g;
|
|
1542
|
+
var DNS = /\b(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.){1,8}[a-z]{2,24}\b/gi;
|
|
1543
|
+
var ENDPOINT = /\b((?:[a-z0-9][a-z0-9.-]{0,253})):(\d{1,5})\b/gi;
|
|
1544
|
+
function isPrivateIp(ip) {
|
|
1545
|
+
return /^(?:10\.|127\.|169\.254\.|192\.168\.|172\.(?:1[6-9]|2\d|3[01])\.)/.test(ip);
|
|
1546
|
+
}
|
|
1547
|
+
function sources(node) {
|
|
1548
|
+
const out = [node.id, node.name];
|
|
1549
|
+
const meta = node.metadata;
|
|
1550
|
+
if (meta) {
|
|
1551
|
+
for (const k of ["host", "hostname", "ip", "address", "dns", "dnsName", "endpoint", "fqdn", "publicIp", "privateIp", "url"]) {
|
|
1552
|
+
const v = meta[k];
|
|
1553
|
+
if (typeof v === "string") out.push(v);
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
return out;
|
|
1557
|
+
}
|
|
1558
|
+
var KNOWN_PROVIDERS = /* @__PURE__ */ new Set(["aws", "gcp", "azure", "k8s", "kubernetes", "localhost", "docker"]);
|
|
1559
|
+
function parseProvider(id) {
|
|
1560
|
+
const seg = id.split(":");
|
|
1561
|
+
if (seg.length >= 3 && KNOWN_PROVIDERS.has(seg[1])) return seg[1];
|
|
1562
|
+
return void 0;
|
|
1563
|
+
}
|
|
1564
|
+
function extractSignals(node) {
|
|
1565
|
+
const text = sources(node).join(" \n ");
|
|
1566
|
+
const publicIps = /* @__PURE__ */ new Set();
|
|
1567
|
+
const privateIps = /* @__PURE__ */ new Set();
|
|
1568
|
+
const dnsNames = /* @__PURE__ */ new Set();
|
|
1569
|
+
const endpoints = /* @__PURE__ */ new Set();
|
|
1570
|
+
for (const m of text.matchAll(IPV4)) (isPrivateIp(m[0]) ? privateIps : publicIps).add(m[0]);
|
|
1571
|
+
for (const m of text.matchAll(DNS)) dnsNames.add(m[0].toLowerCase());
|
|
1572
|
+
for (const m of text.matchAll(ENDPOINT)) endpoints.add(`${m[1].toLowerCase()}:${m[2]}`);
|
|
1573
|
+
const provider = parseProvider(node.id);
|
|
1574
|
+
const sortUniq = (s) => [...s].sort();
|
|
1575
|
+
return {
|
|
1576
|
+
...provider ? { provider } : {},
|
|
1577
|
+
endpoints: sortUniq(endpoints),
|
|
1578
|
+
publicIps: sortUniq(publicIps),
|
|
1579
|
+
privateIps: sortUniq(privateIps),
|
|
1580
|
+
// The DNS regex requires an alphabetic TLD, so pure IPv4s never land here.
|
|
1581
|
+
dnsNames: sortUniq(dnsNames)
|
|
1582
|
+
};
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
// src/correlation/correlate.ts
|
|
1586
|
+
var CORRELATION_CONFIDENCE = {
|
|
1587
|
+
"global-identity": 1,
|
|
1588
|
+
"dns-name": 0.95,
|
|
1589
|
+
"public-ip": 0.9,
|
|
1590
|
+
"endpoint": 0.85,
|
|
1591
|
+
"private-ip": 0.5
|
|
1592
|
+
};
|
|
1593
|
+
var MERGE_THRESHOLD = 0.85;
|
|
1594
|
+
var DSU = class {
|
|
1595
|
+
parent;
|
|
1596
|
+
constructor(n) {
|
|
1597
|
+
this.parent = Array.from({ length: n }, (_, i) => i);
|
|
1598
|
+
}
|
|
1599
|
+
find(x) {
|
|
1600
|
+
while (this.parent[x] !== x) {
|
|
1601
|
+
this.parent[x] = this.parent[this.parent[x]];
|
|
1602
|
+
x = this.parent[x];
|
|
1603
|
+
}
|
|
1604
|
+
return x;
|
|
1605
|
+
}
|
|
1606
|
+
union(a, b) {
|
|
1607
|
+
const ra = this.find(a), rb = this.find(b);
|
|
1608
|
+
if (ra !== rb) this.parent[Math.max(ra, rb)] = Math.min(ra, rb);
|
|
1609
|
+
}
|
|
1610
|
+
};
|
|
1611
|
+
function correlateTopology(nodes, _edges = []) {
|
|
1612
|
+
const n = nodes.length;
|
|
1613
|
+
const signals = nodes.map(extractSignals);
|
|
1614
|
+
const dsu = new DSU(n);
|
|
1615
|
+
const correlations = [];
|
|
1616
|
+
const merged = /* @__PURE__ */ new Set();
|
|
1617
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
1618
|
+
const add = (sig, val, i) => {
|
|
1619
|
+
const k = `${sig}|${val}`;
|
|
1620
|
+
const arr = buckets.get(k);
|
|
1621
|
+
if (arr) arr.push(i);
|
|
1622
|
+
else buckets.set(k, [i]);
|
|
1623
|
+
};
|
|
1624
|
+
nodes.forEach((node, i) => {
|
|
1625
|
+
if (node.globalId) add("global-identity", node.globalId, i);
|
|
1626
|
+
for (const d of signals[i].dnsNames) add("dns-name", d, i);
|
|
1627
|
+
for (const ip of signals[i].publicIps) add("public-ip", ip, i);
|
|
1628
|
+
for (const e of signals[i].endpoints) add("endpoint", e, i);
|
|
1629
|
+
});
|
|
1630
|
+
const order = ["global-identity", "dns-name", "public-ip", "endpoint"];
|
|
1631
|
+
for (const sig of order) {
|
|
1632
|
+
const conf = CORRELATION_CONFIDENCE[sig];
|
|
1633
|
+
const keys = [...buckets.keys()].filter((k) => k.startsWith(`${sig}|`)).sort();
|
|
1634
|
+
for (const key of keys) {
|
|
1635
|
+
const members = buckets.get(key).slice().sort((x, y) => nodes[x].id.localeCompare(nodes[y].id));
|
|
1636
|
+
const value = key.slice(sig.length + 1);
|
|
1637
|
+
for (let j = 1; j < members.length; j++) {
|
|
1638
|
+
const a = members[0], b = members[j];
|
|
1639
|
+
if (conf < MERGE_THRESHOLD) continue;
|
|
1640
|
+
dsu.union(a, b);
|
|
1641
|
+
merged.add(a);
|
|
1642
|
+
merged.add(b);
|
|
1643
|
+
const [s, t] = [nodes[a].id, nodes[b].id].sort();
|
|
1644
|
+
correlations.push({ sourceId: s, targetId: t, relationship: "same_as", signal: sig, confidence: conf, evidence: `[${sig}] shared ${value}` });
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
const clusters = /* @__PURE__ */ new Map();
|
|
1649
|
+
for (let i = 0; i < n; i++) {
|
|
1650
|
+
const r = dsu.find(i);
|
|
1651
|
+
const arr = clusters.get(r);
|
|
1652
|
+
if (arr) arr.push(i);
|
|
1653
|
+
else clusters.set(r, [i]);
|
|
1654
|
+
}
|
|
1655
|
+
const canonical = [];
|
|
1656
|
+
let crossCloud = 0;
|
|
1657
|
+
for (const idxs of clusters.values()) {
|
|
1658
|
+
const memberNodes = idxs.map((i) => nodes[i]);
|
|
1659
|
+
const members = memberNodes.map((m) => m.id).sort();
|
|
1660
|
+
const providers = [...new Set(idxs.map((i) => signals[i].provider).filter((p) => !!p))].sort();
|
|
1661
|
+
const rep = memberNodes.reduce((a, b) => a.id < b.id ? a : b);
|
|
1662
|
+
const memberIds = new Set(members);
|
|
1663
|
+
const internal = correlations.filter((c) => memberIds.has(c.sourceId) && memberIds.has(c.targetId));
|
|
1664
|
+
const confidence = internal.length ? Math.min(...internal.map((c) => c.confidence)) : 1;
|
|
1665
|
+
if (providers.length > 1) crossCloud += 1;
|
|
1666
|
+
canonical.push({ id: rep.id, type: rep.type, name: rep.name, members, providers, confidence });
|
|
1667
|
+
}
|
|
1668
|
+
canonical.sort((a, b) => a.id.localeCompare(b.id));
|
|
1669
|
+
correlations.sort((a, b) => b.confidence - a.confidence || a.sourceId.localeCompare(b.sourceId) || a.targetId.localeCompare(b.targetId));
|
|
1670
|
+
return {
|
|
1671
|
+
canonical,
|
|
1672
|
+
correlations,
|
|
1673
|
+
summary: { rawNodes: n, canonicalNodes: canonical.length, collapsed: n - canonical.length, crossCloud }
|
|
1674
|
+
};
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1540
1677
|
// src/mcp/server.ts
|
|
1541
1678
|
var SERVER_NAME = "cartography";
|
|
1542
|
-
var SERVER_VERSION = "2.
|
|
1679
|
+
var SERVER_VERSION = "2.10.0";
|
|
1543
1680
|
var SERVICE_TYPES = NODE_TYPE_GROUPS.web;
|
|
1544
1681
|
var DATA_TYPES = NODE_TYPE_GROUPS.data;
|
|
1545
1682
|
var lexicalSearch = async (db, sessionId, query, opts) => db.searchNodes(sessionId, query, { types: opts.types, limit: opts.limit }).map((node) => ({ node }));
|
|
@@ -1699,6 +1836,15 @@ function createMcpServer(opts = {}) {
|
|
|
1699
1836
|
return json(db.getGraphSummary(sid));
|
|
1700
1837
|
}
|
|
1701
1838
|
);
|
|
1839
|
+
server.registerTool(
|
|
1840
|
+
"correlate_topology",
|
|
1841
|
+
{ title: "Correlate multi-cloud topology", description: "Collapse the same logical resource discovered across clouds/on-prem (by global identity or a shared DNS name / public IP / endpoint) into canonical entities with confidence-scored same_as links. Read-only, non-destructive (5.1).", inputSchema: {}, annotations: readOnly },
|
|
1842
|
+
() => {
|
|
1843
|
+
const sid = resolveSession();
|
|
1844
|
+
if (!sid) return json({ error: "No discovery session found." });
|
|
1845
|
+
return json(correlateTopology(db.getNodes(sid), db.getEdges(sid)));
|
|
1846
|
+
}
|
|
1847
|
+
);
|
|
1702
1848
|
server.registerTool(
|
|
1703
1849
|
"get_cost_summary",
|
|
1704
1850
|
{ title: "Get cost summary", description: "FinOps rollup: cost by domain and owner, currency/period-bucketed (3.3).", inputSchema: {}, annotations: readOnly },
|
|
@@ -2882,4 +3028,4 @@ export {
|
|
|
2882
3028
|
parseMcpArgs,
|
|
2883
3029
|
startMcp
|
|
2884
3030
|
};
|
|
2885
|
-
//# sourceMappingURL=chunk-
|
|
3031
|
+
//# sourceMappingURL=chunk-ASCA3UFM.js.map
|