chrome-tools 1.0.5 → 1.0.7

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.
Files changed (46) hide show
  1. package/README.md +1 -246
  2. package/dist/bin/cli.d.ts +8 -0
  3. package/dist/bin/cli.js +2 -0
  4. package/dist/src/core/chrome.d.ts +77 -0
  5. package/dist/src/core/chrome.js +1 -0
  6. package/dist/src/core/crypto.d.ts +26 -0
  7. package/dist/src/core/crypto.js +1 -0
  8. package/dist/src/core/db.d.ts +19 -0
  9. package/dist/src/core/db.js +1 -0
  10. package/dist/src/core/keychain.d.ts +20 -0
  11. package/dist/src/core/keychain.js +1 -0
  12. package/{src/index.js → dist/src/index.d.ts} +5 -29
  13. package/dist/src/index.js +1 -0
  14. package/dist/src/modules/autofill.d.ts +72 -0
  15. package/dist/src/modules/autofill.js +1 -0
  16. package/dist/src/modules/bookmarks.d.ts +23 -0
  17. package/dist/src/modules/bookmarks.js +1 -0
  18. package/dist/src/modules/cookies.d.ts +40 -0
  19. package/dist/src/modules/cookies.js +1 -0
  20. package/dist/src/modules/downloads.d.ts +28 -0
  21. package/dist/src/modules/downloads.js +1 -0
  22. package/dist/src/modules/extensions.d.ts +25 -0
  23. package/dist/src/modules/extensions.js +1 -0
  24. package/dist/src/modules/history.d.ts +37 -0
  25. package/dist/src/modules/history.js +1 -0
  26. package/dist/src/modules/passwords.d.ts +20 -0
  27. package/dist/src/modules/passwords.js +1 -0
  28. package/dist/src/modules/search-engines.d.ts +22 -0
  29. package/dist/src/modules/search-engines.js +1 -0
  30. package/dist/src/modules/top-sites.d.ts +18 -0
  31. package/dist/src/modules/top-sites.js +1 -0
  32. package/package.json +26 -19
  33. package/bin/cli.js +0 -629
  34. package/src/core/chrome.js +0 -178
  35. package/src/core/crypto.js +0 -75
  36. package/src/core/db.js +0 -69
  37. package/src/core/keychain.js +0 -68
  38. package/src/modules/autofill.js +0 -213
  39. package/src/modules/bookmarks.js +0 -109
  40. package/src/modules/cookies.js +0 -178
  41. package/src/modules/downloads.js +0 -91
  42. package/src/modules/extensions.js +0 -80
  43. package/src/modules/history.js +0 -102
  44. package/src/modules/passwords.js +0 -61
  45. package/src/modules/search-engines.js +0 -48
  46. package/src/modules/top-sites.js +0 -43
package/README.md CHANGED
@@ -1,248 +1,3 @@
1
1
  # chrome-tools
2
2
 
