@datasynx/agentic-ai-cartography 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -25
- package/dist/{bookmarks-ITLW7U5D.js → bookmarks-KKS4M37J.js} +2 -2
- package/dist/chunk-PVFDCPPW.js +420 -0
- package/dist/chunk-PVFDCPPW.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 +244 -132
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +40 -166
- package/dist/index.js +365 -173
- 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-KKS4M37J.js.map} +0 -0
- /package/dist/{types-ZD6G5JKR.js.map → types-RHMJ6EGX.js.map} +0 -0
package/README.md
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
[](https://nodejs.org)
|
|
11
11
|
[](https://github.com/anthropics/claude-code)
|
|
12
12
|
[](https://www.linkedin.com/company/datasynx-ai/)
|
|
13
|
+
[](https://github.com/datasynx/agentic-ai-cartography)
|
|
13
14
|
|
|
14
15
|
<br/>
|
|
15
16
|
|
|
@@ -42,33 +43,53 @@ $ datasynx-cartography discover
|
|
|
42
43
|
─────────────────────────────────────────────
|
|
43
44
|
DONE 9 nodes, 3 edges in 38.4s
|
|
44
45
|
|
|
45
|
-
|
|
46
|
-
→
|
|
47
|
-
⟳
|
|
46
|
+
SEARCH MORE — Refine discovery interactively
|
|
47
|
+
→ Search for (Enter = finish): hubspot windsurf
|
|
48
|
+
⟳ Searching for: hubspot windsurf
|
|
48
49
|
+ Node saas_tool:hubspot.com [saas_tool] 70% 🔖
|
|
49
50
|
+ Node saas_tool:windsurf [saas_tool] 90%
|
|
50
51
|
```
|
|
51
52
|
|
|
52
53
|
---
|
|
53
54
|
|
|
55
|
+
## Cross-Platform Support
|
|
56
|
+
|
|
57
|
+
Cartography runs natively on **Linux**, **macOS**, and **Windows** — no WSL required on Windows.
|
|
58
|
+
|
|
59
|
+
| Capability | Linux | macOS | Windows |
|
|
60
|
+
|---|---|---|---|
|
|
61
|
+
| **Network scanning** | `ss -tlnp` | `lsof -iTCP -sTCP:LISTEN` | `Get-NetTCPConnection` |
|
|
62
|
+
| **Process listing** | `ps aux` | `ps aux` | `Get-Process` |
|
|
63
|
+
| **Installed apps** | dpkg, rpm, snap, flatpak, `.desktop` | `/Applications`, Homebrew, Spotlight | Registry, winget, choco, scoop |
|
|
64
|
+
| **Command lookup** | `which` | `which` | `Get-Command` (PowerShell) |
|
|
65
|
+
| **File search** | `find` | `find` | `Get-ChildItem -Recurse` |
|
|
66
|
+
| **Shell** | `/bin/sh` | `/bin/sh` | PowerShell (pwsh / powershell.exe) |
|
|
67
|
+
| **DB service detection** | CLI probes (psql, mysql, etc.) | CLI probes | `Get-Service` + CLI probes |
|
|
68
|
+
| **Browser bookmarks** | `~/.config/google-chrome` + Snap/Flatpak | `~/Library/Application Support/...` | `%LOCALAPPDATA%\Google\Chrome\User Data` |
|
|
69
|
+
| **Firefox profiles** | `~/.mozilla/firefox` + Snap/Flatpak | `~/Library/.../Firefox/Profiles` | `%APPDATA%\Mozilla\Firefox\Profiles` |
|
|
70
|
+
| **Safety hook** | Blocks `rm`, `mv`, `kill`, etc. | Blocks `rm`, `mv`, `kill`, etc. | Blocks `Remove-Item`, `Stop-Process`, etc. |
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
54
74
|
## Features
|
|
55
75
|
|
|
56
76
|
| Feature | Details |
|
|
57
77
|
|---------|---------|
|
|
58
|
-
| **Installed App Scan** |
|
|
59
|
-
| **Browser Bookmarks** | Chrome,
|
|
78
|
+
| **Installed App Scan** | Linux: dpkg/snap/flatpak/rpm, macOS: /Applications + Homebrew + Spotlight, Windows: Registry + winget + choco + scoop. 70+ known tools checked via cross-platform command lookup |
|
|
79
|
+
| **Browser Bookmarks** | Chrome, Chromium, Firefox, Brave, Edge, Vivaldi, Opera — all platforms including Snap/Flatpak on Linux |
|
|
80
|
+
| **Database Discovery** | PostgreSQL, MySQL, MongoDB, Redis, SQLite file scan. Windows: `Get-Service` for DB engine detection |
|
|
60
81
|
| **Cloud Scanning** | AWS (EC2/RDS/EKS/S3), GCP (Compute/GKE/Cloud Run), Azure (AKS/WebApps), Kubernetes |
|
|
61
82
|
| **Human-in-the-Loop** | Chat with the agent mid-discovery: type `"hubspot windsurf"` to search for specific tools |
|
|
62
83
|
| **SOP Generation** | Automatically generates Standard Operating Procedures from observed workflows |
|
|
63
84
|
| **SOP Dashboard** | HTML dashboard with all SOPs, step details, frequency stats |
|
|
64
85
|
| **Export Formats** | Mermaid topology, D3.js interactive graph, Backstage YAML, JSON, SOP Markdown |
|
|
65
|
-
| **Safety First** | `PreToolUse` hook blocks all destructive
|
|
86
|
+
| **Safety First** | `PreToolUse` hook blocks all destructive commands — Unix AND PowerShell. 100% read-only |
|
|
66
87
|
|
|
67
88
|
---
|
|
68
89
|
|
|
69
90
|
## Requirements
|
|
70
91
|
|
|
71
|
-
- **Node.js
|
|
92
|
+
- **Node.js >= 18** (Linux, macOS, or Windows)
|
|
72
93
|
- **Claude CLI** — the Agent SDK starts it as a subprocess
|
|
73
94
|
|
|
74
95
|
```bash
|
|
@@ -91,7 +112,7 @@ npm install -g @datasynx/agentic-ai-cartography
|
|
|
91
112
|
## Quick Start
|
|
92
113
|
|
|
93
114
|
```bash
|
|
94
|
-
# Check all requirements
|
|
115
|
+
# Check all requirements (platform-aware)
|
|
95
116
|
datasynx-cartography doctor
|
|
96
117
|
|
|
97
118
|
# Discover your full infrastructure (one-shot, Claude Sonnet)
|
|
@@ -106,7 +127,7 @@ datasynx-cartography seed
|
|
|
106
127
|
# View all browser bookmarks
|
|
107
128
|
datasynx-cartography bookmarks
|
|
108
129
|
|
|
109
|
-
# Full feature reference
|
|
130
|
+
# Full feature reference (shows platform-specific commands)
|
|
110
131
|
datasynx-cartography docs
|
|
111
132
|
```
|
|
112
133
|
|
|
@@ -130,11 +151,12 @@ datasynx-cartography discover [options]
|
|
|
130
151
|
|
|
131
152
|
Discovery pipeline (automatic, in order):
|
|
132
153
|
1. **Browser bookmarks** — every domain classified as saas_tool or web_service
|
|
133
|
-
2. **Installed apps** — all IDEs, business tools, dev tools, browsers
|
|
134
|
-
3. **Local services** — `ss
|
|
135
|
-
4. **
|
|
136
|
-
5. **
|
|
137
|
-
6. **
|
|
154
|
+
2. **Installed apps** — all IDEs, business tools, dev tools, browsers (platform-native detection)
|
|
155
|
+
3. **Local services** — `ss` (Linux), `lsof` (macOS), `Get-NetTCPConnection` (Windows)
|
|
156
|
+
4. **Database discovery** — PostgreSQL, MySQL, MongoDB, Redis, SQLite files
|
|
157
|
+
5. **Cloud & Kubernetes** — AWS/GCP/Azure/k8s (skipped gracefully if not configured)
|
|
158
|
+
6. **Config files** — `.env`, `docker-compose.yml`, etc.
|
|
159
|
+
7. **Human-in-the-loop** — interactive follow-up after initial scan
|
|
138
160
|
|
|
139
161
|
### Analysis & Export
|
|
140
162
|
|
|
@@ -160,7 +182,7 @@ datasynx-output/
|
|
|
160
182
|
├── catalog-info.yaml Backstage service catalog
|
|
161
183
|
├── topology.mermaid Infrastructure topology (graph TB)
|
|
162
184
|
├── dependencies.mermaid Service dependencies (graph LR)
|
|
163
|
-
├──
|
|
185
|
+
├── discovery.html Enterprise discovery frontend (Map + Topology)
|
|
164
186
|
├── sop-dashboard.html HTML dashboard with all SOPs + frequency stats
|
|
165
187
|
├── sops/
|
|
166
188
|
│ ├── deploy-check.md
|
|
@@ -184,21 +206,30 @@ datasynx-output/
|
|
|
184
206
|
|
|
185
207
|
```
|
|
186
208
|
CLI (Commander.js)
|
|
187
|
-
└── Preflight: Claude CLI + API key
|
|
188
|
-
└──
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
209
|
+
└── Preflight: Claude CLI + API key check
|
|
210
|
+
└── Platform Detection (src/platform.ts)
|
|
211
|
+
├── Shell: /bin/sh (Unix) | PowerShell (Windows)
|
|
212
|
+
├── Commands: which (Unix) | Get-Command (Windows)
|
|
213
|
+
└── Agent Orchestrator (src/agent.ts)
|
|
214
|
+
└── runDiscovery() Claude Sonnet + Bash + MCP Tools
|
|
215
|
+
├── scan_bookmarks() browser bookmark extraction (all platforms)
|
|
216
|
+
├── scan_browser_history() anonymized hostname extraction
|
|
217
|
+
├── scan_installed_apps() platform-native app detection
|
|
218
|
+
├── scan_local_databases() DB service + file scanning
|
|
219
|
+
├── scan_k8s_resources() kubectl (readonly)
|
|
220
|
+
├── scan_aws/gcp/azure() cloud CLI scans (readonly)
|
|
221
|
+
├── ask_user() human-in-the-loop questions
|
|
222
|
+
└── Custom MCP Tools → CartographyDB (SQLite WAL)
|
|
196
223
|
```
|
|
197
224
|
|
|
198
225
|
### Safety
|
|
199
226
|
|
|
200
227
|
Every Bash call is guarded by a `PreToolUse` hook that blocks destructive commands:
|
|
201
|
-
|
|
228
|
+
|
|
229
|
+
**Unix:** `rm`, `mv`, `dd`, `chmod`, `kill`, `docker rm/run/exec`, `kubectl delete/apply/exec`, redirects (`>`), and more.
|
|
230
|
+
|
|
231
|
+
**Windows/PowerShell:** `Remove-Item`, `Move-Item`, `Stop-Process`, `Stop-Service`, `Restart-Computer`, `Format-Volume`, `Out-File`, `Set-Content`, and more.
|
|
232
|
+
|
|
202
233
|
**Claude only reads — never writes, never deletes.**
|
|
203
234
|
|
|
204
235
|
---
|
|
@@ -3,10 +3,10 @@ import {
|
|
|
3
3
|
readFirefoxHistory,
|
|
4
4
|
scanAllBookmarks,
|
|
5
5
|
scanAllHistory
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-PVFDCPPW.js";
|
|
7
7
|
export {
|
|
8
8
|
readFirefoxHistory,
|
|
9
9
|
scanAllBookmarks,
|
|
10
10
|
scanAllHistory
|
|
11
11
|
};
|
|
12
|
-
//# sourceMappingURL=bookmarks-
|
|
12
|
+
//# sourceMappingURL=bookmarks-KKS4M37J.js.map
|
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/bookmarks.ts
|
|
4
|
+
import { tmpdir } from "os";
|
|
5
|
+
import { existsSync as existsSync2, readFileSync, readdirSync, copyFileSync, statSync } from "fs";
|
|
6
|
+
import { join as join2 } from "path";
|
|
7
|
+
|
|
8
|
+
// src/platform.ts
|
|
9
|
+
import { homedir } from "os";
|
|
10
|
+
import { join } from "path";
|
|
11
|
+
import { execSync } from "child_process";
|
|
12
|
+
import { existsSync } from "fs";
|
|
13
|
+
var PLATFORM = process.platform;
|
|
14
|
+
var IS_WIN = PLATFORM === "win32";
|
|
15
|
+
var IS_MAC = PLATFORM === "darwin";
|
|
16
|
+
var IS_LINUX = PLATFORM === "linux";
|
|
17
|
+
var HOME = homedir();
|
|
18
|
+
function platformShell() {
|
|
19
|
+
if (!IS_WIN) return "/bin/sh";
|
|
20
|
+
try {
|
|
21
|
+
execSync("pwsh -Version", { stdio: "pipe", timeout: 3e3 });
|
|
22
|
+
return "pwsh";
|
|
23
|
+
} catch {
|
|
24
|
+
return "powershell.exe";
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
var _shell;
|
|
28
|
+
function getShell() {
|
|
29
|
+
if (!_shell) _shell = platformShell();
|
|
30
|
+
return _shell;
|
|
31
|
+
}
|
|
32
|
+
function run(cmd, opts = {}) {
|
|
33
|
+
try {
|
|
34
|
+
return execSync(cmd, {
|
|
35
|
+
stdio: "pipe",
|
|
36
|
+
timeout: opts.timeout ?? 1e4,
|
|
37
|
+
shell: getShell(),
|
|
38
|
+
env: opts.env
|
|
39
|
+
}).toString().trim();
|
|
40
|
+
} catch {
|
|
41
|
+
return "";
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function commandExists(cmd) {
|
|
45
|
+
if (IS_WIN) {
|
|
46
|
+
const r = run(`Get-Command ${cmd} -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Source`, { timeout: 5e3 });
|
|
47
|
+
return r;
|
|
48
|
+
}
|
|
49
|
+
return run(`which ${cmd} 2>/dev/null`);
|
|
50
|
+
}
|
|
51
|
+
function userDataDir() {
|
|
52
|
+
if (IS_WIN) return process.env.APPDATA ?? join(HOME, "AppData", "Roaming");
|
|
53
|
+
if (IS_MAC) return join(HOME, "Library", "Application Support");
|
|
54
|
+
return process.env.XDG_DATA_HOME ?? join(HOME, ".local", "share");
|
|
55
|
+
}
|
|
56
|
+
function browserBasePaths() {
|
|
57
|
+
if (IS_WIN) {
|
|
58
|
+
const local = process.env.LOCALAPPDATA ?? join(HOME, "AppData", "Local");
|
|
59
|
+
return {
|
|
60
|
+
chrome: join(local, "Google", "Chrome", "User Data"),
|
|
61
|
+
chromium: join(local, "Chromium", "User Data"),
|
|
62
|
+
edge: join(local, "Microsoft", "Edge", "User Data"),
|
|
63
|
+
brave: join(local, "BraveSoftware", "Brave-Browser", "User Data"),
|
|
64
|
+
vivaldi: join(local, "Vivaldi", "User Data"),
|
|
65
|
+
opera: join(userDataDir(), "Opera Software", "Opera Stable")
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
if (IS_MAC) {
|
|
69
|
+
const lib = join(HOME, "Library", "Application Support");
|
|
70
|
+
return {
|
|
71
|
+
chrome: join(lib, "Google", "Chrome"),
|
|
72
|
+
chromium: join(lib, "Chromium"),
|
|
73
|
+
edge: join(lib, "Microsoft Edge"),
|
|
74
|
+
brave: join(lib, "BraveSoftware", "Brave-Browser"),
|
|
75
|
+
vivaldi: join(lib, "Vivaldi"),
|
|
76
|
+
opera: join(lib, "com.operasoftware.Opera")
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
chrome: join(HOME, ".config", "google-chrome"),
|
|
81
|
+
chromium: join(HOME, ".config", "chromium"),
|
|
82
|
+
edge: join(HOME, ".config", "microsoft-edge"),
|
|
83
|
+
brave: join(HOME, ".config", "BraveSoftware", "Brave-Browser"),
|
|
84
|
+
vivaldi: join(HOME, ".config", "vivaldi"),
|
|
85
|
+
opera: join(HOME, ".config", "opera")
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function firefoxBaseDirs() {
|
|
89
|
+
if (IS_WIN) {
|
|
90
|
+
const roaming = process.env.APPDATA ?? join(HOME, "AppData", "Roaming");
|
|
91
|
+
return [join(roaming, "Mozilla", "Firefox", "Profiles")];
|
|
92
|
+
}
|
|
93
|
+
if (IS_MAC) {
|
|
94
|
+
return [join(HOME, "Library", "Application Support", "Firefox", "Profiles")];
|
|
95
|
+
}
|
|
96
|
+
return [
|
|
97
|
+
join(HOME, ".mozilla", "firefox"),
|
|
98
|
+
join(HOME, "snap", "firefox", "common", ".mozilla", "firefox"),
|
|
99
|
+
join(HOME, ".var", "app", "org.mozilla.firefox", ".mozilla", "firefox")
|
|
100
|
+
];
|
|
101
|
+
}
|
|
102
|
+
function dbScanDirs() {
|
|
103
|
+
const dirs = [];
|
|
104
|
+
if (IS_WIN) {
|
|
105
|
+
const local = process.env.LOCALAPPDATA ?? join(HOME, "AppData", "Local");
|
|
106
|
+
const roaming = process.env.APPDATA ?? join(HOME, "AppData", "Roaming");
|
|
107
|
+
dirs.push(local, roaming);
|
|
108
|
+
const pd = join(HOME, "AppData", "Local", "Programs");
|
|
109
|
+
if (existsSync(pd)) dirs.push(pd);
|
|
110
|
+
} else if (IS_MAC) {
|
|
111
|
+
dirs.push(join(HOME, "Library", "Application Support"));
|
|
112
|
+
if (existsSync("/var/lib")) dirs.push("/var/lib");
|
|
113
|
+
} else {
|
|
114
|
+
const configDir = join(HOME, ".config");
|
|
115
|
+
const dataDir = join(HOME, ".local", "share");
|
|
116
|
+
if (existsSync(configDir)) dirs.push(configDir);
|
|
117
|
+
if (existsSync(dataDir)) dirs.push(dataDir);
|
|
118
|
+
if (existsSync("/var/lib")) dirs.push("/var/lib");
|
|
119
|
+
}
|
|
120
|
+
return dirs.filter((d) => existsSync(d));
|
|
121
|
+
}
|
|
122
|
+
function findFiles(dirs, patterns, maxDepth, limit) {
|
|
123
|
+
if (dirs.length === 0) return "";
|
|
124
|
+
if (IS_WIN) {
|
|
125
|
+
const includes = patterns.map((p) => `'${p}'`).join(",");
|
|
126
|
+
const pathList = dirs.map((d) => `'${d}'`).join(",");
|
|
127
|
+
return run(
|
|
128
|
+
`Get-ChildItem -Path ${pathList} -Recurse -Depth ${maxDepth} -Include ${includes} -ErrorAction SilentlyContinue | Select-Object -First ${limit} -ExpandProperty FullName`,
|
|
129
|
+
{ timeout: 15e3 }
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
const nameArgs = patterns.map((p) => `-name "${p}"`).join(" -o ");
|
|
133
|
+
const findCmds = dirs.map((d) => `find "${d}" -maxdepth ${maxDepth} \\( ${nameArgs} \\) 2>/dev/null`).join("; ");
|
|
134
|
+
return run(`{ ${findCmds}; } | head -${limit}`, { timeout: 15e3 });
|
|
135
|
+
}
|
|
136
|
+
function scanWindowsPrograms() {
|
|
137
|
+
if (!IS_WIN) return "";
|
|
138
|
+
return run(
|
|
139
|
+
`$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`,
|
|
140
|
+
{ timeout: 2e4 }
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
function scanWindowsDbServices() {
|
|
144
|
+
if (!IS_WIN) return "";
|
|
145
|
+
return run(
|
|
146
|
+
`Get-Service | Where-Object { $_.Name -match 'postgres|mysql|mariadb|mongo|redis|MSSQL|elastic|clickhouse|cassandra' } | Select-Object Name, DisplayName, Status, StartType | Format-Table -AutoSize`,
|
|
147
|
+
{ timeout: 1e4 }
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
function fileUrl(absPath) {
|
|
151
|
+
if (IS_WIN) {
|
|
152
|
+
const normalized = absPath.replace(/\\/g, "/");
|
|
153
|
+
return `file:///${normalized}`;
|
|
154
|
+
}
|
|
155
|
+
return `file://${absPath}`;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// src/bookmarks.ts
|
|
159
|
+
function extractHost(rawUrl, source) {
|
|
160
|
+
try {
|
|
161
|
+
const u = new URL(rawUrl);
|
|
162
|
+
if (u.protocol !== "http:" && u.protocol !== "https:") return null;
|
|
163
|
+
const protocol = u.protocol === "https:" ? "https" : "http";
|
|
164
|
+
const port = u.port ? parseInt(u.port, 10) : protocol === "https" ? 443 : 80;
|
|
165
|
+
const hostname = u.hostname.toLowerCase();
|
|
166
|
+
if (!hostname || hostname === "localhost" || hostname === "127.0.0.1") return null;
|
|
167
|
+
return { hostname, port, protocol, source };
|
|
168
|
+
} catch {
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
function walkChrome(node, source, out) {
|
|
173
|
+
if (node.type === "url" && node.url) {
|
|
174
|
+
const h = extractHost(node.url, source);
|
|
175
|
+
if (h) out.push(h);
|
|
176
|
+
}
|
|
177
|
+
if (node.children) {
|
|
178
|
+
for (const child of node.children) walkChrome(child, source, out);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
function readChromeLike(filePath, source) {
|
|
182
|
+
if (!existsSync2(filePath)) return [];
|
|
183
|
+
try {
|
|
184
|
+
const raw = JSON.parse(readFileSync(filePath, "utf8"));
|
|
185
|
+
const out = [];
|
|
186
|
+
for (const root of Object.values(raw.roots)) {
|
|
187
|
+
if (root) walkChrome(root, source, out);
|
|
188
|
+
}
|
|
189
|
+
return out;
|
|
190
|
+
} catch {
|
|
191
|
+
return [];
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
async function readFirefoxBookmarks(profileDir) {
|
|
195
|
+
const src = join2(profileDir, "places.sqlite");
|
|
196
|
+
if (!existsSync2(src)) return [];
|
|
197
|
+
const tmp = join2(tmpdir(), `cartograph_ff_bm_${Date.now()}.sqlite`);
|
|
198
|
+
try {
|
|
199
|
+
copyFileSync(src, tmp);
|
|
200
|
+
const { default: Database } = await import("better-sqlite3");
|
|
201
|
+
const db = new Database(tmp, { readonly: true, fileMustExist: true });
|
|
202
|
+
const rows = db.prepare(`
|
|
203
|
+
SELECT DISTINCT p.url
|
|
204
|
+
FROM moz_places p
|
|
205
|
+
JOIN moz_bookmarks b ON b.fk = p.id
|
|
206
|
+
WHERE b.type = 1 AND p.url NOT LIKE 'place:%'
|
|
207
|
+
LIMIT 3000
|
|
208
|
+
`).all();
|
|
209
|
+
db.close();
|
|
210
|
+
return rows.map((r) => extractHost(r.url, "firefox")).filter((h) => h !== null);
|
|
211
|
+
} catch {
|
|
212
|
+
return [];
|
|
213
|
+
} finally {
|
|
214
|
+
try {
|
|
215
|
+
(await import("fs")).unlinkSync(tmp);
|
|
216
|
+
} catch {
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
async function readFirefoxHistory(profileDir) {
|
|
221
|
+
const src = join2(profileDir, "places.sqlite");
|
|
222
|
+
if (!existsSync2(src)) return [];
|
|
223
|
+
const tmp = join2(tmpdir(), `cartograph_ff_hist_${Date.now()}.sqlite`);
|
|
224
|
+
try {
|
|
225
|
+
copyFileSync(src, tmp);
|
|
226
|
+
const { default: Database } = await import("better-sqlite3");
|
|
227
|
+
const db = new Database(tmp, { readonly: true, fileMustExist: true });
|
|
228
|
+
const rows = db.prepare(`
|
|
229
|
+
SELECT url, visit_count
|
|
230
|
+
FROM moz_places
|
|
231
|
+
WHERE url NOT LIKE 'place:%'
|
|
232
|
+
AND visit_count > 0
|
|
233
|
+
ORDER BY visit_count DESC
|
|
234
|
+
LIMIT 5000
|
|
235
|
+
`).all();
|
|
236
|
+
db.close();
|
|
237
|
+
return rows.map((r) => {
|
|
238
|
+
const h = extractHost(r.url, "firefox");
|
|
239
|
+
if (!h) return null;
|
|
240
|
+
return { ...h, visitCount: r.visit_count };
|
|
241
|
+
}).filter((h) => h !== null);
|
|
242
|
+
} catch {
|
|
243
|
+
return [];
|
|
244
|
+
} finally {
|
|
245
|
+
try {
|
|
246
|
+
(await import("fs")).unlinkSync(tmp);
|
|
247
|
+
} catch {
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
async function readChromiumHistory(historyPath, source) {
|
|
252
|
+
if (!existsSync2(historyPath)) return [];
|
|
253
|
+
const tmp = join2(tmpdir(), `cartograph_ch_hist_${Date.now()}.sqlite`);
|
|
254
|
+
try {
|
|
255
|
+
copyFileSync(historyPath, tmp);
|
|
256
|
+
const { default: Database } = await import("better-sqlite3");
|
|
257
|
+
const db = new Database(tmp, { readonly: true, fileMustExist: true });
|
|
258
|
+
const rows = db.prepare(`
|
|
259
|
+
SELECT url, visit_count
|
|
260
|
+
FROM urls
|
|
261
|
+
WHERE hidden = 0
|
|
262
|
+
AND visit_count > 0
|
|
263
|
+
ORDER BY visit_count DESC
|
|
264
|
+
LIMIT 5000
|
|
265
|
+
`).all();
|
|
266
|
+
db.close();
|
|
267
|
+
return rows.map((r) => {
|
|
268
|
+
const h = extractHost(r.url, source);
|
|
269
|
+
if (!h) return null;
|
|
270
|
+
return { ...h, visitCount: r.visit_count };
|
|
271
|
+
}).filter((h) => h !== null);
|
|
272
|
+
} catch {
|
|
273
|
+
return [];
|
|
274
|
+
} finally {
|
|
275
|
+
try {
|
|
276
|
+
(await import("fs")).unlinkSync(tmp);
|
|
277
|
+
} catch {
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
var IS_LINUX2 = !IS_MAC && !IS_WIN;
|
|
282
|
+
function chromeLikePaths(base) {
|
|
283
|
+
const paths = [];
|
|
284
|
+
const defaultPath = join2(base, "Default", "Bookmarks");
|
|
285
|
+
if (existsSync2(defaultPath)) paths.push(defaultPath);
|
|
286
|
+
if (existsSync2(base)) {
|
|
287
|
+
try {
|
|
288
|
+
for (const entry of readdirSync(base)) {
|
|
289
|
+
if (entry.startsWith("Profile ")) {
|
|
290
|
+
const p = join2(base, entry, "Bookmarks");
|
|
291
|
+
if (existsSync2(p)) paths.push(p);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
} catch {
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
return paths;
|
|
298
|
+
}
|
|
299
|
+
function chromeLikeHistoryPaths(base) {
|
|
300
|
+
const paths = [];
|
|
301
|
+
const defaultPath = join2(base, "Default", "History");
|
|
302
|
+
if (existsSync2(defaultPath)) paths.push(defaultPath);
|
|
303
|
+
if (existsSync2(base)) {
|
|
304
|
+
try {
|
|
305
|
+
for (const entry of readdirSync(base)) {
|
|
306
|
+
if (entry.startsWith("Profile ")) {
|
|
307
|
+
const p = join2(base, entry, "History");
|
|
308
|
+
if (existsSync2(p)) paths.push(p);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
} catch {
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return paths;
|
|
315
|
+
}
|
|
316
|
+
var BROWSER_BASES = browserBasePaths();
|
|
317
|
+
var CHROME_BASE = BROWSER_BASES.chrome;
|
|
318
|
+
var CHROMIUM_BASE = BROWSER_BASES.chromium;
|
|
319
|
+
var EDGE_BASE = BROWSER_BASES.edge;
|
|
320
|
+
var BRAVE_BASE = BROWSER_BASES.brave;
|
|
321
|
+
var VIVALDI_BASE = BROWSER_BASES.vivaldi;
|
|
322
|
+
var OPERA_BASE = BROWSER_BASES.opera;
|
|
323
|
+
var CHROMIUM_SNAP_BASE = join2(HOME, "snap", "chromium", "common", "chromium");
|
|
324
|
+
var CHROMIUM_FLATPAK_BASE = join2(HOME, ".var", "app", "org.chromium.Chromium", "config", "chromium");
|
|
325
|
+
var CHROME_FLATPAK_BASE = join2(HOME, ".var", "app", "com.google.Chrome", "config", "google-chrome");
|
|
326
|
+
var BRAVE_FLATPAK_BASE = join2(HOME, ".var", "app", "com.brave.Browser", "config", "BraveSoftware", "Brave-Browser");
|
|
327
|
+
var EDGE_FLATPAK_BASE = join2(HOME, ".var", "app", "com.microsoft.Edge", "config", "microsoft-edge");
|
|
328
|
+
function firefoxProfileDirs() {
|
|
329
|
+
const bases = firefoxBaseDirs();
|
|
330
|
+
const dirs = [];
|
|
331
|
+
for (const base of bases) {
|
|
332
|
+
if (!existsSync2(base)) continue;
|
|
333
|
+
try {
|
|
334
|
+
for (const d of readdirSync(base)) {
|
|
335
|
+
const full = join2(base, d);
|
|
336
|
+
try {
|
|
337
|
+
if (statSync(full).isDirectory() && existsSync2(join2(full, "places.sqlite"))) {
|
|
338
|
+
dirs.push(full);
|
|
339
|
+
}
|
|
340
|
+
} catch {
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
} catch {
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
return dirs;
|
|
347
|
+
}
|
|
348
|
+
async function scanAllBookmarks() {
|
|
349
|
+
const all = [];
|
|
350
|
+
for (const p of chromeLikePaths(CHROME_BASE)) all.push(...readChromeLike(p, "chrome"));
|
|
351
|
+
for (const p of chromeLikePaths(CHROMIUM_BASE)) all.push(...readChromeLike(p, "chromium"));
|
|
352
|
+
for (const p of chromeLikePaths(EDGE_BASE)) all.push(...readChromeLike(p, "edge"));
|
|
353
|
+
for (const p of chromeLikePaths(BRAVE_BASE)) all.push(...readChromeLike(p, "brave"));
|
|
354
|
+
for (const p of chromeLikePaths(VIVALDI_BASE)) all.push(...readChromeLike(p, "vivaldi"));
|
|
355
|
+
for (const p of chromeLikePaths(OPERA_BASE)) all.push(...readChromeLike(p, "opera"));
|
|
356
|
+
if (IS_LINUX2) {
|
|
357
|
+
for (const p of chromeLikePaths(CHROMIUM_SNAP_BASE)) all.push(...readChromeLike(p, "chromium-snap"));
|
|
358
|
+
for (const p of chromeLikePaths(CHROMIUM_FLATPAK_BASE)) all.push(...readChromeLike(p, "chromium-flatpak"));
|
|
359
|
+
for (const p of chromeLikePaths(CHROME_FLATPAK_BASE)) all.push(...readChromeLike(p, "chrome-flatpak"));
|
|
360
|
+
for (const p of chromeLikePaths(BRAVE_FLATPAK_BASE)) all.push(...readChromeLike(p, "brave-flatpak"));
|
|
361
|
+
for (const p of chromeLikePaths(EDGE_FLATPAK_BASE)) all.push(...readChromeLike(p, "edge-flatpak"));
|
|
362
|
+
}
|
|
363
|
+
for (const dir of firefoxProfileDirs()) {
|
|
364
|
+
all.push(...await readFirefoxBookmarks(dir));
|
|
365
|
+
}
|
|
366
|
+
const seen = /* @__PURE__ */ new Set();
|
|
367
|
+
return all.filter((h) => {
|
|
368
|
+
if (seen.has(h.hostname)) return false;
|
|
369
|
+
seen.add(h.hostname);
|
|
370
|
+
return true;
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
async function scanAllHistory() {
|
|
374
|
+
const all = [];
|
|
375
|
+
for (const p of chromeLikeHistoryPaths(CHROME_BASE)) all.push(...await readChromiumHistory(p, "chrome"));
|
|
376
|
+
for (const p of chromeLikeHistoryPaths(CHROMIUM_BASE)) all.push(...await readChromiumHistory(p, "chromium"));
|
|
377
|
+
for (const p of chromeLikeHistoryPaths(EDGE_BASE)) all.push(...await readChromiumHistory(p, "edge"));
|
|
378
|
+
for (const p of chromeLikeHistoryPaths(BRAVE_BASE)) all.push(...await readChromiumHistory(p, "brave"));
|
|
379
|
+
for (const p of chromeLikeHistoryPaths(VIVALDI_BASE)) all.push(...await readChromiumHistory(p, "vivaldi"));
|
|
380
|
+
for (const p of chromeLikeHistoryPaths(OPERA_BASE)) all.push(...await readChromiumHistory(p, "opera"));
|
|
381
|
+
if (IS_LINUX2) {
|
|
382
|
+
for (const p of chromeLikeHistoryPaths(CHROMIUM_SNAP_BASE)) all.push(...await readChromiumHistory(p, "chromium-snap"));
|
|
383
|
+
for (const p of chromeLikeHistoryPaths(CHROMIUM_FLATPAK_BASE)) all.push(...await readChromiumHistory(p, "chromium-flatpak"));
|
|
384
|
+
for (const p of chromeLikeHistoryPaths(CHROME_FLATPAK_BASE)) all.push(...await readChromiumHistory(p, "chrome-flatpak"));
|
|
385
|
+
for (const p of chromeLikeHistoryPaths(BRAVE_FLATPAK_BASE)) all.push(...await readChromiumHistory(p, "brave-flatpak"));
|
|
386
|
+
for (const p of chromeLikeHistoryPaths(EDGE_FLATPAK_BASE)) all.push(...await readChromiumHistory(p, "edge-flatpak"));
|
|
387
|
+
}
|
|
388
|
+
for (const dir of firefoxProfileDirs()) {
|
|
389
|
+
all.push(...await readFirefoxHistory(dir));
|
|
390
|
+
}
|
|
391
|
+
const byHost = /* @__PURE__ */ new Map();
|
|
392
|
+
for (const h of all) {
|
|
393
|
+
const existing = byHost.get(h.hostname);
|
|
394
|
+
if (existing) {
|
|
395
|
+
existing.visitCount += h.visitCount;
|
|
396
|
+
} else {
|
|
397
|
+
byHost.set(h.hostname, { ...h });
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
return [...byHost.values()].sort((a, b) => b.visitCount - a.visitCount);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
export {
|
|
404
|
+
PLATFORM,
|
|
405
|
+
IS_WIN,
|
|
406
|
+
IS_MAC,
|
|
407
|
+
IS_LINUX,
|
|
408
|
+
HOME,
|
|
409
|
+
run,
|
|
410
|
+
commandExists,
|
|
411
|
+
dbScanDirs,
|
|
412
|
+
findFiles,
|
|
413
|
+
scanWindowsPrograms,
|
|
414
|
+
scanWindowsDbServices,
|
|
415
|
+
fileUrl,
|
|
416
|
+
readFirefoxHistory,
|
|
417
|
+
scanAllBookmarks,
|
|
418
|
+
scanAllHistory
|
|
419
|
+
};
|
|
420
|
+
//# sourceMappingURL=chunk-PVFDCPPW.js.map
|