chrome-tools 1.0.7 → 1.0.9

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/dist/bin/cli.js CHANGED
@@ -1,2 +1,2 @@
1
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());
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,getDirectorySize as m}from"../src/core/chrome.js";import{getPasswords as p}from"../src/modules/passwords.js";import{getBookmarks as d}from"../src/modules/bookmarks.js";import{getHistory as h,getSearchTerms as f}from"../src/modules/history.js";import{getCookies as u}from"../src/modules/cookies.js";import{getAutofillEntries as g,getCreditCards as y,getAddresses as w}from"../src/modules/autofill.js";import{getDownloads as b}from"../src/modules/downloads.js";import{getExtensions as S}from"../src/modules/extensions.js";import{getSearchEngines as k}from"../src/modules/search-engines.js";import{getTopSites as P}from"../src/modules/top-sites.js";const j=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}(),L=j.PROFILE_PATH||c(),v=j.PROFILE_NAME||void 0;function D(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 N(o,e){0===e?console.log(i.yellow(`\n⚠ No ${o} found.\n`)):console.log(i.green(`\n✔ Found ${e} ${o}.\n`))}function C(o){return o.option("--profile-path <path>","Custom Chrome Profile Path",L).option("--profile <name>","Target a specific profile",v).option("--json","Output as JSON")}function U(o){return o.option("--search <term>","Filter results (case-insensitive)")}function G(o,e=100){return o.option("--limit <n>","Limit number of results",String(e))}const O=new s,$=JSON.parse(o(new URL("../../package.json",import.meta.url),"utf8"));O.name("chrome-tools").description(i.bold.cyan("Chrome Data Toolkit")+i.dim(" — Extract passwords, bookmarks, history, cookies, and more from Chrome on macOS")).version($.version).option("--passwords","Shortcut: list saved passwords").option("--bookmarks","Shortcut: list bookmarks").option("--profiles","Shortcut: list profiles").option("--size","Show each profile's size on disk when listing profiles (slower)").option("--profile-path <path>","Custom Chrome Profile Path",L).option("--profile <name>","Target a specific profile",v).option("--json","Output as JSON").option("--search <term>","Filter results (for shortcut flags)").option("--limit <n>","Limit results (for shortcut flags)").action(o=>{o.passwords?B(o):o.bookmarks?F(o):E(o)});const x=O.command("profiles").description("List Chrome profiles");function E(o){const e=n(o.profilePath),t=l(o.profilePath),s=Boolean(o.size);if(o.json){const o=new Map(t.map(o=>[o.directory,o]));return D(e.map(e=>({...e,...o.get(e.name)||{},...s?{sizeBytes:m(e.path)}:{}})))}if(0===e.length)return void console.log(i.yellow("\n⚠ No Chrome profiles found.\n"));const r=new Map(t.map(o=>[o.directory,o]));console.log(i.bold(`\n📁 Discovered ${e.length} profile(s):\n`));const a=W(s?["Directory","Display Name","Email","Size","Path"]:["Directory","Display Name","Email","Path"],s?[16,22,30,10,42]:[16,22,30,50]);for(const o of e){const e=r.get(o.name)||{},t=[i.cyan(o.name),i.white(e.name||o.name),i.dim(e.email||"—")];s&&t.push(i.yellow(R(m(o.path)))),t.push(i.dim(o.path)),a.push(t)}console.log(a.toString()),N("profile(s)",e.length)}C(x).option("--size","Compute and show each profile's size on disk (slower)").action((o,e)=>{E(e.optsWithGlobals())}),x.command("states").description("Show detailed profile states with available data files").option("--profile-path <path>","Custom Chrome Profile Path",L).option("--json","Output as JSON").action((o,e)=>function(o){const e=a(o.profilePath);if(o.json)return D(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 B(o){const e=p({chromeDir:o.profilePath,profile:o.profile,search:o.search});if(o.json)return D(e);if(0===e.length)return N("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()),N("password(s)",e.length)}U(C(O.command("passwords").description("List saved passwords"))).action((o,e)=>B(e.optsWithGlobals()));function F(o){const e=d({chromeDir:o.profilePath,profile:o.profile,folder:o.folder,search:o.search});if(o.json)return D(e);if(0===e.length)return N("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()),N("bookmark(s)",e.length)}U(C(O.command("bookmarks").description("List bookmarks"))).option("--folder <name>","Filter by folder name").action((o,e)=>F(e.optsWithGlobals()));U(G(C(O.command("history").description("List browsing history")))).action((o,e)=>{const t=e.optsWithGlobals(),s=h({chromeDir:t.profilePath,profile:t.profile,search:t.search,limit:parseInt(t.limit,10)||100});if(t.json)return D(s);if(0===s.length)return N("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()),N("history entry(ies)",s.length)});G(C(O.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 D(s);if(0===s.length)return N("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()),N("search term(s)",s.length)});G(C(O.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=u({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 D(s);if(0===s.length)return N("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()),N("cookie(s)",s.length)});U(G(C(O.command("autofill").description("List autofill entries")))).action((o,e)=>{const t=e.optsWithGlobals(),s=g({chromeDir:t.profilePath,profile:t.profile,search:t.search,limit:parseInt(t.limit,10)||100});if(t.json)return D(s);if(0===s.length)return N("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()),N("autofill entry(ies)",s.length)});C(O.command("credit-cards").description("List saved credit cards")).action((o,e)=>{const t=e.optsWithGlobals(),s=y({chromeDir:t.profilePath,profile:t.profile});if(t.json)return D(s);if(0===s.length)return N("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()),N("credit card(s)",s.length)});C(O.command("addresses").description("List saved addresses")).action((o,e)=>{const t=e.optsWithGlobals(),s=w({chromeDir:t.profilePath,profile:t.profile});if(t.json)return D(s);if(0===s.length)return N("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()),N("address(es)",s.length)});U(G(C(O.command("downloads").description("List download history")))).action((o,e)=>{const t=e.optsWithGlobals(),s=b({chromeDir:t.profilePath,profile:t.profile,search:t.search,limit:parseInt(t.limit,10)||100});if(t.json)return D(s);if(0===s.length)return N("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()),N("download(s)",s.length)});U(C(O.command("extensions").description("List installed extensions"))).action((o,e)=>{const t=e.optsWithGlobals(),s=S({chromeDir:t.profilePath,profile:t.profile,search:t.search});if(t.json)return D(s);if(0===s.length)return N("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()),N("extension(s)",s.length)});C(O.command("search-engines").description("List configured search engines")).action((o,e)=>{const t=e.optsWithGlobals(),s=k({chromeDir:t.profilePath,profile:t.profile});if(t.json)return D(s);if(0===s.length)return N("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()),N("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]}`}C(O.command("top-sites").description("List top sites")).action((o,e)=>{const t=e.optsWithGlobals(),s=P({chromeDir:t.profilePath,profile:t.profile});if(t.json)return D(s);if(0===s.length)return N("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()),N("top site(s)",s.length)}),O.parse();
@@ -58,6 +58,15 @@ export declare function getProfileMetadata(chromeDir?: string): ProfileMetadata[
58
58
  * @returns Enriched profile state objects.
59
59
  */
60
60
  export declare function getProfileStates(chromeDir?: string): ProfileState[];
61
+ /**
62
+ * Recursively computes the total size, in bytes, of a directory's contents.
63
+ * Symbolic links are not followed and unreadable entries are skipped so the
64
+ * walk never throws on a single permission error.
65
+ *
66
+ * @param dirPath - Absolute path to the directory to measure.
67
+ * @returns Total size of all contained files in bytes (0 if missing/unreadable).
68
+ */
69
+ export declare function getDirectorySize(dirPath: string): number;
61
70
  /**
62
71
  * Resolves a profile name to its directory path.
63
72
  *
@@ -1 +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}
1
+ import{existsSync as e,readdirSync as t,readFileSync as r,statSync as o}from"node:fs";import{homedir as i}from"node:os";import{join as a}from"node:path";const n=["Login Data","History","Bookmarks","Cookies","Web Data","Preferences"];export function getDefaultChromeDir(){return a(i(),"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}),i=[];for(const t of r){if(!t.isDirectory())continue;const r=a(o,t.name);n.some(t=>e(a(r,t)))&&i.push({name:t.name,path:r})}return i}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),i=getProfileMetadata(r),s=new Map;for(const e of i)s.set(e.directory,e);return o.map(t=>{const r=s.get(t.name),o=t.path,i={};for(const t of n)i[t.replace(/\s+/g,"_").toLowerCase()]=e(a(o,t));return i.top_sites=e(a(o,"Top Sites")),i.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:i}})}export function getDirectorySize(r){if(!r||!e(r))return 0;let i,n=0;try{i=t(r,{withFileTypes:!0})}catch{return 0}for(const e of i){const t=a(r,e.name);try{if(e.isSymbolicLink())continue;e.isDirectory()?n+=getDirectorySize(t):e.isFile()&&(n+=o(t).size)}catch{}}return 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(),i=discoverProfiles(o);if(r){const e=i.filter(e=>e.name===r);if(0===e.length)throw new Error(`Profile "${r}" not found in ${o}`);return e}return i}
@@ -1 +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}
1
+ import{execSync as e}from"node:child_process";import{pbkdf2Sync as n}from"node:crypto";const t=Buffer.from("saltysalt"),o="Chrome Safe Storage",r=44,i=36,s=45;let a=null,c=null;function l(e,n){const t="See docs/remote-keychain-access.md for non-interactive / SSH setup.";let a;a=e===r||/could not be found/i.test(n)?`No '${o}' item found in the Keychain.\nRun Google Chrome at least once on this machine so it creates the key.`:e===i||/interaction.*not allowed/i.test(n)?"The Keychain needs a GUI authorization prompt that can't be shown here "+(Boolean(process.env.SSH_CONNECTION||process.env.SSH_TTY)||!process.env.TERM_PROGRAM?"(you appear to be on a remote/SSH session).\n":".\n")+'Authorize once from a GUI Terminal (click "Always Allow"), then unlock the\nkeychain in this session:\n security unlock-keychain ~/Library/Keychains/login.keychain-db\n'+t:e===s?`Keychain authorization failed (wrong password or denied).\nTry: security unlock-keychain ~/Library/Keychains/login.keychain-db\n${t}`:`macOS likely showed a Keychain access prompt that was denied or dismissed.\nRun \`security find-generic-password -w -s "${o}"\` in a GUI\nTerminal and click "Always Allow".\n${t}`;const c=[null!==e?`exit ${e}`:null,n||null].filter(Boolean).join(": ");return new Error(`Could not retrieve ${o} key from macOS Keychain.\n${a}`+(c?`\n(security: ${c})`:""))}export function getChromeKeychainPassword(){if(null!==a)return a;let n;try{n=e(`security find-generic-password -w -s "${o}"`,{encoding:"utf-8",stdio:["pipe","pipe","pipe"]}).trim()}catch(e){const{status:n,stderr:t}=function(e){const n=e;return{status:"number"==typeof n?.status?n.status:null,stderr:(n?.stderr??"").toString().trim()}}(e);throw l(n,t)}if(!n)throw new Error(`Keychain returned an empty ${o} key.\nThe item may exist with no password. Re-run Google Chrome to recreate it.`);return a=n,n}export function deriveEncryptionKey(e){if(null!==c)return c;const o=e||getChromeKeychainPassword();if(!o)throw new Error("Keychain password cannot be empty for key derivation.");return c=n(o,t,1003,16,"sha1"),c}export function clearKeychainCache(){a=null,c=null}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chrome-tools",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "CLI & Node.js library for chrome",
5
5
  "type": "module",
6
6
  "main": "./dist/src/index.js",