@datasynx/agentic-ai-cartography 0.2.0 → 0.2.1
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 +253 -14
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +200 -12
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -741,6 +741,174 @@ ${run(c)}`).join("\n\n");
|
|
|
741
741
|
${run(c)}`).join("\n\n");
|
|
742
742
|
return { content: [{ type: "text", text: out }] };
|
|
743
743
|
}),
|
|
744
|
+
tool("scan_installed_apps", "Alle installierten Apps und Tools auf dem PC scannen \u2014 IDEs, Office, Dev-Tools, Business-Apps", {
|
|
745
|
+
searchHint: z.string().optional().describe('Optionaler Suchbegriff um gezielt nach bestimmten Tools zu suchen (z.B. "hubspot windsurf cursor")')
|
|
746
|
+
}, async (args) => {
|
|
747
|
+
const { execSync: execSync3 } = await import("child_process");
|
|
748
|
+
const hint = args["searchHint"];
|
|
749
|
+
const run = (cmd) => {
|
|
750
|
+
try {
|
|
751
|
+
return execSync3(cmd, { stdio: "pipe", timeout: 15e3, shell: "/bin/sh" }).toString().trim();
|
|
752
|
+
} catch {
|
|
753
|
+
return "";
|
|
754
|
+
}
|
|
755
|
+
};
|
|
756
|
+
const platform = process.platform;
|
|
757
|
+
const results = {};
|
|
758
|
+
if (platform === "darwin") {
|
|
759
|
+
results["APPLICATIONS"] = run("ls /Applications/ 2>/dev/null | head -200") || "(leer)";
|
|
760
|
+
results["USER_APPLICATIONS"] = run("ls ~/Applications/ 2>/dev/null | head -100") || "(leer)";
|
|
761
|
+
results["BREW_CASKS"] = run("brew list --cask 2>/dev/null | head -100") || "(brew nicht installiert)";
|
|
762
|
+
results["BREW_FORMULAE"] = run("brew list --formula 2>/dev/null | head -150") || "(brew nicht installiert)";
|
|
763
|
+
results["SPOTLIGHT_APPS"] = run(`mdfind "kMDItemKind == 'Application'" 2>/dev/null | grep -v "^/System" | grep -v "^/Library/Apple" | head -100`) || "(Spotlight nicht verf\xFCgbar)";
|
|
764
|
+
} else if (platform === "linux") {
|
|
765
|
+
results["DPKG"] = run("dpkg --list 2>/dev/null | awk '{print $2}' | head -200") || "(dpkg nicht verf\xFCgbar)";
|
|
766
|
+
results["SNAP"] = run("snap list 2>/dev/null | head -50") || "(snap nicht verf\xFCgbar)";
|
|
767
|
+
results["FLATPAK"] = run("flatpak list 2>/dev/null | head -50") || "(flatpak nicht verf\xFCgbar)";
|
|
768
|
+
results["DESKTOP_FILES"] = run("ls /usr/share/applications/*.desktop ~/.local/share/applications/*.desktop 2>/dev/null | xargs -I{} basename {} .desktop 2>/dev/null | head -100") || "(keine .desktop files)";
|
|
769
|
+
results["RPM"] = run("rpm -qa 2>/dev/null | head -200") || "(rpm nicht verf\xFCgbar)";
|
|
770
|
+
} else if (platform === "win32") {
|
|
771
|
+
results["WINGET"] = run("winget list 2>/dev/null | head -100") || "(winget nicht verf\xFCgbar)";
|
|
772
|
+
results["PROGRAMS_x64"] = run('reg query "HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall" /s /v DisplayName 2>/dev/null | findstr DisplayName | head -100') || "(nicht verf\xFCgbar)";
|
|
773
|
+
}
|
|
774
|
+
const knownTools = [
|
|
775
|
+
// IDEs & Editors
|
|
776
|
+
"code",
|
|
777
|
+
"code-insiders",
|
|
778
|
+
"cursor",
|
|
779
|
+
"windsurf",
|
|
780
|
+
"zed",
|
|
781
|
+
"vim",
|
|
782
|
+
"nvim",
|
|
783
|
+
"emacs",
|
|
784
|
+
"nano",
|
|
785
|
+
"sublime_text",
|
|
786
|
+
"atom",
|
|
787
|
+
"idea",
|
|
788
|
+
"webstorm",
|
|
789
|
+
"pycharm",
|
|
790
|
+
"goland",
|
|
791
|
+
"datagrip",
|
|
792
|
+
"clion",
|
|
793
|
+
"rider",
|
|
794
|
+
"phpstorm",
|
|
795
|
+
"rubymine",
|
|
796
|
+
"appcode",
|
|
797
|
+
// Dev Tools
|
|
798
|
+
"git",
|
|
799
|
+
"gh",
|
|
800
|
+
"docker",
|
|
801
|
+
"docker-compose",
|
|
802
|
+
"podman",
|
|
803
|
+
"kubectl",
|
|
804
|
+
"helm",
|
|
805
|
+
"terraform",
|
|
806
|
+
"ansible",
|
|
807
|
+
"node",
|
|
808
|
+
"npm",
|
|
809
|
+
"npx",
|
|
810
|
+
"yarn",
|
|
811
|
+
"pnpm",
|
|
812
|
+
"bun",
|
|
813
|
+
"deno",
|
|
814
|
+
"python",
|
|
815
|
+
"python3",
|
|
816
|
+
"pip",
|
|
817
|
+
"pip3",
|
|
818
|
+
"pipenv",
|
|
819
|
+
"poetry",
|
|
820
|
+
"conda",
|
|
821
|
+
"ruby",
|
|
822
|
+
"gem",
|
|
823
|
+
"bundler",
|
|
824
|
+
"rails",
|
|
825
|
+
"java",
|
|
826
|
+
"mvn",
|
|
827
|
+
"gradle",
|
|
828
|
+
"kotlin",
|
|
829
|
+
"go",
|
|
830
|
+
"cargo",
|
|
831
|
+
"rustc",
|
|
832
|
+
"php",
|
|
833
|
+
"composer",
|
|
834
|
+
"dotnet",
|
|
835
|
+
"dotnet-sdk",
|
|
836
|
+
// Databases
|
|
837
|
+
"psql",
|
|
838
|
+
"mysql",
|
|
839
|
+
"mysqladmin",
|
|
840
|
+
"mongo",
|
|
841
|
+
"mongosh",
|
|
842
|
+
"redis-cli",
|
|
843
|
+
"sqlite3",
|
|
844
|
+
"clickhouse-client",
|
|
845
|
+
// Cloud CLIs
|
|
846
|
+
"aws",
|
|
847
|
+
"gcloud",
|
|
848
|
+
"az",
|
|
849
|
+
"heroku",
|
|
850
|
+
"fly",
|
|
851
|
+
"vercel",
|
|
852
|
+
"netlify",
|
|
853
|
+
"wrangler",
|
|
854
|
+
// Infra
|
|
855
|
+
"vagrant",
|
|
856
|
+
"packer",
|
|
857
|
+
"consul",
|
|
858
|
+
"vault",
|
|
859
|
+
"nomad",
|
|
860
|
+
// Communication / SaaS
|
|
861
|
+
"slack",
|
|
862
|
+
"discord",
|
|
863
|
+
"zoom",
|
|
864
|
+
"teams",
|
|
865
|
+
"skype",
|
|
866
|
+
"telegram",
|
|
867
|
+
"signal",
|
|
868
|
+
// Browsers
|
|
869
|
+
"google-chrome",
|
|
870
|
+
"chromium",
|
|
871
|
+
"firefox",
|
|
872
|
+
"safari",
|
|
873
|
+
"brave",
|
|
874
|
+
"opera",
|
|
875
|
+
"edge",
|
|
876
|
+
// Monitoring / Analytics
|
|
877
|
+
"datadog-agent",
|
|
878
|
+
"newrelic-agent",
|
|
879
|
+
"prometheus",
|
|
880
|
+
"grafana-cli",
|
|
881
|
+
// Other tools
|
|
882
|
+
"ngrok",
|
|
883
|
+
"stripe",
|
|
884
|
+
"supabase",
|
|
885
|
+
"neon"
|
|
886
|
+
];
|
|
887
|
+
const found = [];
|
|
888
|
+
const notFound = [];
|
|
889
|
+
for (const t of knownTools) {
|
|
890
|
+
const r = run(`which ${t} 2>/dev/null`);
|
|
891
|
+
if (r) found.push(`${t}: ${r}`);
|
|
892
|
+
else notFound.push(t);
|
|
893
|
+
}
|
|
894
|
+
results["WHICH_FOUND"] = found.join("\n") || "(nichts gefunden)";
|
|
895
|
+
results["WHICH_NOT_FOUND"] = notFound.join(", ");
|
|
896
|
+
if (hint) {
|
|
897
|
+
const terms = hint.split(/[\s,]+/).filter(Boolean);
|
|
898
|
+
const hintResults = [];
|
|
899
|
+
for (const term of terms) {
|
|
900
|
+
const safe = term.replace(/[^a-zA-Z0-9._-]/g, "");
|
|
901
|
+
if (!safe) continue;
|
|
902
|
+
const r = run(`which ${safe} 2>/dev/null || find /Applications ~/Applications /usr/bin /usr/local/bin /opt/homebrew/bin ~/.local/bin 2>/dev/null -iname "*${safe}*" -maxdepth 3 2>/dev/null | head -5`);
|
|
903
|
+
if (r) hintResults.push(`${term}: ${r}`);
|
|
904
|
+
else hintResults.push(`${term}: (nicht gefunden)`);
|
|
905
|
+
}
|
|
906
|
+
results["HINT_SEARCH"] = hintResults.join("\n");
|
|
907
|
+
}
|
|
908
|
+
const out = Object.entries(results).map(([k, v]) => `=== ${k} ===
|
|
909
|
+
${v}`).join("\n\n");
|
|
910
|
+
return { content: [{ type: "text", text: out }] };
|
|
911
|
+
}),
|
|
744
912
|
tool("save_sop", "Standard Operating Procedure speichern", {
|
|
745
913
|
workflowId: z.string(),
|
|
746
914
|
title: z.string(),
|
|
@@ -796,11 +964,15 @@ var safetyHook = async (input) => {
|
|
|
796
964
|
};
|
|
797
965
|
|
|
798
966
|
// src/agent.ts
|
|
799
|
-
async function runDiscovery(config, db, sessionId, onEvent, onAskUser) {
|
|
967
|
+
async function runDiscovery(config, db, sessionId, onEvent, onAskUser, hint) {
|
|
800
968
|
const { query } = await import("@anthropic-ai/claude-code");
|
|
801
969
|
const tools = await createCartographyTools(db, sessionId, { onAskUser });
|
|
802
|
-
const
|
|
803
|
-
|
|
970
|
+
const hintSection = hint ? `
|
|
971
|
+
\u26A1 USER-HINT (PRIORIT\xC4T): Der User m\xF6chte gezielt nach folgenden Tools suchen: "${hint}"
|
|
972
|
+
\u2192 scan_installed_apps(searchHint: "${hint}") SOFORT ausf\xFChren und diese Tools als saas_tool oder config_file speichern!
|
|
973
|
+
` : "";
|
|
974
|
+
const systemPrompt = `Du bist ein Infrastruktur-Discovery-Agent. Kartographiere die gesamte Systemlandschaft \u2014 lokale Services, SaaS-Tools UND alle installierten Apps/Tools des Users.
|
|
975
|
+
${hintSection}
|
|
804
976
|
\u2501\u2501 PFLICHT-REIHENFOLGE \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
805
977
|
SCHRITT 1 \u2014 Browser-Lesezeichen (IMMER ZUERST):
|
|
806
978
|
scan_bookmarks() aufrufen \u2192 jede zur\xFCckgegebene Domain klassifizieren:
|
|
@@ -808,26 +980,36 @@ SCHRITT 1 \u2014 Browser-Lesezeichen (IMMER ZUERST):
|
|
|
808
980
|
\u2022 Interne Hosts (IPs, custom.company.com:PORT) \u2192 save_node als web_service
|
|
809
981
|
\u2022 Pers\xF6nliches (Social Media, News, Streaming, Shopping) \u2192 IGNORIEREN, NICHT speichern
|
|
810
982
|
|
|
811
|
-
SCHRITT 2 \u2014
|
|
983
|
+
SCHRITT 2 \u2014 Installierte Apps & Tools (SEHR WICHTIG):
|
|
984
|
+
scan_installed_apps() aufrufen \u2192 ALLE gefundenen Apps/Tools klassifizieren:
|
|
985
|
+
\u2022 IDEs (VS Code, Cursor, Windsurf, JetBrains, etc.) \u2192 save_node als saas_tool mit category="ide"
|
|
986
|
+
\u2022 Office & Produktivit\xE4t (Word, Excel, Notion, Obsidian, etc.) \u2192 save_node als saas_tool mit category="productivity"
|
|
987
|
+
\u2022 Dev-Tools (Docker, kubectl, git, Node, Python, etc.) \u2192 save_node als saas_tool mit category="dev-tool"
|
|
988
|
+
\u2022 Business-Apps (Slack, Zoom, HubSpot, Salesforce, etc.) \u2192 save_node als saas_tool mit category="business"
|
|
989
|
+
\u2022 Browser (Chrome, Firefox, Safari, etc.) \u2192 save_node als saas_tool mit category="browser"
|
|
990
|
+
\u2022 Design-Tools (Figma, Sketch, Adobe, etc.) \u2192 save_node als saas_tool mit category="design"
|
|
991
|
+
ALLE relevanten Tools speichern \u2014 auch wenn offline/lokal!
|
|
992
|
+
|
|
993
|
+
SCHRITT 3 \u2014 Lokale Infrastruktur:
|
|
812
994
|
ss -tlnp && ps aux \u2192 alle lauschenden Ports/Prozesse identifizieren
|
|
813
995
|
Jeden Service vertiefen: DB\u2192Schemas, API\u2192Endpoints, Queue\u2192Topics
|
|
814
996
|
|
|
815
|
-
SCHRITT
|
|
997
|
+
SCHRITT 4 \u2014 Cloud & Kubernetes (falls CLI vorhanden):
|
|
816
998
|
scan_k8s_resources() \u2192 Nodes, Services, Pods, Deployments, Ingresses
|
|
817
999
|
scan_aws_resources() \u2192 EC2, RDS, ELB, EKS, ElastiCache, S3 (falls AWS CLI + Credentials)
|
|
818
1000
|
scan_gcp_resources() \u2192 Compute, SQL, GKE, Cloud Run, Functions (falls gcloud + Auth)
|
|
819
1001
|
scan_azure_resources() \u2192 VMs, AKS, SQL, Redis, WebApps (falls az CLI + Login)
|
|
820
1002
|
Fehler / "nicht verf\xFCgbar" \u2192 ignorieren, weiter mit n\xE4chstem Tool
|
|
821
1003
|
|
|
822
|
-
SCHRITT
|
|
1004
|
+
SCHRITT 5 \u2014 Config-Files:
|
|
823
1005
|
.env, docker-compose.yml, application.yml, kubernetes/*.yml
|
|
824
1006
|
Nur Host:Port extrahieren \u2014 KEINE Credentials
|
|
825
1007
|
|
|
826
|
-
SCHRITT
|
|
1008
|
+
SCHRITT 6 \u2014 R\xFCckfragen bei Unklarheit:
|
|
827
1009
|
ask_user() nutzen wenn: Dienst unklar ist, Kontext fehlt, oder User Input sinnvoll w\xE4re
|
|
828
1010
|
Beispiele: "Welche Umgebung ist das (dev/staging/prod)?", "Ist <host> ein internes Tool?"
|
|
829
1011
|
|
|
830
|
-
SCHRITT
|
|
1012
|
+
SCHRITT 7 \u2014 Fertig wenn alle Spuren ersch\xF6pft.
|
|
831
1013
|
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
832
1014
|
|
|
833
1015
|
PORT-MAPPING: 5432=postgres, 3306=mysql, 27017=mongodb, 6379=redis,
|
|
@@ -837,15 +1019,20 @@ PORT-MAPPING: 5432=postgres, 3306=mysql, 27017=mongodb, 6379=redis,
|
|
|
837
1019
|
REGELN:
|
|
838
1020
|
\u2022 Nur read-only (ss, ps, cat, head, curl -s, docker inspect, kubectl get)
|
|
839
1021
|
\u2022 Node IDs: "type:host:port" oder "type:name" \u2014 keine Pfade, keine Credentials
|
|
840
|
-
\u2022 saas_tool IDs: "saas_tool:github.com", "saas_tool:
|
|
841
|
-
\u2022
|
|
842
|
-
\u2022
|
|
1022
|
+
\u2022 saas_tool IDs: "saas_tool:github.com", "saas_tool:vscode", "saas_tool:cursor"
|
|
1023
|
+
\u2022 Installed-App IDs: "saas_tool:<appname>" z.B. "saas_tool:slack", "saas_tool:docker-desktop"
|
|
1024
|
+
\u2022 Confidence: 0.9 direkt gesehen, 0.7 aus Config/Bookmarks/Apps, 0.5 Vermutung
|
|
1025
|
+
\u2022 metadata erlaubt: { description, category, port, version, path } \u2014 keine Passw\xF6rter
|
|
843
1026
|
\u2022 get_catalog vor save_node \u2192 Duplikate vermeiden
|
|
844
1027
|
\u2022 Edges speichern wenn Verbindungen klar erkennbar sind
|
|
845
1028
|
|
|
846
1029
|
Entrypoints: ${config.entryPoints.join(", ")}`;
|
|
847
|
-
const initialPrompt = `Starte Discovery
|
|
1030
|
+
const initialPrompt = hint ? `Starte Discovery mit USER-HINT: "${hint}".
|
|
1031
|
+
F\xFChre SOFORT scan_installed_apps(searchHint: "${hint}") aus um nach diesen Tools zu suchen.
|
|
1032
|
+
Dann scan_bookmarks, dann lokale Services.
|
|
1033
|
+
Nutze ask_user wenn du Kontext vom User brauchst.` : `Starte Discovery jetzt.
|
|
848
1034
|
F\xFChre SOFORT als erstes scan_bookmarks aus \u2014 noch bevor du ss oder ps verwendest.
|
|
1035
|
+
Danach scan_installed_apps() f\xFCr alle installierten Apps und Tools.
|
|
849
1036
|
Dann systematisch lokale Services, dann Config-Files.
|
|
850
1037
|
Nutze ask_user wenn du Kontext vom User brauchst.`;
|
|
851
1038
|
let turnCount = 0;
|
|
@@ -862,6 +1049,7 @@ Nutze ask_user wenn du Kontext vom User brauchst.`;
|
|
|
862
1049
|
"mcp__cartograph__save_edge",
|
|
863
1050
|
"mcp__cartograph__get_catalog",
|
|
864
1051
|
"mcp__cartograph__scan_bookmarks",
|
|
1052
|
+
"mcp__cartograph__scan_installed_apps",
|
|
865
1053
|
"mcp__cartograph__scan_k8s_resources",
|
|
866
1054
|
"mcp__cartograph__scan_aws_resources",
|
|
867
1055
|
"mcp__cartograph__scan_gcp_resources",
|
|
@@ -1613,7 +1801,7 @@ if (process.env.CARTOGRAPHYY_DAEMON === "1") {
|
|
|
1613
1801
|
function main() {
|
|
1614
1802
|
const program = new Command();
|
|
1615
1803
|
const CMD = "datasynx-cartography";
|
|
1616
|
-
const VERSION = "0.2.
|
|
1804
|
+
const VERSION = "0.2.1";
|
|
1617
1805
|
program.name(CMD).description("AI-powered Infrastructure Cartography & SOP Generation").version(VERSION);
|
|
1618
1806
|
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) => {
|
|
1619
1807
|
checkPrerequisites();
|
|
@@ -1707,6 +1895,10 @@ function main() {
|
|
|
1707
1895
|
} else if (toolName === "scan_bookmarks") {
|
|
1708
1896
|
logLine(cyan("\u{1F516}"), `Browser-Lesezeichen werden gescannt\u2026`);
|
|
1709
1897
|
startSpinner(`scan_bookmarks`);
|
|
1898
|
+
} else if (toolName === "scan_installed_apps") {
|
|
1899
|
+
const sh = event.input["searchHint"];
|
|
1900
|
+
logLine(cyan("\u{1F5A5}"), sh ? `Installierte Apps gesucht: ${bold(sh)}` : `Alle installierten Apps werden gescannt\u2026`);
|
|
1901
|
+
startSpinner(`scan_installed_apps`);
|
|
1710
1902
|
} else if (toolName === "ask_user") {
|
|
1711
1903
|
const q = (event.input["question"] ?? "").substring(0, 100);
|
|
1712
1904
|
logLine(yellow("?"), `${bold("Agent fragt:")} ${q}`);
|
|
@@ -1743,7 +1935,7 @@ function main() {
|
|
|
1743
1935
|
return answer || "(Keine Antwort \u2014 bitte fortfahren)";
|
|
1744
1936
|
};
|
|
1745
1937
|
try {
|
|
1746
|
-
await runDiscovery(config, db, sessionId, handleEvent, onAskUser);
|
|
1938
|
+
await runDiscovery(config, db, sessionId, handleEvent, onAskUser, void 0);
|
|
1747
1939
|
} catch (err) {
|
|
1748
1940
|
stopSpinner();
|
|
1749
1941
|
w(`
|
|
@@ -1821,6 +2013,53 @@ function main() {
|
|
|
1821
2013
|
}
|
|
1822
2014
|
}
|
|
1823
2015
|
w("\n");
|
|
2016
|
+
if (process.stdin.isTTY) {
|
|
2017
|
+
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"));
|
|
2018
|
+
w(` ${bold("WEITERSUCHEN")} ${dim("Discovery interaktiv verfeinern")}
|
|
2019
|
+
`);
|
|
2020
|
+
w(dim(' Gib Suchbegriffe ein (z.B. "hubspot windsurf cursor") oder Enter zum Beenden.\n'));
|
|
2021
|
+
w("\n");
|
|
2022
|
+
nodeCount = 0;
|
|
2023
|
+
edgeCount = 0;
|
|
2024
|
+
let continueDiscovery = true;
|
|
2025
|
+
while (continueDiscovery) {
|
|
2026
|
+
const rlFollowup = createInterface({ input: process.stdin, output: process.stderr });
|
|
2027
|
+
const followupHint = await new Promise(
|
|
2028
|
+
(resolve2) => rlFollowup.question(` ${yellow("\u2192")} Suche nach (Enter = Beenden): `, resolve2)
|
|
2029
|
+
);
|
|
2030
|
+
rlFollowup.close();
|
|
2031
|
+
if (!followupHint.trim()) {
|
|
2032
|
+
continueDiscovery = false;
|
|
2033
|
+
break;
|
|
2034
|
+
}
|
|
2035
|
+
const followupHintTrimmed = followupHint.trim();
|
|
2036
|
+
w("\n");
|
|
2037
|
+
w(` ${cyan(bold("\u27F3"))} Suche nach: ${bold(followupHintTrimmed)}
|
|
2038
|
+
`);
|
|
2039
|
+
w("\n");
|
|
2040
|
+
try {
|
|
2041
|
+
await runDiscovery(config, db, sessionId, handleEvent, onAskUser, followupHintTrimmed);
|
|
2042
|
+
} catch (err) {
|
|
2043
|
+
stopSpinner();
|
|
2044
|
+
w(`
|
|
2045
|
+
${red("\u2717")} Fehler: ${err}
|
|
2046
|
+
`);
|
|
2047
|
+
}
|
|
2048
|
+
stopSpinner();
|
|
2049
|
+
const followupStats = db.getStats(sessionId);
|
|
2050
|
+
w("\n");
|
|
2051
|
+
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"));
|
|
2052
|
+
w(` ${green(bold("\u2713"))} Gesamt jetzt: ${bold(String(followupStats.nodes))} nodes, ${bold(String(followupStats.edges))} edges
|
|
2053
|
+
`);
|
|
2054
|
+
w("\n");
|
|
2055
|
+
exportAll(db, sessionId, config.outputDir);
|
|
2056
|
+
if (existsSync4(htmlPath)) {
|
|
2057
|
+
w(` ${green("\u2192")} ${osc8(`file://${htmlPath}`, bold("topology.html aktualisiert"))}
|
|
2058
|
+
`);
|
|
2059
|
+
}
|
|
2060
|
+
w("\n");
|
|
2061
|
+
}
|
|
2062
|
+
}
|
|
1824
2063
|
db.close();
|
|
1825
2064
|
});
|
|
1826
2065
|
const shadow = program.command("shadow").description("Shadow-Daemon verwalten");
|