@datasynx/agentic-ai-cartography 0.7.0 → 0.8.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/README.md +57 -26
- package/dist/{bookmarks-ITLW7U5D.js → bookmarks-72CDYAHD.js} +2 -2
- package/dist/chunk-3NVQ3ND6.js +412 -0
- package/dist/chunk-3NVQ3ND6.js.map +1 -0
- package/dist/{chunk-A7FTULDM.js → chunk-VZO6XBKX.js} +3 -3
- package/dist/chunk-VZO6XBKX.js.map +1 -0
- package/dist/cli.js +250 -155
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +40 -166
- package/dist/index.js +365 -182
- package/dist/index.js.map +1 -1
- package/dist/{types-ZD6G5JKR.js → types-RHMJ6EGX.js} +2 -2
- package/package.json +7 -7
- package/dist/chunk-2VIAXA5T.js +0 -258
- package/dist/chunk-2VIAXA5T.js.map +0 -1
- package/dist/chunk-A7FTULDM.js.map +0 -1
- /package/dist/{bookmarks-ITLW7U5D.js.map → bookmarks-72CDYAHD.js.map} +0 -0
- /package/dist/{types-ZD6G5JKR.js.map → types-RHMJ6EGX.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -527,7 +527,7 @@ var NodeSchema = z.object({
|
|
|
527
527
|
name: z.string(),
|
|
528
528
|
discoveredVia: z.string(),
|
|
529
529
|
confidence: z.number().min(0).max(1).default(0.5),
|
|
530
|
-
metadata: z.record(z.unknown()).default({}),
|
|
530
|
+
metadata: z.record(z.string(), z.unknown()).default({}),
|
|
531
531
|
tags: z.array(z.string()).default([]),
|
|
532
532
|
domain: z.string().optional().describe('Business domain, e.g. "Marketing", "Finance"'),
|
|
533
533
|
subDomain: z.string().optional().describe('Sub-domain, e.g. "Forecast client orders"'),
|
|
@@ -562,7 +562,7 @@ var DataAssetSchema = z.object({
|
|
|
562
562
|
domain: z.string(),
|
|
563
563
|
subDomain: z.string().optional(),
|
|
564
564
|
qualityScore: z.number().min(0).max(100).optional(),
|
|
565
|
-
metadata: z.record(z.unknown()).default({}),
|
|
565
|
+
metadata: z.record(z.string(), z.unknown()).default({}),
|
|
566
566
|
position: z.object({ q: z.number(), r: z.number() })
|
|
567
567
|
});
|
|
568
568
|
var ClusterSchema = z.object({
|
|
@@ -628,9 +628,154 @@ function defaultConfig(overrides = {}) {
|
|
|
628
628
|
}
|
|
629
629
|
|
|
630
630
|
// src/bookmarks.ts
|
|
631
|
-
import {
|
|
632
|
-
import { existsSync, readFileSync, readdirSync, copyFileSync, statSync } from "fs";
|
|
631
|
+
import { tmpdir } from "os";
|
|
632
|
+
import { existsSync as existsSync2, readFileSync, readdirSync, copyFileSync, statSync } from "fs";
|
|
633
|
+
import { join as join2 } from "path";
|
|
634
|
+
|
|
635
|
+
// src/platform.ts
|
|
636
|
+
import { homedir } from "os";
|
|
633
637
|
import { join } from "path";
|
|
638
|
+
import { execSync } from "child_process";
|
|
639
|
+
import { existsSync } from "fs";
|
|
640
|
+
var PLATFORM = process.platform;
|
|
641
|
+
var IS_WIN = PLATFORM === "win32";
|
|
642
|
+
var IS_MAC = PLATFORM === "darwin";
|
|
643
|
+
var IS_LINUX = PLATFORM === "linux";
|
|
644
|
+
var HOME = homedir();
|
|
645
|
+
function platformShell() {
|
|
646
|
+
if (!IS_WIN) return "/bin/sh";
|
|
647
|
+
try {
|
|
648
|
+
execSync("pwsh -Version", { stdio: "pipe", timeout: 3e3 });
|
|
649
|
+
return "pwsh";
|
|
650
|
+
} catch {
|
|
651
|
+
return "powershell.exe";
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
var _shell;
|
|
655
|
+
function getShell() {
|
|
656
|
+
if (!_shell) _shell = platformShell();
|
|
657
|
+
return _shell;
|
|
658
|
+
}
|
|
659
|
+
function run(cmd, opts = {}) {
|
|
660
|
+
try {
|
|
661
|
+
return execSync(cmd, {
|
|
662
|
+
stdio: "pipe",
|
|
663
|
+
timeout: opts.timeout ?? 1e4,
|
|
664
|
+
shell: getShell(),
|
|
665
|
+
env: opts.env
|
|
666
|
+
}).toString().trim();
|
|
667
|
+
} catch {
|
|
668
|
+
return "";
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
function commandExists(cmd) {
|
|
672
|
+
if (IS_WIN) {
|
|
673
|
+
const r = run(`Get-Command ${cmd} -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Source`, { timeout: 5e3 });
|
|
674
|
+
return r;
|
|
675
|
+
}
|
|
676
|
+
return run(`which ${cmd} 2>/dev/null`);
|
|
677
|
+
}
|
|
678
|
+
function userDataDir() {
|
|
679
|
+
if (IS_WIN) return process.env.APPDATA ?? join(HOME, "AppData", "Roaming");
|
|
680
|
+
if (IS_MAC) return join(HOME, "Library", "Application Support");
|
|
681
|
+
return process.env.XDG_DATA_HOME ?? join(HOME, ".local", "share");
|
|
682
|
+
}
|
|
683
|
+
function browserBasePaths() {
|
|
684
|
+
if (IS_WIN) {
|
|
685
|
+
const local = process.env.LOCALAPPDATA ?? join(HOME, "AppData", "Local");
|
|
686
|
+
return {
|
|
687
|
+
chrome: join(local, "Google", "Chrome", "User Data"),
|
|
688
|
+
chromium: join(local, "Chromium", "User Data"),
|
|
689
|
+
edge: join(local, "Microsoft", "Edge", "User Data"),
|
|
690
|
+
brave: join(local, "BraveSoftware", "Brave-Browser", "User Data"),
|
|
691
|
+
vivaldi: join(local, "Vivaldi", "User Data"),
|
|
692
|
+
opera: join(userDataDir(), "Opera Software", "Opera Stable")
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
if (IS_MAC) {
|
|
696
|
+
const lib = join(HOME, "Library", "Application Support");
|
|
697
|
+
return {
|
|
698
|
+
chrome: join(lib, "Google", "Chrome"),
|
|
699
|
+
chromium: join(lib, "Chromium"),
|
|
700
|
+
edge: join(lib, "Microsoft Edge"),
|
|
701
|
+
brave: join(lib, "BraveSoftware", "Brave-Browser"),
|
|
702
|
+
vivaldi: join(lib, "Vivaldi"),
|
|
703
|
+
opera: join(lib, "com.operasoftware.Opera")
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
return {
|
|
707
|
+
chrome: join(HOME, ".config", "google-chrome"),
|
|
708
|
+
chromium: join(HOME, ".config", "chromium"),
|
|
709
|
+
edge: join(HOME, ".config", "microsoft-edge"),
|
|
710
|
+
brave: join(HOME, ".config", "BraveSoftware", "Brave-Browser"),
|
|
711
|
+
vivaldi: join(HOME, ".config", "vivaldi"),
|
|
712
|
+
opera: join(HOME, ".config", "opera")
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
function firefoxBaseDirs() {
|
|
716
|
+
if (IS_WIN) {
|
|
717
|
+
const roaming = process.env.APPDATA ?? join(HOME, "AppData", "Roaming");
|
|
718
|
+
return [join(roaming, "Mozilla", "Firefox", "Profiles")];
|
|
719
|
+
}
|
|
720
|
+
if (IS_MAC) {
|
|
721
|
+
return [join(HOME, "Library", "Application Support", "Firefox", "Profiles")];
|
|
722
|
+
}
|
|
723
|
+
return [
|
|
724
|
+
join(HOME, ".mozilla", "firefox"),
|
|
725
|
+
join(HOME, "snap", "firefox", "common", ".mozilla", "firefox"),
|
|
726
|
+
join(HOME, ".var", "app", "org.mozilla.firefox", ".mozilla", "firefox")
|
|
727
|
+
];
|
|
728
|
+
}
|
|
729
|
+
function dbScanDirs() {
|
|
730
|
+
const dirs = [];
|
|
731
|
+
if (IS_WIN) {
|
|
732
|
+
const local = process.env.LOCALAPPDATA ?? join(HOME, "AppData", "Local");
|
|
733
|
+
const roaming = process.env.APPDATA ?? join(HOME, "AppData", "Roaming");
|
|
734
|
+
dirs.push(local, roaming);
|
|
735
|
+
const pd = join(HOME, "AppData", "Local", "Programs");
|
|
736
|
+
if (existsSync(pd)) dirs.push(pd);
|
|
737
|
+
} else if (IS_MAC) {
|
|
738
|
+
dirs.push(join(HOME, "Library", "Application Support"));
|
|
739
|
+
if (existsSync("/var/lib")) dirs.push("/var/lib");
|
|
740
|
+
} else {
|
|
741
|
+
const configDir = join(HOME, ".config");
|
|
742
|
+
const dataDir = join(HOME, ".local", "share");
|
|
743
|
+
if (existsSync(configDir)) dirs.push(configDir);
|
|
744
|
+
if (existsSync(dataDir)) dirs.push(dataDir);
|
|
745
|
+
if (existsSync("/var/lib")) dirs.push("/var/lib");
|
|
746
|
+
}
|
|
747
|
+
return dirs.filter((d) => existsSync(d));
|
|
748
|
+
}
|
|
749
|
+
function findFiles(dirs, patterns, maxDepth, limit) {
|
|
750
|
+
if (dirs.length === 0) return "";
|
|
751
|
+
if (IS_WIN) {
|
|
752
|
+
const includes = patterns.map((p) => `'${p}'`).join(",");
|
|
753
|
+
const pathList = dirs.map((d) => `'${d}'`).join(",");
|
|
754
|
+
return run(
|
|
755
|
+
`Get-ChildItem -Path ${pathList} -Recurse -Depth ${maxDepth} -Include ${includes} -ErrorAction SilentlyContinue | Select-Object -First ${limit} -ExpandProperty FullName`,
|
|
756
|
+
{ timeout: 15e3 }
|
|
757
|
+
);
|
|
758
|
+
}
|
|
759
|
+
const nameArgs = patterns.map((p) => `-name "${p}"`).join(" -o ");
|
|
760
|
+
const findCmds = dirs.map((d) => `find "${d}" -maxdepth ${maxDepth} \\( ${nameArgs} \\) 2>/dev/null`).join("; ");
|
|
761
|
+
return run(`{ ${findCmds}; } | head -${limit}`, { timeout: 15e3 });
|
|
762
|
+
}
|
|
763
|
+
function scanWindowsPrograms() {
|
|
764
|
+
if (!IS_WIN) return "";
|
|
765
|
+
return run(
|
|
766
|
+
`$paths = @('HKLM:\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*','HKLM:\\Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*','HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*'); Get-ItemProperty $paths -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName } | Select-Object -Property DisplayName, Publisher, DisplayVersion | Sort-Object DisplayName | Format-Table -AutoSize | Out-String -Width 300`,
|
|
767
|
+
{ timeout: 2e4 }
|
|
768
|
+
);
|
|
769
|
+
}
|
|
770
|
+
function scanWindowsDbServices() {
|
|
771
|
+
if (!IS_WIN) return "";
|
|
772
|
+
return run(
|
|
773
|
+
`Get-Service | Where-Object { $_.Name -match 'postgres|mysql|mariadb|mongo|redis|MSSQL|elastic|clickhouse|cassandra' } | Select-Object Name, DisplayName, Status, StartType | Format-Table -AutoSize`,
|
|
774
|
+
{ timeout: 1e4 }
|
|
775
|
+
);
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
// src/bookmarks.ts
|
|
634
779
|
function extractHost(rawUrl, source) {
|
|
635
780
|
try {
|
|
636
781
|
const u = new URL(rawUrl);
|
|
@@ -654,7 +799,7 @@ function walkChrome(node, source, out) {
|
|
|
654
799
|
}
|
|
655
800
|
}
|
|
656
801
|
function readChromeLike(filePath, source) {
|
|
657
|
-
if (!
|
|
802
|
+
if (!existsSync2(filePath)) return [];
|
|
658
803
|
try {
|
|
659
804
|
const raw = JSON.parse(readFileSync(filePath, "utf8"));
|
|
660
805
|
const out = [];
|
|
@@ -667,9 +812,9 @@ function readChromeLike(filePath, source) {
|
|
|
667
812
|
}
|
|
668
813
|
}
|
|
669
814
|
async function readFirefoxBookmarks(profileDir) {
|
|
670
|
-
const src =
|
|
671
|
-
if (!
|
|
672
|
-
const tmp =
|
|
815
|
+
const src = join2(profileDir, "places.sqlite");
|
|
816
|
+
if (!existsSync2(src)) return [];
|
|
817
|
+
const tmp = join2(tmpdir(), `cartograph_ff_bm_${Date.now()}.sqlite`);
|
|
673
818
|
try {
|
|
674
819
|
copyFileSync(src, tmp);
|
|
675
820
|
const { default: Database2 } = await import("better-sqlite3");
|
|
@@ -693,9 +838,9 @@ async function readFirefoxBookmarks(profileDir) {
|
|
|
693
838
|
}
|
|
694
839
|
}
|
|
695
840
|
async function readFirefoxHistory(profileDir) {
|
|
696
|
-
const src =
|
|
697
|
-
if (!
|
|
698
|
-
const tmp =
|
|
841
|
+
const src = join2(profileDir, "places.sqlite");
|
|
842
|
+
if (!existsSync2(src)) return [];
|
|
843
|
+
const tmp = join2(tmpdir(), `cartograph_ff_hist_${Date.now()}.sqlite`);
|
|
699
844
|
try {
|
|
700
845
|
copyFileSync(src, tmp);
|
|
701
846
|
const { default: Database2 } = await import("better-sqlite3");
|
|
@@ -724,8 +869,8 @@ async function readFirefoxHistory(profileDir) {
|
|
|
724
869
|
}
|
|
725
870
|
}
|
|
726
871
|
async function readChromiumHistory(historyPath, source) {
|
|
727
|
-
if (!
|
|
728
|
-
const tmp =
|
|
872
|
+
if (!existsSync2(historyPath)) return [];
|
|
873
|
+
const tmp = join2(tmpdir(), `cartograph_ch_hist_${Date.now()}.sqlite`);
|
|
729
874
|
try {
|
|
730
875
|
copyFileSync(historyPath, tmp);
|
|
731
876
|
const { default: Database2 } = await import("better-sqlite3");
|
|
@@ -753,18 +898,17 @@ async function readChromiumHistory(historyPath, source) {
|
|
|
753
898
|
}
|
|
754
899
|
}
|
|
755
900
|
}
|
|
756
|
-
var
|
|
757
|
-
var IS_MAC = process.platform === "darwin";
|
|
901
|
+
var IS_LINUX2 = !IS_MAC && !IS_WIN;
|
|
758
902
|
function chromeLikePaths(base) {
|
|
759
903
|
const paths = [];
|
|
760
|
-
const defaultPath =
|
|
761
|
-
if (
|
|
762
|
-
if (
|
|
904
|
+
const defaultPath = join2(base, "Default", "Bookmarks");
|
|
905
|
+
if (existsSync2(defaultPath)) paths.push(defaultPath);
|
|
906
|
+
if (existsSync2(base)) {
|
|
763
907
|
try {
|
|
764
908
|
for (const entry of readdirSync(base)) {
|
|
765
909
|
if (entry.startsWith("Profile ")) {
|
|
766
|
-
const p =
|
|
767
|
-
if (
|
|
910
|
+
const p = join2(base, entry, "Bookmarks");
|
|
911
|
+
if (existsSync2(p)) paths.push(p);
|
|
768
912
|
}
|
|
769
913
|
}
|
|
770
914
|
} catch {
|
|
@@ -774,14 +918,14 @@ function chromeLikePaths(base) {
|
|
|
774
918
|
}
|
|
775
919
|
function chromeLikeHistoryPaths(base) {
|
|
776
920
|
const paths = [];
|
|
777
|
-
const defaultPath =
|
|
778
|
-
if (
|
|
779
|
-
if (
|
|
921
|
+
const defaultPath = join2(base, "Default", "History");
|
|
922
|
+
if (existsSync2(defaultPath)) paths.push(defaultPath);
|
|
923
|
+
if (existsSync2(base)) {
|
|
780
924
|
try {
|
|
781
925
|
for (const entry of readdirSync(base)) {
|
|
782
926
|
if (entry.startsWith("Profile ")) {
|
|
783
|
-
const p =
|
|
784
|
-
if (
|
|
927
|
+
const p = join2(base, entry, "History");
|
|
928
|
+
if (existsSync2(p)) paths.push(p);
|
|
785
929
|
}
|
|
786
930
|
}
|
|
787
931
|
} catch {
|
|
@@ -789,29 +933,28 @@ function chromeLikeHistoryPaths(base) {
|
|
|
789
933
|
}
|
|
790
934
|
return paths;
|
|
791
935
|
}
|
|
792
|
-
var
|
|
793
|
-
var
|
|
794
|
-
var
|
|
795
|
-
var
|
|
796
|
-
var
|
|
797
|
-
var
|
|
798
|
-
var
|
|
799
|
-
var
|
|
800
|
-
var
|
|
801
|
-
var
|
|
802
|
-
var
|
|
803
|
-
var
|
|
804
|
-
var OPERA_BASE = IS_MAC ? `${HOME}/Library/Application Support/com.operasoftware.Opera` : `${HOME}/.config/opera`;
|
|
936
|
+
var BROWSER_BASES = browserBasePaths();
|
|
937
|
+
var CHROME_BASE = BROWSER_BASES.chrome;
|
|
938
|
+
var CHROMIUM_BASE = BROWSER_BASES.chromium;
|
|
939
|
+
var EDGE_BASE = BROWSER_BASES.edge;
|
|
940
|
+
var BRAVE_BASE = BROWSER_BASES.brave;
|
|
941
|
+
var VIVALDI_BASE = BROWSER_BASES.vivaldi;
|
|
942
|
+
var OPERA_BASE = BROWSER_BASES.opera;
|
|
943
|
+
var CHROMIUM_SNAP_BASE = join2(HOME, "snap", "chromium", "common", "chromium");
|
|
944
|
+
var CHROMIUM_FLATPAK_BASE = join2(HOME, ".var", "app", "org.chromium.Chromium", "config", "chromium");
|
|
945
|
+
var CHROME_FLATPAK_BASE = join2(HOME, ".var", "app", "com.google.Chrome", "config", "google-chrome");
|
|
946
|
+
var BRAVE_FLATPAK_BASE = join2(HOME, ".var", "app", "com.brave.Browser", "config", "BraveSoftware", "Brave-Browser");
|
|
947
|
+
var EDGE_FLATPAK_BASE = join2(HOME, ".var", "app", "com.microsoft.Edge", "config", "microsoft-edge");
|
|
805
948
|
function firefoxProfileDirs() {
|
|
806
|
-
const bases =
|
|
949
|
+
const bases = firefoxBaseDirs();
|
|
807
950
|
const dirs = [];
|
|
808
951
|
for (const base of bases) {
|
|
809
|
-
if (!
|
|
952
|
+
if (!existsSync2(base)) continue;
|
|
810
953
|
try {
|
|
811
954
|
for (const d of readdirSync(base)) {
|
|
812
|
-
const full =
|
|
955
|
+
const full = join2(base, d);
|
|
813
956
|
try {
|
|
814
|
-
if (statSync(full).isDirectory() &&
|
|
957
|
+
if (statSync(full).isDirectory() && existsSync2(join2(full, "places.sqlite"))) {
|
|
815
958
|
dirs.push(full);
|
|
816
959
|
}
|
|
817
960
|
} catch {
|
|
@@ -830,7 +973,7 @@ async function scanAllBookmarks() {
|
|
|
830
973
|
for (const p of chromeLikePaths(BRAVE_BASE)) all.push(...readChromeLike(p, "brave"));
|
|
831
974
|
for (const p of chromeLikePaths(VIVALDI_BASE)) all.push(...readChromeLike(p, "vivaldi"));
|
|
832
975
|
for (const p of chromeLikePaths(OPERA_BASE)) all.push(...readChromeLike(p, "opera"));
|
|
833
|
-
if (
|
|
976
|
+
if (IS_LINUX2) {
|
|
834
977
|
for (const p of chromeLikePaths(CHROMIUM_SNAP_BASE)) all.push(...readChromeLike(p, "chromium-snap"));
|
|
835
978
|
for (const p of chromeLikePaths(CHROMIUM_FLATPAK_BASE)) all.push(...readChromeLike(p, "chromium-flatpak"));
|
|
836
979
|
for (const p of chromeLikePaths(CHROME_FLATPAK_BASE)) all.push(...readChromeLike(p, "chrome-flatpak"));
|
|
@@ -855,7 +998,7 @@ async function scanAllHistory() {
|
|
|
855
998
|
for (const p of chromeLikeHistoryPaths(BRAVE_BASE)) all.push(...await readChromiumHistory(p, "brave"));
|
|
856
999
|
for (const p of chromeLikeHistoryPaths(VIVALDI_BASE)) all.push(...await readChromiumHistory(p, "vivaldi"));
|
|
857
1000
|
for (const p of chromeLikeHistoryPaths(OPERA_BASE)) all.push(...await readChromiumHistory(p, "opera"));
|
|
858
|
-
if (
|
|
1001
|
+
if (IS_LINUX2) {
|
|
859
1002
|
for (const p of chromeLikeHistoryPaths(CHROMIUM_SNAP_BASE)) all.push(...await readChromiumHistory(p, "chromium-snap"));
|
|
860
1003
|
for (const p of chromeLikeHistoryPaths(CHROMIUM_FLATPAK_BASE)) all.push(...await readChromiumHistory(p, "chromium-flatpak"));
|
|
861
1004
|
for (const p of chromeLikeHistoryPaths(CHROME_FLATPAK_BASE)) all.push(...await readChromiumHistory(p, "chrome-flatpak"));
|
|
@@ -887,8 +1030,7 @@ function stripSensitive(target) {
|
|
|
887
1030
|
}
|
|
888
1031
|
}
|
|
889
1032
|
async function createCartographyTools(db, sessionId, opts = {}) {
|
|
890
|
-
const
|
|
891
|
-
const { tool, createSdkMcpServer } = sdk;
|
|
1033
|
+
const { tool, createSdkMcpServer } = await import("@anthropic-ai/claude-agent-sdk");
|
|
892
1034
|
const tools = [
|
|
893
1035
|
tool("save_node", "Save an infrastructure node to the catalog", {
|
|
894
1036
|
id: z2.string(),
|
|
@@ -896,7 +1038,7 @@ async function createCartographyTools(db, sessionId, opts = {}) {
|
|
|
896
1038
|
name: z2.string(),
|
|
897
1039
|
discoveredVia: z2.string(),
|
|
898
1040
|
confidence: z2.number().min(0).max(1),
|
|
899
|
-
metadata: z2.record(z2.unknown()).optional(),
|
|
1041
|
+
metadata: z2.record(z2.string(), z2.unknown()).optional(),
|
|
900
1042
|
tags: z2.array(z2.string()).optional(),
|
|
901
1043
|
domain: z2.string().optional().describe('Business domain, e.g. "Marketing", "Finance"'),
|
|
902
1044
|
subDomain: z2.string().optional().describe('Sub-domain, e.g. "Forecast client orders"'),
|
|
@@ -1007,33 +1149,71 @@ async function createCartographyTools(db, sessionId, opts = {}) {
|
|
|
1007
1149
|
tool("scan_local_databases", "Scan for local database files and running DB servers \u2014 PostgreSQL databases, MySQL, SQLite files from installed apps", {
|
|
1008
1150
|
deep: z2.boolean().default(false).optional().describe("Also search home directory recursively for SQLite/DB files (slower)")
|
|
1009
1151
|
}, async (args) => {
|
|
1010
|
-
const { execSync: execSync2 } = await import("child_process");
|
|
1011
|
-
const { homedir: homedir2 } = await import("os");
|
|
1012
|
-
const { existsSync: existsSync3 } = await import("fs");
|
|
1013
1152
|
const deep = args["deep"] ?? false;
|
|
1014
|
-
const HOME2 = homedir2();
|
|
1015
|
-
const run = (cmd) => {
|
|
1016
|
-
try {
|
|
1017
|
-
return execSync2(cmd, { stdio: "pipe", timeout: 1e4, shell: "/bin/sh" }).toString().trim();
|
|
1018
|
-
} catch {
|
|
1019
|
-
return "";
|
|
1020
|
-
}
|
|
1021
|
-
};
|
|
1022
1153
|
const results = {};
|
|
1023
|
-
results["
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1154
|
+
results["PLATFORM"] = `${PLATFORM} (${IS_WIN ? "Windows" : IS_MAC ? "macOS" : "Linux"})`;
|
|
1155
|
+
if (IS_WIN) {
|
|
1156
|
+
results["DB_SERVICES"] = scanWindowsDbServices() || "(no database services found)";
|
|
1157
|
+
}
|
|
1158
|
+
if (commandExists("psql")) {
|
|
1159
|
+
if (IS_WIN) {
|
|
1160
|
+
results["POSTGRES_DATABASES"] = run("psql -lqt", { timeout: 1e4 }) || "(psql found but not running or requires auth)";
|
|
1161
|
+
} else {
|
|
1162
|
+
results["POSTGRES_DATABASES"] = run(`psql -lqt 2>/dev/null | grep -v "template0\\|template1" | awk '{print $1}' | grep -v "^$\\|^|"`) || "(psql not running or not available)";
|
|
1163
|
+
results["POSTGRES_CLUSTERS"] = run("pg_lsclusters 2>/dev/null") || "(pg_lsclusters not available)";
|
|
1164
|
+
}
|
|
1165
|
+
} else {
|
|
1166
|
+
results["POSTGRES_DATABASES"] = "(psql not installed)";
|
|
1167
|
+
}
|
|
1168
|
+
if (commandExists("mysql")) {
|
|
1169
|
+
if (IS_WIN) {
|
|
1170
|
+
results["MYSQL_DATABASES"] = run('mysql --connect-timeout=3 -e "SHOW DATABASES;"', { timeout: 1e4 }) || "(mysql not running or requires auth)";
|
|
1171
|
+
} else {
|
|
1172
|
+
results["MYSQL_DATABASES"] = run('mysql --connect-timeout=3 -e "SHOW DATABASES;" 2>/dev/null') || "(mysql not running or requires auth)";
|
|
1173
|
+
}
|
|
1174
|
+
} else {
|
|
1175
|
+
results["MYSQL_DATABASES"] = "(mysql not installed)";
|
|
1176
|
+
}
|
|
1177
|
+
if (commandExists("mongosh")) {
|
|
1178
|
+
if (IS_WIN) {
|
|
1179
|
+
results["MONGODB_DATABASES"] = run(`mongosh --quiet --eval "db.adminCommand({listDatabases:1}).databases.map(d=>d.name).join('\\n')"`, { timeout: 1e4 }) || "(mongosh not available)";
|
|
1180
|
+
} else {
|
|
1181
|
+
results["MONGODB_DATABASES"] = run(`mongosh --quiet --eval "db.adminCommand({listDatabases:1}).databases.map(d=>d.name).join('\\n')" 2>/dev/null`) || "(mongosh not available)";
|
|
1182
|
+
}
|
|
1183
|
+
} else {
|
|
1184
|
+
results["MONGODB_DATABASES"] = "(mongosh not installed)";
|
|
1185
|
+
}
|
|
1186
|
+
if (commandExists("redis-cli")) {
|
|
1187
|
+
if (IS_WIN) {
|
|
1188
|
+
results["REDIS_INFO"] = run("redis-cli info server", { timeout: 1e4 }).split("\n").slice(0, 5).join("\n") || "(redis-cli not available)";
|
|
1189
|
+
} else {
|
|
1190
|
+
results["REDIS_INFO"] = run("redis-cli info server 2>/dev/null | head -5") || "(redis-cli not available)";
|
|
1191
|
+
}
|
|
1192
|
+
} else {
|
|
1193
|
+
results["REDIS_INFO"] = "(redis-cli not installed)";
|
|
1194
|
+
}
|
|
1195
|
+
const appDirs = dbScanDirs();
|
|
1029
1196
|
if (appDirs.length > 0) {
|
|
1030
|
-
|
|
1031
|
-
results["SQLITE_APP_FILES"] = run(`{ ${findCmds}; } | head -80`) || "(none found)";
|
|
1197
|
+
results["SQLITE_APP_FILES"] = findFiles(appDirs, ["*.sqlite", "*.sqlite3", "*.db"], 4, 80) || "(none found)";
|
|
1032
1198
|
}
|
|
1033
1199
|
if (deep) {
|
|
1034
|
-
|
|
1200
|
+
if (IS_WIN) {
|
|
1201
|
+
results["SQLITE_DEEP_SCAN"] = run(
|
|
1202
|
+
`Get-ChildItem -Path '${HOME}' -Recurse -Depth 6 -Include '*.sqlite','*.sqlite3','*.db' -ErrorAction SilentlyContinue | Where-Object { $_.FullName -notmatch 'node_modules|\\.git' } | Select-Object -First 100 -ExpandProperty FullName`,
|
|
1203
|
+
{ timeout: 3e4 }
|
|
1204
|
+
) || "(none found)";
|
|
1205
|
+
} else {
|
|
1206
|
+
results["SQLITE_DEEP_SCAN"] = run(`find "${HOME}" -maxdepth 6 \\( -name "*.sqlite" -o -name "*.sqlite3" -o -name "*.db" \\) -not -path "*/node_modules/*" -not -path "*/.git/*" 2>/dev/null | head -100`) || "(none found)";
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
if (IS_WIN) {
|
|
1210
|
+
results["DB_CONFIG_FILES"] = run(
|
|
1211
|
+
`Get-ChildItem -Path '${HOME}' -Recurse -Depth 4 -Include '.env','.env.local','database.yml','database.json','docker-compose.yml' -ErrorAction SilentlyContinue | Select-Object -First 20 -ExpandProperty FullName`,
|
|
1212
|
+
{ timeout: 15e3 }
|
|
1213
|
+
) || "(none found)";
|
|
1214
|
+
} else {
|
|
1215
|
+
results["DB_CONFIG_FILES"] = run(`find "${HOME}" -maxdepth 4 \\( -name ".env" -o -name ".env.local" -o -name "database.yml" -o -name "database.json" -o -name "docker-compose.yml" \\) 2>/dev/null | head -20`) || "(none found)";
|
|
1035
1216
|
}
|
|
1036
|
-
results["DB_CONFIG_FILES"] = run(`find "${HOME2}" -maxdepth 4 \\( -name ".env" -o -name ".env.local" -o -name "database.yml" -o -name "database.json" -o -name "docker-compose.yml" \\) 2>/dev/null | head -20`) || "(none found)";
|
|
1037
1217
|
const out = Object.entries(results).map(([k, v]) => `=== ${k} ===
|
|
1038
1218
|
${v}`).join("\n\n");
|
|
1039
1219
|
return { content: [{ type: "text", text: out }] };
|
|
@@ -1041,17 +1221,23 @@ ${v}`).join("\n\n");
|
|
|
1041
1221
|
tool("scan_k8s_resources", "Scan Kubernetes cluster via kubectl \u2014 100% readonly (get, describe)", {
|
|
1042
1222
|
namespace: z2.string().optional().describe("Filter by namespace \u2014 empty = all namespaces")
|
|
1043
1223
|
}, async (args) => {
|
|
1044
|
-
const { execSync: execSync2 } = await import("child_process");
|
|
1045
1224
|
const ns = args["namespace"];
|
|
1046
1225
|
const nsFlag = ns ? `-n ${ns}` : "--all-namespaces";
|
|
1047
|
-
const
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
} catch (e) {
|
|
1051
|
-
return `(error: ${e instanceof Error ? e.message.split("\n")[0] : String(e)})`;
|
|
1052
|
-
}
|
|
1226
|
+
const runK = (cmd) => {
|
|
1227
|
+
const r = run(cmd, { timeout: 15e3 });
|
|
1228
|
+
return r || `(error or not available)`;
|
|
1053
1229
|
};
|
|
1054
|
-
const sections = [
|
|
1230
|
+
const sections = IS_WIN ? [
|
|
1231
|
+
["CONTEXT", "kubectl config current-context"],
|
|
1232
|
+
["NODES", "kubectl get nodes -o wide"],
|
|
1233
|
+
["NAMESPACES", "kubectl get namespaces"],
|
|
1234
|
+
["SERVICES", `kubectl get services ${nsFlag}`],
|
|
1235
|
+
["DEPLOYMENTS", `kubectl get deployments ${nsFlag}`],
|
|
1236
|
+
["STATEFULSETS", `kubectl get statefulsets ${nsFlag}`],
|
|
1237
|
+
["INGRESSES", `kubectl get ingress ${nsFlag}`],
|
|
1238
|
+
["PODS_RUNNING", `kubectl get pods ${nsFlag} --field-selector=status.phase=Running`],
|
|
1239
|
+
["CONFIGMAPS_SYSTEM", "kubectl get configmaps -n kube-system"]
|
|
1240
|
+
] : [
|
|
1055
1241
|
["CONTEXT", 'kubectl config current-context 2>/dev/null || echo "(no context set)"'],
|
|
1056
1242
|
["NODES", "kubectl get nodes -o wide"],
|
|
1057
1243
|
["NAMESPACES", "kubectl get namespaces"],
|
|
@@ -1063,128 +1249,101 @@ ${v}`).join("\n\n");
|
|
|
1063
1249
|
["CONFIGMAPS_SYSTEM", "kubectl get configmaps -n kube-system 2>/dev/null | head -30"]
|
|
1064
1250
|
];
|
|
1065
1251
|
const out = sections.map(([l, c]) => `=== ${l} ===
|
|
1066
|
-
${
|
|
1252
|
+
${runK(c)}`).join("\n\n");
|
|
1067
1253
|
return { content: [{ type: "text", text: out }] };
|
|
1068
1254
|
}),
|
|
1069
1255
|
tool("scan_aws_resources", "Scan AWS infrastructure via AWS CLI \u2014 100% readonly (describe, list)", {
|
|
1070
1256
|
region: z2.string().optional().describe("AWS Region \u2014 default: AWS_DEFAULT_REGION or profile"),
|
|
1071
1257
|
profile: z2.string().optional().describe("AWS CLI profile")
|
|
1072
1258
|
}, async (args) => {
|
|
1073
|
-
const { execSync: execSync2 } = await import("child_process");
|
|
1074
1259
|
const region = args["region"];
|
|
1075
1260
|
const profile = args["profile"];
|
|
1076
1261
|
const env = { ...process.env };
|
|
1077
1262
|
if (region) env["AWS_DEFAULT_REGION"] = region;
|
|
1078
1263
|
const pf = profile ? `--profile ${profile}` : "";
|
|
1079
|
-
const
|
|
1080
|
-
try {
|
|
1081
|
-
return execSync2(cmd, { stdio: "pipe", timeout: 2e4, shell: "/bin/sh", env }).toString().trim();
|
|
1082
|
-
} catch (e) {
|
|
1083
|
-
return `(error: ${e instanceof Error ? e.message.split("\n")[0] : String(e)})`;
|
|
1084
|
-
}
|
|
1085
|
-
};
|
|
1264
|
+
const runAws = (cmd) => run(cmd, { timeout: 2e4, env }) || "(error or not available)";
|
|
1086
1265
|
const sections = [
|
|
1087
1266
|
["IDENTITY", `aws sts get-caller-identity ${pf} --output json`],
|
|
1088
|
-
["EC2", `aws ec2 describe-instances ${pf} --query
|
|
1089
|
-
["RDS", `aws rds describe-db-instances ${pf} --query
|
|
1090
|
-
["ELB_V2", `aws elbv2 describe-load-balancers ${pf} --query
|
|
1267
|
+
["EC2", `aws ec2 describe-instances ${pf} --query "Reservations[*].Instances[*].[InstanceId,InstanceType,State.Name,PublicIpAddress,PrivateIpAddress]" --output table`],
|
|
1268
|
+
["RDS", `aws rds describe-db-instances ${pf} --query "DBInstances[*].[DBInstanceIdentifier,Engine,DBInstanceStatus,Endpoint.Address,Endpoint.Port]" --output table`],
|
|
1269
|
+
["ELB_V2", `aws elbv2 describe-load-balancers ${pf} --query "LoadBalancers[*].[LoadBalancerName,DNSName,Type,State.Code]" --output table`],
|
|
1091
1270
|
["EKS", `aws eks list-clusters ${pf} --output json`],
|
|
1092
|
-
["ELASTICACHE", `aws elasticache describe-cache-clusters ${pf} --query
|
|
1093
|
-
["S3", `aws s3 ls ${pf}
|
|
1094
|
-
["VPC", `aws ec2 describe-vpcs ${pf} --query
|
|
1271
|
+
["ELASTICACHE", `aws elasticache describe-cache-clusters ${pf} --query "CacheClusters[*].[CacheClusterId,Engine,CacheClusterStatus]" --output table`],
|
|
1272
|
+
["S3", `aws s3 ls ${pf}`],
|
|
1273
|
+
["VPC", `aws ec2 describe-vpcs ${pf} --query "Vpcs[*].[VpcId,CidrBlock,IsDefault]" --output table`]
|
|
1095
1274
|
];
|
|
1096
1275
|
const out = sections.map(([l, c]) => `=== ${l} ===
|
|
1097
|
-
${
|
|
1276
|
+
${runAws(c)}`).join("\n\n");
|
|
1098
1277
|
return { content: [{ type: "text", text: out }] };
|
|
1099
1278
|
}),
|
|
1100
1279
|
tool("scan_gcp_resources", "Scan Google Cloud Platform via gcloud CLI \u2014 100% readonly (list, describe)", {
|
|
1101
1280
|
project: z2.string().optional().describe("GCP Project ID \u2014 default: current gcloud project")
|
|
1102
1281
|
}, async (args) => {
|
|
1103
|
-
const { execSync: execSync2 } = await import("child_process");
|
|
1104
1282
|
const project = args["project"];
|
|
1105
1283
|
const pf = project ? `--project ${project}` : "";
|
|
1106
|
-
const
|
|
1107
|
-
try {
|
|
1108
|
-
return execSync2(cmd, { stdio: "pipe", timeout: 2e4, shell: "/bin/sh" }).toString().trim();
|
|
1109
|
-
} catch (e) {
|
|
1110
|
-
return `(error: ${e instanceof Error ? e.message.split("\n")[0] : String(e)})`;
|
|
1111
|
-
}
|
|
1112
|
-
};
|
|
1284
|
+
const runGcp = (cmd) => run(cmd, { timeout: 2e4 }) || "(error or not available)";
|
|
1113
1285
|
const sections = [
|
|
1114
|
-
["IDENTITY", `gcloud config list account --format=
|
|
1115
|
-
["COMPUTE_INSTANCES", `gcloud compute instances list ${pf}
|
|
1116
|
-
["SQL_INSTANCES", `gcloud sql instances list ${pf}
|
|
1117
|
-
["GKE_CLUSTERS", `gcloud container clusters list ${pf}
|
|
1118
|
-
["CLOUD_RUN", `gcloud run services list ${pf} --platform managed
|
|
1119
|
-
["CLOUD_FUNCTIONS", `gcloud functions list ${pf}
|
|
1120
|
-
["REDIS", `gcloud redis instances list ${pf} --regions
|
|
1121
|
-
["PUBSUB", `gcloud pubsub topics list ${pf}
|
|
1122
|
-
["SPANNER", `gcloud spanner instances list ${pf}
|
|
1286
|
+
["IDENTITY", `gcloud config list account --format="value(core.account)"`],
|
|
1287
|
+
["COMPUTE_INSTANCES", `gcloud compute instances list ${pf}`],
|
|
1288
|
+
["SQL_INSTANCES", `gcloud sql instances list ${pf}`],
|
|
1289
|
+
["GKE_CLUSTERS", `gcloud container clusters list ${pf}`],
|
|
1290
|
+
["CLOUD_RUN", `gcloud run services list ${pf} --platform managed`],
|
|
1291
|
+
["CLOUD_FUNCTIONS", `gcloud functions list ${pf}`],
|
|
1292
|
+
["REDIS", `gcloud redis instances list ${pf} --regions=-`],
|
|
1293
|
+
["PUBSUB", `gcloud pubsub topics list ${pf}`],
|
|
1294
|
+
["SPANNER", `gcloud spanner instances list ${pf}`]
|
|
1123
1295
|
];
|
|
1124
1296
|
const out = sections.map(([l, c]) => `=== ${l} ===
|
|
1125
|
-
${
|
|
1297
|
+
${runGcp(c)}`).join("\n\n");
|
|
1126
1298
|
return { content: [{ type: "text", text: out }] };
|
|
1127
1299
|
}),
|
|
1128
1300
|
tool("scan_azure_resources", "Scan Azure infrastructure via az CLI \u2014 100% readonly (list, show)", {
|
|
1129
1301
|
subscription: z2.string().optional().describe("Azure Subscription ID"),
|
|
1130
1302
|
resourceGroup: z2.string().optional().describe("Filter by resource group")
|
|
1131
1303
|
}, async (args) => {
|
|
1132
|
-
const { execSync: execSync2 } = await import("child_process");
|
|
1133
1304
|
const sub = args["subscription"];
|
|
1134
1305
|
const rg = args["resourceGroup"];
|
|
1135
1306
|
const sf = sub ? `--subscription ${sub}` : "";
|
|
1136
1307
|
const rf = rg ? `--resource-group ${rg}` : "";
|
|
1137
|
-
const
|
|
1138
|
-
try {
|
|
1139
|
-
return execSync2(cmd, { stdio: "pipe", timeout: 2e4, shell: "/bin/sh" }).toString().trim();
|
|
1140
|
-
} catch (e) {
|
|
1141
|
-
return `(error: ${e instanceof Error ? e.message.split("\n")[0] : String(e)})`;
|
|
1142
|
-
}
|
|
1143
|
-
};
|
|
1308
|
+
const runAz = (cmd) => run(cmd, { timeout: 2e4 }) || "(error or not available)";
|
|
1144
1309
|
const sections = [
|
|
1145
|
-
["IDENTITY", `az account show --output json ${sf}
|
|
1146
|
-
["VMS", `az vm list ${sf} ${rf} --output table
|
|
1147
|
-
["AKS", `az aks list ${sf} ${rf} --output table
|
|
1148
|
-
["SQL_SERVERS", `az sql server list ${sf} ${rf} --output table
|
|
1149
|
-
["POSTGRES", `az postgres server list ${sf} ${rf} --output table
|
|
1150
|
-
["REDIS", `az redis list ${sf} ${rf} --output table
|
|
1151
|
-
["WEBAPPS", `az webapp list ${sf} ${rf} --output table
|
|
1152
|
-
["CONTAINER_APPS", `az containerapp list ${sf} ${rf} --output table
|
|
1153
|
-
["FUNCTIONS", `az functionapp list ${sf} ${rf} --output table
|
|
1310
|
+
["IDENTITY", `az account show --output json ${sf}`],
|
|
1311
|
+
["VMS", `az vm list ${sf} ${rf} --output table`],
|
|
1312
|
+
["AKS", `az aks list ${sf} ${rf} --output table`],
|
|
1313
|
+
["SQL_SERVERS", `az sql server list ${sf} ${rf} --output table`],
|
|
1314
|
+
["POSTGRES", `az postgres server list ${sf} ${rf} --output table`],
|
|
1315
|
+
["REDIS", `az redis list ${sf} ${rf} --output table`],
|
|
1316
|
+
["WEBAPPS", `az webapp list ${sf} ${rf} --output table`],
|
|
1317
|
+
["CONTAINER_APPS", `az containerapp list ${sf} ${rf} --output table`],
|
|
1318
|
+
["FUNCTIONS", `az functionapp list ${sf} ${rf} --output table`]
|
|
1154
1319
|
];
|
|
1155
1320
|
const out = sections.map(([l, c]) => `=== ${l} ===
|
|
1156
|
-
${
|
|
1321
|
+
${runAz(c)}`).join("\n\n");
|
|
1157
1322
|
return { content: [{ type: "text", text: out }] };
|
|
1158
1323
|
}),
|
|
1159
1324
|
tool("scan_installed_apps", "Scan all installed apps and tools \u2014 IDEs, office, dev tools, business apps, databases", {
|
|
1160
1325
|
searchHint: z2.string().optional().describe('Optional search term to find specific tools (e.g. "hubspot windsurf cursor")')
|
|
1161
1326
|
}, async (args) => {
|
|
1162
|
-
const { execSync: execSync2 } = await import("child_process");
|
|
1163
1327
|
const hint = args["searchHint"];
|
|
1164
|
-
const run = (cmd) => {
|
|
1165
|
-
try {
|
|
1166
|
-
return execSync2(cmd, { stdio: "pipe", timeout: 15e3, shell: "/bin/sh" }).toString().trim();
|
|
1167
|
-
} catch {
|
|
1168
|
-
return "";
|
|
1169
|
-
}
|
|
1170
|
-
};
|
|
1171
|
-
const platform = process.platform;
|
|
1172
1328
|
const results = {};
|
|
1173
|
-
|
|
1329
|
+
results["PLATFORM"] = `${PLATFORM} (${IS_WIN ? "Windows" : IS_MAC ? "macOS" : "Linux"})`;
|
|
1330
|
+
if (IS_MAC) {
|
|
1174
1331
|
results["APPLICATIONS"] = run("ls /Applications/ 2>/dev/null | head -200") || "(empty)";
|
|
1175
1332
|
results["USER_APPLICATIONS"] = run("ls ~/Applications/ 2>/dev/null | head -100") || "(empty)";
|
|
1176
1333
|
results["BREW_CASKS"] = run("brew list --cask 2>/dev/null | head -100") || "(brew not installed)";
|
|
1177
1334
|
results["BREW_FORMULAE"] = run("brew list --formula 2>/dev/null | head -150") || "(brew not installed)";
|
|
1178
1335
|
results["SPOTLIGHT_APPS"] = run(`mdfind "kMDItemKind == 'Application'" 2>/dev/null | grep -v "^/System" | grep -v "^/Library/Apple" | head -100`) || "(Spotlight not available)";
|
|
1179
|
-
} else if (
|
|
1336
|
+
} else if (IS_LINUX) {
|
|
1180
1337
|
results["DPKG"] = run("dpkg --list 2>/dev/null | awk '{print $2}' | head -200") || "(dpkg not available)";
|
|
1181
1338
|
results["SNAP"] = run("snap list 2>/dev/null | head -50") || "(snap not available)";
|
|
1182
1339
|
results["FLATPAK"] = run("flatpak list 2>/dev/null | head -50") || "(flatpak not available)";
|
|
1183
1340
|
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") || "(no .desktop files)";
|
|
1184
1341
|
results["RPM"] = run("rpm -qa 2>/dev/null | head -200") || "(rpm not available)";
|
|
1185
|
-
} else if (
|
|
1186
|
-
results["WINGET"] = run("winget list
|
|
1187
|
-
results["
|
|
1342
|
+
} else if (IS_WIN) {
|
|
1343
|
+
results["WINGET"] = run("winget list --accept-source-agreements", { timeout: 2e4 }) || "(winget not available)";
|
|
1344
|
+
results["INSTALLED_PROGRAMS"] = scanWindowsPrograms() || "(registry scan failed)";
|
|
1345
|
+
results["CHOCO"] = run("choco list --local-only", { timeout: 15e3 }) || "(chocolatey not installed)";
|
|
1346
|
+
results["SCOOP"] = run("scoop list", { timeout: 15e3 }) || "(scoop not installed)";
|
|
1188
1347
|
}
|
|
1189
1348
|
const knownTools = [
|
|
1190
1349
|
// IDEs & Editors
|
|
@@ -1247,7 +1406,6 @@ ${run(c)}`).join("\n\n");
|
|
|
1247
1406
|
"php",
|
|
1248
1407
|
"composer",
|
|
1249
1408
|
"dotnet",
|
|
1250
|
-
"dotnet-sdk",
|
|
1251
1409
|
// Databases
|
|
1252
1410
|
"psql",
|
|
1253
1411
|
"mysql",
|
|
@@ -1288,6 +1446,8 @@ ${run(c)}`).join("\n\n");
|
|
|
1288
1446
|
"brave",
|
|
1289
1447
|
"opera",
|
|
1290
1448
|
"edge",
|
|
1449
|
+
// Windows-specific
|
|
1450
|
+
...IS_WIN ? ["pwsh", "powershell", "wsl", "winget", "choco", "scoop", "notepad++"] : [],
|
|
1291
1451
|
// Monitoring / Analytics
|
|
1292
1452
|
"datadog-agent",
|
|
1293
1453
|
"newrelic-agent",
|
|
@@ -1302,21 +1462,35 @@ ${run(c)}`).join("\n\n");
|
|
|
1302
1462
|
const found = [];
|
|
1303
1463
|
const notFound = [];
|
|
1304
1464
|
for (const t of knownTools) {
|
|
1305
|
-
const r =
|
|
1465
|
+
const r = commandExists(t);
|
|
1306
1466
|
if (r) found.push(`${t}: ${r}`);
|
|
1307
1467
|
else notFound.push(t);
|
|
1308
1468
|
}
|
|
1309
|
-
results["
|
|
1310
|
-
results["
|
|
1469
|
+
results["TOOLS_FOUND"] = found.join("\n") || "(none found)";
|
|
1470
|
+
results["TOOLS_NOT_FOUND"] = notFound.join(", ");
|
|
1311
1471
|
if (hint) {
|
|
1312
1472
|
const terms = hint.split(/[\s,]+/).filter(Boolean);
|
|
1313
1473
|
const hintResults = [];
|
|
1314
1474
|
for (const term of terms) {
|
|
1315
1475
|
const safe = term.replace(/[^a-zA-Z0-9._-]/g, "");
|
|
1316
1476
|
if (!safe) continue;
|
|
1317
|
-
const
|
|
1318
|
-
if (
|
|
1319
|
-
|
|
1477
|
+
const cmdPath = commandExists(safe);
|
|
1478
|
+
if (cmdPath) {
|
|
1479
|
+
hintResults.push(`${term}: ${cmdPath}`);
|
|
1480
|
+
continue;
|
|
1481
|
+
}
|
|
1482
|
+
let fallback = "";
|
|
1483
|
+
if (IS_WIN) {
|
|
1484
|
+
fallback = run(
|
|
1485
|
+
`Get-ChildItem -Path 'C:\\Program Files','C:\\Program Files (x86)','${HOME}\\AppData\\Local\\Programs' -Recurse -Depth 3 -Filter '*${safe}*' -ErrorAction SilentlyContinue | Select-Object -First 5 -ExpandProperty FullName`,
|
|
1486
|
+
{ timeout: 1e4 }
|
|
1487
|
+
);
|
|
1488
|
+
} else if (IS_MAC) {
|
|
1489
|
+
fallback = run(`mdfind -name "${safe}" 2>/dev/null | head -5`);
|
|
1490
|
+
} else {
|
|
1491
|
+
fallback = run(`find /usr/bin /usr/local/bin /opt/homebrew/bin ~/.local/bin /Applications ~/Applications 2>/dev/null -iname "*${safe}*" -maxdepth 3 2>/dev/null | head -5`);
|
|
1492
|
+
}
|
|
1493
|
+
hintResults.push(fallback ? `${term}: ${fallback}` : `${term}: (not found)`);
|
|
1320
1494
|
}
|
|
1321
1495
|
results["HINT_SEARCH"] = hintResults.join("\n");
|
|
1322
1496
|
}
|
|
@@ -1355,9 +1529,9 @@ ${v}`).join("\n\n");
|
|
|
1355
1529
|
}
|
|
1356
1530
|
|
|
1357
1531
|
// src/safety.ts
|
|
1358
|
-
var BLOCKED_CMDS = /\b(rm|mv|cp|dd|mkfs|chmod|chown|chgrp|kill|killall|pkill|reboot|shutdown|poweroff|halt|systemctl\s+(start|stop|restart|enable|disable)|service\s+(start|stop|restart)|docker\s+(rm|rmi|stop|kill|exec|run|build|push)|kubectl\s+(delete|apply|edit|exec|run|create|patch)|apt|yum|dnf|pacman|pip\s+install|npm\s+(install|uninstall)|curl\s+.*-X\s*(POST|PUT|DELETE|PATCH)|wget\s+-O|tee\s)\b/i;
|
|
1359
|
-
var BLOCKED_REDIRECTS = />>|>[^>]/;
|
|
1360
|
-
var safetyHook = async (input) => {
|
|
1532
|
+
var BLOCKED_CMDS = /\b(rm|mv|cp|dd|mkfs|chmod|chown|chgrp|kill|killall|pkill|reboot|shutdown|poweroff|halt|systemctl\s+(start|stop|restart|enable|disable)|service\s+(start|stop|restart)|docker\s+(rm|rmi|stop|kill|exec|run|build|push)|kubectl\s+(delete|apply|edit|exec|run|create|patch)|apt|yum|dnf|pacman|pip\s+install|npm\s+(install|uninstall)|curl\s+.*-X\s*(POST|PUT|DELETE|PATCH)|wget\s+-O|tee\s|Remove-Item|Move-Item|Copy-Item|Stop-Process|Stop-Service|Restart-Service|Start-Service|Set-Service|Invoke-WebRequest\s+.*-Method\s+(POST|PUT|DELETE|PATCH)|del\s|rmdir\s|Format-Volume|Clear-Disk|Stop-Computer|Restart-Computer|Uninstall-Package|Install-Package|Install-Module)\b/i;
|
|
1533
|
+
var BLOCKED_REDIRECTS = />>|>[^>]|Out-File|Set-Content|Add-Content/;
|
|
1534
|
+
var safetyHook = async (input, _toolUseID, _options) => {
|
|
1361
1535
|
if (!("tool_name" in input)) return {};
|
|
1362
1536
|
if (input.tool_name !== "Bash") return {};
|
|
1363
1537
|
const cmd = input.tool_input?.command ?? "";
|
|
@@ -1380,13 +1554,18 @@ var safetyHook = async (input) => {
|
|
|
1380
1554
|
|
|
1381
1555
|
// src/agent.ts
|
|
1382
1556
|
async function runDiscovery(config, db, sessionId, onEvent, onAskUser, hint) {
|
|
1383
|
-
const { query } = await import("@anthropic-ai/claude-
|
|
1557
|
+
const { query } = await import("@anthropic-ai/claude-agent-sdk");
|
|
1384
1558
|
const tools = await createCartographyTools(db, sessionId, { onAskUser });
|
|
1385
1559
|
const hintSection = hint ? `
|
|
1386
1560
|
\u26A1 USER HINT (HIGH PRIORITY): The user wants to find these specific tools: "${hint}"
|
|
1387
1561
|
\u2192 Run scan_installed_apps(searchHint: "${hint}") IMMEDIATELY and save found tools as saas_tool nodes!
|
|
1388
1562
|
` : "";
|
|
1563
|
+
const platformName = IS_WIN ? "Windows" : IS_MAC ? "macOS" : "Linux";
|
|
1564
|
+
const networkScanCmd = IS_WIN ? "Get-NetTCPConnection -State Listen (PowerShell) \u2192 identify all listening ports/processes" : IS_MAC ? "lsof -iTCP -sTCP:LISTEN -n -P && ps aux \u2192 identify all listening ports/processes" : "ss -tlnp && ps aux \u2192 identify all listening ports/processes";
|
|
1565
|
+
const readOnlyTools = IS_WIN ? "Get-NetTCPConnection, Get-Process, Get-Service, Get-ChildItem, curl, docker inspect, kubectl get" : IS_MAC ? "lsof, ps, cat, head, curl -s, docker inspect, kubectl get" : "ss, ps, cat, head, curl -s, docker inspect, kubectl get";
|
|
1566
|
+
const processCmd = IS_WIN ? "Get-Process" : "ps aux";
|
|
1389
1567
|
const systemPrompt = `You are an infrastructure discovery agent. Map the complete system landscape \u2014 local services, SaaS tools, AND all installed apps/tools of the user.
|
|
1568
|
+
PLATFORM: ${platformName} (${PLATFORM})
|
|
1390
1569
|
${hintSection}
|
|
1391
1570
|
\u2501\u2501 MANDATORY SEQUENCE \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
|
|
1392
1571
|
STEP 1 \u2014 Browser Bookmarks (ALWAYS FIRST):
|
|
@@ -1417,7 +1596,8 @@ STEP 4 \u2014 Local Databases & Infrastructure:
|
|
|
1417
1596
|
\u2022 MongoDB running \u2192 save_node as database_server
|
|
1418
1597
|
\u2022 Redis running \u2192 save_node as cache_server
|
|
1419
1598
|
\u2022 SQLite files in app directories \u2192 save_node as database if clearly a business app DB
|
|
1420
|
-
Then run:
|
|
1599
|
+
Then run: ${networkScanCmd}
|
|
1600
|
+
Also run: ${processCmd} \u2192 identify running services
|
|
1421
1601
|
Deepen each service: DB\u2192schemas, API\u2192endpoints, Queue\u2192topics
|
|
1422
1602
|
|
|
1423
1603
|
STEP 5 \u2014 Cloud & Kubernetes (if CLI available):
|
|
@@ -1454,8 +1634,20 @@ PORT MAPPING: 5432=postgres, 3306=mysql, 27017=mongodb, 6379=redis,
|
|
|
1454
1634
|
9092=kafka, 5672=rabbitmq, 80/443/8080/3000=web_service,
|
|
1455
1635
|
9090=prometheus, 8500=consul, 8200=vault, 2379=etcd
|
|
1456
1636
|
|
|
1637
|
+
PLATFORM-SPECIFIC NOTES (${platformName}):
|
|
1638
|
+
${IS_WIN ? `\u2022 Use PowerShell commands: Get-NetTCPConnection, Get-Process, Get-Service, Get-ChildItem
|
|
1639
|
+
\u2022 Do NOT use Unix commands (ss, ps aux, find, which, head, grep) \u2014 they won't work on Windows
|
|
1640
|
+
\u2022 Use $env:LOCALAPPDATA, $env:APPDATA for app data paths
|
|
1641
|
+
\u2022 Registry scan for installed programs is handled by scan_installed_apps` : IS_MAC ? `\u2022 Use lsof -iTCP -sTCP:LISTEN -n -P for port scanning (ss is NOT available on macOS)
|
|
1642
|
+
\u2022 Use ps aux for process listing
|
|
1643
|
+
\u2022 Applications are in /Applications and ~/Applications
|
|
1644
|
+
\u2022 Homebrew (brew) for package management` : `\u2022 Use ss -tlnp for port scanning
|
|
1645
|
+
\u2022 Use ps aux for process listing
|
|
1646
|
+
\u2022 Check dpkg, snap, flatpak for installed packages
|
|
1647
|
+
\u2022 Check Snap/Flatpak browser variants for bookmarks`}
|
|
1648
|
+
|
|
1457
1649
|
RULES:
|
|
1458
|
-
\u2022 Read-only only (
|
|
1650
|
+
\u2022 Read-only only (${readOnlyTools})
|
|
1459
1651
|
\u2022 Node IDs: "type:host:port" or "type:name" \u2014 no paths, no credentials
|
|
1460
1652
|
\u2022 saas_tool IDs: "saas_tool:github.com", "saas_tool:vscode", "saas_tool:cursor"
|
|
1461
1653
|
\u2022 Installed-app IDs: "saas_tool:<appname>" e.g. "saas_tool:slack", "saas_tool:docker-desktop"
|
|
@@ -1482,7 +1674,7 @@ Use ask_user when you need context from the user.`;
|
|
|
1482
1674
|
options: {
|
|
1483
1675
|
model: config.agentModel,
|
|
1484
1676
|
maxTurns: config.maxTurns,
|
|
1485
|
-
|
|
1677
|
+
systemPrompt,
|
|
1486
1678
|
mcpServers: { cartography: tools },
|
|
1487
1679
|
allowedTools: [
|
|
1488
1680
|
"Bash",
|
|
@@ -1543,7 +1735,7 @@ Use ask_user when you need context from the user.`;
|
|
|
1543
1735
|
|
|
1544
1736
|
// src/exporter.ts
|
|
1545
1737
|
import { mkdirSync as mkdirSync2, writeFileSync } from "fs";
|
|
1546
|
-
import { join as
|
|
1738
|
+
import { join as join3 } from "path";
|
|
1547
1739
|
|
|
1548
1740
|
// src/hex.ts
|
|
1549
1741
|
function hexToPixel(q, r, size) {
|
|
@@ -3346,53 +3538,44 @@ function exportJGF(nodes, edges) {
|
|
|
3346
3538
|
}
|
|
3347
3539
|
function exportAll(db, sessionId, outputDir, formats = ["mermaid", "json", "yaml", "html", "map", "discovery", "sops"]) {
|
|
3348
3540
|
mkdirSync2(outputDir, { recursive: true });
|
|
3349
|
-
mkdirSync2(
|
|
3350
|
-
mkdirSync2(
|
|
3541
|
+
mkdirSync2(join3(outputDir, "sops"), { recursive: true });
|
|
3542
|
+
mkdirSync2(join3(outputDir, "workflows"), { recursive: true });
|
|
3351
3543
|
const nodes = db.getNodes(sessionId);
|
|
3352
3544
|
const edges = db.getEdges(sessionId);
|
|
3353
|
-
const jgfPath =
|
|
3545
|
+
const jgfPath = join3(outputDir, "cartography-graph.jgf.json");
|
|
3354
3546
|
writeFileSync(jgfPath, exportJGF(nodes, edges));
|
|
3355
|
-
process.stderr.write("\u2713 cartography-graph.jgf.json\n");
|
|
3356
3547
|
if (formats.includes("mermaid")) {
|
|
3357
|
-
writeFileSync(
|
|
3358
|
-
writeFileSync(
|
|
3359
|
-
process.stderr.write("\u2713 topology.mermaid, dependencies.mermaid\n");
|
|
3548
|
+
writeFileSync(join3(outputDir, "topology.mermaid"), generateTopologyMermaid(nodes, edges));
|
|
3549
|
+
writeFileSync(join3(outputDir, "dependencies.mermaid"), generateDependencyMermaid(nodes, edges));
|
|
3360
3550
|
}
|
|
3361
3551
|
if (formats.includes("json")) {
|
|
3362
|
-
writeFileSync(
|
|
3363
|
-
process.stderr.write("\u2713 catalog.json\n");
|
|
3552
|
+
writeFileSync(join3(outputDir, "catalog.json"), exportJSON(db, sessionId));
|
|
3364
3553
|
}
|
|
3365
3554
|
if (formats.includes("yaml")) {
|
|
3366
|
-
writeFileSync(
|
|
3367
|
-
process.stderr.write("\u2713 catalog-info.yaml\n");
|
|
3555
|
+
writeFileSync(join3(outputDir, "catalog-info.yaml"), exportBackstageYAML(nodes, edges));
|
|
3368
3556
|
}
|
|
3369
3557
|
if (formats.includes("html") || formats.includes("map") || formats.includes("discovery")) {
|
|
3370
|
-
writeFileSync(
|
|
3371
|
-
process.stderr.write("\u2713 discovery.html\n");
|
|
3558
|
+
writeFileSync(join3(outputDir, "discovery.html"), exportDiscoveryApp(nodes, edges));
|
|
3372
3559
|
}
|
|
3373
3560
|
if (formats.includes("sops")) {
|
|
3374
3561
|
const sops = db.getSOPs(sessionId);
|
|
3375
3562
|
for (const sop of sops) {
|
|
3376
3563
|
const filename = sop.title.toLowerCase().replace(/[^a-z0-9]+/g, "-") + ".md";
|
|
3377
|
-
writeFileSync(
|
|
3564
|
+
writeFileSync(join3(outputDir, "sops", filename), exportSOPMarkdown(sop));
|
|
3378
3565
|
const wfFilename = `workflow-${sop.workflowId.substring(0, 8)}.mermaid`;
|
|
3379
|
-
writeFileSync(
|
|
3380
|
-
}
|
|
3381
|
-
if (sops.length > 0) {
|
|
3382
|
-
process.stderr.write(`\u2713 ${sops.length} SOPs + workflow diagrams
|
|
3383
|
-
`);
|
|
3566
|
+
writeFileSync(join3(outputDir, "workflows", wfFilename), generateWorkflowMermaid(sop));
|
|
3384
3567
|
}
|
|
3385
3568
|
}
|
|
3386
3569
|
}
|
|
3387
3570
|
|
|
3388
3571
|
// src/preflight.ts
|
|
3389
|
-
import { execSync } from "child_process";
|
|
3390
|
-
import { existsSync as
|
|
3391
|
-
import { join as
|
|
3572
|
+
import { execSync as execSync2 } from "child_process";
|
|
3573
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
|
|
3574
|
+
import { join as join4 } from "path";
|
|
3392
3575
|
function isOAuthLoggedIn() {
|
|
3393
3576
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "/tmp";
|
|
3394
|
-
const credFile =
|
|
3395
|
-
if (!
|
|
3577
|
+
const credFile = join4(home, ".claude", ".credentials.json");
|
|
3578
|
+
if (!existsSync3(credFile)) return false;
|
|
3396
3579
|
try {
|
|
3397
3580
|
const creds = JSON.parse(readFileSync2(credFile, "utf8"));
|
|
3398
3581
|
const oauth = creds["claudeAiOauth"];
|
|
@@ -3403,7 +3586,7 @@ function isOAuthLoggedIn() {
|
|
|
3403
3586
|
}
|
|
3404
3587
|
function checkPrerequisites() {
|
|
3405
3588
|
try {
|
|
3406
|
-
|
|
3589
|
+
execSync2("claude --version", { stdio: "pipe" });
|
|
3407
3590
|
} catch {
|
|
3408
3591
|
process.stderr.write(
|
|
3409
3592
|
"\n\u274C Claude CLI nicht gefunden.\n Datasynx Cartography braucht die Claude CLI als Runtime-Dependency.\n\n Installieren:\n npm install -g @anthropic-ai/claude-code\n # oder\n curl -fsSL https://claude.ai/install.sh | bash\n\n Danach: claude login\n\n"
|