findweb 0.1.2 → 0.1.3

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 (3) hide show
  1. package/README.md +3 -3
  2. package/dist/index.js +102 -21
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -42,7 +42,7 @@ findweb login
42
42
 
43
43
  - If the profile is already prepared, search runs immediately.
44
44
  - If the profile has not been prepared yet, `findweb` automatically opens the login flow first.
45
- - After you sign in and close the browser window, `findweb` writes a local prepared-profile marker so future searches can start immediately.
45
+ - After sign-in is detected, `findweb` saves a local prepared-profile marker and continues automatically.
46
46
  - By default, the profile directory is `${XDG_DATA_HOME:-~/.local/share}/findweb/chrome-profile` unless you pass `--userDataDir` or set `GOOGLE_SEARCH_USER_DATA_DIR`.
47
47
 
48
48
  In practice, the first search on a fresh profile behaves like this:
@@ -53,7 +53,7 @@ findweb "yc"
53
53
 
54
54
  1. detect missing prepared-profile marker
55
55
  2. open headed Chrome login flow
56
- 3. wait for you to sign in and close the browser
56
+ 3. wait for you to finish signing in
57
57
  4. continue the original search
58
58
 
59
59
  ## Options
@@ -98,7 +98,7 @@ You can trigger that ahead of time with:
98
98
  findweb login
