@datasynx/agentic-ai-cartography 0.1.3 → 0.1.5
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/cli.js +146 -15
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +19 -2
- package/dist/index.js +30 -4
- package/dist/index.js.map +1 -1
- package/package.json +2 -10
package/dist/cli.js
CHANGED
|
@@ -710,7 +710,7 @@ var safetyHook = async (input) => {
|
|
|
710
710
|
};
|
|
711
711
|
|
|
712
712
|
// src/agent.ts
|
|
713
|
-
async function runDiscovery(config, db, sessionId,
|
|
713
|
+
async function runDiscovery(config, db, sessionId, onEvent) {
|
|
714
714
|
const { query } = await import("@anthropic-ai/claude-code");
|
|
715
715
|
const tools = await createCartographyTools(db, sessionId);
|
|
716
716
|
const systemPrompt = `Du bist ein Infrastruktur-Discovery-Agent.
|
|
@@ -735,6 +735,7 @@ REGELN:
|
|
|
735
735
|
- KEINE Credentials speichern
|
|
736
736
|
|
|
737
737
|
Entrypoints: ${config.entryPoints.join(", ")}`;
|
|
738
|
+
let turnCount = 0;
|
|
738
739
|
for await (const msg of query({
|
|
739
740
|
prompt: systemPrompt,
|
|
740
741
|
options: {
|
|
@@ -754,14 +755,39 @@ Entrypoints: ${config.entryPoints.join(", ")}`;
|
|
|
754
755
|
permissionMode: "bypassPermissions"
|
|
755
756
|
}
|
|
756
757
|
})) {
|
|
757
|
-
if (
|
|
758
|
+
if (!onEvent) continue;
|
|
759
|
+
if (msg.type === "assistant") {
|
|
760
|
+
turnCount++;
|
|
761
|
+
onEvent({ kind: "turn", turn: turnCount });
|
|
758
762
|
for (const block of msg.message.content) {
|
|
759
763
|
if (block.type === "text") {
|
|
760
|
-
|
|
764
|
+
onEvent({ kind: "thinking", text: block.text });
|
|
765
|
+
}
|
|
766
|
+
if (block.type === "tool_use") {
|
|
767
|
+
onEvent({
|
|
768
|
+
kind: "tool_call",
|
|
769
|
+
tool: block.name,
|
|
770
|
+
input: block.input
|
|
771
|
+
});
|
|
761
772
|
}
|
|
762
773
|
}
|
|
763
774
|
}
|
|
764
|
-
if (msg.type === "
|
|
775
|
+
if (msg.type === "user") {
|
|
776
|
+
const content = msg.message?.content;
|
|
777
|
+
if (Array.isArray(content)) {
|
|
778
|
+
for (const block of content) {
|
|
779
|
+
if (typeof block === "object" && block !== null && "type" in block && block.type === "tool_result") {
|
|
780
|
+
const tb = block;
|
|
781
|
+
const text = typeof tb.content === "string" ? tb.content : "";
|
|
782
|
+
onEvent({ kind: "tool_result", tool: tb.tool_use_id ?? "", output: text });
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
if (msg.type === "result") {
|
|
788
|
+
onEvent({ kind: "done" });
|
|
789
|
+
return;
|
|
790
|
+
}
|
|
765
791
|
}
|
|
766
792
|
}
|
|
767
793
|
async function runShadowCycle(config, db, sessionId, prevSnapshot, currSnapshot, onOutput) {
|
|
@@ -1594,7 +1620,7 @@ if (process.env.CARTOGRAPHYY_DAEMON === "1") {
|
|
|
1594
1620
|
function main() {
|
|
1595
1621
|
const program = new Command();
|
|
1596
1622
|
const CMD = "datasynx-cartography";
|
|
1597
|
-
const VERSION = "0.1.
|
|
1623
|
+
const VERSION = "0.1.5";
|
|
1598
1624
|
program.name(CMD).description("AI-powered Infrastructure Cartography & SOP Generation").version(VERSION);
|
|
1599
1625
|
program.command("discover").description("Infrastruktur scannen und kartographieren").option("--entry <hosts...>", "Startpunkte", ["localhost"]).option("--depth <n>", "Max Tiefe", "8").option("--max-turns <n>", "Max Agent-Turns", "50").option("--model <m>", "Agent-Model", "claude-sonnet-4-5-20250929").option("--org <name>", "Organisation (f\xFCr Backstage)").option("-o, --output <dir>", "Output-Dir", "./datasynx-output").option("--db <path>", "DB-Pfad").option("-v, --verbose", "Agent-Reasoning anzeigen", false).action(async (opts) => {
|
|
1600
1626
|
checkPrerequisites();
|
|
@@ -1611,30 +1637,135 @@ function main() {
|
|
|
1611
1637
|
});
|
|
1612
1638
|
const db = new CartographyDB(config.dbPath);
|
|
1613
1639
|
const sessionId = db.createSession("discover", config);
|
|
1614
|
-
process.stderr.write(
|
|
1640
|
+
const w = process.stderr.write.bind(process.stderr);
|
|
1641
|
+
const bold = (s) => `\x1B[1m${s}\x1B[0m`;
|
|
1642
|
+
const dim = (s) => `\x1B[2m${s}\x1B[0m`;
|
|
1643
|
+
const cyan = (s) => `\x1B[36m${s}\x1B[0m`;
|
|
1644
|
+
const green = (s) => `\x1B[32m${s}\x1B[0m`;
|
|
1645
|
+
const yellow = (s) => `\x1B[33m${s}\x1B[0m`;
|
|
1646
|
+
const magenta = (s) => `\x1B[35m${s}\x1B[0m`;
|
|
1647
|
+
const SPINNER = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
1648
|
+
let spinIdx = 0;
|
|
1649
|
+
let spinnerTimer = null;
|
|
1650
|
+
let spinnerMsg = "";
|
|
1651
|
+
const startSpinner = (msg) => {
|
|
1652
|
+
spinnerMsg = msg;
|
|
1653
|
+
if (spinnerTimer) clearInterval(spinnerTimer);
|
|
1654
|
+
spinnerTimer = setInterval(() => {
|
|
1655
|
+
const frame = cyan(SPINNER[spinIdx % SPINNER.length] ?? "\u280B");
|
|
1656
|
+
w(`\r ${frame} ${spinnerMsg}\x1B[K`);
|
|
1657
|
+
spinIdx++;
|
|
1658
|
+
}, 80);
|
|
1659
|
+
};
|
|
1660
|
+
const stopSpinner = () => {
|
|
1661
|
+
if (spinnerTimer) {
|
|
1662
|
+
clearInterval(spinnerTimer);
|
|
1663
|
+
spinnerTimer = null;
|
|
1664
|
+
}
|
|
1665
|
+
w(`\r\x1B[K`);
|
|
1666
|
+
};
|
|
1667
|
+
const startTime = Date.now();
|
|
1668
|
+
let turnNum = 0;
|
|
1669
|
+
let nodeCount = 0;
|
|
1670
|
+
let edgeCount = 0;
|
|
1671
|
+
w("\n");
|
|
1672
|
+
w(` ${bold("DISCOVERY")} ${dim(config.entryPoints.join(", "))}
|
|
1673
|
+
`);
|
|
1674
|
+
w(` ${dim("Model: " + config.agentModel + " | MaxTurns: " + config.maxTurns)}
|
|
1675
|
+
`);
|
|
1676
|
+
w(dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n"));
|
|
1677
|
+
w("\n");
|
|
1678
|
+
const logLine = (icon, msg) => {
|
|
1679
|
+
stopSpinner();
|
|
1680
|
+
const elapsed = ((Date.now() - startTime) / 1e3).toFixed(1);
|
|
1681
|
+
w(` ${icon} ${msg} ${dim(elapsed + "s")}
|
|
1615
1682
|
`);
|
|
1616
|
-
|
|
1617
|
-
|
|
1683
|
+
};
|
|
1684
|
+
const handleEvent = (event) => {
|
|
1685
|
+
switch (event.kind) {
|
|
1686
|
+
case "turn":
|
|
1687
|
+
turnNum = event.turn;
|
|
1688
|
+
startSpinner(`Turn ${turnNum}/${config.maxTurns} ${dim(`nodes:${nodeCount} edges:${edgeCount}`)}`);
|
|
1689
|
+
break;
|
|
1690
|
+
case "thinking":
|
|
1691
|
+
if (config.verbose) {
|
|
1692
|
+
stopSpinner();
|
|
1693
|
+
const lines = event.text.split("\n").slice(0, 3);
|
|
1694
|
+
for (const line of lines) {
|
|
1695
|
+
w(` ${dim(" " + line.substring(0, 80))}
|
|
1618
1696
|
`);
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
break;
|
|
1700
|
+
case "tool_call": {
|
|
1701
|
+
const toolName = event.tool.replace("mcp__cartograph__", "");
|
|
1702
|
+
if (toolName === "Bash") {
|
|
1703
|
+
const cmd = (event.input["command"] ?? "").substring(0, 70);
|
|
1704
|
+
startSpinner(`${yellow("$")} ${cmd}`);
|
|
1705
|
+
} else if (toolName === "save_node") {
|
|
1706
|
+
const id = event.input["id"] ?? "?";
|
|
1707
|
+
const type = event.input["type"] ?? "?";
|
|
1708
|
+
nodeCount++;
|
|
1709
|
+
logLine(green("+"), `${bold("Node")} ${cyan(id)} ${dim("(" + type + ")")}`);
|
|
1710
|
+
startSpinner(`Turn ${turnNum}/${config.maxTurns} ${dim(`nodes:${nodeCount} edges:${edgeCount}`)}`);
|
|
1711
|
+
} else if (toolName === "save_edge") {
|
|
1712
|
+
const src = event.input["sourceId"] ?? "?";
|
|
1713
|
+
const tgt = event.input["targetId"] ?? "?";
|
|
1714
|
+
const rel = event.input["relationship"] ?? "\u2192";
|
|
1715
|
+
edgeCount++;
|
|
1716
|
+
logLine(magenta("~"), `${bold("Edge")} ${src} ${dim(rel)} ${cyan(tgt)}`);
|
|
1717
|
+
startSpinner(`Turn ${turnNum}/${config.maxTurns} ${dim(`nodes:${nodeCount} edges:${edgeCount}`)}`);
|
|
1718
|
+
} else if (toolName === "get_catalog") {
|
|
1719
|
+
startSpinner(`Catalog-Check ${dim("(Duplikate vermeiden)")}`);
|
|
1720
|
+
} else {
|
|
1721
|
+
startSpinner(`${toolName}...`);
|
|
1722
|
+
}
|
|
1723
|
+
break;
|
|
1724
|
+
}
|
|
1725
|
+
case "tool_result":
|
|
1726
|
+
break;
|
|
1727
|
+
case "done":
|
|
1728
|
+
stopSpinner();
|
|
1729
|
+
break;
|
|
1730
|
+
}
|
|
1731
|
+
};
|
|
1619
1732
|
try {
|
|
1620
|
-
await runDiscovery(config, db, sessionId,
|
|
1621
|
-
if (config.verbose) process.stdout.write(text + "\n");
|
|
1622
|
-
});
|
|
1733
|
+
await runDiscovery(config, db, sessionId, handleEvent);
|
|
1623
1734
|
} catch (err) {
|
|
1624
|
-
|
|
1735
|
+
stopSpinner();
|
|
1736
|
+
w(`
|
|
1737
|
+
${bold("\x1B[31m\u2717\x1B[0m")} Discovery fehlgeschlagen: ${err}
|
|
1625
1738
|
`);
|
|
1626
1739
|
db.close();
|
|
1627
1740
|
process.exitCode = 1;
|
|
1628
1741
|
return;
|
|
1629
1742
|
}
|
|
1743
|
+
stopSpinner();
|
|
1630
1744
|
db.endSession(sessionId);
|
|
1631
1745
|
const stats = db.getStats(sessionId);
|
|
1632
|
-
|
|
1633
|
-
\
|
|
1746
|
+
const totalSec = ((Date.now() - startTime) / 1e3).toFixed(1);
|
|
1747
|
+
w("\n");
|
|
1748
|
+
w(dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n"));
|
|
1749
|
+
w(` ${green(bold("DONE"))} ${bold(String(stats.nodes))} nodes, ${bold(String(stats.edges))} edges ${dim("in " + totalSec + "s")}
|
|
1634
1750
|
`);
|
|
1751
|
+
w("\n");
|
|
1635
1752
|
exportAll(db, sessionId, config.outputDir);
|
|
1636
|
-
|
|
1753
|
+
w(` ${green("\u2713")} Exported to ${bold(config.outputDir)}
|
|
1754
|
+
`);
|
|
1755
|
+
const nodeList = db.getNodes(sessionId).slice(0, 8);
|
|
1756
|
+
if (nodeList.length > 0) {
|
|
1757
|
+
w("\n");
|
|
1758
|
+
w(` ${bold("Discovered:")}
|
|
1637
1759
|
`);
|
|
1760
|
+
for (const n of nodeList) {
|
|
1761
|
+
const tag = dim(`[${n.type}]`);
|
|
1762
|
+
w(` ${cyan("\u25CF")} ${n.id} ${tag}
|
|
1763
|
+
`);
|
|
1764
|
+
}
|
|
1765
|
+
if (stats.nodes > 8) w(dim(` ... +${stats.nodes - 8} more
|
|
1766
|
+
`));
|
|
1767
|
+
}
|
|
1768
|
+
w("\n");
|
|
1638
1769
|
db.close();
|
|
1639
1770
|
});
|
|
1640
1771
|
const shadow = program.command("shadow").description("Shadow-Daemon verwalten");
|