chrome-tools 1.0.3 → 1.0.5

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
@@ -80,8 +80,8 @@ npx chrome-tools passwords
80
80
  # Filter credentials using a search term (matches URL or username)
81
81
  npx chrome-tools passwords --search "gmail"
82
82
 
83
- # Target a specific profile directory only
84
- npx chrome-tools passwords --profile "Profile 1"
83
+ # Target a specific profile directory only (placed before the subcommand)
84
+ npx chrome-tools --profile "Profile 1" passwords
85
85
  ```
86
86
 
87
87
  #### Bookmarks
@@ -149,20 +149,20 @@ npx chrome-tools top-sites
149
149
 
150
150
  ### Global Options
151
151
 
152
- These options apply globally to all subcommands and can be placed anywhere in your command line:
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
153
 
154
- - `--chrome-dir <path>`: Specify a custom Chrome User Data folder (e.g., a backed-up profile from another machine).
155
- - `--profile <name>`: Limit operations to a single profile directory (e.g., `Default`, `Profile 1`).
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
156
  - `--json`: Output strictly in JSON format (ideal for programmatic pipes or exporting to files).
157
157
  - `--limit <number>`: Restrict the output results count (for history, autofill, cookies, etc.).
158
158
 
159
159
  #### Custom Directory & JSON Examples
160
160
  ```bash
161
161
  # Export bookmarks from a backed-up Chrome folder directly to a JSON file
162
- npx chrome-tools bookmarks --chrome-dir "/path/to/backup/Chrome" --json > bookmarks.json
162
+ npx chrome-tools --profile-path "/path/to/backup/Chrome" bookmarks --json > bookmarks.json
163
163
 
164
164
  # Retrieve all history from a specific profile in JSON format
165
- npx chrome-tools history --profile "Profile 2" --json --limit 10
165
+ npx chrome-tools --profile-path "/path/to/backup/Chrome" --profile "Profile 2" history --json --limit 10
166
166
  ```
167
167
 
168
168
  ---
@@ -187,7 +187,7 @@ console.log(profiles);
187
187
 
188
188
  // 2. Fetch bookmarks programmatically
189
189
  const githubBookmarks = getBookmarks({
190
- chromeDir: "/path/to/chrome/User Data",
190
+ profilePath: "/path/to/chrome/User Data",
191
191
  search: "github"
192
192
  });
193
193
 
@@ -214,27 +214,28 @@ for (const cred of credentials) {
214
214
  ### Available API Modules & Signatures
215
215
 
216
216
  All core functions accept an options object with the following fields:
217
- - `chromeDir` *(string)*: Custom Chrome User Data directory.
217
+ - `profilePath` *(string)*: Custom Chrome User Data/profile directory (alias: `chromeDir`).
218
+ - `chromeDir` *(string)*: Alias for `profilePath`.
218
219
  - `profile` *(string)*: Specific profile subdirectory name (e.g., `"Default"`).
219
220
  - `search` *(string)*: Case-insensitive filter term.
220
221
  - `limit` *(number)*: Maximum returned rows count.
221
222
 
222
223
  | Function | Module Path | Returns |
223
224
  | --- | --- | --- |
224
- | `discoverProfiles(chromeDir)` | `chrome-tools` | `Array<{name, path}>` |
225
- | `getProfileStates(chromeDir)` | `chrome-tools` | `Array<ProfileState>` |
226
- | `getPasswords({chromeDir, profile, search})` | `chrome-tools/passwords` | `Array<Credentials>` |
227
- | `getBookmarks({chromeDir, profile, folder, search})` | `chrome-tools/bookmarks` | `Array<Bookmark>` |
228
- | `getHistory({chromeDir, profile, search, limit})` | `chrome-tools/history` | `Array<HistoryEntry>` |
229
- | `getSearchTerms({chromeDir, profile, limit})` | `chrome-tools/history` | `Array<SearchTerm>` |
230
- | `getCookies({chromeDir, profile, domain, url, decrypt, limit})` | `chrome-tools/cookies` | `Array<Cookie>` |
231
- | `getAutofillEntries({chromeDir, profile, search, limit})` | `chrome-tools/autofill` | `Array<AutofillEntry>` |
232
- | `getCreditCards({chromeDir, profile})` | `chrome-tools/autofill` | `Array<CreditCard>` |
233
- | `getAddresses({chromeDir, profile})` | `chrome-tools/autofill` | `Array<Address>` |
234
- | `getDownloads({chromeDir, profile, search, limit})` | `chrome-tools/downloads` | `Array<Download>` |
235
- | `getExtensions({chromeDir, profile, search})` | `chrome-tools/extensions` | `Array<Extension>` |
236
- | `getSearchEngines({chromeDir, profile})` | `chrome-tools/search-engines` | `Array<SearchEngine>` |
237
- | `getTopSites({chromeDir, profile})` | `chrome-tools/top-sites` | `Array<TopSite>` |
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>` |
238
239
 