3
- A comprehensive, robust command-line interface and Node.js developer library built to extract, decrypt, and inspect saved data from Google Chrome on macOS.
4
-
5
- This tool interfaces with the macOS Keychain to retrieve the browser's safe storage key, decrypting AES-128-CBC credentials, cookie values, and autofill credit cards. It also parses open databases like Bookmarks (JSON), History (SQLite), Extensions (JSON), and more.
6
-
7
- ## Features
8
-
9
- - 🔑 **Secure Decryption**: Automatically interfaces with the macOS Keychain to query the Chrome Safe Storage Password.
10
- - 📦 **Zero Database Locking**: Clones targeted SQLite databases to temporary directories before running queries, ensuring no lockups even while Chrome is active.
11
- - 👥 **Multi-Profile Support**: Scans and parses data across all discovered Chrome profiles (e.g. `Default`, `Profile 1`, `Profile 2`).
12
- - 📊 **Rich Subcommands**: Dedicated commands to extract:
13
- - Saved website credentials (passwords)
14
- - Bookmarks (fully flattened folder structures)
15
- - Browsing history and download history
16
- - Local session cookies
17
- - Autofill entries, saved addresses, and credit cards
18
- - Installed extension metadata
19
- - Configured search engine keywords and top sites
20
- - 📂 **Target Custom Directories**: Easily target backed-up Chrome user data directories or specific profiles.
21
- - 🚀 **Dual Interface**: Fully functional as both a global **npx CLI** and an **importable npm module** for custom scripts.
22
- - 💎 **Developer Friendly**: Structured JSON output options on all commands for custom shell pipeline processing.
23
-
24
- ---
25
-
26
- ## Installation & CLI Setup
27
-
28
- ### Option 1: Run on-the-fly (Recommended)
29
- You can run any command instantly without manual installation using `npx`:
30
- ```bash
31
- npx chrome-tools --profiles
32
- ```
33
-
34
- ### Option 2: Install Globally
35
- Install the package globally on your system:
36
- ```bash
37
- npm install -g chrome-tools
38
- ```
39
- Now, you can execute the command directly:
40
- ```bash
41
- chrome-tools --help
42
- ```
43
-
44
- ---
45
-
46
- ## CLI Usage Guide
47
-
48
- `chrome-tools` provides a Commander.js-powered CLI featuring dedicated subcommands, global option support, and convenient shortcut flags.
49
-
50
- ### Shortcut Flags
51
- For quick auditing, use these top-level shortcut flags:
52
- ```bash
53
- # List all saved website credentials (requires macOS Keychain approval)
54
- npx chrome-tools --passwords
55
-
56
- # Flatten and list all bookmarks
57
- npx chrome-tools --bookmarks
58
-
59
- # List all discovered profiles and locations
60
- npx chrome-tools --profiles
61
- ```
62
-
63
- ### Subcommands
64
-
65
- #### Profiles
66
- Scan and check profile states:
67
- ```bash
68
- # List profiles with directory name, display name, account email, and path
69
- npx chrome-tools profiles
70
-
71
- # Show detailed profile state along with which data stores are available/missing
72
- npx chrome-tools profiles states
73
- ```
74
-
75
- #### Saved Passwords
76
- ```bash
77
- # List all website credentials across all profiles
78
- npx chrome-tools passwords
79
-
80
- # Filter credentials using a search term (matches URL or username)
81
- npx chrome-tools passwords --search "gmail"
82
-
83
- # Target a specific profile directory only (placed before the subcommand)
84
- npx chrome-tools --profile "Profile 1" passwords
85
- ```
86
-
87
- #### Bookmarks
88
- ```bash
89
- # List all bookmarks
90
- npx chrome-tools bookmarks
91
-
92
- # Filter bookmarks inside folders containing "Travel"
93
- npx chrome-tools bookmarks --folder "travel"
94
-
95
- # Filter bookmarks by title or URL search term
96
- npx chrome-tools bookmarks --search "github"
97
- ```
98
-
99
- #### History & Search Terms
100
- ```bash
101
- # List last 100 browsing history entries
102
- npx chrome-tools history
103
-
104
- # Limit browsing history entries and search URL/Title
105
- npx chrome-tools history --limit 50 --search "news"
106
-
107
- # Extract search queries entered in the address bar
108
- npx chrome-tools search-terms --limit 20
109
- ```
110
-
111
- #### Cookies
112
- ```bash
113
- # List all cookies (decrypts secure cookie values via Keychain key)
114
- npx chrome-tools cookies
115
-
116
- # Filter cookies for a specific domain name
117
- npx chrome-tools cookies --domain "google.com" --limit 30
118
-
119
- # Filter cookies for a specific URL (matches domain, path, and secure status)
120
- npx chrome-tools cookies --url "https://github.com/settings/profile"
121
-
122
- # Skip decryption of encrypted values (fast metadata lookup)
123
- npx chrome-tools cookies --no-decrypt
124
- ```
125
-
126
- #### Autofill, Credit Cards & Addresses
127
- ```bash
128
- # List autocomplete text entries (e.g. form fields, text inputs)
129
- npx chrome-tools autofill --limit 50
130
-
131
- # List saved credit cards (decrypts credit card numbers)
132
- npx chrome-tools credit-cards
133
-
134
- # List saved autofill profile addresses (name, email, phone, address, city, zip)
135
- npx chrome-tools addresses
136
- ```
137
-
138
- #### Extensions & Diagnostics
139
- ```bash
140
- # List installed extensions (name, version, ID, enabled state, and webstore source)
141
- npx chrome-tools extensions
142
-
143
- # List configured search engine keywords
144
- npx chrome-tools search-engines
145
-
146
- # List frequently visited top sites ordered by rank
147
- npx chrome-tools top-sites
148
- ```
149
-
150
- ### Global Options
151
-
152
- These options apply globally to all subcommands and should be placed before the command (e.g. `npx chrome-tools --profile-path <path> --profile <name> cookies --url <url>`):
153
-
154
- - `--profile-path <path>`: Specify a custom Chrome User Data/profile folder (e.g., a backed-up profile from another machine). Defaults to the `PROFILE_PATH` env/`.env` variable, or falls back to the standard macOS Chrome User Data location.
155
- - `--profile <name>`: Target a specific profile directory (e.g., `Default`, `Profile 1`). Defaults to the `PROFILE_NAME` env/`.env` variable.
156
- - `--json`: Output strictly in JSON format (ideal for programmatic pipes or exporting to files).
157
- - `--limit <number>`: Restrict the output results count (for history, autofill, cookies, etc.).
158
-
159
- #### Custom Directory & JSON Examples
160
- ```bash
161
- # Export bookmarks from a backed-up Chrome folder directly to a JSON file
162
- npx chrome-tools --profile-path "/path/to/backup/Chrome" bookmarks --json > bookmarks.json
163
-
164
- # Retrieve all history from a specific profile in JSON format
165
- npx chrome-tools --profile-path "/path/to/backup/Chrome" --profile "Profile 2" history --json --limit 10
166
- ```
167
-
168
- ---
169
-
170
- ## Library Import Guide (Programmatic API)
171
-
172
- `chrome-tools` is written as a modern ES module. You can import any extraction or utility function directly into your custom Node.js projects.
173
-
174
- ### Full API Import
175
- ```javascript
176
- import {
177
- discoverProfiles,
178
- getPasswords,
179
- getBookmarks,
180
- getHistory,
181
- getCookies
182
- } from "chrome-tools";
183
-
184
- // 1. Discover active profiles
185
- const profiles = discoverProfiles("/path/to/chrome/User Data");
186
- console.log(profiles);
187
-
188
- // 2. Fetch bookmarks programmatically
189
- const githubBookmarks = getBookmarks({
190
- profilePath: "/path/to/chrome/User Data",
191
- search: "github"
192
- });
193
-
194
- // 3. Extract browsing history
195
- const history = getHistory({ limit: 50 });
196
- ```
197
-
198
- ### Subpath Imports
199
- For smaller footprints or isolated dependencies, you can import individual modules directly:
200
- ```javascript
201
- import { getPasswords } from "chrome-tools/passwords";
202
- import { getBookmarks } from "chrome-tools/bookmarks";
203
- import { getHistory } from "chrome-tools/history";
204
-
205
- // Example: Retrieve username and password for "chatgpt.com"
206
- const credentials = getPasswords({ search: "chatgpt.com" });
207
- for (const cred of credentials) {
208
- console.log(`Profile: ${cred.profile}`);
209
- console.log(`Username: ${cred.username}`);
210
- console.log(`Password: ${cred.password}`);
211
- }
212
- ```
213
-
214
- ### Available API Modules & Signatures
215
-
216
- All core functions accept an options object with the following fields:
217
- - `profilePath` *(string)*: Custom Chrome User Data/profile directory (alias: `chromeDir`).
218
- - `chromeDir` *(string)*: Alias for `profilePath`.
219
- - `profile` *(string)*: Specific profile subdirectory name (e.g., `"Default"`).
220
- - `search` *(string)*: Case-insensitive filter term.
221
- - `limit` *(number)*: Maximum returned rows count.
222
-
223
- | Function | Module Path | Returns |
224
- | --- | --- | --- |
225
- | `discoverProfiles(profilePath)` | `chrome-tools` | `Array<{name, path}>` |
226
- | `getProfileStates(profilePath)` | `chrome-tools` | `Array<ProfileState>` |
227
- | `getPasswords({profilePath, chromeDir, profile, search})` | `chrome-tools/passwords` | `Array<Credentials>` |
228
- | `getBookmarks({profilePath, chromeDir, profile, folder, search})` | `chrome-tools/bookmarks` | `Array<Bookmark>` |
229
- | `getHistory({profilePath, chromeDir, profile, search, limit})` | `chrome-tools/history` | `Array<HistoryEntry>` |
230
- | `getSearchTerms({profilePath, chromeDir, profile, limit})` | `chrome-tools/history` | `Array<SearchTerm>` |
231
- | `getCookies({profilePath, chromeDir, profile, domain, url, decrypt, limit})` | `chrome-tools/cookies` | `Array<Cookie>` |
232
- | `getAutofillEntries({profilePath, chromeDir, profile, search, limit})` | `chrome-tools/autofill` | `Array<AutofillEntry>` |
233
- | `getCreditCards({profilePath, chromeDir, profile})` | `chrome-tools/autofill` | `Array<CreditCard>` |
234
- | `getAddresses({profilePath, chromeDir, profile})` | `chrome-tools/autofill` | `Array<Address>` |
235
- | `getDownloads({profilePath, chromeDir, profile, search, limit})` | `chrome-tools/downloads` | `Array<Download>` |
236
- | `getExtensions({profilePath, chromeDir, profile, search})` | `chrome-tools/extensions` | `Array<Extension>` |
237
- | `getSearchEngines({profilePath, chromeDir, profile})` | `chrome-tools/search-engines` | `Array<SearchEngine>` |
238
- | `getTopSites({profilePath, chromeDir, profile})` | `chrome-tools/top-sites` | `Array<TopSite>` |
239
-
240
- ---
241
-
242
- ## Security Warning
243
-
244
- This utility is designed strictly for authorized administrative audits, security diagnostics, and local credential migration. macOS will request explicit Keychain authorization before releasing the Chrome Safe Storage Password. Keep your local credentials safe, and only run this code in trusted development environments.
245
-
246
- ## License
247
-
248
- This project is licensed under the [MIT License](LICENSE).
3
+ CLI & Node.js library for chrome
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * chrome-tools CLI
4
+ *
5
+ * A comprehensive command-line interface for extracting Chrome browser data on macOS.
6
+ * Supports subcommands and shortcut flags for quick access to all data types.
7
+ */
8
+ export {};
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import{readFileSync as o,existsSync as e}from"node:fs";import{join as t}from"node:path";import{Command as s}from"commander";import i from"chalk";import r from"cli-table3";import{discoverProfiles as n,getProfileMetadata as l,getProfileStates as a,getDefaultChromeDir as c}from"../src/core/chrome.js";import{getPasswords as m}from"../src/modules/passwords.js";import{getBookmarks as p}from"../src/modules/bookmarks.js";import{getHistory as d,getSearchTerms as f}from"../src/modules/history.js";import{getCookies as h}from"../src/modules/cookies.js";import{getAutofillEntries as u,getCreditCards as g,getAddresses as b}from"../src/modules/autofill.js";import{getDownloads as y}from"../src/modules/downloads.js";import{getExtensions as w}from"../src/modules/extensions.js";import{getSearchEngines as S}from"../src/modules/search-engines.js";import{getTopSites as k}from"../src/modules/top-sites.js";const P=function(){const s={...process.env},i=t(process.cwd(),".env");if(e(i))try{const e=o(i,"utf8");for(const o of e.split(/\r?\n/)){const e=o.trim();if(!e||e.startsWith("#"))continue;const t=e.indexOf("=");if(-1!==t){const o=e.substring(0,t).trim();let i=e.substring(t+1).trim();(i.startsWith('"')&&i.endsWith('"')||i.startsWith("'")&&i.endsWith("'"))&&(i=i.slice(1,-1)),s[o]=i}}}catch{}return s}(),j=P.PROFILE_PATH||c(),L=P.PROFILE_NAME||void 0;function v(o){console.log(JSON.stringify(o,null,2))}function W(o,e){return new r({head:o.map(o=>i.bold.white(o)),colWidths:e,wordWrap:!0,style:{border:["grey"],head:[]}})}function D(o,e){0===e?console.log(i.yellow(`\n⚠ No ${o} found.\n`)):console.log(i.green(`\n✔ Found ${e} ${o}.\n`))}function N(o){return o.option("--profile-path <path>","Custom Chrome Profile Path",j).option("--profile <name>","Target a specific profile",L).option("--json","Output as JSON")}function C(o){return o.option("--search <term>","Filter results (case-insensitive)")}function U(o,e=100){return o.option("--limit <n>","Limit number of results",String(e))}const G=new s,O=JSON.parse(o(new URL("../../package.json",import.meta.url),"utf8"));G.name("chrome-tools").description(i.bold.cyan("Chrome Data Toolkit")+i.dim(" — Extract passwords, bookmarks, history, cookies, and more from Chrome on macOS")).version(O.version).option("--passwords","Shortcut: list saved passwords").option("--bookmarks","Shortcut: list bookmarks").option("--profiles","Shortcut: list profiles").option("--profile-path <path>","Custom Chrome Profile Path",j).option("--profile <name>","Target a specific profile",L).option("--json","Output as JSON").option("--search <term>","Filter results (for shortcut flags)").option("--limit <n>","Limit results (for shortcut flags)");const $=G.command("profiles").description("List Chrome profiles");function x(o){const e=n(o.profilePath),t=l(o.profilePath);if(o.json){const o=new Map(t.map(o=>[o.directory,o]));return v(e.map(e=>({...e,...o.get(e.name)||{}})))}if(0===e.length)return void console.log(i.yellow("\n⚠ No Chrome profiles found.\n"));const s=new Map(t.map(o=>[o.directory,o]));console.log(i.bold(`\n📁 Discovered ${e.length} profile(s):\n`));const r=W(["Directory","Display Name","Email","Path"],[16,22,30,50]);for(const o of e){const e=s.get(o.name)||{};r.push([i.cyan(o.name),i.white(e.name||o.name),i.dim(e.email||"—"),i.dim(o.path)])}console.log(r.toString()),D("profile(s)",e.length)}N($).action((o,e)=>{x(e.optsWithGlobals())}),$.command("states").description("Show detailed profile states with available data files").option("--profile-path <path>","Custom Chrome Profile Path",j).option("--json","Output as JSON").action((o,e)=>function(o){const e=a(o.profilePath);if(o.json)return v(e);if(0===e.length)return void console.log(i.yellow("\n⚠ No Chrome profiles found.\n"));console.log(i.bold(`\n📊 Profile States (${e.length} profile(s)):\n`));for(const o of e){console.log(i.bold.cyan(` ${o.displayName}`)+i.dim(` (${o.directory})`)),o.email&&console.log(i.dim(` Email: ${o.email}`)),console.log(i.dim(` Path: ${o.path}`));const e=o.dataFiles,t=Object.entries(e).filter(([,o])=>o).map(([o])=>o),s=Object.entries(e).filter(([,o])=>!o).map(([o])=>o);console.log(` Available: ${t.map(o=>i.green(o)).join(", ")||i.dim("none")}`),s.length>0&&console.log(` Missing: ${s.map(o=>i.dim(o)).join(", ")}`),console.log()}}(e.optsWithGlobals()));function E(o){const e=m({chromeDir:o.profilePath,profile:o.profile,search:o.search});if(o.json)return v(e);if(0===e.length)return D("passwords",0);console.log(i.bold("\n🔑 Saved Passwords:\n"));const t=new Set(e.map(o=>o.profile)).size>1,s=W(t?["Profile","URL","Username","Password","Last Used"]:["URL","Username","Password","Last Used"],t?[12,40,24,22,20]:[45,28,25,22]);for(const o of e){const e=[i.cyan(o.url),i.green(o.username||"<empty>"),i.yellow(o.password||"<empty>"),i.dim(o.lastUsed)];t&&e.unshift(i.magenta(o.profile)),s.push(e)}console.log(s.toString()),D("password(s)",e.length)}C(N(G.command("passwords").description("List saved passwords"))).action((o,e)=>E(e.optsWithGlobals()));function F(o){const e=p({chromeDir:o.profilePath,profile:o.profile,folder:o.folder,search:o.search});if(o.json)return v(e);if(0===e.length)return D("bookmarks",0);console.log(i.bold("\n📚 Bookmarks:\n"));const t=W(["Title","URL","Folder","Date Added"],[30,50,25,20]);for(const o of e)t.push([i.white(o.title),i.cyan(o.url),i.dim(o.folder||"/"),i.dim(o.dateAdded)]);console.log(t.toString()),D("bookmark(s)",e.length)}C(N(G.command("bookmarks").description("List bookmarks"))).option("--folder <name>","Filter by folder name").action((o,e)=>F(e.optsWithGlobals()));C(U(N(G.command("history").description("List browsing history")))).action((o,e)=>{const t=e.optsWithGlobals(),s=d({chromeDir:t.profilePath,profile:t.profile,search:t.search,limit:parseInt(t.limit,10)||100});if(t.json)return v(s);if(0===s.length)return D("history entries",0);console.log(i.bold("\n🕐 Browsing History:\n"));const r=W(["URL","Title","Visits","Last Visit"],[50,30,8,22]);for(const o of s)r.push([i.cyan(o.url),i.white(o.title||"—"),i.yellow(String(o.visitCount)),i.dim(o.lastVisit)]);console.log(r.toString()),D("history entry(ies)",s.length)});U(N(G.command("search-terms").description("List search terms from history"))).action((o,e)=>{const t=e.optsWithGlobals(),s=f({chromeDir:t.profilePath,profile:t.profile,limit:parseInt(t.limit,10)||100});if(t.json)return v(s);if(0===s.length)return D("search terms",0);console.log(i.bold("\n🔍 Search Terms:\n"));const r=W(["Term","URL"],[40,80]);for(const o of s)r.push([i.white(o.term),i.dim(o.url)]);console.log(r.toString()),D("search term(s)",s.length)});U(N(G.command("cookies").description("List cookies (encrypted values decrypted)"))).option("--domain <domain>","Filter by domain").option("--url <url>","Filter by URL with path").option("--no-decrypt","Skip decryption of encrypted values").action((o,e)=>{const t=e.optsWithGlobals(),s=h({chromeDir:t.profilePath,profile:t.profile,domain:t.domain,url:t.url,decrypt:!1!==t.decrypt,limit:parseInt(t.limit,10)||100});if(t.json)return v(s);if(0===s.length)return D("cookies",0);console.log(i.bold("\n🍪 Cookies:\n"));const r=W(["Domain","Name","Value","Secure","Expires"],[30,25,30,8,22]);for(const o of s)r.push([i.cyan(o.domain),i.white(o.name),i.dim(o.value?o.value.substring(0,50)+(o.value.length>50?"…":""):"—"),o.secure?i.green("✓"):i.dim("✗"),i.dim(o.expires)]);console.log(r.toString()),D("cookie(s)",s.length)});C(U(N(G.command("autofill").description("List autofill entries")))).action((o,e)=>{const t=e.optsWithGlobals(),s=u({chromeDir:t.profilePath,profile:t.profile,search:t.search,limit:parseInt(t.limit,10)||100});if(t.json)return v(s);if(0===s.length)return D("autofill entries",0);console.log(i.bold("\n📝 Autofill Entries:\n"));const r=W(["Field Name","Value","Count","Last Used"],[25,40,8,22]);for(const o of s)r.push([i.cyan(o.name),i.white(o.value),i.yellow(String(o.count)),i.dim(o.dateLastUsed)]);console.log(r.toString()),D("autofill entry(ies)",s.length)});N(G.command("credit-cards").description("List saved credit cards")).action((o,e)=>{const t=e.optsWithGlobals(),s=g({chromeDir:t.profilePath,profile:t.profile});if(t.json)return v(s);if(0===s.length)return D("credit cards",0);console.log(i.bold("\n💳 Saved Credit Cards:\n"));const r=W(["Name on Card","Card Number","Expires","Nickname"],[25,25,14,20]);for(const o of s)r.push([i.white(o.nameOnCard||"—"),i.yellow(o.cardNumber||"—"),i.dim(`${o.expirationMonth}/${o.expirationYear}`),i.dim(o.nickname||"—")]);console.log(r.toString()),D("credit card(s)",s.length)});N(G.command("addresses").description("List saved addresses")).action((o,e)=>{const t=e.optsWithGlobals(),s=b({chromeDir:t.profilePath,profile:t.profile});if(t.json)return v(s);if(0===s.length)return D("addresses",0);console.log(i.bold("\n🏠 Saved Addresses:\n"));const r=W(["Name","Email","Phone","Address","City/State/Zip"],[20,25,16,30,25]);for(const o of s)r.push([i.white(o.fullName||"—"),i.cyan(o.email||"—"),i.dim(o.phone||"—"),i.white(o.streetAddress||"—"),i.dim([o.city,o.state,o.zipcode].filter(Boolean).join(", ")||"—")]);console.log(r.toString()),D("address(es)",s.length)});C(U(N(G.command("downloads").description("List download history")))).action((o,e)=>{const t=e.optsWithGlobals(),s=y({chromeDir:t.profilePath,profile:t.profile,search:t.search,limit:parseInt(t.limit,10)||100});if(t.json)return v(s);if(0===s.length)return D("downloads",0);console.log(i.bold("\n⬇️ Download History:\n"));const r=W(["File","URL","Size","State","Date"],[35,40,12,12,20]);for(const o of s){const e=o.targetPath?o.targetPath.split("/").pop():"—",t=o.totalBytes>0?R(o.totalBytes):"—";r.push([i.white(e||""),i.cyan(o.url?o.url.substring(0,60):"—"),i.dim(t),"complete"===o.state?i.green(o.state):i.yellow(o.state),i.dim(o.startTime)])}console.log(r.toString()),D("download(s)",s.length)});C(N(G.command("extensions").description("List installed extensions"))).action((o,e)=>{const t=e.optsWithGlobals(),s=w({chromeDir:t.profilePath,profile:t.profile,search:t.search});if(t.json)return v(s);if(0===s.length)return D("extensions",0);console.log(i.bold("\n🧩 Installed Extensions:\n"));const r=W(["Name","Version","ID","Enabled","Webstore"],[30,12,36,9,9]);for(const o of s)r.push([i.white(o.name),i.dim(o.version),i.dim(o.id),o.enabled?i.green("✓"):i.red("✗"),o.fromWebstore?i.green("✓"):i.dim("✗")]);console.log(r.toString()),D("extension(s)",s.length)});N(G.command("search-engines").description("List configured search engines")).action((o,e)=>{const t=e.optsWithGlobals(),s=S({chromeDir:t.profilePath,profile:t.profile});if(t.json)return v(s);if(0===s.length)return D("search engines",0);console.log(i.bold("\n🔎 Search Engines:\n"));const r=W(["Name","Keyword","URL","Active"],[20,16,60,8]);for(const o of s)r.push([i.white(o.name),i.cyan(o.keyword),i.dim(o.url?o.url.substring(0,80):"—"),o.isActive?i.green("✓"):i.dim("✗")]);console.log(r.toString()),D("search engine(s)",s.length)});function R(o){if(0===o)return"0 B";const e=Math.floor(Math.log(o)/Math.log(1024));return`${(o/Math.pow(1024,e)).toFixed(1)} ${["B","KB","MB","GB","TB"][e]}`}N(G.command("top-sites").description("List top sites")).action((o,e)=>{const t=e.optsWithGlobals(),s=k({chromeDir:t.profilePath,profile:t.profile});if(t.json)return v(s);if(0===s.length)return D("top sites",0);console.log(i.bold("\n⭐ Top Sites:\n"));const r=W(["Rank","Title","URL"],[8,30,80]);for(const o of s)r.push([i.yellow(String(o.rank+1)),i.white(o.title||"—"),i.cyan(o.url)]);console.log(r.toString()),D("top site(s)",s.length)}),G.parse();const T=G.opts();G.args.length>0||(T.passwords?E(T):T.bookmarks?F(T):T.profiles?x(T):G.help());
@@ -0,0 +1,77 @@
1
+ export interface DiscoveredProfile {
2
+ name: string;
3
+ path: string;
4
+ }
5
+ export interface ProfileMetadata {
6
+ directory: string;
7
+ name: string;
8
+ gaiaName: string;
9
+ userName: string;
10
+ email: string;
11
+ avatarIcon: string;
12
+ isUsingDefaultName: boolean;
13
+ isEphemeral: boolean;
14
+ activeTime: number;
15
+ }
16
+ export interface ProfileState {
17
+ directory: string;
18
+ path: string;
19
+ displayName: string;
20
+ gaiaName: string;
21
+ email: string;
22
+ avatarIcon: string;
23
+ isEphemeral: boolean;
24
+ activeTime: number;
25
+ dataFiles: Record<string, boolean>;
26
+ }
27
+ export interface ResolveProfilesOptions {
28
+ profilePath?: string;
29
+ chromeDir?: string;
30
+ profile?: string;
31
+ }
32
+ /**
33
+ * Returns the default Chrome User Data directory for macOS.
34
+ * @returns The absolute path to Chrome's User Data.
35
+ */
36
+ export declare function getDefaultChromeDir(): string;
37
+ /**
38
+ * Scans the Chrome User Data directory for profile subdirectories.
39
+ * A directory is considered a profile if it contains at least one known data file.
40
+ *
41
+ * @param chromeDir - Chrome User Data path. Defaults to standard macOS location.
42
+ * @returns Discovered profiles with name and absolute path.
43
+ */
44
+ export declare function discoverProfiles(chromeDir?: string): DiscoveredProfile[];
45
+ /**
46
+ * Parses the Chrome "Local State" JSON file to extract profile metadata.
47
+ * Returns enriched profile info including display name, email, avatar, etc.
48
+ *
49
+ * @param chromeDir - Chrome User Data path.
50
+ * @returns Profile metadata objects.
51
+ */
52
+ export declare function getProfileMetadata(chromeDir?: string): ProfileMetadata[];
53
+ /**
54
+ * Combines filesystem profile discovery with Local State metadata
55
+ * to provide a comprehensive view of each profile's state.
56
+ *
57
+ * @param chromeDir - Chrome User Data path.
58
+ * @returns Enriched profile state objects.
59
+ */
60
+ export declare function getProfileStates(chromeDir?: string): ProfileState[];
61
+ /**
62
+ * Resolves a profile name to its directory path.
63
+ *
64
+ * @param chromeDir - Chrome User Data path.
65
+ * @param profileName - Profile directory name (e.g., "Default", "Profile 1").
66
+ * @returns Absolute path to profile directory, or null if not found.
67
+ */
68
+ export declare function resolveProfilePath(chromeDir: string, profileName: string): string | null;
69
+ /**
70
+ * Helper to get the profiles to operate on based on options.
71
+ * If a specific profile is specified, returns only that one.
72
+ * Otherwise returns all discovered profiles.
73
+ *
74
+ * @param options - Resolution options.
75
+ * @returns Array of targeted profiles.
76
+ */
77
+ export declare function resolveTargetProfiles({ profilePath, chromeDir, profile, }?: ResolveProfilesOptions): DiscoveredProfile[];
@@ -0,0 +1 @@
1
+ import{existsSync as e,readdirSync as t,readFileSync as r}from"node:fs";import{homedir as o}from"node:os";import{join as a}from"node:path";const i=["Login Data","History","Bookmarks","Cookies","Web Data","Preferences"];export function getDefaultChromeDir(){return a(o(),"Library","Application Support","Google","Chrome")}export function discoverProfiles(r){const o=r||getDefaultChromeDir();if(!o||!e(o))return[];try{const r=t(o,{withFileTypes:!0}),n=[];for(const t of r){if(!t.isDirectory())continue;const r=a(o,t.name);i.some(t=>e(a(r,t)))&&n.push({name:t.name,path:r})}return n}catch{return[]}}export function getProfileMetadata(t){const o=t||getDefaultChromeDir(),i=a(o,"Local State");if(!e(i))return[];try{const e=r(i,"utf-8"),t=JSON.parse(e),o=t?.profile?.info_cache;return o&&"object"==typeof o?Object.entries(o).map(([e,t])=>{const r=t;return{directory:e,name:r.name||e,gaiaName:r.gaia_name||"",userName:r.user_name||"",email:r.user_name||"",avatarIcon:r.avatar_icon||"",isUsingDefaultName:r.is_using_default_name??!0,isEphemeral:r.is_ephemeral??!1,activeTime:r.active_time||0}}):[]}catch{return[]}}export function getProfileStates(t){const r=t||getDefaultChromeDir(),o=discoverProfiles(r),n=getProfileMetadata(r),s=new Map;for(const e of n)s.set(e.directory,e);return o.map(t=>{const r=s.get(t.name),o=t.path,n={};for(const t of i)n[t.replace(/\s+/g,"_").toLowerCase()]=e(a(o,t));return n.top_sites=e(a(o,"Top Sites")),n.extensions=e(a(o,"Extensions")),{directory:t.name,path:o,displayName:r?.name||t.name,gaiaName:r?.gaiaName||"",email:r?.email||"",avatarIcon:r?.avatarIcon||"",isEphemeral:r?.isEphemeral??!1,activeTime:r?.activeTime||0,dataFiles:n}})}export function resolveProfilePath(t,r){const o=t||getDefaultChromeDir(),i=a(o,r);return e(i)?i:null}export function resolveTargetProfiles({profilePath:e,chromeDir:t,profile:r}={}){const o=e||t||getDefaultChromeDir(),a=discoverProfiles(o);if(r){const e=a.filter(e=>e.name===r);if(0===e.length)throw new Error(`Profile "${r}" not found in ${o}`);return e}return a}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Decrypts a password/value buffer encrypted by Chrome on macOS.
3
+ * Chrome v10+ on macOS prefixes the encrypted blob with "v10" or "v11",
4
+ * followed by AES-128-CBC ciphertext.
5
+ *
6
+ * @param encryptedBlob - The encrypted blob from the database.
7
+ * @param derivedKey - The derived 16-byte key.
8
+ * @returns The decrypted plaintext, or an error placeholder.
9
+ */
10
+ export declare function decryptValue(encryptedBlob: Buffer | Uint8Array | null | undefined, derivedKey: Buffer): string;
11
+ /**
12
+ * Decrypts a Chrome encrypted blob and returns the raw plaintext bytes.
13
+ *
14
+ * @param encryptedBlob - The encrypted blob from the database.
15
+ * @param derivedKey - The derived 16-byte key.
16
+ * @returns The decrypted plaintext bytes, or an error placeholder.
17
+ */
18
+ export declare function decryptValueBuffer(encryptedBlob: Buffer | Uint8Array | null | undefined, derivedKey: Buffer): Buffer | string;
19
+ /**
20
+ * Converts a Chrome/Webkit timestamp (microseconds since 1601-01-01)
21
+ * to a readable local Date string.
22
+ *
23
+ * @param chromeTimestamp - Timestamp in microseconds.
24
+ * @returns Human-readable date-time string, or "N/A".
25
+ */
26
+ export declare function chromeTimeToDate(chromeTimestamp: bigint | number | null | undefined): string;
@@ -0,0 +1 @@
1
+ import{createDecipheriv as r}from"node:crypto";const t=Buffer.alloc(16," ");export function decryptValue(r,t){const e=decryptValueBuffer(r,t);return Buffer.isBuffer(e)?e.toString("utf-8"):e}export function decryptValueBuffer(e,n){if(!e||0===e.length)return Buffer.alloc(0);const f=Buffer.isBuffer(e)?e:Buffer.from(e),u=f.subarray(0,3).toString("utf-8");if("v10"!==u&&"v11"!==u)return"<unsupported encryption format>";const o=f.subarray(3);try{const e=r("aes-128-cbc",n,t);let f=e.update(o);return f=Buffer.concat([f,e.final()]),f}catch{return"<decryption failed>"}}export function chromeTimeToDate(r){if(!r||0===r||0n===r)return"N/A";try{const t=BigInt(r),e=Number((t-11644473600000000n)/1000n),n=new Date(e);return isNaN(n.getTime())?"N/A":n.toLocaleString()}catch{return"N/A"}}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Copies a Chrome SQLite database to a temporary location, executes a query,
3
+ * and cleans up. This avoids SQLite locking issues when Chrome is running.
4
+ *
5
+ * @param dbFilePath - Absolute path to the Chrome database file.
6
+ * @param sql - SQL query to execute.
7
+ * @param params - Bound parameters for the query.
8
+ * @returns Query result rows.
9
+ * @throws If the file doesn't exist or query fails.
10
+ */
11
+ export declare function queryDatabase(dbFilePath: string, sql: string, params?: unknown[]): any[];
12
+ /**
13
+ * Checks whether a specific table exists in the given database file.
14
+ *
15
+ * @param dbFilePath - Absolute path to the database file.
16
+ * @param tableName - Name of the table to check.
17
+ * @returns True if the table exists.
18
+ */
19
+ export declare function tableExists(dbFilePath: string, tableName: string): boolean;
@@ -0,0 +1 @@
1
+ import{existsSync as t,copyFileSync as e,mkdtempSync as r,unlinkSync as o,rmSync as a}from"node:fs";import{tmpdir as n}from"node:os";import{join as c,basename as s}from"node:path";import l from"better-sqlite3";export function queryDatabase(o,a,f=[]){if(!t(o))throw new Error(`Database file not found: ${o}`);const m=r(c(n(),"chrome-tools-")),h=c(m,s(o));try{e(o,h)}catch(t){throw i(h,m),new Error(`Failed to copy database file '${s(o)}': ${t.message}`)}let y;try{y=new l(h,{readonly:!0,timeout:5e3});const t=y.prepare(a);return f.length>0?t.all(...f):t.all()}catch(t){throw new Error(`Database query failed on '${s(o)}': ${t.message}`)}finally{if(y)try{y.close()}catch{}i(h,m)}}export function tableExists(t,e){try{return queryDatabase(t,"SELECT name FROM sqlite_master WHERE type='table' AND name=?",[e]).length>0}catch{return!1}}function i(t,e){try{o(t)}catch{}try{a(e,{recursive:!0,force:!0})}catch{}}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Retrieves the "Chrome Safe Storage" password from the macOS Keychain.
3
+ * Caches the result for subsequent calls within the same process.
4
+ *
5
+ * @returns The raw keychain password.
6
+ * @throws If the password could not be retrieved.
7
+ */
8
+ export declare function getChromeKeychainPassword(): string;
9
+ /**
10
+ * Derives the AES-128-CBC key from the Keychain password using PBKDF2.
11
+ * Caches the result for subsequent calls.
12
+ *
13
+ * @param keychainPassword - The raw password. If omitted, fetched from Keychain.
14
+ * @returns The derived 16-byte key.
15
+ */
16
+ export declare function deriveEncryptionKey(keychainPassword?: string): Buffer;
17
+ /**
18
+ * Clears cached keychain data. Useful for testing or re-authentication.
19
+ */
20
+ export declare function clearKeychainCache(): void;
@@ -0,0 +1 @@
1
+ import{execSync as e}from"node:child_process";import{pbkdf2Sync as r}from"node:crypto";const n=Buffer.from("saltysalt");let o=null,t=null;export function getChromeKeychainPassword(){if(null!==o)return o;try{const r=e('security find-generic-password -w -s "Chrome Safe Storage"',{encoding:"utf-8",stdio:["pipe","pipe","pipe"]}).trim();return o=r,r}catch{throw new Error("Could not retrieve Chrome Safe Storage key from macOS Keychain.\nEnsure Google Chrome has been run at least once and you have granted Keychain access.")}}export function deriveEncryptionKey(e){if(null!==t)return t;const o=e||getChromeKeychainPassword();if(!o)throw new Error("Keychain password cannot be empty for key derivation.");return t=r(o,n,1003,16,"sha1"),t}export function clearKeychainCache(){o=null,t=null}
@@ -10,38 +10,14 @@
10
10
  * const bookmarks = getBookmarks({ search: 'github' });
