@datasynx/agentic-ai-cartography 2.3.0 → 2.4.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 CHANGED
@@ -2,9 +2,9 @@
2
2
  import {
3
3
  parseApiArgs,
4
4
  startApi
5
- } from "./chunk-7VZH5PFV.js";
6
- import "./chunk-7QEBFMN4.js";
7
- import "./chunk-WCR47QA2.js";
5
+ } from "./chunk-L4OSL7I6.js";
6
+ import "./chunk-X5JA2UDT.js";
7
+ import "./chunk-QQOQBE2A.js";
8
8
  import "./chunk-2SZ5QHGH.js";
9
9
 
10
10
  // src/api-bin.ts
@@ -20,7 +20,7 @@ import {
20
20
  sanitizeUntrusted,
21
21
  stableStringify,
22
22
  stripSensitive
23
- } from "./chunk-7QEBFMN4.js";
23
+ } from "./chunk-X5JA2UDT.js";
24
24
  import {
25
25
  EdgeSchema,
26
26
  NODE_TYPES,
@@ -29,7 +29,7 @@ import {
29
29
  SECURITY_METADATA_KEYS,
30
30
  SEVERITIES,
31
31
  defaultConfig
32
- } from "./chunk-WCR47QA2.js";
32
+ } from "./chunk-QQOQBE2A.js";
33
33
  import {
34
34
  IS_WIN,
35
35
  PLATFORM,
@@ -965,6 +965,41 @@ var StdoutSink = class {
965
965
 
966
966
  // src/sinks/webhook.ts
967
967
  var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "[::1]", "::1"]);