239
240
  ---
240
241
 
package/bin/cli.js CHANGED
@@ -7,11 +7,13 @@
7
7
  * Supports subcommands and shortcut flags for quick access to all data types.
8
8
  */
9
9
 
10
+ import { readFileSync, existsSync } from "node:fs";
11
+ import { join } from "node:path";
10
12
  import { Command } from "commander";
11
13
  import chalk from "chalk";
12
14
  import Table from "cli-table3";
13
15
 
14
- import { discoverProfiles, getProfileMetadata, getProfileStates } from "../src/core/chrome.js";
16
+ import { discoverProfiles, getProfileMetadata, getProfileStates, getDefaultChromeDir } from "../src/core/chrome.js";
15
17
  import { getPasswords } from "../src/modules/passwords.js";
16
18
  import { getBookmarks } from "../src/modules/bookmarks.js";
17
19
  import { getHistory, getSearchTerms } from "../src/modules/history.js";
@@ -22,6 +24,39 @@ import { getExtensions } from "../src/modules/extensions.js";
22
24
  import { getSearchEngines } from "../src/modules/search-engines.js";
23
25
  import { getTopSites } from "../src/modules/top-sites.js";
24
26
 
27
+ // ─── Environment Loading ─────────────────────────────────────────────────────
28
+
29
+ function loadEnv() {
30
+ const env = { ...process.env };
31
+ const envPath = join(process.cwd(), ".env");
32
+ if (existsSync(envPath)) {
33
+ try {
34
+ const content = readFileSync(envPath, "utf8");
35
+ for (const line of content.split(/\r?\n/)) {
36
+ const trimmed = line.trim();
37
+ if (!trimmed || trimmed.startsWith("#")) continue;
38
+ const index = trimmed.indexOf("=");
39
+ if (index !== -1) {
40
+ const key = trimmed.substring(0, index).trim();
41
+ let val = trimmed.substring(index + 1).trim();
42
+ // Remove wrapping quotes
43
+ if ((val.startsWith('"') && val.endsWith('"')) || (val.startsWith("'") && val.endsWith("'"))) {
44
+ val = val.slice(1, -1);
45
+ }
46
+ env[key] = val;
47
+ }
48
+ }
49
+ } catch {
50
+ // Ignore
51
+ }
52
+ }
53
+ return env;
54
+ }
55
+
56
+ const env = loadEnv();
57
+ const defaultProfilePath = env.PROFILE_PATH || getDefaultChromeDir();
58
+ const defaultProfileName = env.PROFILE_NAME || undefined;
59
+
25
60
  // ─── Output Helpers ──────────────────────────────────────────────────────────
26
61
 