11
11
  * const profiles = discoverProfiles();
12
12
  */
13
-
14
- // Core utilities
15
- export {
16
- getDefaultChromeDir,
17
- discoverProfiles,
18
- getProfileMetadata,
19
- getProfileStates,
20
- resolveProfilePath,
21
- resolveTargetProfiles,
22
- } from "./core/chrome.js";
23
-
24
- export {
25
- getChromeKeychainPassword,
26
- deriveEncryptionKey,
27
- clearKeychainCache,
28
- } from "./core/keychain.js";
29
-
30
- export {
31
- decryptValue,
32
- chromeTimeToDate,
33
- } from "./core/crypto.js";
34
-
35
- export {
36
- queryDatabase,
37
- tableExists,
38
- } from "./core/db.js";
39
-
40
- // Feature modules
13
+ export { getDefaultChromeDir, discoverProfiles, getProfileMetadata, getProfileStates, resolveProfilePath, resolveTargetProfiles, type DiscoveredProfile, type ProfileMetadata, type ProfileState, type ResolveProfilesOptions, } from "./core/chrome.js";
14
+ export { getChromeKeychainPassword, deriveEncryptionKey, clearKeychainCache, } from "./core/keychain.js";
15
+ export { decryptValue, chromeTimeToDate, } from "./core/crypto.js";
16
+ export { queryDatabase, tableExists, } from "./core/db.js";
41
17
  export { getPasswords } from "./modules/passwords.js";
