@datasynx/agentic-ai-cartography 2.6.0 → 2.8.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-X3UWUX3G.js → chunk-5D5ZZEZM.js} +242 -19
- package/dist/chunk-5D5ZZEZM.js.map +1 -0
- package/dist/{chunk-PQ7Q6MI5.js → chunk-TBPGFEMQ.js} +2 -2
- package/dist/{chunk-GA4427LB.js → chunk-YVV6NIT2.js} +11 -1
- package/dist/chunk-YVV6NIT2.js.map +1 -0
- package/dist/cli.js +38 -6
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +228 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +159 -12
- package/dist/index.d.ts +159 -12
- package/dist/index.js +223 -14
- package/dist/index.js.map +1 -1
- package/dist/mcp-bin.js +2 -2
- package/package.json +3 -2
- package/server.json +2 -2
- package/dist/chunk-GA4427LB.js.map +0 -1
- package/dist/chunk-X3UWUX3G.js.map +0 -1
- /package/dist/{chunk-PQ7Q6MI5.js.map → chunk-TBPGFEMQ.js.map} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -45,9 +45,11 @@ __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,
|
|
52
|
+
GraphStoreBackend: () => GraphStoreBackend,
|
|
51
53
|
INGEST_SCHEMA_VERSION: () => INGEST_SCHEMA_VERSION,
|
|
52
54
|
IngestEnvelopeSchema: () => IngestEnvelopeSchema,
|
|
53
55
|
InvalidTenantError: () => InvalidTenantError,
|
|
@@ -66,10 +68,12 @@ __export(src_exports, {
|
|
|
66
68
|
ProviderRegistry: () => ProviderRegistry,
|
|
67
69
|
RELATION_TO_DIRECTION: () => RELATION_TO_DIRECTION,
|
|
68
70
|
ROLES: () => ROLES,
|
|
71
|
+
RateLimiter: () => RateLimiter,
|
|
69
72
|
RoleSchema: () => RoleSchema,
|
|
70
73
|
RuleCheckSchema: () => RuleCheckSchema,
|
|
71
74
|
RulesetSchema: () => RulesetSchema,
|
|
72
75
|
SCAN_ARG_PATTERNS: () => SCAN_ARG_PATTERNS,
|
|
76
|
+
SCHEMA_VERSION: () => SCHEMA_VERSION,
|
|
73
77
|
SDL: () => SDL,
|
|
74
78
|
SEVERITY_WEIGHT: () => SEVERITY_WEIGHT,
|
|
75
79
|
SHARING_LEVELS: () => SHARING_LEVELS,
|
|
@@ -217,6 +221,7 @@ __export(src_exports, {
|
|
|
217
221
|
nodesToAssets: () => nodesToAssets,
|
|
218
222
|
normalizeId: () => normalizeId,
|
|
219
223
|
normalizeTenant: () => normalizeTenant,
|
|
224
|
+
openStoreBackend: () => openStoreBackend,
|
|
220
225
|
orgKeyPath: () => orgKeyPath,
|
|
221
226
|
osUser: () => osUser,
|
|
222
227
|
parseApiArgs: () => parseApiArgs,
|
|
@@ -2850,6 +2855,7 @@ function newAnomalies(base, current) {
|
|
|
2850
2855
|
|
|
2851
2856
|
// src/db.ts
|
|
2852
2857
|
var DEFAULT_TENANT = "local";
|
|
2858
|
+
var SCHEMA_VERSION = 15;
|
|
2853
2859
|
function normalizeTenant(raw) {
|
|
2854
2860
|
if (raw == null) return DEFAULT_TENANT;
|
|
2855
2861
|
const cleaned = sanitizeUntrusted(String(raw)).trim().slice(0, 128);
|
|
@@ -4254,6 +4260,14 @@ var CartographyDB = class {
|
|
|
4254
4260
|
}
|
|
4255
4261
|
return rows.length;
|
|
4256
4262
|
}
|
|
4263
|
+
/**
|
|
4264
|
+
* Retention/compaction (4.7): delete audit events older than `olderThan` (ISO 8601).
|
|
4265
|
+
* The audit trail grows unbounded on a busy collector; this bounds it without touching
|
|
4266
|
+
* sessions/nodes/edges. Returns the number of events removed.
|
|
4267
|
+
*/
|
|
4268
|
+
pruneEventsOlderThan(olderThan) {
|
|
4269
|
+
return this.db.prepare("DELETE FROM activity_events WHERE timestamp < ?").run(olderThan).changes;
|
|
4270
|
+
}
|
|
4257
4271
|
// ── Graph queries (read-only context layer) ─────────────────────────────────
|
|
4258
4272
|
/** Fetch a single node by id within a session. */
|
|
4259
4273
|
getNode(sessionId, nodeId) {
|
|
@@ -4641,6 +4655,148 @@ var SqliteStoreBackend = class {
|
|
|
4641
4655
|
}
|
|
4642
4656
|
};
|
|
4643
4657
|
|
|
4658
|
+
// src/store/graph.ts
|
|
4659
|
+
function toNum(v) {
|
|
4660
|
+
if (typeof v === "number") return v;
|
|
4661
|
+
if (v && typeof v === "object" && "toNumber" in v && typeof v.toNumber === "function") {
|
|
4662
|
+
return v.toNumber();
|
|
4663
|
+
}
|
|
4664
|
+
return Number(v ?? 0);
|
|
4665
|
+
}
|
|
4666
|
+
var GraphStoreBackend = class {
|
|
4667
|
+
constructor(driver) {
|
|
4668
|
+
this.driver = driver;
|
|
4669
|
+
}
|
|
4670
|
+
async run(cypher, params) {
|
|
4671
|
+
const session = this.driver.session();
|
|
4672
|
+
try {
|
|
4673
|
+
return await session.run(cypher, params);
|
|
4674
|
+
} finally {
|
|
4675
|
+
await session.close();
|
|
4676
|
+
}
|
|
4677
|
+
}
|
|
4678
|
+
async upsertNode(org, node, identity, contributor) {
|
|
4679
|
+
const res = await this.run(
|
|
4680
|
+
`MERGE (n:Node {org: $org, globalId: $globalId})
|
|
4681
|
+
ON CREATE SET n._created = true
|
|
4682
|
+
ON MATCH SET n._created = false
|
|
4683
|
+
SET n.id = $id, n.contentHash = $contentHash, n.type = $type, n.name = $name,
|
|
4684
|
+
n.domain = $domain, n.owner = $owner,
|
|
4685
|
+
n.confidence = CASE WHEN n.confidence IS NULL OR $confidence > n.confidence THEN $confidence ELSE n.confidence END
|
|
4686
|
+
MERGE (c:Contributor {org: $org, globalId: $globalId, machineId: $machineId})
|
|
4687
|
+
SET c.hostname = $hostname, c.user = $user, c.at = $at,
|
|
4688
|
+
c.confidence = CASE WHEN c.confidence IS NULL OR $contribConfidence > c.confidence THEN $contribConfidence ELSE c.confidence END
|
|
4689
|
+
RETURN n._created AS created`,
|
|
4690
|
+
{
|
|
4691
|
+
org,
|
|
4692
|
+
globalId: identity.globalId,
|
|
4693
|
+
contentHash: identity.contentHash,
|
|
4694
|
+
id: node.id,
|
|
4695
|
+
type: node.type,
|
|
4696
|
+
name: node.name,
|
|
4697
|
+
domain: node.domain ?? null,
|
|
4698
|
+
owner: node.owner ?? null,
|
|
4699
|
+
confidence: node.confidence,
|
|
4700
|
+
machineId: contributor.machineId,
|
|
4701
|
+
hostname: contributor.hostname,
|
|
4702
|
+
user: contributor.user,
|
|
4703
|
+
at: contributor.at,
|
|
4704
|
+
contribConfidence: contributor.confidence
|
|
4705
|
+
}
|
|
4706
|
+
);
|
|
4707
|
+
return res.records[0]?.get("created") === true ? "created" : "merged";
|
|
4708
|
+
}
|
|
4709
|
+
async insertEdge(org, edge) {
|
|
4710
|
+
await this.run(
|
|
4711
|
+
`MATCH (s:Node {org: $org, id: $source})
|
|
4712
|
+
MATCH (t:Node {org: $org, id: $target})
|
|
4713
|
+
MERGE (s)-[r:DEPENDS {relationship: $rel}]->(t)
|
|
4714
|
+
SET r.evidence = $evidence, r.confidence = $confidence`,
|
|
4715
|
+
{ org, source: edge.sourceId, target: edge.targetId, rel: edge.relationship, evidence: edge.evidence, confidence: edge.confidence }
|
|
4716
|
+
);
|
|
4717
|
+
}
|
|
4718
|
+
async getSummary(org) {
|
|
4719
|
+
const totals = await this.run(
|
|
4720
|
+
`MATCH (n:Node {org: $org})
|
|
4721
|
+
OPTIONAL MATCH (n)-[r:DEPENDS]->(:Node {org: $org})
|
|
4722
|
+
RETURN count(DISTINCT n) AS nodes, count(r) AS edges`,
|
|
4723
|
+
{ org }
|
|
4724
|
+
);
|
|
4725
|
+
const byType = await this.run(`MATCH (n:Node {org: $org}) RETURN n.type AS k, count(*) AS c`, { org });
|
|
4726
|
+
const byDomain = await this.run(`MATCH (n:Node {org: $org}) RETURN coalesce(n.domain, '(none)') AS k, count(*) AS c`, { org });
|
|
4727
|
+
const byRel = await this.run(`MATCH (:Node {org: $org})-[r:DEPENDS]->(:Node {org: $org}) RETURN r.relationship AS k, count(*) AS c`, { org });
|
|
4728
|
+
const top = await this.run(
|
|
4729
|
+
`MATCH (n:Node {org: $org})
|
|
4730
|
+
OPTIONAL MATCH (n)-[r:DEPENDS]-(:Node {org: $org})
|
|
4731
|
+
RETURN n.id AS id, n.name AS name, n.type AS type, count(r) AS degree
|
|
4732
|
+
ORDER BY degree DESC, id ASC LIMIT 10`,
|
|
4733
|
+
{ org }
|
|
4734
|
+
);
|
|
4735
|
+
const contrib = await this.run(`MATCH (c:Contributor {org: $org}) RETURN count(DISTINCT c.machineId) AS contributors`, { org });
|
|
4736
|
+
const counts = (r) => {
|
|
4737
|
+
const out = {};
|
|
4738
|
+
for (const rec of r.records) out[String(rec.get("k"))] = toNum(rec.get("c"));
|
|
4739
|
+
return out;
|
|
4740
|
+
};
|
|
4741
|
+
return {
|
|
4742
|
+
org,
|
|
4743
|
+
totals: { nodes: toNum(totals.records[0]?.get("nodes")), edges: toNum(totals.records[0]?.get("edges")) },
|
|
4744
|
+
nodesByType: counts(byType),
|
|
4745
|
+
nodesByDomain: counts(byDomain),
|
|
4746
|
+
edgesByRelationship: counts(byRel),
|
|
4747
|
+
topConnected: top.records.map((rec) => ({
|
|
4748
|
+
id: String(rec.get("id")),
|
|
4749
|
+
name: String(rec.get("name")),
|
|
4750
|
+
type: String(rec.get("type")),
|
|
4751
|
+
degree: toNum(rec.get("degree"))
|
|
4752
|
+
})),
|
|
4753
|
+
contributors: toNum(contrib.records[0]?.get("contributors"))
|
|
4754
|
+
};
|
|
4755
|
+
}
|
|
4756
|
+
async getContributors(globalId2) {
|
|
4757
|
+
const res = await this.run(
|
|
4758
|
+
`MATCH (c:Contributor {globalId: $globalId})
|
|
4759
|
+
RETURN c.machineId AS machineId, c.hostname AS hostname, c.user AS user, c.org AS org, c.at AS at, c.confidence AS confidence`,
|
|
4760
|
+
{ globalId: globalId2 }
|
|
4761
|
+
);
|
|
4762
|
+
return res.records.map((rec) => ({
|
|
4763
|
+
machineId: String(rec.get("machineId")),
|
|
4764
|
+
hostname: String(rec.get("hostname")),
|
|
4765
|
+
user: String(rec.get("user")),
|
|
4766
|
+
organization: rec.get("org") != null ? String(rec.get("org")) : void 0,
|
|
4767
|
+
at: String(rec.get("at")),
|
|
4768
|
+
confidence: toNum(rec.get("confidence"))
|
|
4769
|
+
}));
|
|
4770
|
+
}
|
|
4771
|
+
async close() {
|
|
4772
|
+
await this.driver.close();
|
|
4773
|
+
}
|
|
4774
|
+
};
|
|
4775
|
+
|
|
4776
|
+
// src/store/index.ts
|
|
4777
|
+
async function defaultNeo4jDriver(url, user, password) {
|
|
4778
|
+
const mod = await import("neo4j-driver");
|
|
4779
|
+
return mod.default.driver(url, mod.default.auth.basic(user, password));
|
|
4780
|
+
}
|
|
4781
|
+
async function openStoreBackend(db, opts = {}) {
|
|
4782
|
+
if (opts.backend === "graph" && opts.graphUrl) {
|
|
4783
|
+
try {
|
|
4784
|
+
const make = opts.driverFactory ?? defaultNeo4jDriver;
|
|
4785
|
+
const driver = await make(opts.graphUrl, opts.graphUser ?? "neo4j", opts.graphPassword ?? "");
|
|
4786
|
+
if (driver.verifyConnectivity) await driver.verifyConnectivity();
|
|
4787
|
+
logInfo("central store: graph backend active", { host: stripSensitive(opts.graphUrl) });
|
|
4788
|
+
return new GraphStoreBackend(driver);
|
|
4789
|
+
} catch (err) {
|
|
4790
|
+
logWarn("central store: graph backend unavailable \u2014 falling back to SQLite", {
|
|
4791
|
+
host: stripSensitive(opts.graphUrl),
|
|
4792
|
+
reason: err instanceof Error ? err.message : String(err)
|
|
4793
|
+
});
|
|
4794
|
+
return new SqliteStoreBackend(db);
|
|
4795
|
+
}
|
|
4796
|
+
}
|
|
4797
|
+
return new SqliteStoreBackend(db);
|
|
4798
|
+
}
|
|
4799
|
+
|
|
4644
4800
|
// src/store/query.ts
|
|
4645
4801
|
var NotFoundError = class extends Error {
|
|
4646
4802
|
constructor(message) {
|
|
@@ -4945,7 +5101,7 @@ var ContributorSchema = import_zod5.z.object({
|
|
|
4945
5101
|
});
|
|
4946
5102
|
var IngestEnvelopeSchema = import_zod5.z.object({
|
|
4947
5103
|
schemaVersion: import_zod5.z.literal(INGEST_SCHEMA_VERSION),
|
|
4948
|
-
org: import_zod5.z.string().min(1).optional(),
|
|
5104
|
+
org: import_zod5.z.string().min(1).max(128).optional(),
|
|
4949
5105
|
items: import_zod5.z.array(import_zod5.z.object({
|
|
4950
5106
|
contentHash: import_zod5.z.string(),
|
|
4951
5107
|
kind: import_zod5.z.enum(["node", "edge"]),
|
|
@@ -4955,9 +5111,9 @@ var IngestEnvelopeSchema = import_zod5.z.object({
|
|
|
4955
5111
|
contributor: ContributorSchema.optional(),
|
|
4956
5112
|
anonymizationLevel: import_zod5.z.enum(["none", "anonymized", "full"]).optional()
|
|
4957
5113
|
});
|
|
4958
|
-
function ingestEnvelope(store, envelope, opts = {}) {
|
|
5114
|
+
async function ingestEnvelope(store, envelope, opts = {}) {
|
|
4959
5115
|
const anonMode = opts.anonMode ?? "reject";
|
|
4960
|
-
const org = envelope.org ?? opts.defaultOrg
|
|
5116
|
+
const org = normalizeTenant(envelope.org ?? opts.defaultOrg);
|
|
4961
5117
|
const level = envelope.anonymizationLevel ?? "anonymized";
|
|
4962
5118
|
const at = (/* @__PURE__ */ new Date()).toISOString();
|
|
4963
5119
|
const contributor = {
|
|
@@ -5008,7 +5164,7 @@ function ingestEnvelope(store, envelope, opts = {}) {
|
|
|
5008
5164
|
}
|
|
5009
5165
|
const safe = check.node;
|
|
5010
5166
|
const identity = computeIdentity(org, safe);
|
|
5011
|
-
const outcome = store.upsertNode(org, safe, identity, { ...contributor, confidence: safe.confidence });
|
|
5167
|
+
const outcome = await store.upsertNode(org, safe, identity, { ...contributor, confidence: safe.confidence });
|
|
5012
5168
|
accepted += 1;
|
|
5013
5169
|
if (outcome === "merged") merged += 1;
|
|
5014
5170
|
acceptedNodeIds.add(safe.id);
|
|
@@ -5024,7 +5180,7 @@ function ingestEnvelope(store, envelope, opts = {}) {
|
|
|
5024
5180
|
if (acceptedNodeIds.size > 0 && (!acceptedNodeIds.has(edge.sourceId) || !acceptedNodeIds.has(edge.targetId))) {
|
|
5025
5181
|
continue;
|
|
5026
5182
|
}
|
|
5027
|
-
store.insertEdge(org, edge);
|
|
5183
|
+
await store.insertEdge(org, edge);
|
|
5028
5184
|
edges += 1;
|
|
5029
5185
|
}
|
|
5030
5186
|
logInfo("ingest", { org, accepted, merged, rejected, edges, violations, level, anonMode });
|
|
@@ -5033,15 +5189,24 @@ function ingestEnvelope(store, envelope, opts = {}) {
|
|
|
5033
5189
|
|
|
5034
5190
|
// src/central/server.ts
|
|
5035
5191
|
function createIngestHandler(store, opts = {}) {
|
|
5036
|
-
|
|
5192
|
+
const quota = opts.quota;
|
|
5193
|
+
return async (body) => {
|
|
5037
5194
|
const parsed = IngestEnvelopeSchema.safeParse(body);
|
|
5038
5195
|
if (!parsed.success) {
|
|
5039
5196
|
const issues = parsed.error.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`);
|
|
5040
5197
|
logWarn("ingest: rejected invalid envelope", { issues });
|
|
5041
5198
|
return { status: 400, body: { error: "invalid envelope", issues } };
|
|
5042
5199
|
}
|
|
5200
|
+
if (quota) {
|
|
5201
|
+
const org = normalizeTenant(parsed.data.org ?? opts.defaultOrg);
|
|
5202
|
+
const decision = quota.take(org);
|
|
5203
|
+
if (!decision.allowed) {
|
|
5204
|
+
logWarn("ingest: rate limited", { org, retryAfterSec: decision.retryAfterSec });
|
|
5205
|
+
return { status: 429, body: { error: "too many requests" }, headers: { "retry-after": String(decision.retryAfterSec) } };
|
|
5206
|
+
}
|
|
5207
|
+
}
|
|
5043
5208
|
try {
|
|
5044
|
-
const result = ingestEnvelope(store, parsed.data, opts);
|
|
5209
|
+
const result = await ingestEnvelope(store, parsed.data, opts);
|
|
5045
5210
|
return { status: 200, body: result };
|
|
5046
5211
|
} catch (err) {
|
|
5047
5212
|
logWarn("ingest: failed", { error: err instanceof Error ? err.message : String(err) });
|
|
@@ -5050,6 +5215,39 @@ function createIngestHandler(store, opts = {}) {
|
|
|
5050
5215
|
};
|
|
5051
5216
|
}
|
|
5052
5217
|
|
|
5218
|
+
// src/central/quota.ts
|
|
5219
|
+
var DEFAULT_INGEST_QUOTA = { capacity: 120, refillMs: 6e4 };
|
|
5220
|
+
var MAX_KEYS = 1e4;
|
|
5221
|
+
var RateLimiter = class {
|
|
5222
|
+
constructor(cfg = DEFAULT_INGEST_QUOTA, now = () => Date.now()) {
|
|
5223
|
+
this.cfg = cfg;
|
|
5224
|
+
this.now = now;
|
|
5225
|
+
}
|
|
5226
|
+
buckets = /* @__PURE__ */ new Map();
|
|
5227
|
+
/** Consume one token for `key`. Returns whether the request is allowed (+ Retry-After when not). */
|
|
5228
|
+
take(key) {
|
|
5229
|
+
const t = this.now();
|
|
5230
|
+
const ratePerMs = this.cfg.capacity / this.cfg.refillMs;
|
|
5231
|
+
let b = this.buckets.get(key);
|
|
5232
|
+
if (!b) {
|
|
5233
|
+
if (this.buckets.size >= MAX_KEYS) {
|
|
5234
|
+
const oldest = this.buckets.keys().next().value;
|
|
5235
|
+
if (oldest !== void 0) this.buckets.delete(oldest);
|
|
5236
|
+
}
|
|
5237
|
+
b = { tokens: this.cfg.capacity, last: t };
|
|
5238
|
+
this.buckets.set(key, b);
|
|
5239
|
+
}
|
|
5240
|
+
b.tokens = Math.min(this.cfg.capacity, b.tokens + Math.max(0, t - b.last) * ratePerMs);
|
|
5241
|
+
b.last = t;
|
|
5242
|
+
if (b.tokens >= 1) {
|
|
5243
|
+
b.tokens -= 1;
|
|
5244
|
+
return { allowed: true, retryAfterSec: 0 };
|
|
5245
|
+
}
|
|
5246
|
+
const waitMs = (1 - b.tokens) / ratePerMs;
|
|
5247
|
+
return { allowed: false, retryAfterSec: Math.max(1, Math.ceil(waitMs / 1e3)) };
|
|
5248
|
+
}
|
|
5249
|
+
};
|
|
5250
|
+
|
|
5053
5251
|
// src/scanners/bookmarks.ts
|
|
5054
5252
|
var PERSONAL = [
|
|
5055
5253
|
"facebook.",
|
|
@@ -5937,7 +6135,7 @@ async function resolveNlQuery(db, sessionId, search, raw, opts) {
|
|
|
5937
6135
|
|
|
5938
6136
|
// src/mcp/server.ts
|
|
5939
6137
|
var SERVER_NAME = "cartography";
|
|
5940
|
-
var SERVER_VERSION = "2.
|
|
6138
|
+
var SERVER_VERSION = "2.8.0";
|
|
5941
6139
|
var SERVICE_TYPES = NODE_TYPE_GROUPS.web;
|
|
5942
6140
|
var DATA_TYPES = NODE_TYPE_GROUPS.data;
|
|
5943
6141
|
var lexicalSearch = async (db, sessionId, query, opts) => db.searchNodes(sessionId, query, { types: opts.types, limit: opts.limit }).map((node) => ({ node }));
|
|
@@ -6012,9 +6210,10 @@ function createMcpServer(opts = {}) {
|
|
|
6012
6210
|
"graph-summary",
|
|
6013
6211
|
"cartography://graph/summary",
|
|
6014
6212
|
{ title: "Topology summary", description: "Low-token aggregate index of the whole landscape \u2014 read this first.", mimeType: "text/markdown" },
|
|
6015
|
-
(uri) => {
|
|
6213
|
+
async (uri) => {
|
|
6016
6214
|
if (org !== void 0) {
|
|
6017
|
-
|
|
6215
|
+
const s = opts.orgSummary ? await opts.orgSummary(org) : db.getOrgSummary(org);
|
|
6216
|
+
return { contents: [{ uri: uri.href, mimeType: "text/markdown", text: summaryText(s) }] };
|
|
6018
6217
|
}
|
|
6019
6218
|
const sid = resolveSession();
|
|
6020
6219
|
if (!sid) return { contents: [{ uri: uri.href, mimeType: "text/markdown", text: "No discovery session found. Run discovery first." }] };
|
|
@@ -6089,8 +6288,8 @@ function createMcpServer(opts = {}) {
|
|
|
6089
6288
|
server.registerTool(
|
|
6090
6289
|
"get_summary",
|
|
6091
6290
|
{ title: "Get topology summary", description: "Low-token overview of the whole landscape (counts, types, domains, most-connected, anomalies).", inputSchema: {}, annotations: readOnly },
|
|
6092
|
-
() => {
|
|
6093
|
-
if (org !== void 0) return json(db.getOrgSummary(org));
|
|
6291
|
+
async () => {
|
|
6292
|
+
if (org !== void 0) return json(opts.orgSummary ? await opts.orgSummary(org) : db.getOrgSummary(org));
|
|
6094
6293
|
const sid = resolveSession();
|
|
6095
6294
|
if (!sid) return json({ error: "No discovery session found." });
|
|
6096
6295
|
return json(db.getGraphSummary(sid));
|
|
@@ -6580,6 +6779,16 @@ async function runHttp(factory, opts = {}) {
|
|
|
6580
6779
|
const httpServer = import_node_http.default.createServer(async (req, res) => {
|
|
6581
6780
|
try {
|
|
6582
6781
|
const url = req.url ?? "";
|
|
6782
|
+
const probePath = new URL(url || "/", "http://probe").pathname;
|
|
6783
|
+
if (probePath === "/healthz") {
|
|
6784
|
+
res.writeHead(200, { "content-type": "application/json" }).end('{"status":"ok"}');
|
|
6785
|
+
return;
|
|
6786
|
+
}
|
|
6787
|
+
if (probePath === "/readyz") {
|
|
6788
|
+
const r = opts.readiness ? opts.readiness() : { ready: true };
|
|
6789
|
+
res.writeHead(r.ready ? 200 : 503, { "content-type": "application/json" }).end(JSON.stringify({ status: r.ready ? "ready" : "unready" }));
|
|
6790
|
+
return;
|
|
6791
|
+
}
|
|
6583
6792
|
const isIngest = url.startsWith("/ingest") && opts.onIngest !== void 0;
|
|
6584
6793
|
if (!url.startsWith("/mcp") && !isIngest) {
|
|
6585
6794
|
res.writeHead(404, { "content-type": "application/json" }).end('{"error":"not found"}');
|
|
@@ -6606,8 +6815,8 @@ async function runHttp(factory, opts = {}) {
|
|
|
6606
6815
|
res.writeHead(413, { "content-type": "application/json" }).end('{"error":"payload too large"}');
|
|
6607
6816
|
return;
|
|
6608
6817
|
}
|
|
6609
|
-
const out = onIngest(value);
|
|
6610
|
-
res.writeHead(out.status, { "content-type": "application/json" }).end(JSON.stringify(out.body));
|
|
6818
|
+
const out = await onIngest(value);
|
|
6819
|
+
res.writeHead(out.status, { "content-type": "application/json", ...out.headers ?? {} }).end(JSON.stringify(out.body));
|
|
6611
6820
|
return;
|
|
6612
6821
|
}
|
|
6613
6822
|
const sessionId = req.headers["mcp-session-id"];
|
|
@@ -11859,9 +12068,11 @@ function checkClaudePrerequisites() {
|
|
|
11859
12068
|
CredentialConfigSchema,
|
|
11860
12069
|
CsvCostSource,
|
|
11861
12070
|
DEFAULT_ANOMALY_THRESHOLDS,
|
|
12071
|
+
DEFAULT_INGEST_QUOTA,
|
|
11862
12072
|
DEFAULT_SERVER_NAME,
|
|
11863
12073
|
DEFAULT_TENANT,
|
|
11864
12074
|
DriftConfigSchema,
|
|
12075
|
+
GraphStoreBackend,
|
|
11865
12076
|
INGEST_SCHEMA_VERSION,
|
|
11866
12077
|
IngestEnvelopeSchema,
|
|
11867
12078
|
InvalidTenantError,
|
|
@@ -11880,10 +12091,12 @@ function checkClaudePrerequisites() {
|
|
|
11880
12091
|
ProviderRegistry,
|
|
11881
12092
|
RELATION_TO_DIRECTION,
|
|
11882
12093
|
ROLES,
|
|
12094
|
+
RateLimiter,
|
|
11883
12095
|
RoleSchema,
|
|
11884
12096
|
RuleCheckSchema,
|
|
11885
12097
|
RulesetSchema,
|
|
11886
12098
|
SCAN_ARG_PATTERNS,
|
|
12099
|
+
SCHEMA_VERSION,
|
|
11887
12100
|
SDL,
|
|
11888
12101
|
SEVERITY_WEIGHT,
|
|
11889
12102
|
SHARING_LEVELS,
|
|
@@ -12031,6 +12244,7 @@ function checkClaudePrerequisites() {
|
|
|
12031
12244
|
nodesToAssets,
|
|
12032
12245
|
normalizeId,
|
|
12033
12246
|
normalizeTenant,
|
|
12247
|
+
openStoreBackend,
|
|
12034
12248
|
orgKeyPath,
|
|
12035
12249
|
osUser,
|
|
12036
12250
|
parseApiArgs,
|