27
62
  function outputJSON(data) {
@@ -49,8 +84,8 @@ function printCount(label, count) {
49
84
 
50
85
  function addCommonOptions(cmd) {
51
86
  return cmd
52
- .option("--chrome-dir <path>", "Custom Chrome User Data directory")
53
- .option("--profile <name>", "Target a specific profile")
87
+ .option("--profile-path <path>", "Custom Chrome Profile Path", defaultProfilePath)
88
+ .option("--profile <name>", "Target a specific profile", defaultProfileName)
54
89
  .option("--json", "Output as JSON");
55
90
  }
56
91
 
@@ -66,35 +101,23 @@ function addLimitOption(cmd, defaultLimit = 100) {
66
101
 
67
102
  const program = new Command();
68
103
 
104
+ const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
105
+
69
106
  program
70
107
  .name("chrome-tools")
71
108
  .description(
72
109
  chalk.bold.cyan("Chrome Data Toolkit") +
73
110
  chalk.dim(" — Extract passwords, bookmarks, history, cookies, and more from Chrome on macOS")
74
111
  )
75
- .version("1.0.0")
112
+ .version(pkg.version)
76
113
  .option("--passwords", "Shortcut: list saved passwords")
77
114
  .option("--bookmarks", "Shortcut: list bookmarks")
78
115
  .option("--profiles", "Shortcut: list profiles")
79
- .option("--chrome-dir <path>", "Custom Chrome User Data directory")
80
- .option("--profile <name>", "Target a specific profile")
116
+ .option("--profile-path <path>", "Custom Chrome Profile Path", defaultProfilePath)
117
+ .option("--profile <name>", "Target a specific profile", defaultProfileName)
81
118
  .option("--json", "Output as JSON")
82
119
  .option("--search <term>", "Filter results (for shortcut flags)")
83
- .option("--limit <n>", "Limit results (for shortcut flags)")
84
- .action((opts) => {
85
- // Handle shortcut flags at the top level
86
- if (opts.passwords) {
87
- return handlePasswords(opts);
88
- }
89
- if (opts.bookmarks) {
90
- return handleBookmarks(opts);
91
- }
92
- if (opts.profiles) {
93
- return handleProfiles(opts);
94
- }
95
- // No shortcut flag — show help
96
- program.help();
97
- });
120
+ .option("--limit <n>", "Limit results (for shortcut flags)");
98
121
 
99
122
  // ─── Profiles Command ───────────────────────────────────────────────────────
100
123
 
@@ -109,13 +132,13 @@ addCommonOptions(profilesCmd).action((opts, cmd) => {
109
132
  profilesCmd
110
133
  .command("states")
111
134
  .description("Show detailed profile states with available data files")
112
- .option("--chrome-dir <path>", "Custom Chrome User Data directory")
135
+ .option("--profile-path <path>", "Custom Chrome Profile Path", defaultProfilePath)
113
136
  .option("--json", "Output as JSON")
114
137
  .action((opts, cmd) => handleProfileStates(cmd.optsWithGlobals()));
115
138
 
116
139
  function handleProfiles(opts) {
117
- const profiles = discoverProfiles(opts.chromeDir);
118
- const metadata = getProfileMetadata(opts.chromeDir);
140
+ const profiles = discoverProfiles(opts.profilePath);
141
+ const metadata = getProfileMetadata(opts.profilePath);
119
142
 
120
143
  if (opts.json) {
121
144
  const metaMap = new Map(metadata.map((m) => [m.directory, m]));
@@ -151,7 +174,7 @@ function handleProfiles(opts) {
151
174
  }
152
175
 
153
176
  function handleProfileStates(opts) {
154
- const states = getProfileStates(opts.chromeDir);
177
+ const states = getProfileStates(opts.profilePath);
155
178
 
156
179
  if (opts.json) {
157
180
  return outputJSON(states);
@@ -190,11 +213,11 @@ function handleProfileStates(opts) {
190
213
  // ─── Passwords Command ──────────────────────────────────────────────────────
191
214
 
192
215
  const passwordsCmd = program.command("passwords").description("List saved passwords");
193
- addCommonOptions(addSearchOption(passwordsCmd)).action((opts, cmd) => handlePasswords(cmd.optsWithGlobals()));
216
+ addSearchOption(addCommonOptions(passwordsCmd)).action((opts, cmd) => handlePasswords(cmd.optsWithGlobals()));
194
217
 
195
218
  function handlePasswords(opts) {
196
219
  const results = getPasswords({
197
- chromeDir: opts.chromeDir,
220
+ chromeDir: opts.profilePath,
198
221
  profile: opts.profile,
199
222
  search: opts.search,
200
223
  });
@@ -230,13 +253,13 @@ function handlePasswords(opts) {
230
253
  // ─── Bookmarks Command ──────────────────────────────────────────────────────
231
254
 
232
255
  const bookmarksCmd = program.command("bookmarks").description("List bookmarks");
233
- addCommonOptions(addSearchOption(bookmarksCmd))
256
+ addSearchOption(addCommonOptions(bookmarksCmd))
234
257
  .option("--folder <name>", "Filter by folder name")
235
258
  .action((opts, cmd) => handleBookmarks(cmd.optsWithGlobals()));
236
259
 
237
260
  function handleBookmarks(opts) {
238
261
  const results = getBookmarks({
239
- chromeDir: opts.chromeDir,
262
+ chromeDir: opts.profilePath,
240
263
  profile: opts.profile,
241
264
  folder: opts.folder,
242
265
  search: opts.search,
@@ -265,10 +288,10 @@ function handleBookmarks(opts) {
265
288
  // ─── History Command ─────────────────────────────────────────────────────────
266
289
 
267
290
  const historyCmd = program.command("history").description("List browsing history");
268
- addCommonOptions(addSearchOption(addLimitOption(historyCmd))).action((opts, cmd) => {
291
+ addSearchOption(addLimitOption(addCommonOptions(historyCmd))).action((opts, cmd) => {
269
292
  const mergedOpts = cmd.optsWithGlobals();
270
293
  const results = getHistory({
271
- chromeDir: mergedOpts.chromeDir,
294
+ chromeDir: mergedOpts.profilePath,
272
295
  profile: mergedOpts.profile,
273
296
  search: mergedOpts.search,
274
297
  limit: parseInt(mergedOpts.limit, 10) || 100,
@@ -297,10 +320,10 @@ addCommonOptions(addSearchOption(addLimitOption(historyCmd))).action((opts, cmd)
297
320
  // ─── Search Terms Command ────────────────────────────────────────────────────
298
321
 
299
322
  const searchTermsCmd = program.command("search-terms").description("List search terms from history");
300
- addCommonOptions(addLimitOption(searchTermsCmd)).action((opts, cmd) => {
323
+ addLimitOption(addCommonOptions(searchTermsCmd)).action((opts, cmd) => {
301
324
  const mergedOpts = cmd.optsWithGlobals();
302
325
  const results = getSearchTerms({
303
- chromeDir: mergedOpts.chromeDir,
326
+ chromeDir: mergedOpts.profilePath,
304
327
  profile: mergedOpts.profile,
305
328
  limit: parseInt(mergedOpts.limit, 10) || 100,
306
329
  });
@@ -323,14 +346,14 @@ addCommonOptions(addLimitOption(searchTermsCmd)).action((opts, cmd) => {
323
346
  // ─── Cookies Command ─────────────────────────────────────────────────────────
324
347
 
325
348
  const cookiesCmd = program.command("cookies").description("List cookies (encrypted values decrypted)");
326
- addCommonOptions(addLimitOption(cookiesCmd))
349
+ addLimitOption(addCommonOptions(cookiesCmd))
327
350
  .option("--domain <domain>", "Filter by domain")
328
351
  .option("--url <url>", "Filter by URL with path")
329
352
  .option("--no-decrypt", "Skip decryption of encrypted values")
330
353
  .action((opts, cmd) => {
331
354
  const mergedOpts = cmd.optsWithGlobals();
332
355
  const results = getCookies({
333
- chromeDir: mergedOpts.chromeDir,
356
+ chromeDir: mergedOpts.profilePath,
334
357
  profile: mergedOpts.profile,
335
358
  domain: mergedOpts.domain,
336
359
  url: mergedOpts.url,
@@ -362,10 +385,10 @@ addCommonOptions(addLimitOption(cookiesCmd))
362
385
  // ─── Autofill Command ────────────────────────────────────────────────────────
363
386
 
364
387
  const autofillCmd = program.command("autofill").description("List autofill entries");
365
- addCommonOptions(addSearchOption(addLimitOption(autofillCmd))).action((opts, cmd) => {
388
+ addSearchOption(addLimitOption(addCommonOptions(autofillCmd))).action((opts, cmd) => {
366
389
  const mergedOpts = cmd.optsWithGlobals();
367
390
  const results = getAutofillEntries({
368
- chromeDir: mergedOpts.chromeDir,
391
+ chromeDir: mergedOpts.profilePath,
369
392
  profile: mergedOpts.profile,
370
393
  search: mergedOpts.search,
371
394
  limit: parseInt(mergedOpts.limit, 10) || 100,
@@ -397,7 +420,7 @@ const creditCardsCmd = program.command("credit-cards").description("List saved c
397
420
  addCommonOptions(creditCardsCmd).action((opts, cmd) => {
398
421
  const mergedOpts = cmd.optsWithGlobals();
399
422
  const results = getCreditCards({
400
- chromeDir: mergedOpts.chromeDir,
423
+ chromeDir: mergedOpts.profilePath,
401
424
  profile: mergedOpts.profile,
402
425
  });
403
426
 
@@ -427,7 +450,7 @@ const addressesCmd = program.command("addresses").description("List saved addres
427
450
  addCommonOptions(addressesCmd).action((opts, cmd) => {
428
451
  const mergedOpts = cmd.optsWithGlobals();
429
452
  const results = getAddresses({
430
- chromeDir: mergedOpts.chromeDir,
453
+ chromeDir: mergedOpts.profilePath,
431
454
  profile: mergedOpts.profile,
432
455
  });
433
456
 
@@ -455,10 +478,10 @@ addCommonOptions(addressesCmd).action((opts, cmd) => {
455
478
  // ─── Downloads Command ───────────────────────────────────────────────────────
456
479
 
457
480
  const downloadsCmd = program.command("downloads").description("List download history");
458
- addCommonOptions(addSearchOption(addLimitOption(downloadsCmd))).action((opts, cmd) => {
481
+ addSearchOption(addLimitOption(addCommonOptions(downloadsCmd))).action((opts, cmd) => {
459
482
  const mergedOpts = cmd.optsWithGlobals();
460
483
  const results = getDownloads({
461
- chromeDir: mergedOpts.chromeDir,
484
+ chromeDir: mergedOpts.profilePath,
462
485
  profile: mergedOpts.profile,
463
486
  search: mergedOpts.search,
464
487
  limit: parseInt(mergedOpts.limit, 10) || 100,
@@ -490,10 +513,10 @@ addCommonOptions(addSearchOption(addLimitOption(downloadsCmd))).action((opts, cm
490
513
  // ─── Extensions Command ─────────────────────────────────────────────────────
491
514
 
492
515
  const extensionsCmd = program.command("extensions").description("List installed extensions");
493
- addCommonOptions(addSearchOption(extensionsCmd)).action((opts, cmd) => {
516
+ addSearchOption(addCommonOptions(extensionsCmd)).action((opts, cmd) => {
494
517
  const mergedOpts = cmd.optsWithGlobals();
495
518
  const results = getExtensions({
496
- chromeDir: mergedOpts.chromeDir,
519
+ chromeDir: mergedOpts.profilePath,
497
520
  profile: mergedOpts.profile,
498
521
  search: mergedOpts.search,
499
522
  });
@@ -525,7 +548,7 @@ const searchEnginesCmd = program.command("search-engines").description("List con
525
548
  addCommonOptions(searchEnginesCmd).action((opts, cmd) => {
526
549
  const mergedOpts = cmd.optsWithGlobals();
527
550
  const results = getSearchEngines({
528
- chromeDir: mergedOpts.chromeDir,
551
+ chromeDir: mergedOpts.profilePath,
529
552
  profile: mergedOpts.profile,
530
553
  });
531
554
 
@@ -555,7 +578,7 @@ const topSitesCmd = program.command("top-sites").description("List top sites");
555
578
  addCommonOptions(topSitesCmd).action((opts, cmd) => {
556
579
  const mergedOpts = cmd.optsWithGlobals();
557
580
  const results = getTopSites({
558
- chromeDir: mergedOpts.chromeDir,
581
+ chromeDir: mergedOpts.profilePath,
559
582
  profile: mergedOpts.profile,
560
583
  });
561
584
 
@@ -590,3 +613,17 @@ function formatBytes(bytes) {
590
613
  // ─── Run ─────────────────────────────────────────────────────────────────────
591
614
 
592
615
  program.parse();
616
+
617
+ const rootOpts = program.opts();
618
+ const hasSubcommand = program.args.length > 0;
619
+ if (!hasSubcommand) {
620
+ if (rootOpts.passwords) {
621
+ handlePasswords(rootOpts);
622
+ } else if (rootOpts.bookmarks) {
623
+ handleBookmarks(rootOpts);
624
+ } else if (rootOpts.profiles) {
625
+ handleProfiles(rootOpts);
626
+ } else {
627
+ program.help();
628
+ }
629
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chrome-tools",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "CLI & Node.js library to extract Chrome browser data (bookmarks, history, cookies, autofill, extensions, and more) on macOS",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -158,12 +158,13 @@ export function resolveProfilePath(chromeDir, profileName) {
158
158
  * Otherwise returns all discovered profiles.
159
159
  *
160
160
  * @param {Object} options
161
- * @param {string} [options.chromeDir] - Chrome User Data path.
161
+ * @param {string} [options.profilePath] - Custom Chrome User Data/profile directory.
162
+ * @param {string} [options.chromeDir] - Chrome User Data path (alias for profilePath).
162
163
  * @param {string} [options.profile] - Specific profile name to target.
163
164
  * @returns {Array<{name: string, path: string}>}
164
165
  */
165
- export function resolveTargetProfiles({ chromeDir, profile } = {}) {
166
- const dir = chromeDir || getDefaultChromeDir();
166
+ export function resolveTargetProfiles({ profilePath, chromeDir, profile } = {}) {
167
+ const dir = profilePath || chromeDir || getDefaultChromeDir();
167
168
  const allProfiles = discoverProfiles(dir);
168
169
 
169
170
  if (profile) {