968
+ async function postJson(opts) {
969
+ const doFetch = opts.fetchImpl ?? (typeof fetch === "function" ? fetch : void 0);
970
+ if (!doFetch) {
971
+ logWarn("sink unavailable: global fetch missing", { sink: opts.sinkName });
972
+ return;
973
+ }
974
+ if (!opts.url) {
975
+ logWarn("sink unavailable: no url configured", { sink: opts.sinkName });
976
+ return;
977
+ }
978
+ if (!isSecureWebhookUrl(opts.url)) {
979
+ logWarn("sink refused: insecure scheme (use https:// or a loopback host)", {
980
+ sink: opts.sinkName,
981
+ host: stripSensitive(opts.url)
982
+ });
983
+ return;
984
+ }
985
+ try {
986
+ const res = await doFetch(opts.url, {
987
+ method: "POST",
988
+ headers: { "content-type": "application/json", ...opts.headers ?? {} },
989
+ body: JSON.stringify(opts.body),
990
+ signal: AbortSignal.timeout(opts.timeoutMs ?? 1e4)
991
+ });
992
+ if (!res.ok) {
993
+ logError("sink delivery failed", { sink: opts.sinkName, host: stripSensitive(opts.url), status: res.status });
994
+ }
995
+ } catch (err) {
996
+ logError("sink delivery failed", {
997
+ sink: opts.sinkName,
998
+ host: stripSensitive(opts.url),
999
+ reason: err instanceof Error ? err.message : String(err)
1000
+ });
1001
+ }
1002
+ }
968
1003
  function isSecureWebhookUrl(url, env = process.env) {
969
1004
  if (env.CARTOGRAPHY_ALLOW_INSECURE_SYNC === "1") return true;
970
1005
  let parsed;
@@ -983,59 +1018,177 @@ var WebhookSink = class {
983
1018
  }
984
1019
  name = "webhook";
985
1020
  async emit(alert) {
986
- if (typeof fetch !== "function") {
987
- logWarn("webhook sink unavailable: global fetch missing", { sink: this.name });
988
- return;
989
- }
990
1021
  const { url, token, timeoutMs } = this.opts;
991
- if (!url) {
992
- logWarn("webhook sink unavailable: no url configured", { sink: this.name });
993
- return;
994
- }
995
- if (!isSecureWebhookUrl(url)) {
996
- logWarn("webhook sink refused: insecure scheme (use https:// or a loopback host)", {
997
- sink: this.name,
998
- host: stripSensitive(url)
999
- });
1000
- return;
1001
- }
1002
- try {
1003
- const res = await fetch(url, {
1004
- method: "POST",
1005
- headers: {
1006
- "content-type": "application/json",
1007
- ...token ? { authorization: `Bearer ${token}` } : {}
1008
- },
1009
- body: JSON.stringify(redactValue(alert)),
1010
- signal: AbortSignal.timeout(timeoutMs ?? 1e4)
1011
- });
1012
- if (!res.ok) {
1013
- logError("webhook sink failed", { sink: this.name, host: stripSensitive(url), status: res.status });
1022
+ await postJson({
1023
+ url,
1024
+ body: redactValue(alert),
1025
+ ...token ? { headers: { authorization: `Bearer ${token}` } } : {},
1026
+ ...timeoutMs !== void 0 ? { timeoutMs } : {},
1027
+ sinkName: this.name
1028
+ });
1029
+ }
1030
+ };
1031
+
1032
+ // src/sinks/providers.ts
1033
+ var MAX_ITEMS = 20;
1034
+ var SEVERITY_EMOJI = { info: "\u{1F7E2}", warning: "\u{1F7E1}", critical: "\u{1F534}" };
1035
+ function headline(alert) {
1036
+ const s = alert.summary;
1037
+ return `${s.nodesAdded}+ / ${s.nodesRemoved}- / ${s.nodesChanged}~ nodes, ${s.edgesAdded}+ / ${s.edgesRemoved}- edges`;
1038
+ }
1039
+ function itemLine(it) {
1040
+ const sec = it.securityFields?.length ? ` [security: ${it.securityFields.join(", ")}]` : "";
1041
+ const fields = it.changedFields?.length ? ` (${it.changedFields.join(", ")})` : "";
1042
+ return `${it.severity.toUpperCase()} \xB7 ${it.kind} \xB7 ${it.label}${fields}${sec}`;
1043
+ }
1044
+ function bodyText(alert) {
1045
+ const lines = alert.items.slice(0, MAX_ITEMS).map(itemLine);
1046
+ const more = alert.items.length > MAX_ITEMS ? [`\u2026and ${alert.items.length - MAX_ITEMS} more`] : [];
1047
+ return [headline(alert), "", ...lines, ...more].join("\n");
1048
+ }
1049
+ function formatSlack(alert) {
1050
+ const title = `${SEVERITY_EMOJI[alert.severity]} Topology drift \u2014 ${alert.severity}`;
1051
+ return {
1052
+ text: `${title}: ${headline(alert)}`,
1053
+ blocks: [
1054
+ { type: "header", text: { type: "plain_text", text: title, emoji: true } },
1055
+ { type: "section", text: { type: "mrkdwn", text: "```" + bodyText(alert) + "```" } },
1056
+ { type: "context", elements: [{ type: "mrkdwn", text: `base ${alert.base.sessionId} \u2192 current ${alert.current.sessionId} \xB7 ${alert.generatedAt}` }] }
1057
+ ]
1058
+ };
1059
+ }
1060
+ var PD_SEVERITY = {
1061
+ info: "info",
1062
+ warning: "warning",
1063
+ critical: "critical"
1064
+ };
1065
+ function formatPagerDuty(alert, routingKey) {
1066
+ return {
1067
+ routing_key: routingKey,
1068
+ event_action: "trigger",
1069
+ // Stable per base→current pair so repeated alerts for the same delta de-duplicate.
1070
+ dedup_key: `cartograph-drift:${alert.base.sessionId}:${alert.current.sessionId}`,
1071
+ payload: {
1072
+ summary: `Cartograph topology drift (${alert.severity}): ${headline(alert)}`,
1073
+ source: "cartograph",
1074
+ severity: PD_SEVERITY[alert.severity],
1075
+ timestamp: alert.generatedAt,
1076
+ custom_details: {
1077
+ summary: alert.summary,
1078
+ items: alert.items.slice(0, MAX_ITEMS).map((it) => ({
1079
+ kind: it.kind,
1080
+ ref: it.ref,
1081
+ severity: it.severity,
1082
+ ...it.changedFields ? { changedFields: it.changedFields } : {},
1083
+ ...it.securityFields ? { securityFields: it.securityFields } : {}
1084
+ }))
1014
1085
  }
1015
- } catch (err) {
1016
- logError("webhook sink failed", {
1017
- sink: this.name,
1018
- host: stripSensitive(url),
1019
- reason: err instanceof Error ? err.message : String(err)
1020
- });
1021
1086
  }
1087
+ };
1088
+ }
1089
+ function formatJira(alert, opts) {
1090
+ return {
1091
+ fields: {
1092
+ project: { key: opts.project },
1093
+ issuetype: { name: opts.issueType ?? "Task" },
1094
+ summary: `Cartograph topology drift (${alert.severity}): ${headline(alert)}`,
1095
+ description: bodyText(alert) + `
1096
+
1097
+ base ${alert.base.sessionId} \u2192 current ${alert.current.sessionId}
1098
+ generated ${alert.generatedAt}`
1099
+ }
1100
+ };
1101
+ }
1102
+
1103
+ // src/sinks/provider-sink.ts
1104
+ var PAGERDUTY_ENQUEUE_URL = "https://events.pagerduty.com/v2/enqueue";
1105
+ function deliver(name, url, body, opts, headers) {
1106
+ return postJson({
1107
+ url,
1108
+ body,
1109
+ sinkName: name,
1110
+ ...headers ? { headers } : {},
1111
+ ...opts.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs } : {},
1112
+ ...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
1113
+ });
1114
+ }
1115
+ var SlackSink = class {
1116
+ constructor(opts) {
1117
+ this.opts = opts;
1118
+ }
1119
+ name = "slack";
1120
+ async emit(alert) {
1121
+ await deliver(this.name, this.opts.url, formatSlack(redactValue(alert)), this.opts);
1122
+ }
1123
+ };
1124
+ var PagerDutySink = class {
1125
+ constructor(opts) {
1126
+ this.opts = opts;
1127
+ }
1128
+ name = "pagerduty";
1129
+ async emit(alert) {
1130
+ const body = formatPagerDuty(redactValue(alert), this.opts.routingKey);
1131
+ await deliver(this.name, this.opts.url || PAGERDUTY_ENQUEUE_URL, body, this.opts);
1132
+ }
1133
+ };
1134
+ var JiraSink = class {
1135
+ constructor(opts) {
1136
+ this.opts = opts;
1137
+ }
1138
+ name = "jira";
1139
+ async emit(alert) {
1140
+ const body = formatJira(redactValue(alert), {
1141
+ project: this.opts.project,
1142
+ ...this.opts.issueType ? { issueType: this.opts.issueType } : {}
1143
+ });
1144
+ const auth = Buffer.from(`${this.opts.email}:${this.opts.token}`).toString("base64");
1145
+ const base = this.opts.url.replace(/\/+$/, "");
1146
+ await deliver(this.name, `${base}/rest/api/2/issue`, body, this.opts, { authorization: `Basic ${auth}` });
1022
1147
  }
1023
1148
  };
1024
1149
 
1025
1150
  // src/sinks/index.ts
1026
1151
  function buildSinks(drift) {
1027
1152
  const configs = drift?.sinks && drift.sinks.length > 0 ? drift.sinks : [{ type: "stdout" }];
1153
+ const envSecret = process.env.CARTOGRAPHY_DRIFT_TOKEN;
1028
1154
  const sinks = [];
1029
1155
  for (const s of configs) {
1030
- if (s.type === "webhook") {
1031
- if (!s.url) continue;
1032
- sinks.push(new WebhookSink({
1033
- url: s.url,
1034
- token: s.token ?? process.env.CARTOGRAPHY_DRIFT_TOKEN,
1035
- timeoutMs: s.timeoutMs
1036
- }));
1037
- } else {
1038
- sinks.push(new StdoutSink());
1156
+ const timeoutMs = s.timeoutMs;
1157
+ switch (s.type) {
1158
+ case "webhook":
1159
+ if (!s.url) {
1160
+ logWarn("drift sink skipped: webhook requires a url", { sink: s.type });
1161
+ break;
1162
+ }
1163
+ sinks.push(new WebhookSink({ url: s.url, token: s.token ?? envSecret, timeoutMs }));
1164
+ break;
1165
+ case "slack":
1166
+ if (!s.url) {
1167
+ logWarn("drift sink skipped: slack requires a webhook url", { sink: s.type });
1168
+ break;
1169
+ }
1170
+ sinks.push(new SlackSink({ url: s.url, timeoutMs }));
1171
+ break;
1172
+ case "pagerduty": {
1173
+ const routingKey = s.routingKey ?? s.token ?? envSecret;
1174
+ if (!routingKey) {
1175
+ logWarn("drift sink skipped: pagerduty requires a routingKey (or CARTOGRAPHY_DRIFT_TOKEN)", { sink: s.type });
1176
+ break;
1177
+ }
1178
+ sinks.push(new PagerDutySink({ url: s.url ?? PAGERDUTY_ENQUEUE_URL, routingKey, timeoutMs }));
1179
+ break;
1180
+ }
1181
+ case "jira": {
1182
+ const token = s.token ?? envSecret;
1183
+ if (!s.url || !s.email || !s.project || !token) {
1184
+ logWarn("drift sink skipped: jira requires url, email, project and a token", { sink: s.type });
1185
+ break;
1186
+ }
1187
+ sinks.push(new JiraSink({ url: s.url, email: s.email, token, project: s.project, issueType: s.issueType, timeoutMs }));
1188
+ break;
1189
+ }
1190
+ default:
1191
+ sinks.push(new StdoutSink());
1039
1192
  }
1040
1193
  }
1041
1194
  return sinks.length > 0 ? sinks : [new StdoutSink()];
@@ -1381,7 +1534,7 @@ async function executeNlQuery(db, sessionId, search, intent, opts = {}) {
1381
1534
 
1382
1535
  // src/mcp/server.ts
1383
1536
  var SERVER_NAME = "cartography";
1384
- var SERVER_VERSION = "2.3.0";
1537
+ var SERVER_VERSION = "2.4.0";
1385
1538
  var SERVICE_TYPES = NODE_TYPE_GROUPS.web;
1386
1539
  var DATA_TYPES = NODE_TYPE_GROUPS.data;
1387
1540
  var lexicalSearch = async (db, sessionId, query, opts) => db.searchNodes(sessionId, query, { types: opts.types, limit: opts.limit }).map((node) => ({ node }));
@@ -2271,7 +2424,7 @@ function revalidateAnonymized(node, level, mode) {
2271
2424
 
2272
2425
  // src/central/ingest.ts
2273
2426
  var INGEST_SCHEMA_VERSION = 1;
2274
- var MAX_ITEMS = 5e4;
2427
+ var MAX_ITEMS2 = 5e4;
2275
2428
  var ContributorSchema = z3.object({
2276
2429
  machineId: z3.string().min(1),
2277
2430
  hostname: z3.string().default("unknown"),
@@ -2285,7 +2438,7 @@ var IngestEnvelopeSchema = z3.object({
2285
2438
  contentHash: z3.string(),
2286
2439
  kind: z3.enum(["node", "edge"]),
2287
2440
  payload: z3.unknown()
2288
- })).max(MAX_ITEMS),
2441
+ })).max(MAX_ITEMS2),
2289
2442
  // Extensions (forward-compatible; 2.11 does not yet send these).
2290
2443
  contributor: ContributorSchema.optional(),
2291
2444
  anonymizationLevel: z3.enum(["none", "anonymized", "full"]).optional()
@@ -2462,4 +2615,4 @@ export {
2462
2615
  parseMcpArgs,
2463
2616
  startMcp
2464
2617
  };
2465
- //# sourceMappingURL=chunk-B2AKONVW.js.map
2618
+ //# sourceMappingURL=chunk-B4QWX7CP.js.map