@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/cli.js
CHANGED
|
@@ -6,11 +6,22 @@ import {
|
|
|
6
6
|
NODE_TYPES,
|
|
7
7
|
SOPStepSchema,
|
|
8
8
|
defaultConfig
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-VZO6XBKX.js";
|
|
10
10
|
import {
|
|
11
|
+
HOME,
|
|
12
|
+
IS_LINUX,
|
|
13
|
+
IS_MAC,
|
|
14
|
+
IS_WIN,
|
|
15
|
+
PLATFORM,
|
|
16
|
+
commandExists,
|
|
17
|
+
dbScanDirs,
|
|
18
|
+
findFiles,
|
|
19
|
+
run,
|
|
11
20
|
scanAllBookmarks,
|
|
12
|
-
scanAllHistory
|
|
13
|
-
|
|
21
|
+
scanAllHistory,
|
|
22
|
+
scanWindowsDbServices,
|
|
23
|
+
scanWindowsPrograms
|
|
24
|
+
} from "./chunk-3NVQ3ND6.js";
|
|
14
25
|
|
|
15
26
|
// src/cli.ts
|
|
16
27
|
import { Command } from "commander";
|
|
@@ -555,8 +566,7 @@ function stripSensitive(target) {
|
|
|
555
566
|
}
|
|
556
567
|
}
|
|
557
568
|
async function createCartographyTools(db, sessionId, opts = {}) {
|
|
558
|
-
const
|
|
559
|
-
const { tool, createSdkMcpServer } = sdk;
|
|
569
|
+
const { tool, createSdkMcpServer } = await import("@anthropic-ai/claude-agent-sdk");
|
|
560
570
|
const tools = [
|
|
561
571
|
tool("save_node", "Save an infrastructure node to the catalog", {
|
|
562
572
|
id: z.string(),
|
|
@@ -564,7 +574,7 @@ async function createCartographyTools(db, sessionId, opts = {}) {
|
|
|
564
574
|
name: z.string(),
|
|
565
575
|
discoveredVia: z.string(),
|
|
566
576
|
confidence: z.number().min(0).max(1),
|
|
567
|
-
metadata: z.record(z.unknown()).optional(),
|
|
577
|
+
metadata: z.record(z.string(), z.unknown()).optional(),
|
|
568
578
|
tags: z.array(z.string()).optional(),
|
|
569
579
|
domain: z.string().optional().describe('Business domain, e.g. "Marketing", "Finance"'),
|
|
570
580
|
subDomain: z.string().optional().describe('Sub-domain, e.g. "Forecast client orders"'),
|
|
@@ -675,33 +685,71 @@ async function createCartographyTools(db, sessionId, opts = {}) {
|
|
|
675
685
|
tool("scan_local_databases", "Scan for local database files and running DB servers \u2014 PostgreSQL databases, MySQL, SQLite files from installed apps", {
|
|
676
686
|
deep: z.boolean().default(false).optional().describe("Also search home directory recursively for SQLite/DB files (slower)")
|
|
677
687
|
}, async (args) => {
|
|
678
|
-
const { execSync: execSync2 } = await import("child_process");
|
|
679
|
-
const { homedir } = await import("os");
|
|
680
|
-
const { existsSync: existsSync3 } = await import("fs");
|
|
681
688
|
const deep = args["deep"] ?? false;
|
|
682
|
-
const HOME = homedir();
|
|
683
|
-
const run = (cmd) => {
|
|
684
|
-
try {
|
|
685
|
-
return execSync2(cmd, { stdio: "pipe", timeout: 1e4, shell: "/bin/sh" }).toString().trim();
|
|
686
|
-
} catch {
|
|
687
|
-
return "";
|
|
688
|
-
}
|
|
689
|
-
};
|
|
690
689
|
const results = {};
|
|
691
|
-
results["
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
690
|
+
results["PLATFORM"] = `${PLATFORM} (${IS_WIN ? "Windows" : IS_MAC ? "macOS" : "Linux"})`;
|
|
691
|
+
if (IS_WIN) {
|
|
692
|
+
results["DB_SERVICES"] = scanWindowsDbServices() || "(no database services found)";
|
|
693
|
+
}
|
|
694
|
+
if (commandExists("psql")) {
|
|
695
|
+
if (IS_WIN) {
|
|
696
|
+
results["POSTGRES_DATABASES"] = run("psql -lqt", { timeout: 1e4 }) || "(psql found but not running or requires auth)";
|
|
697
|
+
} else {
|
|
698
|
+
results["POSTGRES_DATABASES"] = run(`psql -lqt 2>/dev/null | grep -v "template0\\|template1" | awk '{print $1}' | grep -v "^$\\|^|"`) || "(psql not running or not available)";
|
|
699
|
+
results["POSTGRES_CLUSTERS"] = run("pg_lsclusters 2>/dev/null") || "(pg_lsclusters not available)";
|
|
700
|
+
}
|
|
701
|
+
} else {
|
|
702
|
+
results["POSTGRES_DATABASES"] = "(psql not installed)";
|
|
703
|
+
}
|
|
704
|
+
if (commandExists("mysql")) {
|
|
705
|
+
if (IS_WIN) {
|
|
706
|
+
results["MYSQL_DATABASES"] = run('mysql --connect-timeout=3 -e "SHOW DATABASES;"', { timeout: 1e4 }) || "(mysql not running or requires auth)";
|
|
707
|
+
} else {
|
|
708
|
+
results["MYSQL_DATABASES"] = run('mysql --connect-timeout=3 -e "SHOW DATABASES;" 2>/dev/null') || "(mysql not running or requires auth)";
|
|
709
|
+
}
|
|
710
|
+
} else {
|
|
711
|
+
results["MYSQL_DATABASES"] = "(mysql not installed)";
|
|
712
|
+
}
|
|
713
|
+
if (commandExists("mongosh")) {
|
|
714
|
+
if (IS_WIN) {
|
|
715
|
+
results["MONGODB_DATABASES"] = run(`mongosh --quiet --eval "db.adminCommand({listDatabases:1}).databases.map(d=>d.name).join('\\n')"`, { timeout: 1e4 }) || "(mongosh not available)";
|
|
716
|
+
} else {
|
|
717
|
+
results["MONGODB_DATABASES"] = run(`mongosh --quiet --eval "db.adminCommand({listDatabases:1}).databases.map(d=>d.name).join('\\n')" 2>/dev/null`) || "(mongosh not available)";
|
|
718
|
+
}
|
|
719
|
+
} else {
|
|
720
|
+
results["MONGODB_DATABASES"] = "(mongosh not installed)";
|
|
721
|
+
}
|
|
722
|
+
if (commandExists("redis-cli")) {
|
|
723
|
+
if (IS_WIN) {
|
|
724
|
+
results["REDIS_INFO"] = run("redis-cli info server", { timeout: 1e4 }).split("\n").slice(0, 5).join("\n") || "(redis-cli not available)";
|
|
725
|
+
} else {
|
|
726
|
+
results["REDIS_INFO"] = run("redis-cli info server 2>/dev/null | head -5") || "(redis-cli not available)";
|
|
727
|
+
}
|
|
728
|
+
} else {
|
|
729
|
+
results["REDIS_INFO"] = "(redis-cli not installed)";
|
|
730
|
+
}
|
|
731
|
+
const appDirs = dbScanDirs();
|
|
697
732
|
if (appDirs.length > 0) {
|
|
698
|
-
|
|
699
|
-
results["SQLITE_APP_FILES"] = run(`{ ${findCmds}; } | head -80`) || "(none found)";
|
|
733
|
+
results["SQLITE_APP_FILES"] = findFiles(appDirs, ["*.sqlite", "*.sqlite3", "*.db"], 4, 80) || "(none found)";
|
|
700
734
|
}
|
|
701
735
|
if (deep) {
|
|
702
|
-
|
|
736
|
+
if (IS_WIN) {
|
|
737
|
+
results["SQLITE_DEEP_SCAN"] = run(
|
|
738
|
+
`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`,
|
|
739
|
+
{ timeout: 3e4 }
|
|
740
|
+
) || "(none found)";
|
|
741
|
+
} else {
|
|
742
|
+
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)";
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
if (IS_WIN) {
|
|
746
|
+
results["DB_CONFIG_FILES"] = run(
|
|
747
|
+
`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`,
|
|
748
|
+
{ timeout: 15e3 }
|
|
749
|
+
) || "(none found)";
|
|
750
|
+
} else {
|
|
751
|
+
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)";
|
|
703
752
|
}
|
|
704
|
-
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)";
|
|
705
753
|
const out = Object.entries(results).map(([k, v]) => `=== ${k} ===
|
|
706
754
|
${v}`).join("\n\n");
|
|
707
755
|
return { content: [{ type: "text", text: out }] };
|
|
@@ -709,17 +757,23 @@ ${v}`).join("\n\n");
|
|
|
709
757
|
tool("scan_k8s_resources", "Scan Kubernetes cluster via kubectl \u2014 100% readonly (get, describe)", {
|
|
710
758
|
namespace: z.string().optional().describe("Filter by namespace \u2014 empty = all namespaces")
|
|
711
759
|
}, async (args) => {
|
|
712
|
-
const { execSync: execSync2 } = await import("child_process");
|
|
713
760
|
const ns = args["namespace"];
|
|
714
761
|
const nsFlag = ns ? `-n ${ns}` : "--all-namespaces";
|
|
715
|
-
const
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
} catch (e) {
|
|
719
|
-
return `(error: ${e instanceof Error ? e.message.split("\n")[0] : String(e)})`;
|
|
720
|
-
}
|
|
762
|
+
const runK = (cmd) => {
|
|
763
|
+
const r = run(cmd, { timeout: 15e3 });
|
|
764
|
+
return r || `(error or not available)`;
|
|
721
765
|
};
|
|
722
|
-
const sections = [
|
|
766
|
+
const sections = IS_WIN ? [
|
|
767
|
+
["CONTEXT", "kubectl config current-context"],
|
|
768
|
+
["NODES", "kubectl get nodes -o wide"],
|
|
769
|
+
["NAMESPACES", "kubectl get namespaces"],
|
|
770
|
+
["SERVICES", `kubectl get services ${nsFlag}`],
|
|
771
|
+
["DEPLOYMENTS", `kubectl get deployments ${nsFlag}`],
|
|
772
|
+
["STATEFULSETS", `kubectl get statefulsets ${nsFlag}`],
|
|
773
|
+
["INGRESSES", `kubectl get ingress ${nsFlag}`],
|
|
774
|
+
["PODS_RUNNING", `kubectl get pods ${nsFlag} --field-selector=status.phase=Running`],
|
|
775
|
+
["CONFIGMAPS_SYSTEM", "kubectl get configmaps -n kube-system"]
|
|
776
|
+
] : [
|
|
723
777
|
["CONTEXT", 'kubectl config current-context 2>/dev/null || echo "(no context set)"'],
|
|
724
778
|
["NODES", "kubectl get nodes -o wide"],
|
|
725
779
|
["NAMESPACES", "kubectl get namespaces"],
|
|
@@ -731,128 +785,101 @@ ${v}`).join("\n\n");
|
|
|
731
785
|
["CONFIGMAPS_SYSTEM", "kubectl get configmaps -n kube-system 2>/dev/null | head -30"]
|
|
732
786
|
];
|
|
733
787
|
const out = sections.map(([l, c]) => `=== ${l} ===
|
|
734
|
-
${
|
|
788
|
+
${runK(c)}`).join("\n\n");
|
|
735
789
|
return { content: [{ type: "text", text: out }] };
|
|
736
790
|
}),
|
|
737
791
|
tool("scan_aws_resources", "Scan AWS infrastructure via AWS CLI \u2014 100% readonly (describe, list)", {
|
|
738
792
|
region: z.string().optional().describe("AWS Region \u2014 default: AWS_DEFAULT_REGION or profile"),
|
|
739
793
|
profile: z.string().optional().describe("AWS CLI profile")
|
|
740
794
|
}, async (args) => {
|
|
741
|
-
const { execSync: execSync2 } = await import("child_process");
|
|
742
795
|
const region = args["region"];
|
|
743
796
|
const profile = args["profile"];
|
|
744
797
|
const env = { ...process.env };
|
|
745
798
|
if (region) env["AWS_DEFAULT_REGION"] = region;
|
|
746
799
|
const pf = profile ? `--profile ${profile}` : "";
|
|
747
|
-
const
|
|
748
|
-
try {
|
|
749
|
-
return execSync2(cmd, { stdio: "pipe", timeout: 2e4, shell: "/bin/sh", env }).toString().trim();
|
|
750
|
-
} catch (e) {
|
|
751
|
-
return `(error: ${e instanceof Error ? e.message.split("\n")[0] : String(e)})`;
|
|
752
|
-
}
|
|
753
|
-
};
|
|
800
|
+
const runAws = (cmd) => run(cmd, { timeout: 2e4, env }) || "(error or not available)";
|
|
754
801
|
const sections = [
|
|
755
802
|
["IDENTITY", `aws sts get-caller-identity ${pf} --output json`],
|
|
756
|
-
["EC2", `aws ec2 describe-instances ${pf} --query
|
|
757
|
-
["RDS", `aws rds describe-db-instances ${pf} --query
|
|
758
|
-
["ELB_V2", `aws elbv2 describe-load-balancers ${pf} --query
|
|
803
|
+
["EC2", `aws ec2 describe-instances ${pf} --query "Reservations[*].Instances[*].[InstanceId,InstanceType,State.Name,PublicIpAddress,PrivateIpAddress]" --output table`],
|
|
804
|
+
["RDS", `aws rds describe-db-instances ${pf} --query "DBInstances[*].[DBInstanceIdentifier,Engine,DBInstanceStatus,Endpoint.Address,Endpoint.Port]" --output table`],
|
|
805
|
+
["ELB_V2", `aws elbv2 describe-load-balancers ${pf} --query "LoadBalancers[*].[LoadBalancerName,DNSName,Type,State.Code]" --output table`],
|
|
759
806
|
["EKS", `aws eks list-clusters ${pf} --output json`],
|
|
760
|
-
["ELASTICACHE", `aws elasticache describe-cache-clusters ${pf} --query
|
|
761
|
-
["S3", `aws s3 ls ${pf}
|
|
762
|
-
["VPC", `aws ec2 describe-vpcs ${pf} --query
|
|
807
|
+
["ELASTICACHE", `aws elasticache describe-cache-clusters ${pf} --query "CacheClusters[*].[CacheClusterId,Engine,CacheClusterStatus]" --output table`],
|
|
808
|
+
["S3", `aws s3 ls ${pf}`],
|
|
809
|
+
["VPC", `aws ec2 describe-vpcs ${pf} --query "Vpcs[*].[VpcId,CidrBlock,IsDefault]" --output table`]
|
|
763
810
|
];
|
|
764
811
|
const out = sections.map(([l, c]) => `=== ${l} ===
|
|
765
|
-
${
|
|
812
|
+
${runAws(c)}`).join("\n\n");
|
|
766
813
|
return { content: [{ type: "text", text: out }] };
|
|
767
814
|
}),
|
|
768
815
|
tool("scan_gcp_resources", "Scan Google Cloud Platform via gcloud CLI \u2014 100% readonly (list, describe)", {
|
|
769
816
|
project: z.string().optional().describe("GCP Project ID \u2014 default: current gcloud project")
|
|
770
817
|
}, async (args) => {
|
|
771
|
-
const { execSync: execSync2 } = await import("child_process");
|
|
772
818
|
const project = args["project"];
|
|
773
819
|
const pf = project ? `--project ${project}` : "";
|
|
774
|
-
const
|
|
775
|
-
try {
|
|
776
|
-
return execSync2(cmd, { stdio: "pipe", timeout: 2e4, shell: "/bin/sh" }).toString().trim();
|
|
777
|
-
} catch (e) {
|
|
778
|
-
return `(error: ${e instanceof Error ? e.message.split("\n")[0] : String(e)})`;
|
|
779
|
-
}
|
|
780
|
-
};
|
|
820
|
+
const runGcp = (cmd) => run(cmd, { timeout: 2e4 }) || "(error or not available)";
|
|
781
821
|
const sections = [
|
|
782
|
-
["IDENTITY", `gcloud config list account --format=
|
|
783
|
-
["COMPUTE_INSTANCES", `gcloud compute instances list ${pf}
|
|
784
|
-
["SQL_INSTANCES", `gcloud sql instances list ${pf}
|
|
785
|
-
["GKE_CLUSTERS", `gcloud container clusters list ${pf}
|
|
786
|
-
["CLOUD_RUN", `gcloud run services list ${pf} --platform managed
|
|
787
|
-
["CLOUD_FUNCTIONS", `gcloud functions list ${pf}
|
|
788
|
-
["REDIS", `gcloud redis instances list ${pf} --regions
|
|
789
|
-
["PUBSUB", `gcloud pubsub topics list ${pf}
|
|
790
|
-
["SPANNER", `gcloud spanner instances list ${pf}
|
|
822
|
+
["IDENTITY", `gcloud config list account --format="value(core.account)"`],
|
|
823
|
+
["COMPUTE_INSTANCES", `gcloud compute instances list ${pf}`],
|
|
824
|
+
["SQL_INSTANCES", `gcloud sql instances list ${pf}`],
|
|
825
|
+
["GKE_CLUSTERS", `gcloud container clusters list ${pf}`],
|
|
826
|
+
["CLOUD_RUN", `gcloud run services list ${pf} --platform managed`],
|
|
827
|
+
["CLOUD_FUNCTIONS", `gcloud functions list ${pf}`],
|
|
828
|
+
["REDIS", `gcloud redis instances list ${pf} --regions=-`],
|
|
829
|
+
["PUBSUB", `gcloud pubsub topics list ${pf}`],
|
|
830
|
+
["SPANNER", `gcloud spanner instances list ${pf}`]
|
|
791
831
|
];
|
|
792
832
|
const out = sections.map(([l, c]) => `=== ${l} ===
|
|
793
|
-
${
|
|
833
|
+
${runGcp(c)}`).join("\n\n");
|
|
794
834
|
return { content: [{ type: "text", text: out }] };
|
|
795
835
|
}),
|
|
796
836
|
tool("scan_azure_resources", "Scan Azure infrastructure via az CLI \u2014 100% readonly (list, show)", {
|
|
797
837
|
subscription: z.string().optional().describe("Azure Subscription ID"),
|
|
798
838
|
resourceGroup: z.string().optional().describe("Filter by resource group")
|
|
799
839
|
}, async (args) => {
|
|
800
|
-
const { execSync: execSync2 } = await import("child_process");
|
|
801
840
|
const sub = args["subscription"];
|
|
802
841
|
const rg = args["resourceGroup"];
|
|
803
842
|
const sf = sub ? `--subscription ${sub}` : "";
|
|
804
843
|
const rf = rg ? `--resource-group ${rg}` : "";
|
|
805
|
-
const
|
|
806
|
-
try {
|
|
807
|
-
return execSync2(cmd, { stdio: "pipe", timeout: 2e4, shell: "/bin/sh" }).toString().trim();
|
|
808
|
-
} catch (e) {
|
|
809
|
-
return `(error: ${e instanceof Error ? e.message.split("\n")[0] : String(e)})`;
|
|
810
|
-
}
|
|
811
|
-
};
|
|
844
|
+
const runAz = (cmd) => run(cmd, { timeout: 2e4 }) || "(error or not available)";
|
|
812
845
|
const sections = [
|
|
813
|
-
["IDENTITY", `az account show --output json ${sf}
|
|
814
|
-
["VMS", `az vm list ${sf} ${rf} --output table
|
|
815
|
-
["AKS", `az aks list ${sf} ${rf} --output table
|
|
816
|
-
["SQL_SERVERS", `az sql server list ${sf} ${rf} --output table
|
|
817
|
-
["POSTGRES", `az postgres server list ${sf} ${rf} --output table
|
|
818
|
-
["REDIS", `az redis list ${sf} ${rf} --output table
|
|
819
|
-
["WEBAPPS", `az webapp list ${sf} ${rf} --output table
|
|
820
|
-
["CONTAINER_APPS", `az containerapp list ${sf} ${rf} --output table
|
|
821
|
-
["FUNCTIONS", `az functionapp list ${sf} ${rf} --output table
|
|
846
|
+
["IDENTITY", `az account show --output json ${sf}`],
|
|
847
|
+
["VMS", `az vm list ${sf} ${rf} --output table`],
|
|
848
|
+
["AKS", `az aks list ${sf} ${rf} --output table`],
|
|
849
|
+
["SQL_SERVERS", `az sql server list ${sf} ${rf} --output table`],
|
|
850
|
+
["POSTGRES", `az postgres server list ${sf} ${rf} --output table`],
|
|
851
|
+
["REDIS", `az redis list ${sf} ${rf} --output table`],
|
|
852
|
+
["WEBAPPS", `az webapp list ${sf} ${rf} --output table`],
|
|
853
|
+
["CONTAINER_APPS", `az containerapp list ${sf} ${rf} --output table`],
|
|
854
|
+
["FUNCTIONS", `az functionapp list ${sf} ${rf} --output table`]
|
|
822
855
|
];
|
|
823
856
|
const out = sections.map(([l, c]) => `=== ${l} ===
|
|
824
|
-
${
|
|
857
|
+
${runAz(c)}`).join("\n\n");
|
|
825
858
|
return { content: [{ type: "text", text: out }] };
|
|
826
859
|
}),
|
|
827
860
|
tool("scan_installed_apps", "Scan all installed apps and tools \u2014 IDEs, office, dev tools, business apps, databases", {
|
|
828
861
|
searchHint: z.string().optional().describe('Optional search term to find specific tools (e.g. "hubspot windsurf cursor")')
|
|
829
862
|
}, async (args) => {
|
|
830
|
-
const { execSync: execSync2 } = await import("child_process");
|
|
831
863
|
const hint = args["searchHint"];
|
|
832
|
-
const run = (cmd) => {
|
|
833
|
-
try {
|
|
834
|
-
return execSync2(cmd, { stdio: "pipe", timeout: 15e3, shell: "/bin/sh" }).toString().trim();
|
|
835
|
-
} catch {
|
|
836
|
-
return "";
|
|
837
|
-
}
|
|
838
|
-
};
|
|
839
|
-
const platform = process.platform;
|
|
840
864
|
const results = {};
|
|
841
|
-
|
|
865
|
+
results["PLATFORM"] = `${PLATFORM} (${IS_WIN ? "Windows" : IS_MAC ? "macOS" : "Linux"})`;
|
|
866
|
+
if (IS_MAC) {
|
|
842
867
|
results["APPLICATIONS"] = run("ls /Applications/ 2>/dev/null | head -200") || "(empty)";
|
|
843
868
|
results["USER_APPLICATIONS"] = run("ls ~/Applications/ 2>/dev/null | head -100") || "(empty)";
|
|
844
869
|
results["BREW_CASKS"] = run("brew list --cask 2>/dev/null | head -100") || "(brew not installed)";
|
|
845
870
|
results["BREW_FORMULAE"] = run("brew list --formula 2>/dev/null | head -150") || "(brew not installed)";
|
|
846
871
|
results["SPOTLIGHT_APPS"] = run(`mdfind "kMDItemKind == 'Application'" 2>/dev/null | grep -v "^/System" | grep -v "^/Library/Apple" | head -100`) || "(Spotlight not available)";
|
|
847
|
-
} else if (
|
|
872
|
+
} else if (IS_LINUX) {
|
|
848
873
|
results["DPKG"] = run("dpkg --list 2>/dev/null | awk '{print $2}' | head -200") || "(dpkg not available)";
|
|
849
874
|
results["SNAP"] = run("snap list 2>/dev/null | head -50") || "(snap not available)";
|
|
850
875
|
results["FLATPAK"] = run("flatpak list 2>/dev/null | head -50") || "(flatpak not available)";
|
|
851
876
|
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)";
|
|
852
877
|
results["RPM"] = run("rpm -qa 2>/dev/null | head -200") || "(rpm not available)";
|
|
853
|
-
} else if (
|
|
854
|
-
results["WINGET"] = run("winget list
|
|
855
|
-
results["
|
|
878
|
+
} else if (IS_WIN) {
|
|
879
|
+
results["WINGET"] = run("winget list --accept-source-agreements", { timeout: 2e4 }) || "(winget not available)";
|
|
880
|
+
results["INSTALLED_PROGRAMS"] = scanWindowsPrograms() || "(registry scan failed)";
|
|
881
|
+
results["CHOCO"] = run("choco list --local-only", { timeout: 15e3 }) || "(chocolatey not installed)";
|
|
882
|
+
results["SCOOP"] = run("scoop list", { timeout: 15e3 }) || "(scoop not installed)";
|
|
856
883
|
}
|
|
857
884
|
const knownTools = [
|
|
858
885
|
// IDEs & Editors
|
|
@@ -915,7 +942,6 @@ ${run(c)}`).join("\n\n");
|
|
|
915
942
|
"php",
|
|
916
943
|
"composer",
|
|
917
944
|
"dotnet",
|
|
918
|
-
"dotnet-sdk",
|
|
919
945
|
// Databases
|
|
920
946
|
"psql",
|
|
921
947
|
"mysql",
|
|
@@ -956,6 +982,8 @@ ${run(c)}`).join("\n\n");
|
|
|
956
982
|
"brave",
|
|
957
983
|
"opera",
|
|
958
984
|
"edge",
|
|
985
|
+
// Windows-specific
|
|
986
|
+
...IS_WIN ? ["pwsh", "powershell", "wsl", "winget", "choco", "scoop", "notepad++"] : [],
|
|
959
987
|
// Monitoring / Analytics
|
|
960
988
|
"datadog-agent",
|
|
961
989
|
"newrelic-agent",
|
|
@@ -970,21 +998,35 @@ ${run(c)}`).join("\n\n");
|
|
|
970
998
|
const found = [];
|
|
971
999
|
const notFound = [];
|
|
972
1000
|
for (const t of knownTools) {
|
|
973
|
-
const r =
|
|
1001
|
+
const r = commandExists(t);
|
|
974
1002
|
if (r) found.push(`${t}: ${r}`);
|
|
975
1003
|
else notFound.push(t);
|
|
976
1004
|
}
|
|
977
|
-
results["
|
|
978
|
-
results["
|
|
1005
|
+
results["TOOLS_FOUND"] = found.join("\n") || "(none found)";
|
|
1006
|
+
results["TOOLS_NOT_FOUND"] = notFound.join(", ");
|
|
979
1007
|
if (hint) {
|
|
980
1008
|
const terms = hint.split(/[\s,]+/).filter(Boolean);
|
|
981
1009
|
const hintResults = [];
|
|
982
1010
|
for (const term of terms) {
|
|
983
1011
|
const safe = term.replace(/[^a-zA-Z0-9._-]/g, "");
|
|
984
1012
|
if (!safe) continue;
|
|
985
|
-
const
|
|
986
|
-
if (
|
|
987
|
-
|
|
1013
|
+
const cmdPath = commandExists(safe);
|
|
1014
|
+
if (cmdPath) {
|
|
1015
|
+
hintResults.push(`${term}: ${cmdPath}`);
|
|
1016
|
+
continue;
|
|
1017
|
+
}
|
|
1018
|
+
let fallback = "";
|
|
1019
|
+
if (IS_WIN) {
|
|
1020
|
+
fallback = run(
|
|
1021
|
+
`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`,
|
|
1022
|
+
{ timeout: 1e4 }
|
|
1023
|
+
);
|
|
1024
|
+
} else if (IS_MAC) {
|
|
1025
|
+
fallback = run(`mdfind -name "${safe}" 2>/dev/null | head -5`);
|
|
1026
|
+
} else {
|
|
1027
|
+
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`);
|
|
1028
|
+
}
|
|
1029
|
+
hintResults.push(fallback ? `${term}: ${fallback}` : `${term}: (not found)`);
|
|
988
1030
|
}
|
|
989
1031
|
results["HINT_SEARCH"] = hintResults.join("\n");
|
|
990
1032
|
}
|
|
@@ -1023,9 +1065,9 @@ ${v}`).join("\n\n");
|
|
|
1023
1065
|
}
|
|
1024
1066
|
|
|
1025
1067
|
// src/safety.ts
|
|
1026
|
-
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;
|
|
1027
|
-
var BLOCKED_REDIRECTS = />>|>[^>]/;
|
|
1028
|
-
var safetyHook = async (input) => {
|
|
1068
|
+
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;
|
|
1069
|
+
var BLOCKED_REDIRECTS = />>|>[^>]|Out-File|Set-Content|Add-Content/;
|
|
1070
|
+
var safetyHook = async (input, _toolUseID, _options) => {
|
|
1029
1071
|
if (!("tool_name" in input)) return {};
|
|
1030
1072
|
if (input.tool_name !== "Bash") return {};
|
|
1031
1073
|
const cmd = input.tool_input?.command ?? "";
|
|
@@ -1048,13 +1090,18 @@ var safetyHook = async (input) => {
|
|
|
1048
1090
|
|
|
1049
1091
|
// src/agent.ts
|
|
1050
1092
|
async function runDiscovery(config, db, sessionId, onEvent, onAskUser, hint) {
|
|
1051
|
-
const { query } = await import("@anthropic-ai/claude-
|
|
1093
|
+
const { query } = await import("@anthropic-ai/claude-agent-sdk");
|
|
1052
1094
|
const tools = await createCartographyTools(db, sessionId, { onAskUser });
|
|
1053
1095
|
const hintSection = hint ? `
|
|
1054
1096
|
\u26A1 USER HINT (HIGH PRIORITY): The user wants to find these specific tools: "${hint}"
|
|
1055
1097
|
\u2192 Run scan_installed_apps(searchHint: "${hint}") IMMEDIATELY and save found tools as saas_tool nodes!
|
|
1056
1098
|
` : "";
|
|
1099
|
+
const platformName = IS_WIN ? "Windows" : IS_MAC ? "macOS" : "Linux";
|
|
1100
|
+
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";
|
|
1101
|
+
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";
|
|
1102
|
+
const processCmd = IS_WIN ? "Get-Process" : "ps aux";
|
|
1057
1103
|
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.
|
|
1104
|
+
PLATFORM: ${platformName} (${PLATFORM})
|
|
1058
1105
|
${hintSection}
|
|
1059
1106
|
\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
|
|
1060
1107
|
STEP 1 \u2014 Browser Bookmarks (ALWAYS FIRST):
|
|
@@ -1085,7 +1132,8 @@ STEP 4 \u2014 Local Databases & Infrastructure:
|
|
|
1085
1132
|
\u2022 MongoDB running \u2192 save_node as database_server
|
|
1086
1133
|
\u2022 Redis running \u2192 save_node as cache_server
|
|
1087
1134
|
\u2022 SQLite files in app directories \u2192 save_node as database if clearly a business app DB
|
|
1088
|
-
Then run:
|
|
1135
|
+
Then run: ${networkScanCmd}
|
|
1136
|
+
Also run: ${processCmd} \u2192 identify running services
|
|
1089
1137
|
Deepen each service: DB\u2192schemas, API\u2192endpoints, Queue\u2192topics
|
|
1090
1138
|
|
|
1091
1139
|
STEP 5 \u2014 Cloud & Kubernetes (if CLI available):
|
|
@@ -1122,8 +1170,20 @@ PORT MAPPING: 5432=postgres, 3306=mysql, 27017=mongodb, 6379=redis,
|
|
|
1122
1170
|
9092=kafka, 5672=rabbitmq, 80/443/8080/3000=web_service,
|
|
1123
1171
|
9090=prometheus, 8500=consul, 8200=vault, 2379=etcd
|
|
1124
1172
|
|
|
1173
|
+
PLATFORM-SPECIFIC NOTES (${platformName}):
|
|
1174
|
+
${IS_WIN ? `\u2022 Use PowerShell commands: Get-NetTCPConnection, Get-Process, Get-Service, Get-ChildItem
|
|
1175
|
+
\u2022 Do NOT use Unix commands (ss, ps aux, find, which, head, grep) \u2014 they won't work on Windows
|
|
1176
|
+
\u2022 Use $env:LOCALAPPDATA, $env:APPDATA for app data paths
|
|
1177
|
+
\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)
|
|
1178
|
+
\u2022 Use ps aux for process listing
|
|
1179
|
+
\u2022 Applications are in /Applications and ~/Applications
|
|
1180
|
+
\u2022 Homebrew (brew) for package management` : `\u2022 Use ss -tlnp for port scanning
|
|
1181
|
+
\u2022 Use ps aux for process listing
|
|
1182
|
+
\u2022 Check dpkg, snap, flatpak for installed packages
|
|
1183
|
+
\u2022 Check Snap/Flatpak browser variants for bookmarks`}
|
|
1184
|
+
|
|
1125
1185
|
RULES:
|
|
1126
|
-
\u2022 Read-only only (
|
|
1186
|
+
\u2022 Read-only only (${readOnlyTools})
|
|
1127
1187
|
\u2022 Node IDs: "type:host:port" or "type:name" \u2014 no paths, no credentials
|
|
1128
1188
|
\u2022 saas_tool IDs: "saas_tool:github.com", "saas_tool:vscode", "saas_tool:cursor"
|
|
1129
1189
|
\u2022 Installed-app IDs: "saas_tool:<appname>" e.g. "saas_tool:slack", "saas_tool:docker-desktop"
|
|
@@ -1150,7 +1210,7 @@ Use ask_user when you need context from the user.`;
|
|
|
1150
1210
|
options: {
|
|
1151
1211
|
model: config.agentModel,
|
|
1152
1212
|
maxTurns: config.maxTurns,
|
|
1153
|
-
|
|
1213
|
+
systemPrompt,
|
|
1154
1214
|
mcpServers: { cartography: tools },
|
|
1155
1215
|
allowedTools: [
|
|
1156
1216
|
"Bash",
|
|
@@ -2776,23 +2836,18 @@ function exportAll(db, sessionId, outputDir, formats = ["mermaid", "json", "yaml
|
|
|
2776
2836
|
const edges = db.getEdges(sessionId);
|
|
2777
2837
|
const jgfPath = join2(outputDir, "cartography-graph.jgf.json");
|
|
2778
2838
|
writeFileSync(jgfPath, exportJGF(nodes, edges));
|
|
2779
|
-
process.stderr.write("\u2713 cartography-graph.jgf.json\n");
|
|
2780
2839
|
if (formats.includes("mermaid")) {
|
|
2781
2840
|
writeFileSync(join2(outputDir, "topology.mermaid"), generateTopologyMermaid(nodes, edges));
|
|
2782
2841
|
writeFileSync(join2(outputDir, "dependencies.mermaid"), generateDependencyMermaid(nodes, edges));
|
|
2783
|
-
process.stderr.write("\u2713 topology.mermaid, dependencies.mermaid\n");
|
|
2784
2842
|
}
|
|
2785
2843
|
if (formats.includes("json")) {
|
|
2786
2844
|
writeFileSync(join2(outputDir, "catalog.json"), exportJSON(db, sessionId));
|
|
2787
|
-
process.stderr.write("\u2713 catalog.json\n");
|
|
2788
2845
|
}
|
|
2789
2846
|
if (formats.includes("yaml")) {
|
|
2790
2847
|
writeFileSync(join2(outputDir, "catalog-info.yaml"), exportBackstageYAML(nodes, edges));
|
|
2791
|
-
process.stderr.write("\u2713 catalog-info.yaml\n");
|
|
2792
2848
|
}
|
|
2793
2849
|
if (formats.includes("html") || formats.includes("map") || formats.includes("discovery")) {
|
|
2794
2850
|
writeFileSync(join2(outputDir, "discovery.html"), exportDiscoveryApp(nodes, edges));
|
|
2795
|
-
process.stderr.write("\u2713 discovery.html\n");
|
|
2796
2851
|
}
|
|
2797
2852
|
if (formats.includes("sops")) {
|
|
2798
2853
|
const sops = db.getSOPs(sessionId);
|
|
@@ -2802,10 +2857,6 @@ function exportAll(db, sessionId, outputDir, formats = ["mermaid", "json", "yaml
|
|
|
2802
2857
|
const wfFilename = `workflow-${sop.workflowId.substring(0, 8)}.mermaid`;
|
|
2803
2858
|
writeFileSync(join2(outputDir, "workflows", wfFilename), generateWorkflowMermaid(sop));
|
|
2804
2859
|
}
|
|
2805
|
-
if (sops.length > 0) {
|
|
2806
|
-
process.stderr.write(`\u2713 ${sops.length} SOPs + workflow diagrams
|
|
2807
|
-
`);
|
|
2808
|
-
}
|
|
2809
2860
|
}
|
|
2810
2861
|
}
|
|
2811
2862
|
|
|
@@ -2824,8 +2875,8 @@ main();
|
|
|
2824
2875
|
function main() {
|
|
2825
2876
|
const program = new Command();
|
|
2826
2877
|
const CMD = "datasynx-cartography";
|
|
2827
|
-
const VERSION = "0.
|
|
2828
|
-
program.name(CMD).description("AI-powered Infrastructure
|
|
2878
|
+
const VERSION = "0.7.0";
|
|
2879
|
+
program.name(CMD).description("AI-powered Infrastructure Discovery & Agentic AI Cartography").version(VERSION);
|
|
2829
2880
|
program.command("discover").description("Scan and map your infrastructure").option("--entry <hosts...>", "Entry points", ["localhost"]).option("--depth <n>", "Max crawl depth", "8").option("--max-turns <n>", "Max agent turns", "50").option("--model <m>", "Agent model", "claude-sonnet-4-5-20250929").option("--org <name>", "Organization name (for Backstage)").option("-o, --output <dir>", "Output directory", "./datasynx-output").option("--db <path>", "DB path").option("-v, --verbose", "Show agent reasoning", false).action(async (opts) => {
|
|
2830
2881
|
checkPrerequisites();
|
|
2831
2882
|
const config = defaultConfig({
|
|
@@ -3034,12 +3085,11 @@ function main() {
|
|
|
3034
3085
|
`);
|
|
3035
3086
|
}
|
|
3036
3087
|
}
|
|
3037
|
-
exportAll(db, sessionId, config.outputDir);
|
|
3038
|
-
const osc8 = (url, label) => `\x1B]8;;${url}\x1B\\${label}\x1B]8;;\x1B\\`;
|
|
3088
|
+
exportAll(db, sessionId, config.outputDir, ["discovery"]);
|
|
3039
3089
|
const discoveryPath = resolve(config.outputDir, "discovery.html");
|
|
3040
3090
|
w("\n");
|
|
3041
3091
|
if (existsSync2(discoveryPath)) {
|
|
3042
|
-
w(` ${green("\
|
|
3092
|
+
w(` ${green("\u2713")} ${bold("discovery.html")} ${dim("\u2190 Enterprise Map")}
|
|
3043
3093
|
`);
|
|
3044
3094
|
}
|
|
3045
3095
|
w("\n");
|
|
@@ -3082,9 +3132,9 @@ function main() {
|
|
|
3082
3132
|
w(` ${green(bold("\u2713"))} Total now: ${bold(String(followupStats.nodes))} nodes, ${bold(String(followupStats.edges))} edges
|
|
3083
3133
|
`);
|
|
3084
3134
|
w("\n");
|
|
3085
|
-
exportAll(db, sessionId, config.outputDir);
|
|
3135
|
+
exportAll(db, sessionId, config.outputDir, ["discovery"]);
|
|
3086
3136
|
if (existsSync2(discoveryPath)) {
|
|
3087
|
-
w(` ${green("\
|
|
3137
|
+
w(` ${green("\u2713")} ${bold("discovery.html updated")}
|
|
3088
3138
|
`);
|
|
3089
3139
|
}
|
|
3090
3140
|
w("\n");
|
|
@@ -3319,9 +3369,39 @@ ${infraSummary.substring(0, 12e3)}`;
|
|
|
3319
3369
|
const out = process.stdout.write.bind(process.stdout);
|
|
3320
3370
|
const b = bold;
|
|
3321
3371
|
const line = () => out(dim("\u2500".repeat(60)) + "\n");
|
|
3372
|
+
const platformName = IS_WIN ? "Windows" : IS_MAC ? "macOS" : "Linux";
|
|
3322
3373
|
out("\n");
|
|
3323
3374
|
out(b(" DATASYNX CARTOGRAPHY") + " " + dim("v" + VERSION) + "\n");
|
|
3324
|
-
out(dim(" AI-powered Infrastructure
|
|
3375
|
+
out(dim(" AI-powered Infrastructure Discovery & Agentic AI Cartography\n"));
|
|
3376
|
+
out(dim(` Platform: ${platformName}
|
|
3377
|
+
`));
|
|
3378
|
+
out("\n");
|
|
3379
|
+
line();
|
|
3380
|
+
out(b(cyan(" CROSS-PLATFORM SUPPORT\n")));
|
|
3381
|
+
out("\n");
|
|
3382
|
+
out(` ${green("Linux")} ss, ps, dpkg/snap/flatpak, find, /bin/sh
|
|
3383
|
+
`);
|
|
3384
|
+
out(` ${green("macOS")} lsof, ps, /Applications, Homebrew, Spotlight, /bin/sh
|
|
3385
|
+
`);
|
|
3386
|
+
out(` ${green("Windows")} Get-NetTCPConnection, Get-Process, Get-Service, Registry,
|
|
3387
|
+
`);
|
|
3388
|
+
out(` winget, choco, scoop, PowerShell
|
|
3389
|
+
`);
|
|
3390
|
+
out("\n");
|
|
3391
|
+
out(dim(" Network scanning:\n"));
|
|
3392
|
+
out(dim(" Linux: ss -tlnp\n"));
|
|
3393
|
+
out(dim(" macOS: lsof -iTCP -sTCP:LISTEN -n -P\n"));
|
|
3394
|
+
out(dim(" Windows: Get-NetTCPConnection -State Listen\n"));
|
|
3395
|
+
out("\n");
|
|
3396
|
+
out(dim(" Installed apps:\n"));
|
|
3397
|
+
out(dim(" Linux: dpkg, rpm, snap, flatpak, .desktop files\n"));
|
|
3398
|
+
out(dim(" macOS: /Applications, brew list, Spotlight (mdfind)\n"));
|
|
3399
|
+
out(dim(" Windows: winget list, Registry scan, choco, scoop\n"));
|
|
3400
|
+
out("\n");
|
|
3401
|
+
out(dim(" Browser bookmarks & history:\n"));
|
|
3402
|
+
out(dim(" Linux: ~/.config/google-chrome, Snap/Flatpak variants, ~/.mozilla\n"));
|
|
3403
|
+
out(dim(" macOS: ~/Library/Application Support/Google/Chrome, ~/Library/.../Firefox\n"));
|
|
3404
|
+
out(dim(" Windows: %LOCALAPPDATA%\\Google\\Chrome\\User Data, %APPDATA%\\Mozilla\n"));
|
|
3325
3405
|
out("\n");
|
|
3326
3406
|
line();
|
|
3327
3407
|
out(b(cyan(" CARTOGRAPHY\n")));
|
|
@@ -3330,7 +3410,7 @@ ${infraSummary.substring(0, 12e3)}`;
|
|
|
3330
3410
|
`);
|
|
3331
3411
|
out(` Scans your local infrastructure (Claude Sonnet).
|
|
3332
3412
|
`);
|
|
3333
|
-
out(` Claude autonomously runs ss, ps, curl, docker inspect, kubectl get
|
|
3413
|
+
out(` Claude autonomously runs ${IS_WIN ? "Get-NetTCPConnection, Get-Process" : IS_MAC ? "lsof, ps" : "ss, ps"}, curl, docker inspect, kubectl get
|
|
3334
3414
|
`);
|
|
3335
3415
|
out(` and stores everything in SQLite.
|
|
3336
3416
|
`);
|
|
@@ -3346,13 +3426,7 @@ ${infraSummary.substring(0, 12e3)}`;
|
|
|
3346
3426
|
out("\n");
|
|
3347
3427
|
out(dim(" Output:\n"));
|
|
3348
3428
|
out(dim(" datasynx-output/\n"));
|
|
3349
|
-
out(dim("
|
|
3350
|
-
out(dim(" catalog-info.yaml Backstage service catalog\n"));
|
|
3351
|
-
out(dim(" topology.mermaid Infrastructure topology (graph TB)\n"));
|
|
3352
|
-
out(dim(" dependencies.mermaid Service dependencies (graph LR)\n"));
|
|
3353
|
-
out(dim(" discovery.html Enterprise discovery frontend (Map + Topology)\n"));
|
|
3354
|
-
out(dim(" sops/ Generated SOPs as Markdown\n"));
|
|
3355
|
-
out(dim(" workflows/ Workflow flowcharts as Mermaid\n"));
|
|
3429
|
+
out(dim(" discovery.html Enterprise Map\n"));
|
|
3356
3430
|
out("\n");
|
|
3357
3431
|
line();
|
|
3358
3432
|
out(b(cyan(" ANALYSIS & EXPORT\n")));
|
|
@@ -3379,14 +3453,25 @@ ${infraSummary.substring(0, 12e3)}`;
|
|
|
3379
3453
|
out(b(cyan(" ARCHITECTURE\n")));
|
|
3380
3454
|
out("\n");
|
|
3381
3455
|
out(dim(" CLI (Commander)\n"));
|
|
3382
|
-
out(dim(" \u2514\u2500\u2500 Preflight: Claude CLI check + API key
|
|
3383
|
-
out(dim(" \u2514\u2500\u2500
|
|
3384
|
-
out(dim(" \u2514\u2500\u2500
|
|
3385
|
-
out(dim("
|
|
3386
|
-
out(dim("
|
|
3387
|
-
out(dim("
|
|
3388
|
-
out(dim("
|
|
3389
|
-
out(dim("
|
|
3456
|
+
out(dim(" \u2514\u2500\u2500 Preflight: Claude CLI check + API key\n"));
|
|
3457
|
+
out(dim(" \u2514\u2500\u2500 Platform Detection (platform.ts)\n"));
|
|
3458
|
+
out(dim(" \u2514\u2500\u2500 Shell: /bin/sh (Unix) | PowerShell (Windows)\n"));
|
|
3459
|
+
out(dim(" \u2514\u2500\u2500 Agent Orchestrator (agent.ts)\n"));
|
|
3460
|
+
out(dim(" \u2514\u2500\u2500 runDiscovery() \u2192 Claude Sonnet + Bash + MCP Tools\n"));
|
|
3461
|
+
out(dim(" \u2514\u2500\u2500 Custom MCP Tools (tools.ts)\n"));
|
|
3462
|
+
out(dim(" save_node, save_edge,\n"));
|
|
3463
|
+
out(dim(" scan_bookmarks, scan_browser_history,\n"));
|
|
3464
|
+
out(dim(" scan_installed_apps, scan_local_databases\n"));
|
|
3465
|
+
out(dim(" scan_k8s, scan_aws, scan_gcp, scan_azure\n"));
|
|
3466
|
+
out(dim(" \u2514\u2500\u2500 CartographyDB (SQLite WAL)\n"));
|
|
3467
|
+
out("\n");
|
|
3468
|
+
line();
|
|
3469
|
+
out(b(cyan(" SAFETY\n")));
|
|
3470
|
+
out("\n");
|
|
3471
|
+
out(dim(" PreToolUse hook blocks ALL destructive commands:\n"));
|
|
3472
|
+
out(dim(" Unix: rm, mv, dd, chmod, kill, docker rm/run, kubectl delete, >\n"));
|
|
3473
|
+
out(dim(" PowerShell: Remove-Item, Stop-Process, Stop-Service, Out-File, etc.\n"));
|
|
3474
|
+
out(dim(" Claude only reads \u2014 never writes, never deletes.\n"));
|
|
3390
3475
|
out("\n");
|
|
3391
3476
|
line();
|
|
3392
3477
|
out(b(cyan(" SETUP\n")));
|
|
@@ -3396,7 +3481,11 @@ ${infraSummary.substring(0, 12e3)}`;
|
|
|
3396
3481
|
out(" claude login\n");
|
|
3397
3482
|
out("\n");
|
|
3398
3483
|
out(dim(" # 2. API Key (if not using claude login)\n"));
|
|
3399
|
-
|
|
3484
|
+
if (IS_WIN) {
|
|
3485
|
+
out(' $env:ANTHROPIC_API_KEY="sk-ant-..."\n');
|
|
3486
|
+
} else {
|
|
3487
|
+
out(" export ANTHROPIC_API_KEY=sk-ant-...\n");
|
|
3488
|
+
}
|
|
3400
3489
|
out("\n");
|
|
3401
3490
|
out(dim(" # 3. Go\n"));
|
|
3402
3491
|
out(" datasynx-cartography discover\n");
|
|
@@ -3405,7 +3494,7 @@ ${infraSummary.substring(0, 12e3)}`;
|
|
|
3405
3494
|
out("\n");
|
|
3406
3495
|
});
|
|
3407
3496
|
program.command("bookmarks").description("View all browser bookmarks (Chrome, Chromium, Edge, Brave, Vivaldi, Opera, Firefox)").action(async () => {
|
|
3408
|
-
const { scanAllBookmarks: scanAllBookmarks2 } = await import("./bookmarks-
|
|
3497
|
+
const { scanAllBookmarks: scanAllBookmarks2 } = await import("./bookmarks-72CDYAHD.js");
|
|
3409
3498
|
const out = (s) => process.stdout.write(s);
|
|
3410
3499
|
process.stderr.write(" Scanning bookmarks...\n\n");
|
|
3411
3500
|
const hosts = await scanAllBookmarks2();
|
|
@@ -3492,7 +3581,7 @@ ${infraSummary.substring(0, 12e3)}`;
|
|
|
3492
3581
|
`);
|
|
3493
3582
|
return;
|
|
3494
3583
|
}
|
|
3495
|
-
const { NODE_TYPES: NODE_TYPES2 } = await import("./types-
|
|
3584
|
+
const { NODE_TYPES: NODE_TYPES2 } = await import("./types-RHMJ6EGX.js");
|
|
3496
3585
|
if (!process.stdin.isTTY) {
|
|
3497
3586
|
w(red("\n \u2717 Interactive mode requires a terminal (use --file for non-interactive)\n\n"));
|
|
3498
3587
|
process.exitCode = 1;
|
|
@@ -3633,12 +3722,18 @@ ${infraSummary.substring(0, 12e3)}`;
|
|
|
3633
3722
|
}
|
|
3634
3723
|
}
|
|
3635
3724
|
const localTools = [
|
|
3636
|
-
["docker", "docker --version"]
|
|
3637
|
-
["ss", "ss --version"]
|
|
3725
|
+
["docker", "docker --version", "all"]
|
|
3638
3726
|
];
|
|
3727
|
+
if (IS_WIN) {
|
|
3728
|
+
localTools.push(["PowerShell (Get-NetTCPConnection)", 'powershell -Command "Get-NetTCPConnection -State Listen | Select-Object -First 1"', "win32"]);
|
|
3729
|
+
} else if (IS_MAC) {
|
|
3730
|
+
localTools.push(["lsof", "lsof -v 2>&1 | head -1", "darwin"]);
|
|
3731
|
+
} else {
|
|
3732
|
+
localTools.push(["ss", "ss --version", "linux"]);
|
|
3733
|
+
}
|
|
3639
3734
|
for (const [name, cmd] of localTools) {
|
|
3640
3735
|
try {
|
|
3641
|
-
execSync2(cmd, { stdio: "pipe" });
|
|
3736
|
+
execSync2(cmd, { stdio: "pipe", timeout: 1e4 });
|
|
3642
3737
|
ok(`${name} ${dim2("(discovery tool)")}`);
|
|
3643
3738
|
} catch {
|
|
3644
3739
|
warn(`${name} not found ${dim2("\u2014 discovery without " + name + " will be limited")}`);
|
|
@@ -3673,7 +3768,7 @@ ${infraSummary.substring(0, 12e3)}`;
|
|
|
3673
3768
|
o(_c(" |___/ ") + "\n");
|
|
3674
3769
|
o("\n");
|
|
3675
3770
|
o(_b(" Cartography") + " " + _d("v" + VERSION) + "\n");
|
|
3676
|
-
o(_d(" AI-powered Infrastructure Discovery &
|
|
3771
|
+
o(_d(" AI-powered Infrastructure Discovery & Agentic AI Cartography\n"));
|
|
3677
3772
|
o(_d(" Built on Claude Agent SDK\n"));
|
|
3678
3773
|
o("\n");
|
|
3679
3774
|
if (process.argv.length <= 2) {
|