chrome-tools 1.0.4 → 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,12 +7,13 @@
7
7
  * Supports subcommands and shortcut flags for quick access to all data types.
8
8
  */
9
9
 
10
- import { readFileSync } from "node:fs";
10
+ import { readFileSync, existsSync } from "node:fs";
11
+ import { join } from "node:path";
11
12
  import { Command } from "commander";
12
13
  import chalk from "chalk";
13
14
  import Table from "cli-table3";
14
15
 
15
- import { discoverProfiles, getProfileMetadata, getProfileStates } from "../src/core/chrome.js";
16
+ import { discoverProfiles, getProfileMetadata, getProfileStates, getDefaultChromeDir } from "../src/core/chrome.js";
16
17
  import { getPasswords } from "../src/modules/passwords.js";
17
18
  import { getBookmarks } from "../src/modules/bookmarks.js";
18
19
  import { getHistory, getSearchTerms } from "../src/modules/history.js";
@@ -23,6 +24,39 @@ import { getExtensions } from "../src/modules/extensions.js";
23
24
  import { getSearchEngines } from "../src/modules/search-engines.js";
24
25
  import { getTopSites } from "../src/modules/top-sites.js";
25
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
+
26
60
  // ─── Output Helpers ──────────────────────────────────────────────────────────
27
61
 
28
62
  function outputJSON(data) {
@@ -50,8 +84,8 @@ function printCount(label, count) {
50
84
 
51
85
  function addCommonOptions(cmd) {
52
86
  return cmd
53
- .option("--chrome-dir <path>", "Custom Chrome User Data directory")
54
- .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)
55
89
  .option("--json", "Output as JSON");
56
90
  }
57
91
 
@@ -79,25 +113,11 @@ program
79
113
  .option("--passwords", "Shortcut: list saved passwords")
80
114
  .option("--bookmarks", "Shortcut: list bookmarks")
81
115
  .option("--profiles", "Shortcut: list profiles")
82
- .option("--chrome-dir <path>", "Custom Chrome User Data directory")
83
- .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)
84
118
  .option("--json", "Output as JSON")
85
119
  .option("--search <term>", "Filter results (for shortcut flags)")
86
- .option("--limit <n>", "Limit results (for shortcut flags)")
87
- .action((opts) => {
88
- // Handle shortcut flags at the top level
89
- if (opts.passwords) {
90
- return handlePasswords(opts);
91
- }
92
- if (opts.bookmarks) {
93
- return handleBookmarks(opts);
94
- }
95
- if (opts.profiles) {
96
- return handleProfiles(opts);
97
- }
98
- // No shortcut flag — show help
99
- program.help();
100
- });
120
+ .option("--limit <n>", "Limit results (for shortcut flags)");
101
121
 
102
122
  // ─── Profiles Command ───────────────────────────────────────────────────────
103
123
 
