@datasynx/agentic-ai-cartography 2.5.0 → 2.7.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/api-bin.js +2 -2
- package/dist/{chunk-RYQ4KQCK.js → chunk-HLWNO3RF.js} +70 -6
- package/dist/chunk-HLWNO3RF.js.map +1 -0
- package/dist/{chunk-NQXZUWOI.js → chunk-TBPGFEMQ.js} +91 -3
- package/dist/chunk-TBPGFEMQ.js.map +1 -0
- package/dist/{chunk-GA4427LB.js → chunk-YVV6NIT2.js} +11 -1
- package/dist/chunk-YVV6NIT2.js.map +1 -0
- package/dist/cli.js +28 -29
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +171 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +100 -3
- package/dist/index.d.ts +100 -3
- package/dist/index.js +166 -37
- package/dist/index.js.map +1 -1
- package/dist/mcp-bin.js +2 -2
- package/package.json +1 -1
- package/server.json +2 -2
- package/dist/chunk-GA4427LB.js.map +0 -1
- package/dist/chunk-NQXZUWOI.js.map +0 -1
- package/dist/chunk-RYQ4KQCK.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -45,6 +45,7 @@ __export(src_exports, {
|
|
|
45
45
|
CredentialConfigSchema: () => CredentialConfigSchema,
|
|
46
46
|
CsvCostSource: () => CsvCostSource,
|
|
47
47
|
DEFAULT_ANOMALY_THRESHOLDS: () => DEFAULT_ANOMALY_THRESHOLDS,
|
|
48
|
+
DEFAULT_INGEST_QUOTA: () => DEFAULT_INGEST_QUOTA,
|
|
48
49
|
DEFAULT_SERVER_NAME: () => DEFAULT_SERVER_NAME,
|
|
49
50
|
DEFAULT_TENANT: () => DEFAULT_TENANT,
|
|
50
51
|
DriftConfigSchema: () => DriftConfigSchema,
|
|
@@ -66,10 +67,12 @@ __export(src_exports, {
|
|
|
66
67
|
ProviderRegistry: () => ProviderRegistry,
|
|
67
68
|
RELATION_TO_DIRECTION: () => RELATION_TO_DIRECTION,
|
|
68
69
|
ROLES: () => ROLES,
|
|
70
|
+
RateLimiter: () => RateLimiter,
|
|
69
71
|
RoleSchema: () => RoleSchema,
|
|
70
72
|
RuleCheckSchema: () => RuleCheckSchema,
|
|
71
73
|
RulesetSchema: () => RulesetSchema,
|
|
72
74
|
SCAN_ARG_PATTERNS: () => SCAN_ARG_PATTERNS,
|
|
75
|
+
SCHEMA_VERSION: () => SCHEMA_VERSION,
|
|
73
76
|
SDL: () => SDL,
|
|
74
77
|
SEVERITY_WEIGHT: () => SEVERITY_WEIGHT,
|
|
75
78
|
SHARING_LEVELS: () => SHARING_LEVELS,
|
|
@@ -149,6 +152,7 @@ __export(src_exports, {
|
|
|
149
152
|
diffTopology: () => diffTopology,
|
|
150
153
|
edgesToConnections: () => edgesToConnections,
|
|
151
154
|
enrichCosts: () => enrichCosts,
|
|
155
|
+
entitiesToYaml: () => entitiesToYaml,
|
|
152
156
|
evaluateCheck: () => evaluateCheck,
|
|
153
157
|
evaluateRule: () => evaluateRule,
|
|
154
158
|
evidenceLine: () => evidenceLine,
|
|
@@ -278,6 +282,7 @@ __export(src_exports, {
|
|
|
278
282
|
startApi: () => startApi,
|
|
279
283
|
stripSensitive: () => stripSensitive,
|
|
280
284
|
timingSafeEqual: () => timingSafeEqual,
|
|
285
|
+
toBackstageEntities: () => toBackstageEntities,
|
|
281
286
|
validateScanner: () => validateScanner,
|
|
282
287
|
vscodeDeeplink: () => vscodeDeeplink,
|
|
283
288
|
zodToJsonSchema: () => zodToJsonSchema
|
|
@@ -2848,6 +2853,7 @@ function newAnomalies(base, current) {
|
|
|
2848
2853
|
|
|
2849
2854
|
// src/db.ts
|
|
2850
2855
|
var DEFAULT_TENANT = "local";
|
|
2856
|
+
var SCHEMA_VERSION = 15;
|
|
2851
2857
|
function normalizeTenant(raw) {
|
|
2852
2858
|
if (raw == null) return DEFAULT_TENANT;
|
|
2853
2859
|
const cleaned = sanitizeUntrusted(String(raw)).trim().slice(0, 128);
|
|
@@ -4252,6 +4258,14 @@ var CartographyDB = class {
|
|
|
4252
4258
|
}
|
|
4253
4259
|
return rows.length;
|
|
4254
4260
|
}
|
|
4261
|
+
/**
|
|
4262
|
+
* Retention/compaction (4.7): delete audit events older than `olderThan` (ISO 8601).
|
|
4263
|
+
* The audit trail grows unbounded on a busy collector; this bounds it without touching
|
|
4264
|
+
* sessions/nodes/edges. Returns the number of events removed.
|
|
4265
|
+
*/
|
|
4266
|
+
pruneEventsOlderThan(olderThan) {
|
|
4267
|
+
return this.db.prepare("DELETE FROM activity_events WHERE timestamp < ?").run(olderThan).changes;
|
|
4268
|
+
}
|
|
4255
4269
|
// ── Graph queries (read-only context layer) ─────────────────────────────────
|
|
4256
4270
|
/** Fetch a single node by id within a session. */
|
|
4257
4271
|
getNode(sessionId, nodeId) {
|
|
@@ -4690,6 +4704,9 @@ var SqliteQueryBackend = class {
|
|
|
4690
4704
|
node(ctx, id, sessionId) {
|
|
4691
4705
|
return this.db.getNode(this.resolveSession(ctx, sessionId), id);
|
|
4692
4706
|
}
|
|
4707
|
+
edges(ctx, sessionId) {
|
|
4708
|
+
return this.db.getEdges(this.resolveSession(ctx, sessionId));
|
|
4709
|
+
}
|
|
4693
4710
|
dependencies(ctx, id, q, sessionId) {
|
|
4694
4711
|
const sid = this.resolveSession(ctx, sessionId);
|
|
4695
4712
|
return this.db.getDependencies(sid, id, {
|
|
@@ -4940,7 +4957,7 @@ var ContributorSchema = import_zod5.z.object({
|
|
|
4940
4957
|
});
|
|
4941
4958
|
var IngestEnvelopeSchema = import_zod5.z.object({
|
|
4942
4959
|
schemaVersion: import_zod5.z.literal(INGEST_SCHEMA_VERSION),
|
|
4943
|
-
org: import_zod5.z.string().min(1).optional(),
|
|
4960
|
+
org: import_zod5.z.string().min(1).max(128).optional(),
|
|
4944
4961
|
items: import_zod5.z.array(import_zod5.z.object({
|
|
4945
4962
|
contentHash: import_zod5.z.string(),
|
|
4946
4963
|
kind: import_zod5.z.enum(["node", "edge"]),
|
|
@@ -5028,6 +5045,7 @@ function ingestEnvelope(store, envelope, opts = {}) {
|
|
|
5028
5045
|
|
|
5029
5046
|
// src/central/server.ts
|
|
5030
5047
|
function createIngestHandler(store, opts = {}) {
|
|
5048
|
+
const quota = opts.quota;
|
|
5031
5049
|
return (body) => {
|
|
5032
5050
|
const parsed = IngestEnvelopeSchema.safeParse(body);
|
|
5033
5051
|
if (!parsed.success) {
|
|
@@ -5035,6 +5053,14 @@ function createIngestHandler(store, opts = {}) {
|
|
|
5035
5053
|
logWarn("ingest: rejected invalid envelope", { issues });
|
|
5036
5054
|
return { status: 400, body: { error: "invalid envelope", issues } };
|
|
5037
5055
|
}
|
|
5056
|
+
if (quota) {
|
|
5057
|
+
const org = normalizeTenant(parsed.data.org ?? opts.defaultOrg);
|
|
5058
|
+
const decision = quota.take(org);
|
|
5059
|
+
if (!decision.allowed) {
|
|
5060
|
+
logWarn("ingest: rate limited", { org, retryAfterSec: decision.retryAfterSec });
|
|
5061
|
+
return { status: 429, body: { error: "too many requests" }, headers: { "retry-after": String(decision.retryAfterSec) } };
|
|
5062
|
+
}
|
|
5063
|
+
}
|
|
5038
5064
|
try {
|
|
5039
5065
|
const result = ingestEnvelope(store, parsed.data, opts);
|
|
5040
5066
|
return { status: 200, body: result };
|
|
@@ -5045,6 +5071,39 @@ function createIngestHandler(store, opts = {}) {
|
|
|
5045
5071
|
};
|
|
5046
5072
|
}
|
|
5047
5073
|
|
|
5074
|
+
// src/central/quota.ts
|
|
5075
|
+
var DEFAULT_INGEST_QUOTA = { capacity: 120, refillMs: 6e4 };
|
|
5076
|
+
var MAX_KEYS = 1e4;
|
|
5077
|
+
var RateLimiter = class {
|
|
5078
|
+
constructor(cfg = DEFAULT_INGEST_QUOTA, now = () => Date.now()) {
|
|
5079
|
+
this.cfg = cfg;
|
|
5080
|
+
this.now = now;
|
|
5081
|
+
}
|
|
5082
|
+
buckets = /* @__PURE__ */ new Map();
|
|
5083
|
+
/** Consume one token for `key`. Returns whether the request is allowed (+ Retry-After when not). */
|
|
5084
|
+
take(key) {
|
|
5085
|
+
const t = this.now();
|
|
5086
|
+
const ratePerMs = this.cfg.capacity / this.cfg.refillMs;
|
|
5087
|
+
let b = this.buckets.get(key);
|
|
5088
|
+
if (!b) {
|
|
5089
|
+
if (this.buckets.size >= MAX_KEYS) {
|
|
5090
|
+
const oldest = this.buckets.keys().next().value;
|
|
5091
|
+
if (oldest !== void 0) this.buckets.delete(oldest);
|
|
5092
|
+
}
|
|
5093
|
+
b = { tokens: this.cfg.capacity, last: t };
|
|
5094
|
+
this.buckets.set(key, b);
|
|
5095
|
+
}
|
|
5096
|
+
b.tokens = Math.min(this.cfg.capacity, b.tokens + Math.max(0, t - b.last) * ratePerMs);
|
|
5097
|
+
b.last = t;
|
|
5098
|
+
if (b.tokens >= 1) {
|
|
5099
|
+
b.tokens -= 1;
|
|
5100
|
+
return { allowed: true, retryAfterSec: 0 };
|
|
5101
|
+
}
|
|
5102
|
+
const waitMs = (1 - b.tokens) / ratePerMs;
|
|
5103
|
+
return { allowed: false, retryAfterSec: Math.max(1, Math.ceil(waitMs / 1e3)) };
|
|
5104
|
+
}
|
|
5105
|
+
};
|
|
5106
|
+
|
|
5048
5107
|
// src/scanners/bookmarks.ts
|
|
5049
5108
|
var PERSONAL = [
|
|
5050
5109
|
"facebook.",
|
|
@@ -5932,7 +5991,7 @@ async function resolveNlQuery(db, sessionId, search, raw, opts) {
|
|
|
5932
5991
|
|
|
5933
5992
|
// src/mcp/server.ts
|
|
5934
5993
|
var SERVER_NAME = "cartography";
|
|
5935
|
-
var SERVER_VERSION = "2.
|
|
5994
|
+
var SERVER_VERSION = "2.7.0";
|
|
5936
5995
|
var SERVICE_TYPES = NODE_TYPE_GROUPS.web;
|
|
5937
5996
|
var DATA_TYPES = NODE_TYPE_GROUPS.data;
|
|
5938
5997
|
var lexicalSearch = async (db, sessionId, query, opts) => db.searchNodes(sessionId, query, { types: opts.types, limit: opts.limit }).map((node) => ({ node }));
|
|
@@ -6575,6 +6634,16 @@ async function runHttp(factory, opts = {}) {
|
|
|
6575
6634
|
const httpServer = import_node_http.default.createServer(async (req, res) => {
|
|
6576
6635
|
try {
|
|
6577
6636
|
const url = req.url ?? "";
|
|
6637
|
+
const probePath = new URL(url || "/", "http://probe").pathname;
|
|
6638
|
+
if (probePath === "/healthz") {
|
|
6639
|
+
res.writeHead(200, { "content-type": "application/json" }).end('{"status":"ok"}');
|
|
6640
|
+
return;
|
|
6641
|
+
}
|
|
6642
|
+
if (probePath === "/readyz") {
|
|
6643
|
+
const r = opts.readiness ? opts.readiness() : { ready: true };
|
|
6644
|
+
res.writeHead(r.ready ? 200 : 503, { "content-type": "application/json" }).end(JSON.stringify({ status: r.ready ? "ready" : "unready" }));
|
|
6645
|
+
return;
|
|
6646
|
+
}
|
|
6578
6647
|
const isIngest = url.startsWith("/ingest") && opts.onIngest !== void 0;
|
|
6579
6648
|
if (!url.startsWith("/mcp") && !isIngest) {
|
|
6580
6649
|
res.writeHead(404, { "content-type": "application/json" }).end('{"error":"not found"}');
|
|
@@ -6602,7 +6671,7 @@ async function runHttp(factory, opts = {}) {
|
|
|
6602
6671
|
return;
|
|
6603
6672
|
}
|
|
6604
6673
|
const out = onIngest(value);
|
|
6605
|
-
res.writeHead(out.status, { "content-type": "application/json" }).end(JSON.stringify(out.body));
|
|
6674
|
+
res.writeHead(out.status, { "content-type": "application/json", ...out.headers ?? {} }).end(JSON.stringify(out.body));
|
|
6606
6675
|
return;
|
|
6607
6676
|
}
|
|
6608
6677
|
const sessionId = req.headers["mcp-session-id"];
|
|
@@ -7465,6 +7534,54 @@ function headerValue(req, name) {
|
|
|
7465
7534
|
return v;
|
|
7466
7535
|
}
|
|
7467
7536
|
|
|
7537
|
+
// src/backstage.ts
|
|
7538
|
+
var COMPONENT_TYPES = ["web_service", "container", "pod"];
|
|
7539
|
+
function sanitize(id) {
|
|
7540
|
+
return id.replace(/[^a-zA-Z0-9_]/g, "_");
|
|
7541
|
+
}
|
|
7542
|
+
function toBackstageEntities(nodes, edges, opts = {}) {
|
|
7543
|
+
const owner = opts.org ?? "unknown";
|
|
7544
|
+
return nodes.map((node) => {
|
|
7545
|
+
const kind = COMPONENT_TYPES.includes(node.type) ? "Component" : node.type === "api_endpoint" ? "API" : "Resource";
|
|
7546
|
+
const dependsOn = edges.filter((e) => e.sourceId === node.id).map((e) => `resource:default/${sanitize(e.targetId)}`);
|
|
7547
|
+
return {
|
|
7548
|
+
apiVersion: "backstage.io/v1alpha1",
|
|
7549
|
+
kind,
|
|
7550
|
+
metadata: {
|
|
7551
|
+
name: sanitize(node.id),
|
|
7552
|
+
annotations: {
|
|
7553
|
+
"cartography/discovered-at": node.discoveredAt,
|
|
7554
|
+
"cartography/confidence": String(node.confidence)
|
|
7555
|
+
}
|
|
7556
|
+
},
|
|
7557
|
+
spec: {
|
|
7558
|
+
type: node.type,
|
|
7559
|
+
lifecycle: "production",
|
|
7560
|
+
owner: node.owner ?? owner,
|
|
7561
|
+
...dependsOn.length > 0 ? { dependsOn } : {}
|
|
7562
|
+
}
|
|
7563
|
+
};
|
|
7564
|
+
});
|
|
7565
|
+
}
|
|
7566
|
+
function entitiesToYaml(entities) {
|
|
7567
|
+
return entities.map((e) => {
|
|
7568
|
+
const lines = [
|
|
7569
|
+
`apiVersion: ${e.apiVersion}`,
|
|
7570
|
+
`kind: ${e.kind}`,
|
|
7571
|
+
`metadata:`,
|
|
7572
|
+
` name: ${e.metadata.name}`,
|
|
7573
|
+
` annotations:`,
|
|
7574
|
+
...Object.entries(e.metadata.annotations).map(([k, v]) => ` ${k}: "${v}"`),
|
|
7575
|
+
`spec:`,
|
|
7576
|
+
` type: ${e.spec.type}`,
|
|
7577
|
+
` lifecycle: ${e.spec.lifecycle}`,
|
|
7578
|
+
` owner: ${e.spec.owner}`,
|
|
7579
|
+
...e.spec.dependsOn && e.spec.dependsOn.length > 0 ? [" dependsOn:", ...e.spec.dependsOn.map((d) => ` - ${d}`)] : []
|
|
7580
|
+
];
|
|
7581
|
+
return lines.join("\n");
|
|
7582
|
+
}).join("\n---\n");
|
|
7583
|
+
}
|
|
7584
|
+
|
|
7468
7585
|
// src/api/schemas.ts
|
|
7469
7586
|
var import_zod8 = require("zod");
|
|
7470
7587
|
var DIRECTIONS = ["downstream", "upstream", "both"];
|
|
@@ -7600,6 +7717,21 @@ var ErrorResponse = import_zod8.z.object({
|
|
|
7600
7717
|
error: import_zod8.z.string(),
|
|
7601
7718
|
code: import_zod8.z.string().optional()
|
|
7602
7719
|
});
|
|
7720
|
+
var BackstageEntitySchema = import_zod8.z.object({
|
|
7721
|
+
apiVersion: import_zod8.z.literal("backstage.io/v1alpha1"),
|
|
7722
|
+
kind: import_zod8.z.enum(["Component", "API", "Resource"]),
|
|
7723
|
+
metadata: import_zod8.z.object({
|
|
7724
|
+
name: import_zod8.z.string(),
|
|
7725
|
+
annotations: import_zod8.z.record(import_zod8.z.string(), import_zod8.z.string())
|
|
7726
|
+
}),
|
|
7727
|
+
spec: import_zod8.z.object({
|
|
7728
|
+
type: import_zod8.z.string(),
|
|
7729
|
+
lifecycle: import_zod8.z.string(),
|
|
7730
|
+
owner: import_zod8.z.string(),
|
|
7731
|
+
dependsOn: import_zod8.z.array(import_zod8.z.string()).optional()
|
|
7732
|
+
})
|
|
7733
|
+
});
|
|
7734
|
+
var BackstageCatalogResponse = import_zod8.z.object({ entities: import_zod8.z.array(BackstageEntitySchema) });
|
|
7603
7735
|
var API_SCHEMAS = {
|
|
7604
7736
|
Node: NodeSchema2,
|
|
7605
7737
|
Edge: EdgeSchema2,
|
|
@@ -7611,10 +7743,13 @@ var API_SCHEMAS = {
|
|
|
7611
7743
|
Session: SessionSchema,
|
|
7612
7744
|
Sessions: SessionsResponse,
|
|
7613
7745
|
Health: HealthResponse,
|
|
7614
|
-
Error: ErrorResponse
|
|
7746
|
+
Error: ErrorResponse,
|
|
7747
|
+
BackstageEntity: BackstageEntitySchema,
|
|
7748
|
+
BackstageCatalog: BackstageCatalogResponse
|
|
7615
7749
|
};
|
|
7616
7750
|
|
|
7617
7751
|
// src/api/rest.ts
|
|
7752
|
+
var BACKSTAGE_NODE_CAP = 1e3;
|
|
7618
7753
|
function toApiNode(n) {
|
|
7619
7754
|
const out = { id: n.id, type: n.type, name: n.name, confidence: n.confidence, tags: n.tags };
|
|
7620
7755
|
if (n.domain !== void 0) out["domain"] = n.domain;
|
|
@@ -7749,6 +7884,14 @@ function handleHealth(ctx, d) {
|
|
|
7749
7884
|
const h = d.backend.health(ctx);
|
|
7750
7885
|
return ok(validateOut(HealthResponse, { status: "ok", version: d.version, store: h.store, sessions: h.sessions }));
|
|
7751
7886
|
}
|
|
7887
|
+
function handleBackstageCatalog(ctx, d) {
|
|
7888
|
+
return guard(() => {
|
|
7889
|
+
const page = d.backend.nodes(ctx, { limit: BACKSTAGE_NODE_CAP });
|
|
7890
|
+
const edges = d.backend.edges(ctx);
|
|
7891
|
+
const entities = toBackstageEntities(page.nodes, edges, { org: ctx.tenant });
|
|
7892
|
+
return ok(validateOut(BackstageCatalogResponse, { entities }));
|
|
7893
|
+
});
|
|
7894
|
+
}
|
|
7752
7895
|
|
|
7753
7896
|
// src/api/openapi.ts
|
|
7754
7897
|
function defOf(schema) {
|
|
@@ -7905,6 +8048,13 @@ function buildOpenApiDocument(opts) {
|
|
|
7905
8048
|
parameters: [TENANT_PARAM],
|
|
7906
8049
|
responses: { "200": ok2("Sessions", "Sessions"), ...errorResponses() }
|
|
7907
8050
|
}
|
|
8051
|
+
},
|
|
8052
|
+
"/v1/backstage/catalog": {
|
|
8053
|
+
get: {
|
|
8054
|
+
summary: "The tenant topology as Backstage catalog entities (live data source, 4.6)",
|
|
8055
|
+
parameters: [SESSION_PARAM, TENANT_PARAM],
|
|
8056
|
+
responses: { "200": ok2("BackstageCatalog", "Backstage catalog entities"), ...errorResponses() }
|
|
8057
|
+
}
|
|
7908
8058
|
}
|
|
7909
8059
|
}
|
|
7910
8060
|
};
|
|
@@ -8419,6 +8569,8 @@ function dispatchRest(ctx, path, url, deps) {
|
|
|
8419
8569
|
return handleDiff(ctx, url, deps);
|
|
8420
8570
|
case "/v1/sessions":
|
|
8421
8571
|
return handleSessions(ctx, deps);
|
|
8572
|
+
case "/v1/backstage/catalog":
|
|
8573
|
+
return handleBackstageCatalog(ctx, deps);
|
|
8422
8574
|
default: {
|
|
8423
8575
|
const m = DEPENDENCIES_RE.exec(path);
|
|
8424
8576
|
if (m) return handleDependencies(ctx, decodeURIComponent(m[1]), url, deps);
|
|
@@ -9925,7 +10077,7 @@ var MERMAID_CLASSES = {
|
|
|
9925
10077
|
saas_tool: "fill:#2a1a2a,stroke:#9a3a9a,color:#daf",
|
|
9926
10078
|
unknown: "fill:#2a2a2a,stroke:#5a5a5a,color:#aaa"
|
|
9927
10079
|
};
|
|
9928
|
-
function
|
|
10080
|
+
function sanitize2(id) {
|
|
9929
10081
|
return id.replace(/[^a-zA-Z0-9_]/g, "_");
|
|
9930
10082
|
}
|
|
9931
10083
|
function nodeLabel(node) {
|
|
@@ -9967,14 +10119,14 @@ function generateTopologyMermaid(nodes, edges) {
|
|
|
9967
10119
|
const label = LAYER_LABELS[layerKey] ?? layerKey;
|
|
9968
10120
|
lines.push(` subgraph ${layerKey}["${label}"]`);
|
|
9969
10121
|
for (const node of layerNodes) {
|
|
9970
|
-
lines.push(` ${
|
|
10122
|
+
lines.push(` ${sanitize2(node.id)}${nodeLabel(node)}:::${node.type.replace(/_/g, "")}`);
|
|
9971
10123
|
}
|
|
9972
10124
|
lines.push(" end");
|
|
9973
10125
|
lines.push("");
|
|
9974
10126
|
}
|
|
9975
10127
|
for (const edge of edges) {
|
|
9976
|
-
const src =
|
|
9977
|
-
const tgt =
|
|
10128
|
+
const src = sanitize2(edge.sourceId);
|
|
10129
|
+
const tgt = sanitize2(edge.targetId);
|
|
9978
10130
|
const label = EDGE_LABELS[edge.relationship] ?? edge.relationship;
|
|
9979
10131
|
const arrow = edge.confidence < 0.6 ? `-. "${label}" .->` : `-->|"${label}"|`;
|
|
9980
10132
|
lines.push(` ${src} ${arrow} ${tgt}`);
|
|
@@ -10000,12 +10152,12 @@ function generateDependencyMermaid(nodes, edges) {
|
|
|
10000
10152
|
}
|
|
10001
10153
|
lines.push("");
|
|
10002
10154
|
for (const node of usedNodes) {
|
|
10003
|
-
lines.push(` ${
|
|
10155
|
+
lines.push(` ${sanitize2(node.id)}${nodeLabel(node)}:::${node.type.replace(/_/g, "")}`);
|
|
10004
10156
|
}
|
|
10005
10157
|
lines.push("");
|
|
10006
10158
|
for (const edge of depEdges) {
|
|
10007
10159
|
const label = EDGE_LABELS[edge.relationship] ?? edge.relationship;
|
|
10008
|
-
lines.push(` ${
|
|
10160
|
+
lines.push(` ${sanitize2(edge.sourceId)} -->|"${label}"| ${sanitize2(edge.targetId)}`);
|
|
10009
10161
|
}
|
|
10010
10162
|
return lines.join("\n");
|
|
10011
10163
|
}
|
|
@@ -10056,44 +10208,21 @@ function generateDiffMermaid(diff) {
|
|
|
10056
10208
|
ensureEndpoint(e.targetId);
|
|
10057
10209
|
}
|
|
10058
10210
|
for (const { node, cls, suffix } of entries.values()) {
|
|
10059
|
-
lines.push(` ${
|
|
10211
|
+
lines.push(` ${sanitize2(node.id)}${diffNodeLabel(node, suffix)}:::${cls}`);
|
|
10060
10212
|
}
|
|
10061
10213
|
lines.push("");
|
|
10062
10214
|
for (const e of diff.edges.added) {
|
|
10063
10215
|
const label = EDGE_LABELS[e.relationship] ?? e.relationship;
|
|
10064
|
-
lines.push(` ${
|
|
10216
|
+
lines.push(` ${sanitize2(e.sourceId)} ==>|"+ ${label}"| ${sanitize2(e.targetId)}`);
|
|
10065
10217
|
}
|
|
10066
10218
|
for (const e of diff.edges.removed) {
|
|
10067
10219
|
const label = EDGE_LABELS[e.relationship] ?? e.relationship;
|
|
10068
|
-
lines.push(` ${
|
|
10220
|
+
lines.push(` ${sanitize2(e.sourceId)} -.->|"- ${label}"| ${sanitize2(e.targetId)}`);
|
|
10069
10221
|
}
|
|
10070
10222
|
return lines.join("\n");
|
|
10071
10223
|
}
|
|
10072
10224
|
function exportBackstageYAML(nodes, edges, org) {
|
|
10073
|
-
|
|
10074
|
-
const docs = [];
|
|
10075
|
-
for (const node of nodes) {
|
|
10076
|
-
const isComponent = ["web_service", "container", "pod"].includes(node.type);
|
|
10077
|
-
const isAPI = node.type === "api_endpoint";
|
|
10078
|
-
const kind = isComponent ? "Component" : isAPI ? "API" : "Resource";
|
|
10079
|
-
const deps = edges.filter((e) => e.sourceId === node.id).map((e) => ` - resource:default/${sanitize(e.targetId)}`);
|
|
10080
|
-
const doc = [
|
|
10081
|
-
`apiVersion: backstage.io/v1alpha1`,
|
|
10082
|
-
`kind: ${kind}`,
|
|
10083
|
-
`metadata:`,
|
|
10084
|
-
` name: ${sanitize(node.id)}`,
|
|
10085
|
-
` annotations:`,
|
|
10086
|
-
` cartography/discovered-at: "${node.discoveredAt}"`,
|
|
10087
|
-
` cartography/confidence: "${node.confidence}"`,
|
|
10088
|
-
`spec:`,
|
|
10089
|
-
` type: ${node.type}`,
|
|
10090
|
-
` lifecycle: production`,
|
|
10091
|
-
` owner: ${node.owner ?? owner}`,
|
|
10092
|
-
...deps.length > 0 ? [" dependsOn:", ...deps] : []
|
|
10093
|
-
].join("\n");
|
|
10094
|
-
docs.push(doc);
|
|
10095
|
-
}
|
|
10096
|
-
return docs.join("\n---\n");
|
|
10225
|
+
return entitiesToYaml(toBackstageEntities(nodes, edges, org !== void 0 ? { org } : {}));
|
|
10097
10226
|
}
|
|
10098
10227
|
function exportJSON(db, sessionId) {
|
|
10099
10228
|
const nodes = db.getNodes(sessionId);
|
|
@@ -11794,6 +11923,7 @@ function checkClaudePrerequisites() {
|
|
|
11794
11923
|
CredentialConfigSchema,
|
|
11795
11924
|
CsvCostSource,
|
|
11796
11925
|
DEFAULT_ANOMALY_THRESHOLDS,
|
|
11926
|
+
DEFAULT_INGEST_QUOTA,
|
|
11797
11927
|
DEFAULT_SERVER_NAME,
|
|
11798
11928
|
DEFAULT_TENANT,
|
|
11799
11929
|
DriftConfigSchema,
|
|
@@ -11815,10 +11945,12 @@ function checkClaudePrerequisites() {
|
|
|
11815
11945
|
ProviderRegistry,
|
|
11816
11946
|
RELATION_TO_DIRECTION,
|
|
11817
11947
|
ROLES,
|
|
11948
|
+
RateLimiter,
|
|
11818
11949
|
RoleSchema,
|
|
11819
11950
|
RuleCheckSchema,
|
|
11820
11951
|
RulesetSchema,
|
|
11821
11952
|
SCAN_ARG_PATTERNS,
|
|
11953
|
+
SCHEMA_VERSION,
|
|
11822
11954
|
SDL,
|
|
11823
11955
|
SEVERITY_WEIGHT,
|
|
11824
11956
|
SHARING_LEVELS,
|
|
@@ -11898,6 +12030,7 @@ function checkClaudePrerequisites() {
|
|
|
11898
12030
|
diffTopology,
|
|
11899
12031
|
edgesToConnections,
|
|
11900
12032
|
enrichCosts,
|
|
12033
|
+
entitiesToYaml,
|
|
11901
12034
|
evaluateCheck,
|
|
11902
12035
|
evaluateRule,
|
|
11903
12036
|
evidenceLine,
|
|
@@ -12027,6 +12160,7 @@ function checkClaudePrerequisites() {
|
|
|
12027
12160
|
startApi,
|
|
12028
12161
|
stripSensitive,
|
|
12029
12162
|
timingSafeEqual,
|
|
12163
|
+
toBackstageEntities,
|
|
12030
12164
|
validateScanner,
|
|
12031
12165
|
vscodeDeeplink,
|
|
12032
12166
|
zodToJsonSchema
|