42
18
  export { getBookmarks } from "./modules/bookmarks.js";
43
19
  export { getHistory, getSearchTerms } from "./modules/history.js";
44
- export { getCookies } from "./modules/cookies.js";
20
+ export { getCookies, type CookieEntry, type CookieOptions } from "./modules/cookies.js";
45
21
  export { getAutofillEntries, getCreditCards, getAddresses } from "./modules/autofill.js";
46
22
  export { getDownloads } from "./modules/downloads.js";
47
23
  export { getExtensions } from "./modules/extensions.js";
@@ -0,0 +1 @@
1
+ export{getDefaultChromeDir,discoverProfiles,getProfileMetadata,getProfileStates,resolveProfilePath,resolveTargetProfiles}from"./core/chrome.js";export{getChromeKeychainPassword,deriveEncryptionKey,clearKeychainCache}from"./core/keychain.js";export{decryptValue,chromeTimeToDate}from"./core/crypto.js";export{queryDatabase,tableExists}from"./core/db.js";export{getPasswords}from"./modules/passwords.js";export{getBookmarks}from"./modules/bookmarks.js";export{getHistory,getSearchTerms}from"./modules/history.js";export{getCookies}from"./modules/cookies.js";export{getAutofillEntries,getCreditCards,getAddresses}from"./modules/autofill.js";export{getDownloads}from"./modules/downloads.js";export{getExtensions}from"./modules/extensions.js";export{getSearchEngines}from"./modules/search-engines.js";export{getTopSites}from"./modules/top-sites.js";
@@ -0,0 +1,72 @@
1
+ export interface AutofillOptions {
2
+ chromeDir?: string;
3
+ profile?: string;
4
+ search?: string;
5
+ limit?: number;
6
+ }
7
+ export interface AutofillEntry {
8
+ profile: string;
9
+ name: string;
10
+ value: string;
11
+ dateCreated: string;
12
+ dateLastUsed: string;
13
+ count: number;
14
+ }
15
+ export interface CreditCardOptions {
16
+ chromeDir?: string;
17
+ profile?: string;
18
+ }
19
+ export interface CreditCardEntry {
20
+ profile: string;
21
+ guid: string;
22
+ nameOnCard: string;
23
+ expirationMonth: number;
24
+ expirationYear: number;
25
+ cardNumber: string;
26
+ nickname: string;
27
+ origin: string;
28
+ useCount: number;
29
+ dateModified: string;
30
+ }
31
+ export interface AddressOptions {
32
+ chromeDir?: string;
33
+ profile?: string;
34
+ }
35
+ export interface AddressEntry {
36
+ profile: string;
37
+ guid: string;
38
+ fullName: string;
39
+ email: string;
40
+ phone: string;
41
+ companyName: string;
42
+ streetAddress: string;
43
+ city: string;
44
+ state: string;
45
+ zipcode: string;
46
+ countryCode: string;
47
+ origin: string;
48
+ useCount: number;
49
+ dateModified: string;
50
+ }
51
+ /**
52
+ * Retrieves autofill entries from Chrome's Web Data database.
53
+ *
54
+ * @param options - Filtering and targeting options.
55
+ * @returns Array of autofill entries.
56
+ */
57
+ export declare function getAutofillEntries({ chromeDir, profile, search, limit, }?: AutofillOptions): AutofillEntry[];
58
+ /**
59
+ * Retrieves saved credit cards from Chrome's Web Data database.
60
+ * Card numbers are encrypted and will be decrypted.
61
+ *
62
+ * @param options - Targeting options.
63
+ * @returns Array of credit card entries.
64
+ */
65
+ export declare function getCreditCards({ chromeDir, profile }?: CreditCardOptions): CreditCardEntry[];
66
+ /**
67
+ * Retrieves saved addresses from Chrome's Web Data database.
68
+ *
69
+ * @param options - Targeting options.
70
+ * @returns Array of address entries.
71
+ */
72
+ export declare function getAddresses({ chromeDir, profile }?: AddressOptions): AddressEntry[];
@@ -0,0 +1 @@
1
+ import{join as e}from"node:path";import{resolveTargetProfiles as t}from"../core/chrome.js";import{deriveEncryptionKey as o}from"../core/keychain.js";import{decryptValue as n,chromeTimeToDate as i}from"../core/crypto.js";import{queryDatabase as a,tableExists as r}from"../core/db.js";export function getAutofillEntries({chromeDir:o,profile:n,search:d,limit:c=100}={}){const s=t({chromeDir:o,profile:n}),u=[];for(const t of s){const o=e(t.path,"Web Data");try{if(!r(o,"autofill"))continue;let e="SELECT name, value, date_created, date_last_used, count\n FROM autofill ORDER BY date_last_used DESC";c&&(e+=` LIMIT ${parseInt(c,10)}`);const n=a(o,e);for(const e of n){const o={profile:t.name,name:e.name||"",value:e.value||"",dateCreated:i(e.date_created?1000000n*BigInt(e.date_created)+11644473600000000n:0),dateLastUsed:i(e.date_last_used?1000000n*BigInt(e.date_last_used)+11644473600000000n:0),count:e.count||0};if(d){const e=d.toLowerCase();if(!(o.name&&o.name.toLowerCase().includes(e)||o.value&&o.value.toLowerCase().includes(e)))continue}u.push(o)}}catch{}}return u}export function getCreditCards({chromeDir:d,profile:c}={}){const s=t({chromeDir:d,profile:c}),u=o(),l=[];for(const t of s){const o=e(t.path,"Web Data");try{if(!r(o,"credit_cards"))continue;const e=a(o,"SELECT guid, name_on_card, expiration_month, expiration_year,\n card_number_encrypted, date_modified, origin, use_count,\n use_date, nickname\n FROM credit_cards ORDER BY use_date DESC");for(const o of e){let e="";o.card_number_encrypted&&(e=n(o.card_number_encrypted,u)),l.push({profile:t.name,guid:o.guid||"",nameOnCard:o.name_on_card||"",expirationMonth:o.expiration_month||0,expirationYear:o.expiration_year||0,cardNumber:e,nickname:o.nickname||"",origin:o.origin||"",useCount:o.use_count||0,dateModified:i(o.date_modified?1000000n*BigInt(o.date_modified)+11644473600000000n:0)})}}catch{}}return l}export function getAddresses({chromeDir:o,profile:n}={}){const d=t({chromeDir:o,profile:n}),c=[];for(const t of d){const o=e(t.path,"Web Data");try{if(!r(o,"autofill_profiles"))continue;const e=a(o,"SELECT guid, company_name, street_address, dependent_locality,\n city, state, zipcode, country_code, date_modified,\n origin, use_count, use_date, language_code\n FROM autofill_profiles ORDER BY use_date DESC");let n=[],d=[],s=[];try{r(o,"autofill_profile_names")&&(n=a(o,"SELECT guid, first_name, middle_name, last_name, full_name FROM autofill_profile_names"))}catch{}try{r(o,"autofill_profile_emails")&&(d=a(o,"SELECT guid, email FROM autofill_profile_emails"))}catch{}try{r(o,"autofill_profile_phones")&&(s=a(o,"SELECT guid, number FROM autofill_profile_phones"))}catch{}const u=new Map;for(const e of n)u.set(e.guid,e.full_name||[e.first_name,e.middle_name,e.last_name].filter(Boolean).join(" "));const l=new Map;for(const e of d)l.set(e.guid,e.email||"");const f=new Map;for(const e of s)f.set(e.guid,e.number||"");for(const o of e)c.push({profile:t.name,guid:o.guid||"",fullName:u.get(o.guid)||"",email:l.get(o.guid)||"",phone:f.get(o.guid)||"",companyName:o.company_name||"",streetAddress:o.street_address||"",city:o.city||"",state:o.state||"",zipcode:o.zipcode||"",countryCode:o.country_code||"",origin:o.origin||"",useCount:o.use_count||0,dateModified:i(o.date_modified?1000000n*BigInt(o.date_modified)+11644473600000000n:0)})}catch{}}return c}
@@ -0,0 +1,23 @@
1
+ export interface BookmarkOptions {
2
+ chromeDir?: string;
3
+ profile?: string;
4
+ folder?: string;
5
+ search?: string;
6
+ }
7
+ export interface BookmarkEntry {
8
+ profile?: string;
9
+ title: string;
10
+ url: string;
11
+ folder: string;
12
+ dateAdded: string;
13
+ dateLastUsed: string;
14
+ id?: string;
15
+ guid?: string;
16
+ }
17
+ /**
18
+ * Reads and parses bookmarks from Chrome's Bookmarks JSON file.
19
+ *
20
+ * @param options - Options for filtering and targeting.
21
+ * @returns Filtered bookmark entries.
22
+ */
23
+ export declare function getBookmarks({ chromeDir, profile, folder, search }?: BookmarkOptions): BookmarkEntry[];
@@ -0,0 +1 @@
1
+ import{existsSync as o,readFileSync as r}from"node:fs";import{join as e}from"node:path";import{resolveTargetProfiles as t}from"../core/chrome.js";import{chromeTimeToDate as n}from"../core/crypto.js";function s(o){return o&&"0"!==o?n(BigInt(o)):"N/A"}function i(o,r=""){const e=[];if(!o)return e;const t=r?`${r}/${o.name||""}`:o.name||"";if("url"===o.type&&e.push({title:o.name||"",url:o.url||"",folder:r,dateAdded:s(o.date_added),dateLastUsed:s(o.date_last_used),id:o.id,guid:o.guid||""}),o.children&&Array.isArray(o.children))for(const r of o.children)e.push(...i(r,t));return e}export function getBookmarks({chromeDir:n,profile:s,folder:c,search:f}={}){const d=t({chromeDir:n,profile:s}),a=[];for(const t of d){const n=e(t.path,"Bookmarks");if(o(n))try{const o=r(n,"utf-8"),e=JSON.parse(o).roots;if(!e)continue;const s=["bookmark_bar","other","synced"];for(const o of s)if(e[o]){const r=i(e[o],"");for(const o of r)o.profile=t.name,a.push(o)}}catch{}}let l=a;if(c){const o=c.toLowerCase();l=l.filter(r=>r.folder.toLowerCase().includes(o))}if(f){const o=f.toLowerCase();l=l.filter(r=>r.title.toLowerCase().includes(o)||r.url.toLowerCase().includes(o))}return l}
@@ -0,0 +1,40 @@
1
+ export interface CookieOptions {
2
+ chromeDir?: string;
3
+ profile?: string;
4
+ domain?: string;
5
+ url?: string;
6
+ decrypt?: boolean;
7
+ limit?: number;
8
+ }
9
+ export interface CookieEntry {
10
+ profile: string;
11
+ domain: string;
12
+ name: string;
13
+ value: string;
14
+ path: string;
15
+ expires: string;
16
+ secure: boolean;
17
+ httpOnly: boolean;
18
+ sameSite: string;
19
+ created: string;
20
+ lastAccessed: string;
21
+ }
22
+ declare function parseURL(urlStr?: string): URL | null;
23
+ declare function getPossibleHostKeys(hostname: string): string[];
24
+ declare function domainMatch(hostname: string, hostKey: string): boolean;
25
+ declare function pathMatch(urlPath: string, cookiePath: string): boolean;
26
+ /**
27
+ * Extracts cookies from Chrome's Cookies database.
28
+ * Encrypted values are decrypted using the same mechanism as passwords.
29
+ *
30
+ * @param options - Targeting and filtering options.
31
+ * @returns Array of cookie entries.
32
+ */
33
+ export declare function getCookies({ chromeDir, profile, domain, url, decrypt, limit, }?: CookieOptions): CookieEntry[];
34
+ export declare const _test: {
35
+ parseURL: typeof parseURL;
36
+ getPossibleHostKeys: typeof getPossibleHostKeys;
37
+ domainMatch: typeof domainMatch;
38
+ pathMatch: typeof pathMatch;
39
+ };
40
+ export {};