99
99
  ```
100
100
 
101
- This opens a visible Chrome window with the Google sign-in page. After signing in, close the browser. The session is saved to the profile directory, and `findweb` records that the profile is ready for future searches.
101
+ This opens a visible Chrome window with the Google sign-in page. After sign-in is detected, `findweb` saves the session to the profile directory and records that the profile is ready for future searches.
102
102
 
103
103
  ## Output
104
104
 
package/dist/index.js CHANGED
@@ -76479,12 +76479,59 @@ async function waitForIdle(page) {
76479
76479
  }
76480
76480
 
76481
76481
  // src/search/search.ts
76482
+ var LOGIN_POLL_MS = 250;
76483
+ function wait2(delayMs) {
76484
+ return new Promise((resolve6) => setTimeout(resolve6, delayMs));
76485
+ }
76486
+ function hostnameFromUrl(url) {
76487
+ try {
76488
+ return new URL(url).hostname.toLowerCase();
76489
+ } catch {
76490
+ return null;
76491
+ }
76492
+ }
76493
+ function isCompletedLoginUrl(url) {
76494
+ const hostname = hostnameFromUrl(url);
76495
+ return hostname !== null && hostname !== "accounts.google.com" && (hostname === "google.com" || hostname.endsWith(".google.com"));
76496
+ }
76497
+ function hasCompletedLoginPage(urls) {
76498
+ return urls.some((url) => isCompletedLoginUrl(url));
76499
+ }
76500
+ async function pageUrls(browser) {
76501
+ const pages = await browser.pages();
76502
+ return pages.map((page) => page.url());
76503
+ }
76504
+ async function waitForCompletedLogin(loginPage, browser) {
76505
+ let lastSeenUrls = [loginPage.url()];
76506
+ while (true) {
76507
+ if (browser.connected) {
76508
+ lastSeenUrls = await pageUrls(browser).catch(() => lastSeenUrls);
76509
+ if (hasCompletedLoginPage(lastSeenUrls)) {
76510
+ return;
76511
+ }
76512
+ }
76513
+ if (!browser.connected || loginPage.isClosed()) {
76514
+ if (hasCompletedLoginPage(lastSeenUrls)) {
76515
+ return;
76516
+ }
76517
+ throw new Error("Google login was not completed.");
76518
+ }
76519
+ await wait2(LOGIN_POLL_MS);
76520
+ }
76521
+ }
76482
76522
  function isSorryPage(url) {
76483
76523
  return url.includes("/sorry/");
76484
76524
  }
76485
76525
  function toErrorMessage(error) {
76486
76526
  return error instanceof Error ? error.message : String(error);
76487
76527
  }
76528
+ function isInterruptedLoginError(error, loginPage, browser) {
76529
+ if (!browser.connected || loginPage?.isClosed()) {
76530
+ return true;
76531
+ }
76532
+ const message = toErrorMessage(error);
76533
+ return message.includes("Navigating frame was detached") || message.includes("LifecycleWatcher disposed") || message.includes("Target closed") || message.includes("Session closed");
76534
+ }
76488
76535
  async function searchQuery(options) {
76489
76536
  const page = await options.browser.newPage();
76490
76537
  try {
@@ -76546,19 +76593,29 @@ async function searchQueriesInTabs(browser, blocker, queries, options) {
76546
76593
  return results;
76547
76594
  }
76548
76595
  async function runLoginSession(options) {
76549
- const loginPage = await options.browser.newPage();
76550
- await preparePage(loginPage, options.lang);
76551
- await loginPage.goto(`https://accounts.google.com/ServiceLogin?continue=${encodeURIComponent(`https://www.google.com/?hl=${options.lang}&gl=${options.gl}&pws=0`)}&hl=${encodeURIComponent(options.lang)}`, { waitUntil: "networkidle2" });
76552
- await new Promise((resolve6) => {
76553
- const done = () => resolve6();
76554
- options.browser.once("disconnected", done);
76555
- process.once("SIGINT", async () => {
76556
- await options.browser.close().catch(() => {
76557
- return;
76558
- });
76559
- done();
76596
+ let loginPage = null;
76597
+ const handleSigint = () => {
76598
+ options.browser.close().catch(() => {
76599
+ return;
76560
76600
  });
76561
- });
76601
+ };
76602
+ process.once("SIGINT", handleSigint);
76603
+ try {
76604
+ loginPage = await options.browser.newPage();
76605
+ await preparePage(loginPage, options.lang);
76606
+ await loginPage.goto(`https://accounts.google.com/ServiceLogin?continue=${encodeURIComponent(`https://www.google.com/?hl=${options.lang}&gl=${options.gl}&pws=0`)}&hl=${encodeURIComponent(options.lang)}`, { waitUntil: "networkidle2" });
76607
+ await waitForCompletedLogin(loginPage, options.browser);
76608
+ } catch (error) {
76609
+ if (isInterruptedLoginError(error, loginPage, options.browser)) {
76610
+ throw new Error("Google login was not completed.");
76611
+ }
76612
+ throw error;
76613
+ } finally {
76614
+ process.off("SIGINT", handleSigint);
76615
+ await loginPage?.close().catch(() => {
76616
+ return;
76617
+ });
76618
+ }
76562
76619
  }
76563
76620
 
76564
76621
  // src/cli/profile.ts
@@ -76588,8 +76645,8 @@ async function markProfilePrepared(userDataDir) {
76588
76645
  // src/cli/flows/login.ts
76589
76646
  function printLoginInstructions(userDataDir) {
76590
76647
  console.log(`Login browser launched with profile: ${userDataDir}`);
76591
- console.log("Sign in to Google to prepare this profile for future searches.");
76592
- console.log("Close the browser window when you are done.");
76648
+ console.log("Complete Google sign-in to prepare this profile for future searches.");
76649
+ console.log("The session is saved automatically once sign-in is detected.");
76593
76650
  }
76594
76651
  async function runInteractiveLoginFlow(options) {
76595
76652
  await ensureProfileDir(options.userDataDir);
@@ -90229,6 +90286,10 @@ function createLoginArgs() {
90229
90286
  }
90230
90287
  };
90231
90288
  }
90289
+ function printCommandError(error48) {
90290
+ const message = error48 instanceof Error ? error48.message : String(error48);
90291
+ console.error(`ERROR: ${message}`);
90292
+ }
90232
90293
  async function runLogin(args) {
90233
90294
  const options = normalizeLoginOptions({
90234
90295
  gl: typeof args.gl === "string" ? args.gl : undefined,
@@ -90242,11 +90303,16 @@ function createLoginCommand(commandName = "findweb login") {
90242
90303
  meta: { name: commandName, description: "Open a reusable Google sign-in session." },
90243
90304
  args: createLoginArgs(),
90244
90305
  async run({ args }) {
90245
- await runLogin({
90246
- gl: args.gl,
90247
- lang: args.lang,
90248
- userDataDir: args.userDataDir
90249
- });
90306
+ try {
90307
+ await runLogin({
90308
+ gl: args.gl,
90309
+ lang: args.lang,
90310
+ userDataDir: args.userDataDir
90311
+ });
90312
+ } catch (error48) {
90313
+ printCommandError(error48);
90314
+ process.exitCode = 1;
90315
+ }
90250
90316
  }
90251
90317
  });
90252
90318
  }
@@ -101432,6 +101498,10 @@ var searchArgs = {
101432
101498
  description: "Print JSON output"
101433
101499
  }
101434
101500
  };
101501
+ function printCommandError2(error49) {
101502
+ const message = error49 instanceof Error ? error49.message : String(error49);
101503
+ console.error(`ERROR: ${message}`);
101504
+ }
101435
101505
  async function runSearch(args) {
101436
101506
  const options = normalizeSearchOptions({
101437
101507
  _: args._,
@@ -101480,7 +101550,12 @@ function createSearchCommand(commandName = "findweb") {
101480
101550
  },
101481
101551
  args: searchArgs,
101482
101552
  async run({ args }) {
101483
- await runSearch(args);
101553
+ try {
101554
+ await runSearch(args);
101555
+ } catch (error49) {
101556
+ printCommandError2(error49);
101557
+ process4.exitCode = 1;
101558
+ }
101484
101559
  }
101485
101560
  });
101486
101561
  }
@@ -101560,4 +101635,10 @@ async function main() {
101560
101635
  }
101561
101636
  await runMain(createSearchCommand(), { rawArgs: action.rawArgs });
101562
101637
  }
101563
- await main();
101638
+ try {
101639
+ await main();
101640
+ } catch (error49) {
101641
+ const message = error49 instanceof Error ? error49.message : String(error49);
101642
+ console.error(`ERROR: ${message}`);
101643
+ process5.exitCode = 1;
101644
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "findweb",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Google search CLI powered by system Chrome",
5
5
  "type": "module",
6
6
  "packageManager": "bun@1.3.11",