@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 CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # 🗺️ Datasynx Cartography
4
4
 
5
- **AI-powered Infrastructure Cartography & SOP Generation**
5
+ **AI-powered Infrastructure Discovery & Agentic AI Cartography**
6
6
 
7
7
  [![npm version](https://img.shields.io/npm/v/@datasynx/agentic-ai-cartography?style=flat-square&color=CB3837&logo=npm&logoColor=white)](https://www.npmjs.com/package/@datasynx/agentic-ai-cartography)
8
8
  [![npm downloads](https://img.shields.io/npm/dm/@datasynx/agentic-ai-cartography?style=flat-square&color=CB3837&logo=npm&logoColor=white)](https://www.npmjs.com/package/@datasynx/agentic-ai-cartography)
@@ -10,6 +10,7 @@
10
10
  [![Node.js ≥18](https://img.shields.io/badge/Node.js-%E2%89%A518-339933?style=flat-square&logo=node.js&logoColor=white)](https://nodejs.org)
11
11
  [![Built with Claude](https://img.shields.io/badge/Built_with-Claude_Agent_SDK-D4A017?style=flat-square&logo=anthropic&logoColor=white)](https://github.com/anthropics/claude-code)
12
12
  [![LinkedIn](https://img.shields.io/badge/LinkedIn-Datasynx_AI-0077B5?style=flat-square&logo=linkedin&logoColor=white)](https://www.linkedin.com/company/datasynx-ai/)
13
+ [![Platform](https://img.shields.io/badge/Platform-Linux%20%7C%20macOS%20%7C%20Windows-blue?style=flat-square)](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
- WEITERSUCHENDiscovery interaktiv verfeinern
46
- Suche nach (Enter = Beenden): hubspot windsurf
47
- Suche nach: hubspot windsurf
46
+ SEARCH MORERefine 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** | Scans `/Applications`, Homebrew, dpkg/snap/flatpak, Spotlight + 60 known tools via `which` |
59
- | **Browser Bookmarks** | Chrome, Firefox, Safari, Brave, Edge — extracts business/SaaS domains automatically |
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 Bash commands — 100% read-only |
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 18**
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`, `ps`, port-to-service mapping
135
- 4. **Cloud & Kubernetes** — AWS/GCP/Azure/k8s (skipped gracefully if not configured)
136
- 5. **Config files** — `.env`, `docker-compose.yml`, etc.
137
- 6. **Human-in-the-loop** — interactive follow-up after initial scan
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
- ├── topology.html Interactive D3.js force graph
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 + interval validation
188
- └── Agent Orchestrator (src/agent.ts)
189
- └── runDiscovery() Claude Sonnet + Bash + MCP Tools
190
- ├── scan_bookmarks() browser bookmark extraction
191
- ├── scan_installed_apps() /Applications, brew, dpkg, which
192
- ├── scan_k8s_resources() kubectl (readonly)
193
- ├── scan_aws/gcp/azure() cloud CLI scans (readonly)
194
- └── ask_user() human-in-the-loop questions
195
- └── Custom MCP Tools → CartographyDB (SQLite WAL)
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
- `rm`, `mv`, `dd`, `chmod`, `kill`, `docker rm/run/exec`, `kubectl delete/apply/exec`, redirects (`>`), pipes to shell, and more.
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-2VIAXA5T.js";
6
+ } from "./chunk-3NVQ3ND6.js";
7
7
  export {
8
8
  readFirefoxHistory,
9
9
  scanAllBookmarks,
10
10
  scanAllHistory
11
11
  };
12
- //# sourceMappingURL=bookmarks-ITLW7U5D.js.map
12
+ //# sourceMappingURL=bookmarks-72CDYAHD.js.map
@@ -0,0 +1,412 @@
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
+
151
+ // src/bookmarks.ts
152
+ function extractHost(rawUrl, source) {
153
+ try {
154
+ const u = new URL(rawUrl);
155
+ if (u.protocol !== "http:" && u.protocol !== "https:") return null;
156
+ const protocol = u.protocol === "https:" ? "https" : "http";
157
+ const port = u.port ? parseInt(u.port, 10) : protocol === "https" ? 443 : 80;
158
+ const hostname = u.hostname.toLowerCase();
159
+ if (!hostname || hostname === "localhost" || hostname === "127.0.0.1") return null;
160
+ return { hostname, port, protocol, source };
161
+ } catch {
162
+ return null;
163
+ }
164
+ }
165
+ function walkChrome(node, source, out) {
166
+ if (node.type === "url" && node.url) {
167
+ const h = extractHost(node.url, source);
168
+ if (h) out.push(h);
169
+ }
170
+ if (node.children) {
171
+ for (const child of node.children) walkChrome(child, source, out);
172
+ }
173
+ }
174
+ function readChromeLike(filePath, source) {
175
+ if (!existsSync2(filePath)) return [];
176
+ try {
177
+ const raw = JSON.parse(readFileSync(filePath, "utf8"));
178
+ const out = [];
179
+ for (const root of Object.values(raw.roots)) {
180
+ if (root) walkChrome(root, source, out);
181
+ }
182
+ return out;
183
+ } catch {
184
+ return [];
185
+ }
186
+ }
187
+ async function readFirefoxBookmarks(profileDir) {
188
+ const src = join2(profileDir, "places.sqlite");
189
+ if (!existsSync2(src)) return [];
190
+ const tmp = join2(tmpdir(), `cartograph_ff_bm_${Date.now()}.sqlite`);
191
+ try {
192
+ copyFileSync(src, tmp);
193
+ const { default: Database } = await import("better-sqlite3");
194
+ const db = new Database(tmp, { readonly: true, fileMustExist: true });
195
+ const rows = db.prepare(`
196
+ SELECT DISTINCT p.url
197
+ FROM moz_places p
198
+ JOIN moz_bookmarks b ON b.fk = p.id
199
+ WHERE b.type = 1 AND p.url NOT LIKE 'place:%'
200
+ LIMIT 3000
201
+ `).all();
202
+ db.close();
203
+ return rows.map((r) => extractHost(r.url, "firefox")).filter((h) => h !== null);
204
+ } catch {
205
+ return [];
206
+ } finally {
207
+ try {
208
+ (await import("fs")).unlinkSync(tmp);
209
+ } catch {
210
+ }
211
+ }
212
+ }
213
+ async function readFirefoxHistory(profileDir) {
214
+ const src = join2(profileDir, "places.sqlite");
215
+ if (!existsSync2(src)) return [];
216
+ const tmp = join2(tmpdir(), `cartograph_ff_hist_${Date.now()}.sqlite`);
217
+ try {
218
+ copyFileSync(src, tmp);
219
+ const { default: Database } = await import("better-sqlite3");
220
+ const db = new Database(tmp, { readonly: true, fileMustExist: true });
221
+ const rows = db.prepare(`
222
+ SELECT url, visit_count
223
+ FROM moz_places
224
+ WHERE url NOT LIKE 'place:%'
225
+ AND visit_count > 0
226
+ ORDER BY visit_count DESC
227
+ LIMIT 5000
228
+ `).all();
229
+ db.close();
230
+ return rows.map((r) => {
231
+ const h = extractHost(r.url, "firefox");
232
+ if (!h) return null;
233
+ return { ...h, visitCount: r.visit_count };
234
+ }).filter((h) => h !== null);
235
+ } catch {
236
+ return [];
237
+ } finally {
238
+ try {
239
+ (await import("fs")).unlinkSync(tmp);
240
+ } catch {
241
+ }
242
+ }
243
+ }
244
+ async function readChromiumHistory(historyPath, source) {
245
+ if (!existsSync2(historyPath)) return [];
246
+ const tmp = join2(tmpdir(), `cartograph_ch_hist_${Date.now()}.sqlite`);
247
+ try {
248
+ copyFileSync(historyPath, tmp);
249
+ const { default: Database } = await import("better-sqlite3");
250
+ const db = new Database(tmp, { readonly: true, fileMustExist: true });
251
+ const rows = db.prepare(`
252
+ SELECT url, visit_count
253
+ FROM urls
254
+ WHERE hidden = 0
255
+ AND visit_count > 0
256
+ ORDER BY visit_count DESC
257
+ LIMIT 5000
258
+ `).all();
259
+ db.close();
260
+ return rows.map((r) => {
261
+ const h = extractHost(r.url, source);
262
+ if (!h) return null;
263
+ return { ...h, visitCount: r.visit_count };
264
+ }).filter((h) => h !== null);
265
+ } catch {
266
+ return [];
267
+ } finally {
268
+ try {
269
+ (await import("fs")).unlinkSync(tmp);
270
+ } catch {
271
+ }
272
+ }
273
+ }
274
+ var IS_LINUX2 = !IS_MAC && !IS_WIN;
275
+ function chromeLikePaths(base) {
276
+ const paths = [];
277
+ const defaultPath = join2(base, "Default", "Bookmarks");
278
+ if (existsSync2(defaultPath)) paths.push(defaultPath);
279
+ if (existsSync2(base)) {
280
+ try {
281
+ for (const entry of readdirSync(base)) {
282
+ if (entry.startsWith("Profile ")) {
283
+ const p = join2(base, entry, "Bookmarks");
284
+ if (existsSync2(p)) paths.push(p);
285
+ }
286
+ }
287
+ } catch {
288
+ }
289
+ }
290
+ return paths;
291
+ }
292
+ function chromeLikeHistoryPaths(base) {
293
+ const paths = [];
294
+ const defaultPath = join2(base, "Default", "History");
295
+ if (existsSync2(defaultPath)) paths.push(defaultPath);
296
+ if (existsSync2(base)) {
297
+ try {
298
+ for (const entry of readdirSync(base)) {
299
+ if (entry.startsWith("Profile ")) {
300
+ const p = join2(base, entry, "History");
301
+ if (existsSync2(p)) paths.push(p);
302
+ }
303
+ }
304
+ } catch {
305
+ }
306
+ }
307
+ return paths;
308
+ }
309
+ var BROWSER_BASES = browserBasePaths();
310
+ var CHROME_BASE = BROWSER_BASES.chrome;
311
+ var CHROMIUM_BASE = BROWSER_BASES.chromium;
312
+ var EDGE_BASE = BROWSER_BASES.edge;
313
+ var BRAVE_BASE = BROWSER_BASES.brave;
314
+ var VIVALDI_BASE = BROWSER_BASES.vivaldi;
315
+ var OPERA_BASE = BROWSER_BASES.opera;
316
+ var CHROMIUM_SNAP_BASE = join2(HOME, "snap", "chromium", "common", "chromium");
317
+ var CHROMIUM_FLATPAK_BASE = join2(HOME, ".var", "app", "org.chromium.Chromium", "config", "chromium");
318
+ var CHROME_FLATPAK_BASE = join2(HOME, ".var", "app", "com.google.Chrome", "config", "google-chrome");
319
+ var BRAVE_FLATPAK_BASE = join2(HOME, ".var", "app", "com.brave.Browser", "config", "BraveSoftware", "Brave-Browser");
320
+ var EDGE_FLATPAK_BASE = join2(HOME, ".var", "app", "com.microsoft.Edge", "config", "microsoft-edge");
321
+ function firefoxProfileDirs() {
322
+ const bases = firefoxBaseDirs();
323
+ const dirs = [];
324
+ for (const base of bases) {
325
+ if (!existsSync2(base)) continue;
326
+ try {
327
+ for (const d of readdirSync(base)) {
328
+ const full = join2(base, d);
329
+ try {
330
+ if (statSync(full).isDirectory() && existsSync2(join2(full, "places.sqlite"))) {
331
+ dirs.push(full);
332
+ }
333
+ } catch {
334
+ }
335
+ }
336
+ } catch {
337
+ }
338
+ }
339
+ return dirs;
340
+ }
341
+ async function scanAllBookmarks() {
342
+ const all = [];
343
+ for (const p of chromeLikePaths(CHROME_BASE)) all.push(...readChromeLike(p, "chrome"));
344
+ for (const p of chromeLikePaths(CHROMIUM_BASE)) all.push(...readChromeLike(p, "chromium"));
345
+ for (const p of chromeLikePaths(EDGE_BASE)) all.push(...readChromeLike(p, "edge"));
346
+ for (const p of chromeLikePaths(BRAVE_BASE)) all.push(...readChromeLike(p, "brave"));
347
+ for (const p of chromeLikePaths(VIVALDI_BASE)) all.push(...readChromeLike(p, "vivaldi"));
348
+ for (const p of chromeLikePaths(OPERA_BASE)) all.push(...readChromeLike(p, "opera"));
349
+ if (IS_LINUX2) {
350
+ for (const p of chromeLikePaths(CHROMIUM_SNAP_BASE)) all.push(...readChromeLike(p, "chromium-snap"));
351
+ for (const p of chromeLikePaths(CHROMIUM_FLATPAK_BASE)) all.push(...readChromeLike(p, "chromium-flatpak"));
352
+ for (const p of chromeLikePaths(CHROME_FLATPAK_BASE)) all.push(...readChromeLike(p, "chrome-flatpak"));
353
+ for (const p of chromeLikePaths(BRAVE_FLATPAK_BASE)) all.push(...readChromeLike(p, "brave-flatpak"));
354
+ for (const p of chromeLikePaths(EDGE_FLATPAK_BASE)) all.push(...readChromeLike(p, "edge-flatpak"));
355
+ }
356
+ for (const dir of firefoxProfileDirs()) {
357
+ all.push(...await readFirefoxBookmarks(dir));
358
+ }
359
+ const seen = /* @__PURE__ */ new Set();
360
+ return all.filter((h) => {
361
+ if (seen.has(h.hostname)) return false;
362
+ seen.add(h.hostname);
363
+ return true;
364
+ });
365
+ }
366
+ async function scanAllHistory() {
367
+ const all = [];
368
+ for (const p of chromeLikeHistoryPaths(CHROME_BASE)) all.push(...await readChromiumHistory(p, "chrome"));
369
+ for (const p of chromeLikeHistoryPaths(CHROMIUM_BASE)) all.push(...await readChromiumHistory(p, "chromium"));
370
+ for (const p of chromeLikeHistoryPaths(EDGE_BASE)) all.push(...await readChromiumHistory(p, "edge"));
371
+ for (const p of chromeLikeHistoryPaths(BRAVE_BASE)) all.push(...await readChromiumHistory(p, "brave"));
372
+ for (const p of chromeLikeHistoryPaths(VIVALDI_BASE)) all.push(...await readChromiumHistory(p, "vivaldi"));
373
+ for (const p of chromeLikeHistoryPaths(OPERA_BASE)) all.push(...await readChromiumHistory(p, "opera"));
374
+ if (IS_LINUX2) {
375
+ for (const p of chromeLikeHistoryPaths(CHROMIUM_SNAP_BASE)) all.push(...await readChromiumHistory(p, "chromium-snap"));
376
+ for (const p of chromeLikeHistoryPaths(CHROMIUM_FLATPAK_BASE)) all.push(...await readChromiumHistory(p, "chromium-flatpak"));
377
+ for (const p of chromeLikeHistoryPaths(CHROME_FLATPAK_BASE)) all.push(...await readChromiumHistory(p, "chrome-flatpak"));
378
+ for (const p of chromeLikeHistoryPaths(BRAVE_FLATPAK_BASE)) all.push(...await readChromiumHistory(p, "brave-flatpak"));
379
+ for (const p of chromeLikeHistoryPaths(EDGE_FLATPAK_BASE)) all.push(...await readChromiumHistory(p, "edge-flatpak"));
380
+ }
381
+ for (const dir of firefoxProfileDirs()) {
382
+ all.push(...await readFirefoxHistory(dir));
383
+ }
384
+ const byHost = /* @__PURE__ */ new Map();
385
+ for (const h of all) {
386
+ const existing = byHost.get(h.hostname);
387
+ if (existing) {
388
+ existing.visitCount += h.visitCount;
389
+ } else {
390
+ byHost.set(h.hostname, { ...h });
391
+ }
392
+ }
393
+ return [...byHost.values()].sort((a, b) => b.visitCount - a.visitCount);
394
+ }
395
+
396
+ export {
397
+ PLATFORM,
398
+ IS_WIN,
399
+ IS_MAC,
400
+ IS_LINUX,
401
+ HOME,
402
+ run,
403
+ commandExists,
404
+ dbScanDirs,
405
+ findFiles,
406
+ scanWindowsPrograms,
407
+ scanWindowsDbServices,
408
+ readFirefoxHistory,
409
+ scanAllBookmarks,
410
+ scanAllHistory
411
+ };
412
+ //# sourceMappingURL=chunk-3NVQ3ND6.js.map