@@ -112,13 +132,13 @@ addCommonOptions(profilesCmd).action((opts, cmd) => {
112
132
  profilesCmd
113
133
  .command("states")
114
134
  .description("Show detailed profile states with available data files")
115
- .option("--chrome-dir <path>", "Custom Chrome User Data directory")
135
+ .option("--profile-path <path>", "Custom Chrome Profile Path", defaultProfilePath)
116
136
  .option("--json", "Output as JSON")
117
137
  .action((opts, cmd) => handleProfileStates(cmd.optsWithGlobals()));
118
138
 
119
139
  function handleProfiles(opts) {
120
- const profiles = discoverProfiles(opts.chromeDir);
121
- const metadata = getProfileMetadata(opts.chromeDir);
140
+ const profiles = discoverProfiles(opts.profilePath);
141
+ const metadata = getProfileMetadata(opts.profilePath);
122
142
 
123
143
  if (opts.json) {
124
144
  const metaMap = new Map(metadata.map((m) => [m.directory, m]));
@@ -154,7 +174,7 @@ function handleProfiles(opts) {
154
174
  }
155
175
 
156
176
  function handleProfileStates(opts) {
157
- const states = getProfileStates(opts.chromeDir);
177
+ const states = getProfileStates(opts.profilePath);
158
178
 
159
179
  if (opts.json) {
160
180
  return outputJSON(states);
@@ -193,11 +213,11 @@ function handleProfileStates(opts) {
193
213
  // ─── Passwords Command ──────────────────────────────────────────────────────
194
214
 
195
215
  const passwordsCmd = program.command("passwords").description("List saved passwords");
196
- addCommonOptions(addSearchOption(passwordsCmd)).action((opts, cmd) => handlePasswords(cmd.optsWithGlobals()));
216
+ addSearchOption(addCommonOptions(passwordsCmd)).action((opts, cmd) => handlePasswords(cmd.optsWithGlobals()));
197
217
 
198
218
  function handlePasswords(opts) {
199
219
  const results = getPasswords({
200
- chromeDir: opts.chromeDir,
220
+ chromeDir: opts.profilePath,
201
221
  profile: opts.profile,
202
222
  search: opts.search,
203
223
  });
@@ -233,13 +253,13 @@ function handlePasswords(opts) {
233
253
  // ─── Bookmarks Command ──────────────────────────────────────────────────────
234
254
 
235
255
  const bookmarksCmd = program.command("bookmarks").description("List bookmarks");
236
- addCommonOptions(addSearchOption(bookmarksCmd))
256
+ addSearchOption(addCommonOptions(bookmarksCmd))
237
257
  .option("--folder <name>", "Filter by folder name")
238
258
  .action((opts, cmd) => handleBookmarks(cmd.optsWithGlobals()));
239
259
 
240
260
  function handleBookmarks(opts) {
241
261
  const results = getBookmarks({
242
- chromeDir: opts.chromeDir,
262
+ chromeDir: opts.profilePath,
243
263
  profile: opts.profile,
244
264
  folder: opts.folder,
245
265
  search: opts.search,
@@ -268,10 +288,10 @@ function handleBookmarks(opts) {
268
288
  // ─── History Command ─────────────────────────────────────────────────────────
269
289
 
270
290
  const historyCmd = program.command("history").description("List browsing history");
271
- addCommonOptions(addSearchOption(addLimitOption(historyCmd))).action((opts, cmd) => {
291
+ addSearchOption(addLimitOption(addCommonOptions(historyCmd))).action((opts, cmd) => {
272
292
  const mergedOpts = cmd.optsWithGlobals();
273
293
  const results = getHistory({
274
- chromeDir: mergedOpts.chromeDir,
294
+ chromeDir: mergedOpts.profilePath,
275
295
  profile: mergedOpts.profile,
276
296
  search: mergedOpts.search,
277
297
  limit: parseInt(mergedOpts.limit, 10) || 100,
@@ -300,10 +320,10 @@ addCommonOptions(addSearchOption(addLimitOption(historyCmd))).action((opts, cmd)
300
320
  // ─── Search Terms Command ────────────────────────────────────────────────────
301
321
 
302
322
  const searchTermsCmd = program.command("search-terms").description("List search terms from history");
303
- addCommonOptions(addLimitOption(searchTermsCmd)).action((opts, cmd) => {
323
+ addLimitOption(addCommonOptions(searchTermsCmd)).action((opts, cmd) => {
304
324
  const mergedOpts = cmd.optsWithGlobals();
305
325
  const results = getSearchTerms({
306
- chromeDir: mergedOpts.chromeDir,
326
+ chromeDir: mergedOpts.profilePath,
307
327
  profile: mergedOpts.profile,
308
328
  limit: parseInt(mergedOpts.limit, 10) || 100,
309
329
  });
@@ -326,14 +346,14 @@ addCommonOptions(addLimitOption(searchTermsCmd)).action((opts, cmd) => {
326
346
  // ─── Cookies Command ─────────────────────────────────────────────────────────
327
347
 
328
348
  const cookiesCmd = program.command("cookies").description("List cookies (encrypted values decrypted)");
329
- addCommonOptions(addLimitOption(cookiesCmd))
349
+ addLimitOption(addCommonOptions(cookiesCmd))
330
350
  .option("--domain <domain>", "Filter by domain")
331
351
  .option("--url <url>", "Filter by URL with path")
332
352
  .option("--no-decrypt", "Skip decryption of encrypted values")
333
353
  .action((opts, cmd) => {
334
354
  const mergedOpts = cmd.optsWithGlobals();
335
355
  const results = getCookies({
336
- chromeDir: mergedOpts.chromeDir,
356
+ chromeDir: mergedOpts.profilePath,
337
357
  profile: mergedOpts.profile,
338
358
  domain: mergedOpts.domain,
339
359
  url: mergedOpts.url,
@@ -365,10 +385,10 @@ addCommonOptions(addLimitOption(cookiesCmd))
365
385
  // ─── Autofill Command ────────────────────────────────────────────────────────
366
386
 
367
387
  const autofillCmd = program.command("autofill").description("List autofill entries");
368
- addCommonOptions(addSearchOption(addLimitOption(autofillCmd))).action((opts, cmd) => {
388
+ addSearchOption(addLimitOption(addCommonOptions(autofillCmd))).action((opts, cmd) => {
369
389
  const mergedOpts = cmd.optsWithGlobals();
370
390
  const results = getAutofillEntries({
371
- chromeDir: mergedOpts.chromeDir,
391
+ chromeDir: mergedOpts.profilePath,
372
392
  profile: mergedOpts.profile,
373
393
  search: mergedOpts.search,
374
394
  limit: parseInt(mergedOpts.limit, 10) || 100,
@@ -400,7 +420,7 @@ const creditCardsCmd = program.command("credit-cards").description("List saved c
400
420
  addCommonOptions(creditCardsCmd).action((opts, cmd) => {
401
421
  const mergedOpts = cmd.optsWithGlobals();
402
422
  const results = getCreditCards({
403
- chromeDir: mergedOpts.chromeDir,
423
+ chromeDir: mergedOpts.profilePath,
404
424
  profile: mergedOpts.profile,
405
425
  });
406
426
 
@@ -430,7 +450,7 @@ const addressesCmd = program.command("addresses").description("List saved addres
430
450
  addCommonOptions(addressesCmd).action((opts, cmd) => {
431
451
  const mergedOpts = cmd.optsWithGlobals();
432
452
  const results = getAddresses({
433
- chromeDir: mergedOpts.chromeDir,
453
+ chromeDir: mergedOpts.profilePath,
434
454
  profile: mergedOpts.profile,
435
455
  });
436
456
 
@@ -458,10 +478,10 @@ addCommonOptions(addressesCmd).action((opts, cmd) => {
458
478
  // ─── Downloads Command ───────────────────────────────────────────────────────
459
479
 
460
480
  const downloadsCmd = program.command("downloads").description("List download history");
461
- addCommonOptions(addSearchOption(addLimitOption(downloadsCmd))).action((opts, cmd) => {
481
+ addSearchOption(addLimitOption(addCommonOptions(downloadsCmd))).action((opts, cmd) => {
462
482
  const mergedOpts = cmd.optsWithGlobals();
463
483
  const results = getDownloads({
464
- chromeDir: mergedOpts.chromeDir,
484
+ chromeDir: mergedOpts.profilePath,
465
485
  profile: mergedOpts.profile,
466
486
  search: mergedOpts.search,
467
487
  limit: parseInt(mergedOpts.limit, 10) || 100,
@@ -493,10 +513,10 @@ addCommonOptions(addSearchOption(addLimitOption(downloadsCmd))).action((opts, cm
493
513
  // ─── Extensions Command ─────────────────────────────────────────────────────
494
514
 
495
515
  const extensionsCmd = program.command("extensions").description("List installed extensions");
496
- addCommonOptions(addSearchOption(extensionsCmd)).action((opts, cmd) => {
516
+ addSearchOption(addCommonOptions(extensionsCmd)).action((opts, cmd) => {
497
517
  const mergedOpts = cmd.optsWithGlobals();
498
518
  const results = getExtensions({
499
- chromeDir: mergedOpts.chromeDir,
519
+ chromeDir: mergedOpts.profilePath,
500
520
  profile: mergedOpts.profile,
501
521
  search: mergedOpts.search,
502
522
  });
@@ -528,7 +548,7 @@ const searchEnginesCmd = program.command("search-engines").description("List con
528
548
  addCommonOptions(searchEnginesCmd).action((opts, cmd) => {
529
549
  const mergedOpts = cmd.optsWithGlobals();
530
550
  const results = getSearchEngines({
531
- chromeDir: mergedOpts.chromeDir,
551
+ chromeDir: mergedOpts.profilePath,
532
552
  profile: mergedOpts.profile,
533
553
  });
534
554
 
@@ -558,7 +578,7 @@ const topSitesCmd = program.command("top-sites").description("List top sites");
558
578
  addCommonOptions(topSitesCmd).action((opts, cmd) => {
559
579
  const mergedOpts = cmd.optsWithGlobals();
560
580
  const results = getTopSites({
561
- chromeDir: mergedOpts.chromeDir,
581
+ chromeDir: mergedOpts.profilePath,
562
582
  profile: mergedOpts.profile,
563
583
  });
564
584
 
@@ -593,3 +613,17 @@ function formatBytes(bytes) {
593
613
  // ─── Run ─────────────────────────────────────────────────────────────────────
594
614
 
595
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.4",
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) {