findweb 0.1.1 → 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.
- package/README.md +4 -3
- package/dist/index.js +136 -48
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -42,7 +42,8 @@ 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
|
|
45
|
+
- After sign-in is detected, `findweb` saves a local prepared-profile marker and continues automatically.
|
|
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`.
|
|
46
47
|
|
|
47
48
|
In practice, the first search on a fresh profile behaves like this:
|
|
48
49
|
|
|
@@ -52,7 +53,7 @@ findweb "yc"
|
|
|
52
53
|
|
|
53
54
|
1. detect missing prepared-profile marker
|
|
54
55
|
2. open headed Chrome login flow
|
|
55
|
-
3. wait for you to
|
|
56
|
+
3. wait for you to finish signing in
|
|
56
57
|
4. continue the original search
|
|
57
58
|
|
|
58
59
|
## Options
|
|
@@ -97,7 +98,7 @@ You can trigger that ahead of time with:
|
|
|
97
98
|
findweb login
|
|
98
99
|
```
|
|
99
100
|
|
|
100
|
-
This opens a visible Chrome window with the Google sign-in page. After
|
|
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.
|
|
101
102
|
|
|
102
103
|
## Output
|
|
103
104
|
|
package/dist/index.js
CHANGED
|
@@ -66932,9 +66932,10 @@ async function runMain(cmd, opts = {}) {
|
|
|
66932
66932
|
}
|
|
66933
66933
|
|
|
66934
66934
|
// src/search/browser.ts
|
|
66935
|
-
import { existsSync as existsSync3 } from "fs";
|
|
66936
66935
|
import { spawn as spawn3 } from "child_process";
|
|
66937
66936
|
import http2 from "http";
|
|
66937
|
+
import os9 from "os";
|
|
66938
|
+
import path12 from "path";
|
|
66938
66939
|
|
|
66939
66940
|
// node_modules/puppeteer-core/lib/esm/puppeteer/api/api.js
|
|
66940
66941
|
init_Browser();
|
|
@@ -76307,13 +76308,19 @@ async function closeSearchBrowser(activeBrowser) {
|
|
|
76307
76308
|
activeBrowser.chromeProcess.kill("SIGTERM");
|
|
76308
76309
|
}
|
|
76309
76310
|
}
|
|
76311
|
+
function defaultXdgDataHome() {
|
|
76312
|
+
const configured = process.env.XDG_DATA_HOME;
|
|
76313
|
+
if (configured && path12.isAbsolute(configured)) {
|
|
76314
|
+
return configured;
|
|
76315
|
+
}
|
|
76316
|
+
return path12.join(os9.homedir(), ".local", "share");
|
|
76317
|
+
}
|
|
76310
76318
|
function defaultUserDataDir() {
|
|
76311
76319
|
const configured = process.env.GOOGLE_SEARCH_USER_DATA_DIR;
|
|
76312
76320
|
if (configured) {
|
|
76313
76321
|
return configured;
|
|
76314
76322
|
}
|
|
76315
|
-
|
|
76316
|
-
return ["/tmp/google-search-profile", legacyProfile].find((candidate) => existsSync3(candidate)) ?? "/tmp/google-search-profile";
|
|
76323
|
+
return path12.join(defaultXdgDataHome(), "findweb", "chrome-profile");
|
|
76317
76324
|
}
|
|
76318
76325
|
|
|
76319
76326
|
// src/search/page.ts
|
|
@@ -76472,12 +76479,59 @@ async function waitForIdle(page) {
|
|
|
76472
76479
|
}
|
|
76473
76480
|
|
|
76474
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
|
+
}
|
|
76475
76522
|
function isSorryPage(url) {
|
|
76476
76523
|
return url.includes("/sorry/");
|
|
76477
76524
|
}
|
|
76478
76525
|
function toErrorMessage(error) {
|
|
76479
76526
|
return error instanceof Error ? error.message : String(error);
|
|
76480
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
|
+
}
|
|
76481
76535
|
async function searchQuery(options) {
|
|
76482
76536
|
const page = await options.browser.newPage();
|
|
76483
76537
|
try {
|
|
@@ -76539,30 +76593,40 @@ async function searchQueriesInTabs(browser, blocker, queries, options) {
|
|
|
76539
76593
|
return results;
|
|
76540
76594
|
}
|
|
76541
76595
|
async function runLoginSession(options) {
|
|
76542
|
-
|
|
76543
|
-
|
|
76544
|
-
|
|
76545
|
-
|
|
76546
|
-
const done = () => resolve6();
|
|
76547
|
-
options.browser.once("disconnected", done);
|
|
76548
|
-
process.once("SIGINT", async () => {
|
|
76549
|
-
await options.browser.close().catch(() => {
|
|
76550
|
-
return;
|
|
76551
|
-
});
|
|
76552
|
-
done();
|
|
76596
|
+
let loginPage = null;
|
|
76597
|
+
const handleSigint = () => {
|
|
76598
|
+
options.browser.close().catch(() => {
|
|
76599
|
+
return;
|
|
76553
76600
|
});
|
|
76554
|
-
}
|
|
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
|
+
}
|
|
76555
76619
|
}
|
|
76556
76620
|
|
|
76557
76621
|
// src/cli/profile.ts
|
|
76558
76622
|
import fs7 from "fs/promises";
|
|
76559
|
-
import
|
|
76623
|
+
import path13 from "path";
|
|
76560
76624
|
var PROFILE_READY_MARKER = ".findweb-profile-ready";
|
|
76561
76625
|
async function ensureProfileDir(dirPath) {
|
|
76562
76626
|
await fs7.mkdir(dirPath, { recursive: true });
|
|
76563
76627
|
}
|
|
76564
76628
|
function readyMarkerPath(userDataDir) {
|
|
76565
|
-
return
|
|
76629
|
+
return path13.join(userDataDir, PROFILE_READY_MARKER);
|
|
76566
76630
|
}
|
|
76567
76631
|
async function hasPreparedProfile(userDataDir) {
|
|
76568
76632
|
try {
|
|
@@ -76581,8 +76645,8 @@ async function markProfilePrepared(userDataDir) {
|
|
|
76581
76645
|
// src/cli/flows/login.ts
|
|
76582
76646
|
function printLoginInstructions(userDataDir) {
|
|
76583
76647
|
console.log(`Login browser launched with profile: ${userDataDir}`);
|
|
76584
|
-
console.log("
|
|
76585
|
-
console.log("
|
|
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.");
|
|
76586
76650
|
}
|
|
76587
76651
|
async function runInteractiveLoginFlow(options) {
|
|
76588
76652
|
await ensureProfileDir(options.userDataDir);
|
|
@@ -76615,7 +76679,7 @@ async function ensureInteractiveLogin(options) {
|
|
|
76615
76679
|
}
|
|
76616
76680
|
|
|
76617
76681
|
// src/cli/schema.ts
|
|
76618
|
-
import
|
|
76682
|
+
import path14 from "path";
|
|
76619
76683
|
|
|
76620
76684
|
// node_modules/zod/v4/classic/external.js
|
|
76621
76685
|
var exports_external = {};
|
|
@@ -77382,10 +77446,10 @@ function mergeDefs(...defs) {
|
|
|
77382
77446
|
function cloneDef(schema) {
|
|
77383
77447
|
return mergeDefs(schema._zod.def);
|
|
77384
77448
|
}
|
|
77385
|
-
function getElementAtPath(obj,
|
|
77386
|
-
if (!
|
|
77449
|
+
function getElementAtPath(obj, path14) {
|
|
77450
|
+
if (!path14)
|
|
77387
77451
|
return obj;
|
|
77388
|
-
return
|
|
77452
|
+
return path14.reduce((acc, key) => acc?.[key], obj);
|
|
77389
77453
|
}
|
|
77390
77454
|
function promiseAllObject(promisesObj) {
|
|
77391
77455
|
const keys = Object.keys(promisesObj);
|
|
@@ -77766,11 +77830,11 @@ function aborted(x, startIndex = 0) {
|
|
|
77766
77830
|
}
|
|
77767
77831
|
return false;
|
|
77768
77832
|
}
|
|
77769
|
-
function prefixIssues(
|
|
77833
|
+
function prefixIssues(path14, issues) {
|
|
77770
77834
|
return issues.map((iss) => {
|
|
77771
77835
|
var _a4;
|
|
77772
77836
|
(_a4 = iss).path ?? (_a4.path = []);
|
|
77773
|
-
iss.path.unshift(
|
|
77837
|
+
iss.path.unshift(path14);
|
|
77774
77838
|
return iss;
|
|
77775
77839
|
});
|
|
77776
77840
|
}
|
|
@@ -77953,7 +78017,7 @@ function formatError(error, mapper = (issue2) => issue2.message) {
|
|
|
77953
78017
|
}
|
|
77954
78018
|
function treeifyError(error, mapper = (issue2) => issue2.message) {
|
|
77955
78019
|
const result = { errors: [] };
|
|
77956
|
-
const processError = (error2,
|
|
78020
|
+
const processError = (error2, path14 = []) => {
|
|
77957
78021
|
var _a4, _b2;
|
|
77958
78022
|
for (const issue2 of error2.issues) {
|
|
77959
78023
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
@@ -77963,7 +78027,7 @@ function treeifyError(error, mapper = (issue2) => issue2.message) {
|
|
|
77963
78027
|
} else if (issue2.code === "invalid_element") {
|
|
77964
78028
|
processError({ issues: issue2.issues }, issue2.path);
|
|
77965
78029
|
} else {
|
|
77966
|
-
const fullpath = [...
|
|
78030
|
+
const fullpath = [...path14, ...issue2.path];
|
|
77967
78031
|
if (fullpath.length === 0) {
|
|
77968
78032
|
result.errors.push(mapper(issue2));
|
|
77969
78033
|
continue;
|
|
@@ -77995,8 +78059,8 @@ function treeifyError(error, mapper = (issue2) => issue2.message) {
|
|
|
77995
78059
|
}
|
|
77996
78060
|
function toDotPath(_path) {
|
|
77997
78061
|
const segs = [];
|
|
77998
|
-
const
|
|
77999
|
-
for (const seg of
|
|
78062
|
+
const path14 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
78063
|
+
for (const seg of path14) {
|
|
78000
78064
|
if (typeof seg === "number")
|
|
78001
78065
|
segs.push(`[${seg}]`);
|
|
78002
78066
|
else if (typeof seg === "symbol")
|
|
@@ -89743,13 +89807,13 @@ function resolveRef(ref, ctx) {
|
|
|
89743
89807
|
if (!ref.startsWith("#")) {
|
|
89744
89808
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
89745
89809
|
}
|
|
89746
|
-
const
|
|
89747
|
-
if (
|
|
89810
|
+
const path14 = ref.slice(1).split("/").filter(Boolean);
|
|
89811
|
+
if (path14.length === 0) {
|
|
89748
89812
|
return ctx.rootSchema;
|
|
89749
89813
|
}
|
|
89750
89814
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
89751
|
-
if (
|
|
89752
|
-
const key =
|
|
89815
|
+
if (path14[0] === defsKey) {
|
|
89816
|
+
const key = path14[1];
|
|
89753
89817
|
if (!key || !ctx.defs[key]) {
|
|
89754
89818
|
throw new Error(`Reference not found: ${ref}`);
|
|
89755
89819
|
}
|
|
@@ -90158,7 +90222,7 @@ var sharedSchema = exports_external.object({
|
|
|
90158
90222
|
headed: exports_external.boolean().default(false),
|
|
90159
90223
|
json: exports_external.boolean().default(false),
|
|
90160
90224
|
lang: defaultedString("lang", "en"),
|
|
90161
|
-
userDataDir: defaultedString("userDataDir", defaultUserDataDir()).transform((value) =>
|
|
90225
|
+
userDataDir: defaultedString("userDataDir", defaultUserDataDir()).transform((value) => path14.resolve(value))
|
|
90162
90226
|
});
|
|
90163
90227
|
var searchSchema = sharedSchema.extend({
|
|
90164
90228
|
num: positiveInteger("num").default(3),
|
|
@@ -90222,6 +90286,10 @@ function createLoginArgs() {
|
|
|
90222
90286
|
}
|
|
90223
90287
|
};
|
|
90224
90288
|
}
|
|
90289
|
+
function printCommandError(error48) {
|
|
90290
|
+
const message = error48 instanceof Error ? error48.message : String(error48);
|
|
90291
|
+
console.error(`ERROR: ${message}`);
|
|
90292
|
+
}
|
|
90225
90293
|
async function runLogin(args) {
|
|
90226
90294
|
const options = normalizeLoginOptions({
|
|
90227
90295
|
gl: typeof args.gl === "string" ? args.gl : undefined,
|
|
@@ -90235,11 +90303,16 @@ function createLoginCommand(commandName = "findweb login") {
|
|
|
90235
90303
|
meta: { name: commandName, description: "Open a reusable Google sign-in session." },
|
|
90236
90304
|
args: createLoginArgs(),
|
|
90237
90305
|
async run({ args }) {
|
|
90238
|
-
|
|
90239
|
-
|
|
90240
|
-
|
|
90241
|
-
|
|
90242
|
-
|
|
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
|
+
}
|
|
90243
90316
|
}
|
|
90244
90317
|
});
|
|
90245
90318
|
}
|
|
@@ -90249,8 +90322,8 @@ import process4 from "process";
|
|
|
90249
90322
|
|
|
90250
90323
|
// src/search/blocker.ts
|
|
90251
90324
|
import fs8 from "fs/promises";
|
|
90252
|
-
import
|
|
90253
|
-
import
|
|
90325
|
+
import os10 from "os";
|
|
90326
|
+
import path15 from "path";
|
|
90254
90327
|
|
|
90255
90328
|
// node_modules/tldts-core/dist/es6/src/domain.js
|
|
90256
90329
|
function shareSameDomainSuffix(hostname3, vhost) {
|
|
@@ -99514,8 +99587,8 @@ class FilterEngine extends EventEmitter5 {
|
|
|
99514
99587
|
if (caching === undefined) {
|
|
99515
99588
|
return init();
|
|
99516
99589
|
}
|
|
99517
|
-
const { path:
|
|
99518
|
-
return read(
|
|
99590
|
+
const { path: path15, read, write } = caching;
|
|
99591
|
+
return read(path15).then((buffer) => this.deserialize(buffer)).catch(() => init().then((engine) => write(path15, engine.serialize()).then(() => engine)));
|
|
99519
99592
|
}
|
|
99520
99593
|
static empty(config3 = {}) {
|
|
99521
99594
|
return new this({ config: config3 });
|
|
@@ -101307,13 +101380,13 @@ class PuppeteerBlocker extends FilterEngine {
|
|
|
101307
101380
|
// src/search/blocker.ts
|
|
101308
101381
|
var blockerPromise;
|
|
101309
101382
|
function defaultCacheDir() {
|
|
101310
|
-
return process.env.GOOGLE_SEARCH_CACHE_DIR ??
|
|
101383
|
+
return process.env.GOOGLE_SEARCH_CACHE_DIR ?? path15.join(os10.homedir(), ".cache", "google-search");
|
|
101311
101384
|
}
|
|
101312
101385
|
async function readCache(filePath) {
|
|
101313
101386
|
return fs8.readFile(filePath);
|
|
101314
101387
|
}
|
|
101315
101388
|
async function writeCache(filePath, buffer) {
|
|
101316
|
-
await fs8.mkdir(
|
|
101389
|
+
await fs8.mkdir(path15.dirname(filePath), { recursive: true });
|
|
101317
101390
|
await fs8.writeFile(filePath, buffer);
|
|
101318
101391
|
}
|
|
101319
101392
|
async function loadBlocker() {
|
|
@@ -101322,7 +101395,7 @@ async function loadBlocker() {
|
|
|
101322
101395
|
const cacheDir = defaultCacheDir();
|
|
101323
101396
|
await fs8.mkdir(cacheDir, { recursive: true });
|
|
101324
101397
|
return PuppeteerBlocker.fromPrebuiltAdsAndTracking(globalThis.fetch.bind(globalThis), {
|
|
101325
|
-
path:
|
|
101398
|
+
path: path15.join(cacheDir, "ghostery-engine.bin"),
|
|
101326
101399
|
read: readCache,
|
|
101327
101400
|
write: writeCache
|
|
101328
101401
|
});
|
|
@@ -101425,6 +101498,10 @@ var searchArgs = {
|
|
|
101425
101498
|
description: "Print JSON output"
|
|
101426
101499
|
}
|
|
101427
101500
|
};
|
|
101501
|
+
function printCommandError2(error49) {
|
|
101502
|
+
const message = error49 instanceof Error ? error49.message : String(error49);
|
|
101503
|
+
console.error(`ERROR: ${message}`);
|
|
101504
|
+
}
|
|
101428
101505
|
async function runSearch(args) {
|
|
101429
101506
|
const options = normalizeSearchOptions({
|
|
101430
101507
|
_: args._,
|
|
@@ -101473,7 +101550,12 @@ function createSearchCommand(commandName = "findweb") {
|
|
|
101473
101550
|
},
|
|
101474
101551
|
args: searchArgs,
|
|
101475
101552
|
async run({ args }) {
|
|
101476
|
-
|
|
101553
|
+
try {
|
|
101554
|
+
await runSearch(args);
|
|
101555
|
+
} catch (error49) {
|
|
101556
|
+
printCommandError2(error49);
|
|
101557
|
+
process4.exitCode = 1;
|
|
101558
|
+
}
|
|
101477
101559
|
}
|
|
101478
101560
|
});
|
|
101479
101561
|
}
|
|
@@ -101553,4 +101635,10 @@ async function main() {
|
|
|
101553
101635
|
}
|
|
101554
101636
|
await runMain(createSearchCommand(), { rawArgs: action.rawArgs });
|
|
101555
101637
|
}
|
|
101556
|
-
|
|
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
|
+
}
|