@datasynx/agentic-ai-cartography 0.9.2 → 1.1.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/README.md +3 -2
- package/dist/{bookmarks-72CDYAHD.js → bookmarks-BWNVQGPG.js} +4 -2
- package/dist/{chunk-3NVQ3ND6.js → chunk-QKNYI3SU.js} +50 -3
- package/dist/chunk-QKNYI3SU.js.map +1 -0
- package/dist/cli.js +418 -167
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +36 -1
- package/dist/index.js +507 -281
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
- package/dist/chunk-3NVQ3ND6.js.map +0 -1
- /package/dist/{bookmarks-72CDYAHD.js.map → bookmarks-BWNVQGPG.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -2,6 +2,203 @@
|
|
|
2
2
|
import Database from "better-sqlite3";
|
|
3
3
|
import { mkdirSync } from "fs";
|
|
4
4
|
import { dirname } from "path";
|
|
5
|
+
import { z as z2 } from "zod";
|
|
6
|
+
|
|
7
|
+
// src/types.ts
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
var NODE_TYPES = [
|
|
10
|
+
"host",
|
|
11
|
+
"database_server",
|
|
12
|
+
"database",
|
|
13
|
+
"table",
|
|
14
|
+
"web_service",
|
|
15
|
+
"api_endpoint",
|
|
16
|
+
"cache_server",
|
|
17
|
+
"message_broker",
|
|
18
|
+
"queue",
|
|
19
|
+
"topic",
|
|
20
|
+
"container",
|
|
21
|
+
"pod",
|
|
22
|
+
"k8s_cluster",
|
|
23
|
+
"config_file",
|
|
24
|
+
"saas_tool",
|
|
25
|
+
"unknown"
|
|
26
|
+
];
|
|
27
|
+
var EDGE_RELATIONSHIPS = [
|
|
28
|
+
"connects_to",
|
|
29
|
+
"reads_from",
|
|
30
|
+
"writes_to",
|
|
31
|
+
"calls",
|
|
32
|
+
"contains",
|
|
33
|
+
"depends_on"
|
|
34
|
+
];
|
|
35
|
+
var NodeSchema = z.object({
|
|
36
|
+
id: z.string().describe('Format: "{type}:{host}:{port}" oder "{type}:{name}"'),
|
|
37
|
+
type: z.enum(NODE_TYPES),
|
|
38
|
+
name: z.string(),
|
|
39
|
+
discoveredVia: z.string(),
|
|
40
|
+
confidence: z.number().min(0).max(1).default(0.5),
|
|
41
|
+
metadata: z.record(z.string(), z.unknown()).default({}),
|
|
42
|
+
tags: z.array(z.string()).default([]),
|
|
43
|
+
domain: z.string().optional().describe('Business domain, e.g. "Marketing", "Finance"'),
|
|
44
|
+
subDomain: z.string().optional().describe('Sub-domain, e.g. "Forecast client orders"'),
|
|
45
|
+
qualityScore: z.number().min(0).max(100).optional().describe("Data quality score 0\u2013100")
|
|
46
|
+
});
|
|
47
|
+
var EdgeSchema = z.object({
|
|
48
|
+
sourceId: z.string(),
|
|
49
|
+
targetId: z.string(),
|
|
50
|
+
relationship: z.enum(EDGE_RELATIONSHIPS),
|
|
51
|
+
evidence: z.string(),
|
|
52
|
+
confidence: z.number().min(0).max(1).default(0.5)
|
|
53
|
+
});
|
|
54
|
+
var DataAssetSchema = z.object({
|
|
55
|
+
id: z.string(),
|
|
56
|
+
name: z.string(),
|
|
57
|
+
domain: z.string(),
|
|
58
|
+
subDomain: z.string().optional(),
|
|
59
|
+
qualityScore: z.number().min(0).max(100).optional(),
|
|
60
|
+
metadata: z.record(z.string(), z.unknown()).default({}),
|
|
61
|
+
position: z.object({ q: z.number(), r: z.number() })
|
|
62
|
+
});
|
|
63
|
+
var ClusterSchema = z.object({
|
|
64
|
+
id: z.string(),
|
|
65
|
+
label: z.string(),
|
|
66
|
+
domain: z.string(),
|
|
67
|
+
color: z.string(),
|
|
68
|
+
assetIds: z.array(z.string()),
|
|
69
|
+
centroid: z.object({ x: z.number(), y: z.number() })
|
|
70
|
+
});
|
|
71
|
+
var ConnectionSchema = z.object({
|
|
72
|
+
id: z.string(),
|
|
73
|
+
sourceAssetId: z.string(),
|
|
74
|
+
targetAssetId: z.string(),
|
|
75
|
+
type: z.string().optional()
|
|
76
|
+
});
|
|
77
|
+
var DOMAIN_COLORS = {
|
|
78
|
+
"Quality Control": "#1a2744",
|
|
79
|
+
"Supply Chain": "#1e3a6e",
|
|
80
|
+
"Marketing": "#6a7fb5",
|
|
81
|
+
"Finance": "#3a8a8a",
|
|
82
|
+
"HR": "#2a5a9a",
|
|
83
|
+
"Logistics": "#0e7490",
|
|
84
|
+
"Sales": "#1d4ed8",
|
|
85
|
+
"Engineering": "#4338ca",
|
|
86
|
+
"Operations": "#0891b2",
|
|
87
|
+
"Data Layer": "#1e3352",
|
|
88
|
+
"Web / API": "#1a3a1a",
|
|
89
|
+
"Messaging": "#2a1a3a",
|
|
90
|
+
"Infrastructure": "#0f2a40",
|
|
91
|
+
"Other": "#374151"
|
|
92
|
+
};
|
|
93
|
+
var DOMAIN_PALETTE = [
|
|
94
|
+
"#1a2e5a",
|
|
95
|
+
"#1e3a8a",
|
|
96
|
+
"#1d4ed8",
|
|
97
|
+
"#2563eb",
|
|
98
|
+
"#3b82f6",
|
|
99
|
+
"#6366f1",
|
|
100
|
+
"#818cf8",
|
|
101
|
+
"#7c9fc3",
|
|
102
|
+
"#0e7490",
|
|
103
|
+
"#0891b2",
|
|
104
|
+
"#06b6d4",
|
|
105
|
+
"#22d3ee",
|
|
106
|
+
"#0d9488",
|
|
107
|
+
"#14b8a6",
|
|
108
|
+
"#2dd4bf",
|
|
109
|
+
"#5eead4"
|
|
110
|
+
];
|
|
111
|
+
function defaultConfig(overrides = {}) {
|
|
112
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? "/tmp";
|
|
113
|
+
return {
|
|
114
|
+
maxDepth: 8,
|
|
115
|
+
maxTurns: 50,
|
|
116
|
+
entryPoints: ["localhost"],
|
|
117
|
+
agentModel: "claude-sonnet-4-5-20250929",
|
|
118
|
+
outputDir: "./cartography-output",
|
|
119
|
+
dbPath: `${home}/.cartography/cartography.db`,
|
|
120
|
+
verbose: false,
|
|
121
|
+
...overrides
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// src/db.ts
|
|
126
|
+
var SessionRowSchema = z2.object({
|
|
127
|
+
id: z2.string(),
|
|
128
|
+
mode: z2.literal("discover"),
|
|
129
|
+
started_at: z2.string(),
|
|
130
|
+
completed_at: z2.string().nullable().optional(),
|
|
131
|
+
config: z2.string()
|
|
132
|
+
});
|
|
133
|
+
var NodeRowSchema = z2.object({
|
|
134
|
+
id: z2.string(),
|
|
135
|
+
session_id: z2.string(),
|
|
136
|
+
type: z2.enum(NODE_TYPES),
|
|
137
|
+
name: z2.string(),
|
|
138
|
+
discovered_via: z2.string().nullable().optional(),
|
|
139
|
+
discovered_at: z2.string(),
|
|
140
|
+
path_id: z2.string().nullable().optional(),
|
|
141
|
+
depth: z2.number().default(0),
|
|
142
|
+
confidence: z2.number().default(0.5),
|
|
143
|
+
metadata: z2.string().default("{}"),
|
|
144
|
+
tags: z2.string().default("[]"),
|
|
145
|
+
domain: z2.string().nullable().optional(),
|
|
146
|
+
sub_domain: z2.string().nullable().optional(),
|
|
147
|
+
quality_score: z2.number().nullable().optional()
|
|
148
|
+
});
|
|
149
|
+
var EdgeRowSchema = z2.object({
|
|
150
|
+
id: z2.string(),
|
|
151
|
+
session_id: z2.string(),
|
|
152
|
+
source_id: z2.string(),
|
|
153
|
+
target_id: z2.string(),
|
|
154
|
+
relationship: z2.enum(EDGE_RELATIONSHIPS),
|
|
155
|
+
evidence: z2.string().nullable().optional(),
|
|
156
|
+
confidence: z2.number().default(0.5),
|
|
157
|
+
discovered_at: z2.string()
|
|
158
|
+
});
|
|
159
|
+
var EventRowSchema = z2.object({
|
|
160
|
+
id: z2.string(),
|
|
161
|
+
session_id: z2.string(),
|
|
162
|
+
task_id: z2.string().nullable().optional(),
|
|
163
|
+
timestamp: z2.string(),
|
|
164
|
+
event_type: z2.string(),
|
|
165
|
+
process: z2.string(),
|
|
166
|
+
pid: z2.number(),
|
|
167
|
+
target: z2.string().nullable().optional(),
|
|
168
|
+
target_type: z2.string().nullable().optional(),
|
|
169
|
+
port: z2.number().nullable().optional(),
|
|
170
|
+
duration_ms: z2.number().nullable().optional()
|
|
171
|
+
});
|
|
172
|
+
var TaskRowSchema = z2.object({
|
|
173
|
+
id: z2.string(),
|
|
174
|
+
session_id: z2.string(),
|
|
175
|
+
description: z2.string().nullable().optional(),
|
|
176
|
+
started_at: z2.string(),
|
|
177
|
+
completed_at: z2.string().nullable().optional(),
|
|
178
|
+
steps: z2.string().default("[]"),
|
|
179
|
+
involved_services: z2.string().default("[]"),
|
|
180
|
+
status: z2.enum(["active", "completed", "cancelled"])
|
|
181
|
+
});
|
|
182
|
+
var WorkflowRowSchema = z2.object({
|
|
183
|
+
id: z2.string(),
|
|
184
|
+
session_id: z2.string(),
|
|
185
|
+
name: z2.string().nullable().optional(),
|
|
186
|
+
pattern: z2.string(),
|
|
187
|
+
task_ids: z2.string().default("[]"),
|
|
188
|
+
occurrences: z2.number().default(1),
|
|
189
|
+
first_seen: z2.string(),
|
|
190
|
+
last_seen: z2.string(),
|
|
191
|
+
avg_duration_ms: z2.number().nullable().optional(),
|
|
192
|
+
involved_services: z2.string().default("[]")
|
|
193
|
+
});
|
|
194
|
+
var ConnectionRowSchema = z2.object({
|
|
195
|
+
id: z2.string(),
|
|
196
|
+
session_id: z2.string(),
|
|
197
|
+
source_asset_id: z2.string(),
|
|
198
|
+
target_asset_id: z2.string(),
|
|
199
|
+
type: z2.string().nullable().optional(),
|
|
200
|
+
created_at: z2.string()
|
|
201
|
+
});
|
|
5
202
|
var SCHEMA = `
|
|
6
203
|
PRAGMA journal_mode = WAL;
|
|
7
204
|
PRAGMA foreign_keys = ON;
|
|
@@ -166,12 +363,13 @@ var CartographyDB = class {
|
|
|
166
363
|
return rows.map((r) => this.mapSession(r));
|
|
167
364
|
}
|
|
168
365
|
mapSession(r) {
|
|
366
|
+
const v = SessionRowSchema.parse(r);
|
|
169
367
|
return {
|
|
170
|
-
id:
|
|
171
|
-
mode:
|
|
172
|
-
startedAt:
|
|
173
|
-
completedAt:
|
|
174
|
-
config:
|
|
368
|
+
id: v.id,
|
|
369
|
+
mode: v.mode,
|
|
370
|
+
startedAt: v.started_at,
|
|
371
|
+
completedAt: v.completed_at ?? void 0,
|
|
372
|
+
config: v.config
|
|
175
373
|
};
|
|
176
374
|
}
|
|
177
375
|
// ── Nodes ───────────────────────────────
|
|
@@ -202,21 +400,22 @@ var CartographyDB = class {
|
|
|
202
400
|
return rows.map((r) => this.mapNode(r));
|
|
203
401
|
}
|
|
204
402
|
mapNode(r) {
|
|
403
|
+
const v = NodeRowSchema.parse(r);
|
|
205
404
|
return {
|
|
206
|
-
id:
|
|
207
|
-
sessionId:
|
|
208
|
-
type:
|
|
209
|
-
name:
|
|
210
|
-
discoveredVia:
|
|
211
|
-
discoveredAt:
|
|
212
|
-
depth:
|
|
213
|
-
confidence:
|
|
214
|
-
metadata: JSON.parse(
|
|
215
|
-
tags: JSON.parse(
|
|
216
|
-
pathId:
|
|
217
|
-
domain:
|
|
218
|
-
subDomain:
|
|
219
|
-
qualityScore:
|
|
405
|
+
id: v.id,
|
|
406
|
+
sessionId: v.session_id,
|
|
407
|
+
type: v.type,
|
|
408
|
+
name: v.name,
|
|
409
|
+
discoveredVia: v.discovered_via ?? "",
|
|
410
|
+
discoveredAt: v.discovered_at,
|
|
411
|
+
depth: v.depth,
|
|
412
|
+
confidence: v.confidence,
|
|
413
|
+
metadata: JSON.parse(v.metadata),
|
|
414
|
+
tags: JSON.parse(v.tags),
|
|
415
|
+
pathId: v.path_id ?? void 0,
|
|
416
|
+
domain: v.domain ?? void 0,
|
|
417
|
+
subDomain: v.sub_domain ?? void 0,
|
|
418
|
+
qualityScore: v.quality_score ?? void 0
|
|
220
419
|
};
|
|
221
420
|
}
|
|
222
421
|
deleteNode(sessionId, nodeId) {
|
|
@@ -245,16 +444,19 @@ var CartographyDB = class {
|
|
|
245
444
|
}
|
|
246
445
|
getEdges(sessionId) {
|
|
247
446
|
const rows = this.db.prepare("SELECT * FROM edges WHERE session_id = ?").all(sessionId);
|
|
248
|
-
return rows.map((r) =>
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
447
|
+
return rows.map((r) => {
|
|
448
|
+
const v = EdgeRowSchema.parse(r);
|
|
449
|
+
return {
|
|
450
|
+
id: v.id,
|
|
451
|
+
sessionId: v.session_id,
|
|
452
|
+
sourceId: v.source_id,
|
|
453
|
+
targetId: v.target_id,
|
|
454
|
+
relationship: v.relationship,
|
|
455
|
+
evidence: v.evidence ?? "",
|
|
456
|
+
confidence: v.confidence,
|
|
457
|
+
discoveredAt: v.discovered_at
|
|
458
|
+
};
|
|
459
|
+
});
|
|
258
460
|
}
|
|
259
461
|
// ── Events ──────────────────────────────
|
|
260
462
|
insertEvent(sessionId, event, taskId) {
|
|
@@ -278,19 +480,22 @@ var CartographyDB = class {
|
|
|
278
480
|
}
|
|
279
481
|
getEvents(sessionId, since) {
|
|
280
482
|
const rows = since ? this.db.prepare("SELECT * FROM activity_events WHERE session_id = ? AND timestamp > ? ORDER BY timestamp").all(sessionId, since) : this.db.prepare("SELECT * FROM activity_events WHERE session_id = ? ORDER BY timestamp").all(sessionId);
|
|
281
|
-
return rows.map((r) =>
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
483
|
+
return rows.map((r) => {
|
|
484
|
+
const v = EventRowSchema.parse(r);
|
|
485
|
+
return {
|
|
486
|
+
id: v.id,
|
|
487
|
+
sessionId: v.session_id,
|
|
488
|
+
taskId: v.task_id ?? void 0,
|
|
489
|
+
timestamp: v.timestamp,
|
|
490
|
+
eventType: v.event_type,
|
|
491
|
+
process: v.process,
|
|
492
|
+
pid: v.pid,
|
|
493
|
+
target: v.target ?? void 0,
|
|
494
|
+
targetType: v.target_type ?? void 0,
|
|
495
|
+
port: v.port ?? void 0,
|
|
496
|
+
durationMs: v.duration_ms ?? void 0
|
|
497
|
+
};
|
|
498
|
+
});
|
|
294
499
|
}
|
|
295
500
|
// ── Tasks ───────────────────────────────
|
|
296
501
|
startTask(sessionId, description) {
|
|
@@ -324,15 +529,16 @@ var CartographyDB = class {
|
|
|
324
529
|
return rows.map((r) => this.mapTask(r));
|
|
325
530
|
}
|
|
326
531
|
mapTask(r) {
|
|
532
|
+
const v = TaskRowSchema.parse(r);
|
|
327
533
|
return {
|
|
328
|
-
id:
|
|
329
|
-
sessionId:
|
|
330
|
-
description:
|
|
331
|
-
startedAt:
|
|
332
|
-
completedAt:
|
|
333
|
-
steps:
|
|
334
|
-
involvedServices:
|
|
335
|
-
status:
|
|
534
|
+
id: v.id,
|
|
535
|
+
sessionId: v.session_id,
|
|
536
|
+
description: v.description ?? void 0,
|
|
537
|
+
startedAt: v.started_at,
|
|
538
|
+
completedAt: v.completed_at ?? void 0,
|
|
539
|
+
steps: v.steps,
|
|
540
|
+
involvedServices: v.involved_services,
|
|
541
|
+
status: v.status
|
|
336
542
|
};
|
|
337
543
|
}
|
|
338
544
|
// ── Workflows ───────────────────────────
|
|
@@ -358,18 +564,21 @@ var CartographyDB = class {
|
|
|
358
564
|
}
|
|
359
565
|
getWorkflows(sessionId) {
|
|
360
566
|
const rows = this.db.prepare("SELECT * FROM workflows WHERE session_id = ?").all(sessionId);
|
|
361
|
-
return rows.map((r) =>
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
567
|
+
return rows.map((r) => {
|
|
568
|
+
const v = WorkflowRowSchema.parse(r);
|
|
569
|
+
return {
|
|
570
|
+
id: v.id,
|
|
571
|
+
sessionId: v.session_id,
|
|
572
|
+
name: v.name ?? void 0,
|
|
573
|
+
pattern: v.pattern,
|
|
574
|
+
taskIds: v.task_ids,
|
|
575
|
+
occurrences: v.occurrences,
|
|
576
|
+
firstSeen: v.first_seen,
|
|
577
|
+
lastSeen: v.last_seen,
|
|
578
|
+
avgDurationMs: v.avg_duration_ms ?? 0,
|
|
579
|
+
involvedServices: v.involved_services
|
|
580
|
+
};
|
|
581
|
+
});
|
|
373
582
|
}
|
|
374
583
|
// ── Connections (user-created hex map links) ─────────────────────────────
|
|
375
584
|
upsertConnection(sessionId, conn) {
|
|
@@ -386,14 +595,17 @@ var CartographyDB = class {
|
|
|
386
595
|
}
|
|
387
596
|
getConnections(sessionId) {
|
|
388
597
|
const rows = this.db.prepare("SELECT * FROM connections WHERE session_id = ?").all(sessionId);
|
|
389
|
-
return rows.map((r) =>
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
598
|
+
return rows.map((r) => {
|
|
599
|
+
const v = ConnectionRowSchema.parse(r);
|
|
600
|
+
return {
|
|
601
|
+
id: v.id,
|
|
602
|
+
sessionId: v.session_id,
|
|
603
|
+
sourceAssetId: v.source_asset_id,
|
|
604
|
+
targetAssetId: v.target_asset_id,
|
|
605
|
+
type: v.type ?? void 0,
|
|
606
|
+
createdAt: v.created_at
|
|
607
|
+
};
|
|
608
|
+
});
|
|
397
609
|
}
|
|
398
610
|
deleteConnection(sessionId, connectionId) {
|
|
399
611
|
this.db.prepare("DELETE FROM connections WHERE session_id = ? AND id = ?").run(sessionId, connectionId);
|
|
@@ -408,6 +620,31 @@ var CartographyDB = class {
|
|
|
408
620
|
const row = this.db.prepare("SELECT action FROM node_approvals WHERE pattern = ?").get(pattern);
|
|
409
621
|
return row?.action;
|
|
410
622
|
}
|
|
623
|
+
// ── Pruning ──────────────────────────────
|
|
624
|
+
/**
|
|
625
|
+
* Delete a session and all its associated data (nodes, edges, events, tasks, workflows, connections).
|
|
626
|
+
*/
|
|
627
|
+
deleteSession(sessionId) {
|
|
628
|
+
this.db.prepare("DELETE FROM connections WHERE session_id = ?").run(sessionId);
|
|
629
|
+
this.db.prepare("DELETE FROM workflows WHERE session_id = ?").run(sessionId);
|
|
630
|
+
this.db.prepare("DELETE FROM activity_events WHERE session_id = ?").run(sessionId);
|
|
631
|
+
this.db.prepare("DELETE FROM tasks WHERE session_id = ?").run(sessionId);
|
|
632
|
+
this.db.prepare("DELETE FROM edges WHERE session_id = ?").run(sessionId);
|
|
633
|
+
this.db.prepare("DELETE FROM nodes WHERE session_id = ?").run(sessionId);
|
|
634
|
+
this.db.prepare("DELETE FROM sessions WHERE id = ?").run(sessionId);
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* Prune sessions older than the given ISO date string. Returns count of deleted sessions.
|
|
638
|
+
*/
|
|
639
|
+
pruneSessions(olderThan) {
|
|
640
|
+
const rows = this.db.prepare(
|
|
641
|
+
"SELECT id FROM sessions WHERE started_at < ?"
|
|
642
|
+
).all(olderThan);
|
|
643
|
+
for (const row of rows) {
|
|
644
|
+
this.deleteSession(row.id);
|
|
645
|
+
}
|
|
646
|
+
return rows.length;
|
|
647
|
+
}
|
|
411
648
|
// ── Stats ───────────────────────────────
|
|
412
649
|
getStats(sessionId) {
|
|
413
650
|
const nodes = this.db.prepare("SELECT COUNT(*) as c FROM nodes WHERE session_id = ?").get(sessionId).c;
|
|
@@ -419,129 +656,11 @@ var CartographyDB = class {
|
|
|
419
656
|
};
|
|
420
657
|
|
|
421
658
|
// src/tools.ts
|
|
422
|
-
import { z as
|
|
423
|
-
|
|
424
|
-
// src/types.ts
|
|
425
|
-
import { z } from "zod";
|
|
426
|
-
var NODE_TYPES = [
|
|
427
|
-
"host",
|
|
428
|
-
"database_server",
|
|
429
|
-
"database",
|
|
430
|
-
"table",
|
|
431
|
-
"web_service",
|
|
432
|
-
"api_endpoint",
|
|
433
|
-
"cache_server",
|
|
434
|
-
"message_broker",
|
|
435
|
-
"queue",
|
|
436
|
-
"topic",
|
|
437
|
-
"container",
|
|
438
|
-
"pod",
|
|
439
|
-
"k8s_cluster",
|
|
440
|
-
"config_file",
|
|
441
|
-
"saas_tool",
|
|
442
|
-
"unknown"
|
|
443
|
-
];
|
|
444
|
-
var EDGE_RELATIONSHIPS = [
|
|
445
|
-
"connects_to",
|
|
446
|
-
"reads_from",
|
|
447
|
-
"writes_to",
|
|
448
|
-
"calls",
|
|
449
|
-
"contains",
|
|
450
|
-
"depends_on"
|
|
451
|
-
];
|
|
452
|
-
var NodeSchema = z.object({
|
|
453
|
-
id: z.string().describe('Format: "{type}:{host}:{port}" oder "{type}:{name}"'),
|
|
454
|
-
type: z.enum(NODE_TYPES),
|
|
455
|
-
name: z.string(),
|
|
456
|
-
discoveredVia: z.string(),
|
|
457
|
-
confidence: z.number().min(0).max(1).default(0.5),
|
|
458
|
-
metadata: z.record(z.string(), z.unknown()).default({}),
|
|
459
|
-
tags: z.array(z.string()).default([]),
|
|
460
|
-
domain: z.string().optional().describe('Business domain, e.g. "Marketing", "Finance"'),
|
|
461
|
-
subDomain: z.string().optional().describe('Sub-domain, e.g. "Forecast client orders"'),
|
|
462
|
-
qualityScore: z.number().min(0).max(100).optional().describe("Data quality score 0\u2013100")
|
|
463
|
-
});
|
|
464
|
-
var EdgeSchema = z.object({
|
|
465
|
-
sourceId: z.string(),
|
|
466
|
-
targetId: z.string(),
|
|
467
|
-
relationship: z.enum(EDGE_RELATIONSHIPS),
|
|
468
|
-
evidence: z.string(),
|
|
469
|
-
confidence: z.number().min(0).max(1).default(0.5)
|
|
470
|
-
});
|
|
471
|
-
var DataAssetSchema = z.object({
|
|
472
|
-
id: z.string(),
|
|
473
|
-
name: z.string(),
|
|
474
|
-
domain: z.string(),
|
|
475
|
-
subDomain: z.string().optional(),
|
|
476
|
-
qualityScore: z.number().min(0).max(100).optional(),
|
|
477
|
-
metadata: z.record(z.string(), z.unknown()).default({}),
|
|
478
|
-
position: z.object({ q: z.number(), r: z.number() })
|
|
479
|
-
});
|
|
480
|
-
var ClusterSchema = z.object({
|
|
481
|
-
id: z.string(),
|
|
482
|
-
label: z.string(),
|
|
483
|
-
domain: z.string(),
|
|
484
|
-
color: z.string(),
|
|
485
|
-
assetIds: z.array(z.string()),
|
|
486
|
-
centroid: z.object({ x: z.number(), y: z.number() })
|
|
487
|
-
});
|
|
488
|
-
var ConnectionSchema = z.object({
|
|
489
|
-
id: z.string(),
|
|
490
|
-
sourceAssetId: z.string(),
|
|
491
|
-
targetAssetId: z.string(),
|
|
492
|
-
type: z.string().optional()
|
|
493
|
-
});
|
|
494
|
-
var DOMAIN_COLORS = {
|
|
495
|
-
"Quality Control": "#1a2744",
|
|
496
|
-
"Supply Chain": "#1e3a6e",
|
|
497
|
-
"Marketing": "#6a7fb5",
|
|
498
|
-
"Finance": "#3a8a8a",
|
|
499
|
-
"HR": "#2a5a9a",
|
|
500
|
-
"Logistics": "#0e7490",
|
|
501
|
-
"Sales": "#1d4ed8",
|
|
502
|
-
"Engineering": "#4338ca",
|
|
503
|
-
"Operations": "#0891b2",
|
|
504
|
-
"Data Layer": "#1e3352",
|
|
505
|
-
"Web / API": "#1a3a1a",
|
|
506
|
-
"Messaging": "#2a1a3a",
|
|
507
|
-
"Infrastructure": "#0f2a40",
|
|
508
|
-
"Other": "#374151"
|
|
509
|
-
};
|
|
510
|
-
var DOMAIN_PALETTE = [
|
|
511
|
-
"#1a2e5a",
|
|
512
|
-
"#1e3a8a",
|
|
513
|
-
"#1d4ed8",
|
|
514
|
-
"#2563eb",
|
|
515
|
-
"#3b82f6",
|
|
516
|
-
"#6366f1",
|
|
517
|
-
"#818cf8",
|
|
518
|
-
"#7c9fc3",
|
|
519
|
-
"#0e7490",
|
|
520
|
-
"#0891b2",
|
|
521
|
-
"#06b6d4",
|
|
522
|
-
"#22d3ee",
|
|
523
|
-
"#0d9488",
|
|
524
|
-
"#14b8a6",
|
|
525
|
-
"#2dd4bf",
|
|
526
|
-
"#5eead4"
|
|
527
|
-
];
|
|
528
|
-
function defaultConfig(overrides = {}) {
|
|
529
|
-
const home = process.env.HOME ?? process.env.USERPROFILE ?? "/tmp";
|
|
530
|
-
return {
|
|
531
|
-
maxDepth: 8,
|
|
532
|
-
maxTurns: 50,
|
|
533
|
-
entryPoints: ["localhost"],
|
|
534
|
-
agentModel: "claude-sonnet-4-5-20250929",
|
|
535
|
-
outputDir: "./cartography-output",
|
|
536
|
-
dbPath: `${home}/.cartography/cartography.db`,
|
|
537
|
-
verbose: false,
|
|
538
|
-
...overrides
|
|
539
|
-
};
|
|
540
|
-
}
|
|
659
|
+
import { z as z3 } from "zod";
|
|
541
660
|
|
|
542
661
|
// src/bookmarks.ts
|
|
543
662
|
import { tmpdir } from "os";
|
|
544
|
-
import { existsSync as existsSync2, readFileSync, readdirSync, copyFileSync, statSync } from "fs";
|
|
663
|
+
import { existsSync as existsSync2, readFileSync, readdirSync, copyFileSync, statSync, unlinkSync } from "fs";
|
|
545
664
|
import { join as join2 } from "path";
|
|
546
665
|
|
|
547
666
|
// src/platform.ts
|
|
@@ -568,13 +687,42 @@ function getShell() {
|
|
|
568
687
|
if (!_shell) _shell = platformShell();
|
|
569
688
|
return _shell;
|
|
570
689
|
}
|
|
690
|
+
var SAFE_ENV_KEYS = [
|
|
691
|
+
"PATH",
|
|
692
|
+
"HOME",
|
|
693
|
+
"USER",
|
|
694
|
+
"LANG",
|
|
695
|
+
"LC_ALL",
|
|
696
|
+
"TERM",
|
|
697
|
+
"SHELL",
|
|
698
|
+
"USERPROFILE",
|
|
699
|
+
"LOCALAPPDATA",
|
|
700
|
+
"APPDATA",
|
|
701
|
+
"PROGRAMFILES",
|
|
702
|
+
"XDG_CONFIG_HOME",
|
|
703
|
+
"XDG_DATA_HOME",
|
|
704
|
+
"XDG_RUNTIME_DIR",
|
|
705
|
+
"AWS_DEFAULT_REGION",
|
|
706
|
+
"AWS_PROFILE",
|
|
707
|
+
"AWS_CONFIG_FILE",
|
|
708
|
+
"KUBECONFIG",
|
|
709
|
+
"GOOGLE_APPLICATION_CREDENTIALS",
|
|
710
|
+
"AZURE_CONFIG_DIR"
|
|
711
|
+
];
|
|
712
|
+
function safeEnv() {
|
|
713
|
+
const env = {};
|
|
714
|
+
for (const key of SAFE_ENV_KEYS) {
|
|
715
|
+
if (process.env[key]) env[key] = process.env[key];
|
|
716
|
+
}
|
|
717
|
+
return env;
|
|
718
|
+
}
|
|
571
719
|
function run(cmd, opts = {}) {
|
|
572
720
|
try {
|
|
573
721
|
return execSync(cmd, {
|
|
574
722
|
stdio: "pipe",
|
|
575
723
|
timeout: opts.timeout ?? 1e4,
|
|
576
724
|
shell: getShell(),
|
|
577
|
-
env: opts.env
|
|
725
|
+
env: opts.env ?? safeEnv()
|
|
578
726
|
}).toString().trim();
|
|
579
727
|
} catch {
|
|
580
728
|
return "";
|
|
@@ -688,6 +836,23 @@ function scanWindowsDbServices() {
|
|
|
688
836
|
}
|
|
689
837
|
|
|
690
838
|
// src/bookmarks.ts
|
|
839
|
+
function cleanupTempFiles() {
|
|
840
|
+
let cleaned = 0;
|
|
841
|
+
const tmp = tmpdir();
|
|
842
|
+
try {
|
|
843
|
+
for (const f of readdirSync(tmp)) {
|
|
844
|
+
if (f.startsWith("cartograph_") && f.endsWith(".sqlite")) {
|
|
845
|
+
try {
|
|
846
|
+
unlinkSync(join2(tmp, f));
|
|
847
|
+
cleaned++;
|
|
848
|
+
} catch {
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
} catch {
|
|
853
|
+
}
|
|
854
|
+
return cleaned;
|
|
855
|
+
}
|
|
691
856
|
function extractHost(rawUrl, source) {
|
|
692
857
|
try {
|
|
693
858
|
const u = new URL(rawUrl);
|
|
@@ -933,6 +1098,22 @@ async function scanAllHistory() {
|
|
|
933
1098
|
}
|
|
934
1099
|
|
|
935
1100
|
// src/tools.ts
|
|
1101
|
+
function createScanRunner(runFn, opts = {}) {
|
|
1102
|
+
const threshold = opts.threshold ?? 3;
|
|
1103
|
+
let consecutiveFailures = 0;
|
|
1104
|
+
let tripped = false;
|
|
1105
|
+
return (cmd) => {
|
|
1106
|
+
if (tripped) return "(skipped \u2014 circuit breaker: too many consecutive failures)";
|
|
1107
|
+
const result = runFn(cmd, { timeout: opts.timeout ?? 2e4, env: opts.env });
|
|
1108
|
+
if (!result) {
|
|
1109
|
+
consecutiveFailures++;
|
|
1110
|
+
if (consecutiveFailures >= threshold) tripped = true;
|
|
1111
|
+
return "(error or not available)";
|
|
1112
|
+
}
|
|
1113
|
+
consecutiveFailures = 0;
|
|
1114
|
+
return result;
|
|
1115
|
+
};
|
|
1116
|
+
}
|
|
936
1117
|
function stripSensitive(target) {
|
|
937
1118
|
try {
|
|
938
1119
|
const url = new URL(target.startsWith("http") ? target : `tcp://${target}`);
|
|
@@ -945,16 +1126,16 @@ async function createCartographyTools(db, sessionId, opts = {}) {
|
|
|
945
1126
|
const { tool, createSdkMcpServer } = await import("@anthropic-ai/claude-agent-sdk");
|
|
946
1127
|
const tools = [
|
|
947
1128
|
tool("save_node", "Save an infrastructure node to the catalog", {
|
|
948
|
-
id:
|
|
949
|
-
type:
|
|
950
|
-
name:
|
|
951
|
-
discoveredVia:
|
|
952
|
-
confidence:
|
|
953
|
-
metadata:
|
|
954
|
-
tags:
|
|
955
|
-
domain:
|
|
956
|
-
subDomain:
|
|
957
|
-
qualityScore:
|
|
1129
|
+
id: z3.string(),
|
|
1130
|
+
type: z3.enum(NODE_TYPES),
|
|
1131
|
+
name: z3.string(),
|
|
1132
|
+
discoveredVia: z3.string(),
|
|
1133
|
+
confidence: z3.number().min(0).max(1),
|
|
1134
|
+
metadata: z3.record(z3.string(), z3.unknown()).optional(),
|
|
1135
|
+
tags: z3.array(z3.string()).optional(),
|
|
1136
|
+
domain: z3.string().optional().describe('Business domain, e.g. "Marketing", "Finance"'),
|
|
1137
|
+
subDomain: z3.string().optional().describe('Sub-domain, e.g. "Forecast client orders"'),
|
|
1138
|
+
qualityScore: z3.number().min(0).max(100).optional().describe("Data quality score 0\u2013100")
|
|
958
1139
|
}, async (args) => {
|
|
959
1140
|
const node = {
|
|
960
1141
|
id: stripSensitive(args["id"]),
|
|
@@ -972,11 +1153,11 @@ async function createCartographyTools(db, sessionId, opts = {}) {
|
|
|
972
1153
|
return { content: [{ type: "text", text: `\u2713 Node: ${node.id}` }] };
|
|
973
1154
|
}),
|
|
974
1155
|
tool("save_edge", "Save a relationship (edge) between two nodes \u2014 ALWAYS save edges when connections are clear", {
|
|
975
|
-
sourceId:
|
|
976
|
-
targetId:
|
|
977
|
-
relationship:
|
|
978
|
-
evidence:
|
|
979
|
-
confidence:
|
|
1156
|
+
sourceId: z3.string(),
|
|
1157
|
+
targetId: z3.string(),
|
|
1158
|
+
relationship: z3.enum(EDGE_RELATIONSHIPS),
|
|
1159
|
+
evidence: z3.string(),
|
|
1160
|
+
confidence: z3.number().min(0).max(1)
|
|
980
1161
|
}, async (args) => {
|
|
981
1162
|
db.insertEdge(sessionId, {
|
|
982
1163
|
sourceId: args["sourceId"],
|
|
@@ -988,7 +1169,7 @@ async function createCartographyTools(db, sessionId, opts = {}) {
|
|
|
988
1169
|
return { content: [{ type: "text", text: `\u2713 ${args["sourceId"]}\u2192${args["targetId"]}` }] };
|
|
989
1170
|
}),
|
|
990
1171
|
tool("get_catalog", "Get the current catalog \u2014 use before save_node to avoid duplicates", {
|
|
991
|
-
includeEdges:
|
|
1172
|
+
includeEdges: z3.boolean().default(true)
|
|
992
1173
|
}, async (args) => {
|
|
993
1174
|
const nodes = db.getNodes(sessionId);
|
|
994
1175
|
const edges = args["includeEdges"] ? db.getEdges(sessionId) : [];
|
|
@@ -1003,8 +1184,8 @@ async function createCartographyTools(db, sessionId, opts = {}) {
|
|
|
1003
1184
|
};
|
|
1004
1185
|
}),
|
|
1005
1186
|
tool("ask_user", "Ask the user a question \u2014 for clarifications, missing context, or consent (e.g. before scanning browser history)", {
|
|
1006
|
-
question:
|
|
1007
|
-
context:
|
|
1187
|
+
question: z3.string().describe("The question for the user (clear and specific)"),
|
|
1188
|
+
context: z3.string().optional().describe("Optional context explaining why this is relevant")
|
|
1008
1189
|
}, async (args) => {
|
|
1009
1190
|
const question = args["question"];
|
|
1010
1191
|
const context = args["context"];
|
|
@@ -1017,7 +1198,7 @@ async function createCartographyTools(db, sessionId, opts = {}) {
|
|
|
1017
1198
|
};
|
|
1018
1199
|
}),
|
|
1019
1200
|
tool("scan_bookmarks", "Scan all browser bookmarks \u2014 hostnames only, no personal data (Chrome, Chromium, Edge, Brave, Vivaldi, Opera, Firefox)", {
|
|
1020
|
-
minConfidence:
|
|
1201
|
+
minConfidence: z3.number().min(0).max(1).default(0.5).optional()
|
|
1021
1202
|
}, async () => {
|
|
1022
1203
|
const hosts = await scanAllBookmarks();
|
|
1023
1204
|
return {
|
|
@@ -1037,7 +1218,7 @@ async function createCartographyTools(db, sessionId, opts = {}) {
|
|
|
1037
1218
|
};
|
|
1038
1219
|
}),
|
|
1039
1220
|
tool("scan_browser_history", "Scan browser history \u2014 anonymized hostnames + visit frequency. ALWAYS call ask_user for consent before using this tool.", {
|
|
1040
|
-
minVisits:
|
|
1221
|
+
minVisits: z3.number().min(1).default(3).optional().describe("Minimum visit count to include a host (filters rarely-visited sites)")
|
|
1041
1222
|
}, async (args) => {
|
|
1042
1223
|
const minVisits = args["minVisits"] ?? 3;
|
|
1043
1224
|
const hosts = await scanAllHistory();
|
|
@@ -1059,7 +1240,7 @@ async function createCartographyTools(db, sessionId, opts = {}) {
|
|
|
1059
1240
|
};
|
|
1060
1241
|
}),
|
|
1061
1242
|
tool("scan_local_databases", "Scan for local database files and running DB servers \u2014 PostgreSQL databases, MySQL, SQLite files from installed apps", {
|
|
1062
|
-
deep:
|
|
1243
|
+
deep: z3.boolean().default(false).optional().describe("Also search home directory recursively for SQLite/DB files (slower)")
|
|
1063
1244
|
}, async (args) => {
|
|
1064
1245
|
const deep = args["deep"] ?? false;
|
|
1065
1246
|
const results = {};
|
|
@@ -1131,14 +1312,11 @@ ${v}`).join("\n\n");
|
|
|
1131
1312
|
return { content: [{ type: "text", text: out }] };
|
|
1132
1313
|
}),
|
|
1133
1314
|
tool("scan_k8s_resources", "Scan Kubernetes cluster via kubectl \u2014 100% readonly (get, describe)", {
|
|
1134
|
-
namespace:
|
|
1315
|
+
namespace: z3.string().optional().describe("Filter by namespace \u2014 empty = all namespaces")
|
|
1135
1316
|
}, async (args) => {
|
|
1136
1317
|
const ns = args["namespace"];
|
|
1137
1318
|
const nsFlag = ns ? `-n ${ns}` : "--all-namespaces";
|
|
1138
|
-
const runK = (
|
|
1139
|
-
const r = run(cmd, { timeout: 15e3 });
|
|
1140
|
-
return r || `(error or not available)`;
|
|
1141
|
-
};
|
|
1319
|
+
const runK = createScanRunner(run, { timeout: 15e3, threshold: 3 });
|
|
1142
1320
|
const sections = IS_WIN ? [
|
|
1143
1321
|
["CONTEXT", "kubectl config current-context"],
|
|
1144
1322
|
["NODES", "kubectl get nodes -o wide"],
|
|
@@ -1165,15 +1343,15 @@ ${runK(c)}`).join("\n\n");
|
|
|
1165
1343
|
return { content: [{ type: "text", text: out }] };
|
|
1166
1344
|
}),
|
|
1167
1345
|
tool("scan_aws_resources", "Scan AWS infrastructure via AWS CLI \u2014 100% readonly (describe, list)", {
|
|
1168
|
-
region:
|
|
1169
|
-
profile:
|
|
1346
|
+
region: z3.string().optional().describe("AWS Region \u2014 default: AWS_DEFAULT_REGION or profile"),
|
|
1347
|
+
profile: z3.string().optional().describe("AWS CLI profile")
|
|
1170
1348
|
}, async (args) => {
|
|
1171
1349
|
const region = args["region"];
|
|
1172
1350
|
const profile = args["profile"];
|
|
1173
1351
|
const env = { ...process.env };
|
|
1174
1352
|
if (region) env["AWS_DEFAULT_REGION"] = region;
|
|
1175
1353
|
const pf = profile ? `--profile ${profile}` : "";
|
|
1176
|
-
const runAws = (
|
|
1354
|
+
const runAws = createScanRunner(run, { timeout: 2e4, env, threshold: 3 });
|
|
1177
1355
|
const sections = [
|
|
1178
1356
|
["IDENTITY", `aws sts get-caller-identity ${pf} --output json`],
|
|
1179
1357
|
["EC2", `aws ec2 describe-instances ${pf} --query "Reservations[*].Instances[*].[InstanceId,InstanceType,State.Name,PublicIpAddress,PrivateIpAddress]" --output table`],
|
|
@@ -1189,11 +1367,11 @@ ${runAws(c)}`).join("\n\n");
|
|
|
1189
1367
|
return { content: [{ type: "text", text: out }] };
|
|
1190
1368
|
}),
|
|
1191
1369
|
tool("scan_gcp_resources", "Scan Google Cloud Platform via gcloud CLI \u2014 100% readonly (list, describe)", {
|
|
1192
|
-
project:
|
|
1370
|
+
project: z3.string().optional().describe("GCP Project ID \u2014 default: current gcloud project")
|
|
1193
1371
|
}, async (args) => {
|
|
1194
1372
|
const project = args["project"];
|
|
1195
1373
|
const pf = project ? `--project ${project}` : "";
|
|
1196
|
-
const runGcp = (
|
|
1374
|
+
const runGcp = createScanRunner(run, { timeout: 2e4, threshold: 3 });
|
|
1197
1375
|
const sections = [
|
|
1198
1376
|
["IDENTITY", `gcloud config list account --format="value(core.account)"`],
|
|
1199
1377
|
["COMPUTE_INSTANCES", `gcloud compute instances list ${pf}`],
|
|
@@ -1210,14 +1388,14 @@ ${runGcp(c)}`).join("\n\n");
|
|
|
1210
1388
|
return { content: [{ type: "text", text: out }] };
|
|
1211
1389
|
}),
|
|
1212
1390
|
tool("scan_azure_resources", "Scan Azure infrastructure via az CLI \u2014 100% readonly (list, show)", {
|
|
1213
|
-
subscription:
|
|
1214
|
-
resourceGroup:
|
|
1391
|
+
subscription: z3.string().optional().describe("Azure Subscription ID"),
|
|
1392
|
+
resourceGroup: z3.string().optional().describe("Filter by resource group")
|
|
1215
1393
|
}, async (args) => {
|
|
1216
1394
|
const sub = args["subscription"];
|
|
1217
1395
|
const rg = args["resourceGroup"];
|
|
1218
1396
|
const sf = sub ? `--subscription ${sub}` : "";
|
|
1219
1397
|
const rf = rg ? `--resource-group ${rg}` : "";
|
|
1220
|
-
const runAz = (
|
|
1398
|
+
const runAz = createScanRunner(run, { timeout: 2e4, threshold: 3 });
|
|
1221
1399
|
const sections = [
|
|
1222
1400
|
["IDENTITY", `az account show --output json ${sf}`],
|
|
1223
1401
|
["VMS", `az vm list ${sf} ${rf} --output table`],
|
|
@@ -1234,7 +1412,7 @@ ${runAz(c)}`).join("\n\n");
|
|
|
1234
1412
|
return { content: [{ type: "text", text: out }] };
|
|
1235
1413
|
}),
|
|
1236
1414
|
tool("scan_installed_apps", "Scan all installed apps and tools \u2014 IDEs, office, dev tools, business apps, databases", {
|
|
1237
|
-
searchHint:
|
|
1415
|
+
searchHint: z3.string().optional().describe('Optional search term to find specific tools (e.g. "hubspot windsurf cursor")')
|
|
1238
1416
|
}, async (args) => {
|
|
1239
1417
|
const hint = args["searchHint"];
|
|
1240
1418
|
const results = {};
|
|
@@ -1558,68 +1736,81 @@ Then scan_local_databases() for database servers and SQLite files.
|
|
|
1558
1736
|
Then systematically scan local services, then config files.
|
|
1559
1737
|
Finally, map all edges (Step 8 \u2014 critical!) before finishing.
|
|
1560
1738
|
Use ask_user when you need context from the user.`;
|
|
1739
|
+
const MAX_DISCOVERY_MS = 30 * 60 * 1e3;
|
|
1561
1740
|
let turnCount = 0;
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1741
|
+
try {
|
|
1742
|
+
const startTime = Date.now();
|
|
1743
|
+
for await (const msg of query({
|
|
1744
|
+
prompt: initialPrompt,
|
|
1745
|
+
options: {
|
|
1746
|
+
model: config.agentModel,
|
|
1747
|
+
maxTurns: config.maxTurns,
|
|
1748
|
+
systemPrompt,
|
|
1749
|
+
mcpServers: { cartography: tools },
|
|
1750
|
+
allowedTools: [
|
|
1751
|
+
"Bash",
|
|
1752
|
+
"mcp__cartograph__save_node",
|
|
1753
|
+
"mcp__cartograph__save_edge",
|
|
1754
|
+
"mcp__cartograph__get_catalog",
|
|
1755
|
+
"mcp__cartograph__scan_bookmarks",
|
|
1756
|
+
"mcp__cartograph__scan_browser_history",
|
|
1757
|
+
"mcp__cartograph__scan_installed_apps",
|
|
1758
|
+
"mcp__cartograph__scan_local_databases",
|
|
1759
|
+
"mcp__cartograph__scan_k8s_resources",
|
|
1760
|
+
"mcp__cartograph__scan_aws_resources",
|
|
1761
|
+
"mcp__cartograph__scan_gcp_resources",
|
|
1762
|
+
"mcp__cartograph__scan_azure_resources",
|
|
1763
|
+
"mcp__cartograph__ask_user"
|
|
1764
|
+
],
|
|
1765
|
+
hooks: {
|
|
1766
|
+
PreToolUse: [{ matcher: "Bash", hooks: [safetyHook] }]
|
|
1767
|
+
},
|
|
1768
|
+
permissionMode: "bypassPermissions"
|
|
1769
|
+
}
|
|
1770
|
+
})) {
|
|
1771
|
+
if (Date.now() - startTime > MAX_DISCOVERY_MS) {
|
|
1772
|
+
onEvent?.({ kind: "error", text: `Discovery timeout after ${MAX_DISCOVERY_MS / 6e4} minutes` });
|
|
1773
|
+
onEvent?.({ kind: "done" });
|
|
1774
|
+
return;
|
|
1775
|
+
}
|
|
1776
|
+
if (!onEvent) continue;
|
|
1777
|
+
if (msg.type === "assistant") {
|
|
1778
|
+
turnCount++;
|
|
1779
|
+
onEvent({ kind: "turn", turn: turnCount });
|
|
1780
|
+
for (const block of msg.message.content) {
|
|
1781
|
+
if (block.type === "text") {
|
|
1782
|
+
onEvent({ kind: "thinking", text: block.text });
|
|
1783
|
+
}
|
|
1784
|
+
if (block.type === "tool_use") {
|
|
1785
|
+
onEvent({
|
|
1786
|
+
kind: "tool_call",
|
|
1787
|
+
tool: block.name,
|
|
1788
|
+
input: block.input
|
|
1789
|
+
});
|
|
1790
|
+
}
|
|
1604
1791
|
}
|
|
1605
1792
|
}
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1793
|
+
if (msg.type === "user") {
|
|
1794
|
+
const content = msg.message?.content;
|
|
1795
|
+
if (Array.isArray(content)) {
|
|
1796
|
+
for (const block of content) {
|
|
1797
|
+
if (typeof block === "object" && block !== null && "type" in block && block.type === "tool_result") {
|
|
1798
|
+
const tb = block;
|
|
1799
|
+
const text = typeof tb.content === "string" ? tb.content : "";
|
|
1800
|
+
onEvent({ kind: "tool_result", tool: tb.tool_use_id ?? "", output: text });
|
|
1801
|
+
}
|
|
1615
1802
|
}
|
|
1616
1803
|
}
|
|
1617
1804
|
}
|
|
1805
|
+
if (msg.type === "result") {
|
|
1806
|
+
onEvent({ kind: "done" });
|
|
1807
|
+
return;
|
|
1808
|
+
}
|
|
1618
1809
|
}
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1810
|
+
} catch (err) {
|
|
1811
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1812
|
+
onEvent?.({ kind: "error", text: `Discovery error: ${message}` });
|
|
1813
|
+
throw err;
|
|
1623
1814
|
}
|
|
1624
1815
|
}
|
|
1625
1816
|
|
|
@@ -3238,11 +3429,40 @@ function checkPrerequisites() {
|
|
|
3238
3429
|
process.stderr.write("\u2713 Eingeloggt via claude login (Subscription)\n");
|
|
3239
3430
|
}
|
|
3240
3431
|
}
|
|
3432
|
+
|
|
3433
|
+
// src/logger.ts
|
|
3434
|
+
var verboseMode = false;
|
|
3435
|
+
function setVerbose(v) {
|
|
3436
|
+
verboseMode = v;
|
|
3437
|
+
}
|
|
3438
|
+
function log(level, message, context) {
|
|
3439
|
+
if (level === "DEBUG" && !verboseMode) return;
|
|
3440
|
+
const entry = {
|
|
3441
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3442
|
+
level,
|
|
3443
|
+
message,
|
|
3444
|
+
...context && Object.keys(context).length > 0 ? { context } : {}
|
|
3445
|
+
};
|
|
3446
|
+
process.stderr.write(JSON.stringify(entry) + "\n");
|
|
3447
|
+
}
|
|
3448
|
+
function logDebug(message, context) {
|
|
3449
|
+
log("DEBUG", message, context);
|
|
3450
|
+
}
|
|
3451
|
+
function logInfo(message, context) {
|
|
3452
|
+
log("INFO", message, context);
|
|
3453
|
+
}
|
|
3454
|
+
function logWarn(message, context) {
|
|
3455
|
+
log("WARN", message, context);
|
|
3456
|
+
}
|
|
3457
|
+
function logError(message, context) {
|
|
3458
|
+
log("ERROR", message, context);
|
|
3459
|
+
}
|
|
3241
3460
|
export {
|
|
3242
3461
|
CartographyDB,
|
|
3243
3462
|
assignColors,
|
|
3244
3463
|
buildMapData,
|
|
3245
3464
|
checkPrerequisites,
|
|
3465
|
+
cleanupTempFiles,
|
|
3246
3466
|
computeCentroid,
|
|
3247
3467
|
computeClusterBounds,
|
|
3248
3468
|
createCartographyTools,
|
|
@@ -3264,10 +3484,16 @@ export {
|
|
|
3264
3484
|
hexSpiral,
|
|
3265
3485
|
hexToPixel,
|
|
3266
3486
|
layoutClusters,
|
|
3487
|
+
log,
|
|
3488
|
+
logDebug,
|
|
3489
|
+
logError,
|
|
3490
|
+
logInfo,
|
|
3491
|
+
logWarn,
|
|
3267
3492
|
nodesToAssets,
|
|
3268
3493
|
pixelToHex,
|
|
3269
3494
|
runDiscovery,
|
|
3270
3495
|
safetyHook,
|
|
3496
|
+
setVerbose,
|
|
3271
3497
|
shadeVariant,
|
|
3272
3498
|
stripSensitive
|
|
3273
3499
|
};
|