@apmantza/greedysearch-pi 1.8.6 → 1.8.8

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/CHANGELOG.md CHANGED
@@ -1,5 +1,88 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.8.8] — 2026-05-09
4
+
5
+ ### Added
6
+
7
+ - **`/set-greedy-locale` Pi command** (`index.ts`) — Set default locale for search results (e.g., `/set-greedy-locale de`, `/set-greedy-locale --clear`, `/set-greedy-locale --show`). Saves to `~/.config/greedysearch/config.json`.
8
+ - **Browser lifecycle defense patterns** (`src/search/browser-lifecycle.mjs`, new) — Centralized lifecycle management adopted from open-websearch's robust cross-process browser patterns:
9
+ - **Structured JSON metadata** (`greedysearch-chrome-metadata.json`) replaces three scattered text files (PID, mode, activity) with a single file tracking `browserPid`, `debugPort`, `tempDir`, `clientPids[]`, `sessionMode`, `lastActivity`, `launchedAt`. Backward-compatible — legacy files still written.
10
+ - **Process command-line verification** — `verifyBrowserProcess()` checks not just PID alive but that the process command line contains the profile dir and debug port. Prevents PID collision false-positives where a different process reuses the same PID.
11
+ - **Cross-process launch lock** — `acquireLaunchLock()` uses exclusive-create (`wx` flag) to prevent concurrent `ensureChrome()` calls from racing to launch Chrome. Stale lock recovery after 15s.
12
+ - **Stale session cleanup** — `cleanupStaleSessions()` runs once per process on first `ensureChrome()`. Scans metadata for dead PIDs, verifies survivors via command line, force-kills orphans, reclaims ghost processes on port 9222.
13
+ - **Client PID tracking** — `registerClient`/`unregisterClient` track which processes share the Chrome instance.
14
+ - **Mode-specific idle timeouts** (`src/search/chrome.mjs`) — Headless Chrome keeps the aggressive 5-minute idle timeout (`GREEDY_SEARCH_IDLE_TIMEOUT_MINUTES`) since it's cheap to restart. Visible Chrome (explicitly launched for captcha/cookie setup) gets a 60-minute grace period (`GREEDY_SEARCH_VISIBLE_IDLE_TIMEOUT_MINUTES`) to avoid wasting the user's captcha investment. Set either to 0 to disable for that mode.
15
+
16
+ ### Added
17
+
18
+ - **System command path resolution** (`src/utils/system-cmds.mjs`, new) — `resolveSystemCmd()` resolves `powershell`, `netstat`, `taskkill`, `ps`, `lsof`, `ss`, `grep` to absolute paths for secure execution. `isPathSafe()` validates PATH environment variable composition. Satisfies SonarCloud security hotspot requirements for `execFileSync`/`execSync` PATH safety.
19
+
20
+ ### Fixed
21
+
22
+ - **SonarCloud security hotspots — 15 resolved** — Addressed all flagged items:
23
+ - **11 ReDoS-prone regex patterns**: Replaced greedy `.{0,50}` in fetcher's content quality check with lazy quantifier `.{0,50}?`; replaced alternation-heavy split regex in bing-copilot with `[^\S\n]*` horizontal whitespace; replaced `[\s\S]*` JSON extraction patterns in synthesis.mjs with `indexOf`/`lastIndexOf` brace matching; replaced `.+?\.` in selectors with `[^.]+`; replaced `\s+\S*$` trim patterns in sources.mjs, common.mjs, and content.mjs with `lastIndexOf` word-boundary detection; replaced markdown link regex in common.mjs with O(n) indexOf-based parser.
24
+ - **4 PATH-injection hotspots in browser-lifecycle.mjs and chrome.mjs**: Created `resolveSystemCmd()` utility returning absolute paths for `powershell.exe`, `netstat.exe`, `taskkill.exe` (Windows) and `/usr/bin/ps`, `/usr/bin/lsof`, `/usr/sbin/ss`, `/usr/bin/grep` (Unix). Replaced all bare command names in `execFileSync`/`execSync` calls.
25
+
26
+ - **SonarCloud minor vulnerability false positives** — Confirmed both remaining issues are false positives (internal diagnostic logging in `bin/gschrome.mjs` and test debug output in `test/fetcher-cli.mjs`). Verified via full smoke test suite: all 33 unit tests pass, all 4 engines (Perplexity, Bing, Google, Gemini) return results at all depths (fast/standard/deep), CDP safety wrappers correctly enforce mode boundaries.
27
+
28
+ - **SonarCloud security hotspots** (re-verified) — All previously fixed hotspots remain resolved: replaced `spawn("node", ...)` with `spawn(process.execPath, ...)`, replaced `Math.random()` with `crypto.randomInt()`, 19 remaining hotspots confirmed as false positives (hardcoded `execSync` commands, simple regex patterns).
29
+
30
+ ### Fixed
31
+
32
+ - **Headless→visible mode switching** (`src/search/chrome.mjs`) — `ensureChrome()` only handled the case where visible was requested but headless Chrome was running. When headless was requested (the default) but visible Chrome was running, it silently kept visible mode — causing env var mismatches that broke extractors like Perplexity. Now properly detects both directions and kills/relaunches in the correct mode.
33
+
34
+ - **SonarCloud security hotspots** — Replaced `spawn("node", ...)` with `spawn(process.execPath, ...)` in cdp wrapper, `runExtractor`, `synthesizeWithGemini`, and test helper to prevent PATH-based binary substitution. Replaced `Math.random()` with `crypto.randomInt()` in `jitter()` for non-security-sensitive timing variance. Remaining 19 hotspots are verified false positives (hardcoded `execSync` commands, simple regex patterns).
35
+ - **Bing stealth not active on page load** (`src/search/chrome.mjs`) — `injectHeadlessStealth` was fire-and-forget (`.catch(() => {})`). The CDP `Page.addScriptToEvaluateOnNewDocument` command is async — extractors often navigated to Copilot before stealth registered. Cloudflare saw headless fingerprints and blocked the page. Fixed by awaiting stealth for Bing tabs. Perplexity/Google kept fire-and-forget since Perplexity's anti-bot detects the awaited patches.
36
+ - **Bing copy button handler not hydrated** (`extractors/bing-copilot.mjs`) — Copilot's React copy button exists in the DOM before its click handler is bound. `clickCopyAndPollClipboard` clicked too early → clipboard interceptor empty → 13s wasted polling + DOM fallback. Added 800ms hydration delay after `waitForCopyButton`. Solo Bing went from 37-73s → 16s.
37
+ - **Manual verification blocked synthesis** (`bin/search.mjs`) — When Bing/Perplexity needed manual verification after visible recovery, `search.mjs` returned early with `synthesize: false`, discarding all engine results. Now synthesis continues with whichever engines succeeded. Visible Chrome stays open for the user.
38
+ - **Source-fetch crash after visible→headless recovery** (`src/search/fetch-source.mjs`) — After recovery killed/restarted Chrome, stale CDP tab references in parallel source-fetch workers caused "No target matching prefix" crashes. Workers now catch `fetchSourceContent` errors; `fetchSourceContentBrowser` returns error objects instead of throwing.
39
+ - **Progress tracker "🔄 synthesizing" hang** (`src/tools/shared.ts`) — When synthesis was skipped (manual verification), the progress tracker showed "🔄 synthesizing" forever because no `PROGRESS:synthesis:done` was ever emitted. Now handles `done`/`error`/`skipped` synthesis states.
40
+ - **Gemini synthesis eval timeout** (`bin/cdp.mjs`) — CDP daemon `TIMEOUT` was 30s, but `waitForStreamComplete` uses a single `Runtime.evaluate` call that can run 60-90s for long synthesis prompts. Increased to 90s.
41
+
42
+ ### Performance
43
+
44
+ - **Reduced timeouts across all extractors** — Navigation: 35s→20s, verification retry: 30s→10s (Bing/Perplexity), 60s→10s (Gemini/Google), post-nav settle: 1200ms→600ms (Bing), 1200ms→600ms (Gemini). Turnstile never clears in headless, so 30s of retry loops were pure waste.
45
+ - **Hard per-engine timeouts raised** (`bin/search.mjs`) — Fast: 22s→30s, Standard/Deep: 35s→55s. CDP contention from 3 parallel extractors adds overhead that the old budgets didn't account for.
46
+ - **Tab creation split: Bing gets blank+stealth, others pre-seeded** (`src/search/chrome.mjs`) — `Target.createTarget` navigation is less detectable than CDP `Page.navigate` for Perplexity/Google. Bing needs blank tab + awaited stealth to hide headless fingerprints from Copilot's Cloudflare.
47
+
48
+ ### Performance
49
+
50
+ - **Hard per-engine timeouts** (`bin/search.mjs`) — Fast mode: 22s per engine. Standard/deep: 35s per engine. Slow engines are skipped instead of stalling the whole batch. Previously a single slow engine could push `all` searches to 60–90s.
51
+ - **Parallel tab creation** (`bin/search.mjs`, `src/search/chrome.mjs`) — All engine tabs open simultaneously instead of sequential 300ms staggered delays. Tabs are pre-seeded to each engine's homepage so extractors skip redundant initial navigation.
52
+ - **Reduced settle delays** (`extractors/common.mjs`) — `postNav` 1500→800ms, `postNavSlow` 2000→1200ms, `postClick`/`postType` 400→300ms, `afterVerify` 3000→1500ms. Safe because tabs now load the target domain before the extractor even starts.
53
+ - **Higher source-fetch concurrency** (`src/search/constants.mjs`) — Default `GREEDY_FETCH_CONCURRENCY` raised from 2 → 4.
54
+ - **Faster HTTP timeouts** (`src/search/fetch-source.mjs`) — HTTP fetch timeout 15s → 10s, browser fallback settle 1500ms → 800ms.
55
+ - **Non-blocking cleanup** (`bin/search.mjs`) — Removed the 1500ms hard sleep at process exit; `minimizeChrome` now fire-and-forget.
56
+ - **Domain-aware navigation skip** (`extractors/bing-copilot.mjs`, `extractors/perplexity.mjs`, `extractors/google-ai.mjs`) — When a tab is already on the engine's domain (pre-seeded by orchestrator), skip the redundant `cdp nav` call and settle delay.
57
+ - **Fast mode keeps short engine budgets** (`bin/search.mjs`) — Fast mode still uses 22s per-engine extraction timeouts and skips source fetch/synthesis work. Verification recovery can now run in fast mode when Bing/Perplexity are blocked, because returning no result is worse than the retry cost.
58
+
59
+ ### Anti-Bot Detection Hardening (Anti-CDP Evasion)
60
+
61
+ - **Runtime.enable evasion** (`bin/cdp.mjs`) — The primary CDP detection vector (Cloudflare/DataDome watch for `Runtime.consoleAPICalled` timing) has been eliminated. All `Runtime.evaluate` calls now use an explicit `contextId` captured via brief `Runtime.enable` → `Runtime.disable` at daemon startup (~100ms window). No persistent Runtime domain enable for the session. See: rebrowser.net / DataDome research.
62
+ - **Stale PID / ghost Chrome cleanup** (`src/search/chrome.mjs`) — `killChrome()` now uses port-based process detection via `netstat`/`lsof` instead of relying solely on the PID file. Handles ghost processes that hold port 9222 after the tracked PID dies. Old `killHeadlessChrome` kept as backward-compat alias.
63
+ - **Idle cleanup for both modes** (`src/search/chrome.mjs`) — `checkAndKillIdle()` no longer gates on `GREEDY_SEARCH_HEADLESS=1`. Both headless and visible Chrome auto-kill after idle timeout. Disable with `GREEDY_SEARCH_IDLE_TIMEOUT_MINUTES=0`.
64
+ - **`--disable-blink-features=AutomationControlled` for visible mode** (`bin/launch.mjs`, `bin/gschrome.mjs`) — Previously headless-only. The flag and `--window-size` now apply to both modes, suppressing `navigator.webdriver` in visible Chrome too.
65
+ - **Stealth injection for visible mode** (`src/search/chrome.mjs`, `extractors/common.mjs`) — Canvas noise, plugin spoofing, `window.chrome.runtime`, and console safening now inject on both headless and visible tabs.
66
+ - **Client Hints consistency** (`src/fetcher.mjs`) — Added `Sec-CH-UA`, `Sec-CH-UA-Mobile`, `Sec-CH-UA-Platform` headers to `DEFAULT_HEADERS`, matching the Chrome 122 user-agent. Inconsistency between UA and Client Hints is a strong bot signal.
67
+ - **Perplexity Cloudflare verification** (`extractors/perplexity.mjs`) — Added `handleVerification` call after navigation. Perplexity was the only engine missing Cloudflare challenge handling (Bing, Gemini, Google AI already had it).
68
+ - **Chrome TLS fetch fallback** (`src/search/fetch-source.mjs`) — New `fetchSourceViaChrome()` uses `Network.loadNetworkResource` (Chrome 124+) to fetch with authentic Chrome TLS/JA3+HTTP/2 fingerprints when Node.js HTTP fails. Zero navigation overhead.
69
+
70
+ ### Added
71
+
72
+ - **`visible` / `alwaysVisible` search options** (`src/tools/greedy-search-handler.ts`, `src/tools/shared.ts`, `bin/search.mjs`) — Agents can now force visible Chrome per call with `visible: true`, `alwaysVisible: true`, or `headless: false`. CLI aliases: `--visible`, `--always-visible`. Global env: `GREEDY_SEARCH_ALWAYS_VISIBLE=1`.
73
+ - **GreedySearch Chrome commands for Pi** (`index.ts`) — Added `/greedy-visible`, `/greedy-status`, and `/greedy-kill` so users do not need to know package install paths to manage the dedicated Chrome instance.
74
+ - **Safe CDP wrappers** (`bin/cdp-greedy.mjs`, `bin/cdp-visible.mjs`, `bin/cdp-headless.mjs`) — Agents can inspect only the dedicated GreedySearch Chrome profile. The wrappers always set `CDP_PROFILE_DIR` and mode-specific wrappers refuse to attach to the wrong mode, preventing accidental main-Chrome pollution.
75
+ - **`bin/kill-visible.mjs`** — Strong visible/port cleanup helper backed by `launch-visible.mjs`'s PID + port nuke path.
76
+ - **`bin/gschrome.mjs`** — Standalone Chrome lifecycle manager: `launch-headless`, `launch-visible`, `kill`, `status`. Port-based PID detection, forces mode switches, writes `DevToolsActivePort` for CDP.
77
+
78
+ ### Fixed
79
+
80
+ - **Single-engine visible recovery** (`bin/search.mjs`) — `engine: "bing"` and `engine: "perplexity"` now perform the same headless → visible retry as `engine: "all"` when blocked by Cloudflare, captcha, timeout, missing input, or clipboard failures.
81
+ - **Bing visible clipboard race** (`extractors/bing-copilot.mjs`) — Waits for the assistant copy button, polls clipboard interception after click, retries copy/poll, then falls back to visible DOM text. Fixes cases where Copilot visibly answered but the extractor returned `Clipboard interceptor returned empty text`.
82
+ - **Manual verification flow** (`bin/search.mjs`, `src/formatters/results.ts`) — If visible retry reaches a human verification challenge, GreedySearch leaves visible Chrome open and returns a clear “solve verification, then rerun” result instead of killing the browser and returning no results.
83
+ - **Visible/headless process cleanup** (`bin/launch.mjs`, `bin/visible.mjs`) — Fixed Windows `taskkill` arguments, added port fallback cleanup for `--kill`, and made `visible.mjs --kill` delegate to the stronger `launch-visible.mjs` cleanup path.
84
+ - **README install paths and skill guidance** (`README.md`, `skills/greedy-search/skill.md`) — Corrected Pi git/npm package paths, documented visible mode and safe CDP wrappers, and removed stale `coding_task` guidance from the agent skill.
85
+
3
86
  ## [1.8.6] — 2026-05-04
4
87
 
5
88
  ### Bing Copilot: Headless Cloudflare Recovery
package/README.md CHANGED
@@ -37,8 +37,8 @@ greedy_search({
37
37
  engine: "all",
38
38
  depth: "deep",
39
39
  });
40
- // Headless is the default — no window. To see the browser:
41
- // Set GREEDY_SEARCH_VISIBLE=1 before launching Pi
40
+ // Headless is the default — no window. To force visible Chrome:
41
+ greedy_search({ query: "Bing captcha setup", engine: "bing", visible: true });
42
42
  ```
43
43
 
44
44
  ## Parameters (`greedy_search`)
@@ -48,15 +48,17 @@ greedy_search({
48
48
  - `depth`: `standard` (default), `fast`, `deep`
49
49
  - `fullAnswer`: return full single-engine output instead of preview
50
50
  - `headless`: set to `false` to show Chrome window (default: `true`)
51
+ - `visible` / `alwaysVisible`: set to `true` to always use visible Chrome for this search
51
52
 
52
53
  ## Environment variables
53
54
 
54
- | Variable | Default | Description |
55
- | ------------------------------------ | ------------- | --------------------------------------------------------- |
56
- | `GREEDY_SEARCH_VISIBLE` | (unset) | Set to `1` to show Chrome window instead of headless |
57
- | `GREEDY_SEARCH_IDLE_TIMEOUT_MINUTES` | `5` | Minutes of inactivity before auto-killing headless Chrome |
58
- | `GREEDY_SEARCH_LOCALE` | `en` | Default result language (en, de, fr, es, ja, etc.) |
59
- | `CHROME_PATH` | auto-detected | Path to Chrome/Chromium executable |
55
+ | Variable | Default | Description |
56
+ | ------------------------------------ | ------------- | ------------------------------------------------------------- |
57
+ | `GREEDY_SEARCH_VISIBLE` | (unset) | Set to `1` to show Chrome window instead of headless |
58
+ | `GREEDY_SEARCH_ALWAYS_VISIBLE` | (unset) | Set to `1` to force visible Chrome for all GreedySearch runs |
59
+ | `GREEDY_SEARCH_IDLE_TIMEOUT_MINUTES` | `5` | Minutes of inactivity before auto-killing GreedySearch Chrome |
60
+ | `GREEDY_SEARCH_LOCALE` | `en` | Default result language (en, de, fr, es, ja, etc.) |
61
+ | `CHROME_PATH` | auto-detected | Path to Chrome/Chromium executable |
60
62
 
61
63
  ## Depth modes
62
64
 
@@ -66,23 +68,49 @@ greedy_search({
66
68
 
67
69
  ## Runtime commands
68
70
 
69
- ````bash
70
- # Headless (default, no GUI)
71
- node ~/.pi/agent/git/GreedySearch-pi/bin/launch.mjs
72
- node ~/.pi/agent/git/GreedySearch-pi/bin/launch.mjs --status
73
- node ~/.pi/agent/git/GreedySearch-pi/bin/launch.mjs --kill
71
+ Inside Pi, prefer the extension commands (no package path needed):
74
72
 
75
- # Visible (show browser window — useful for one-time Cloudflare clearance)
76
- node ~/.pi/agent/git/GreedySearch-pi/bin/launch-visible.mjs
77
- node ~/.pi/agent/git/GreedySearch-pi/bin/launch-visible.mjs --kill
73
+ ```text
74
+ /greedy-visible # launch visible Chrome for captcha/login/cookie setup
75
+ /greedy-status # show GreedySearch Chrome status
76
+ /greedy-kill # stop GreedySearch Chrome
77
+ /set-greedy-locale # set default result language (de, fr, es, ja, etc.)
78
+ ```
79
+
80
+ Git install path:
81
+
82
+ ```bash
83
+ GS=~/.pi/agent/git/github.com/apmantza/GreedySearch-pi
84
+ node "$GS/bin/launch.mjs" --status
85
+ node "$GS/bin/visible.mjs" # visible mode
86
+ node "$GS/bin/visible.mjs" --kill # strong visible/port cleanup
87
+ node "$GS/bin/kill-visible.mjs" # same as visible.mjs --kill
88
+ node "$GS/bin/cdp-visible.mjs" list # safe CDP: GreedySearch visible Chrome only
89
+ node "$GS/bin/cdp-headless.mjs" list # safe CDP: GreedySearch headless Chrome only
90
+ node "$GS/bin/cdp-greedy.mjs" list # safe CDP: any GreedySearch Chrome mode
91
+ ```
92
+
93
+ npm global install path:
78
94
 
79
- # Chrome auto-cleaned after 5 min idle (prevents OOM)
80
- # Override: GREEDY_SEARCH_IDLE_TIMEOUT_MINUTES=10
95
+ ```bash
96
+ GS="$(npm root -g)/@apmantza/greedysearch-pi"
97
+ node "$GS/bin/launch.mjs" --status
98
+ node "$GS/bin/visible.mjs"
99
+ node "$GS/bin/visible.mjs" --kill
100
+ node "$GS/bin/kill-visible.mjs"
101
+ node "$GS/bin/cdp-visible.mjs" list
102
+ node "$GS/bin/cdp-headless.mjs" list
103
+ node "$GS/bin/cdp-greedy.mjs" list
104
+ ```
105
+
106
+ Chrome is auto-cleaned after 5 min idle. Override with `GREEDY_SEARCH_IDLE_TIMEOUT_MINUTES=10` or disable with `0`.
107
+
108
+ **CDP safety:** use `cdp-visible.mjs`, `cdp-headless.mjs`, or `cdp-greedy.mjs` for debugging. They always set `CDP_PROFILE_DIR` to the dedicated GreedySearch Chrome profile and never fall back to your main Chrome session. Avoid calling raw `bin/cdp.mjs` manually unless you explicitly set `CDP_PROFILE_DIR`.
81
109
 
82
110
  ## Requirements
83
111
 
84
112
  - Chrome
85
- - Node.js 20.11.0+ (22+ recommended)
113
+ - Node.js 22+
86
114
 
87
115
  ## Known engine quirks
88
116
 
@@ -90,13 +118,9 @@ node ~/.pi/agent/git/GreedySearch-pi/bin/launch-visible.mjs --kill
90
118
 
91
119
  Bing Copilot detects headless Chrome and sandboxes all AI responses inside nested iframes (`copilot.microsoft.com` → `copilot.fun` → `blob:`). In this mode the copy button is hidden and the Cloudflare Turnstile challenge blocks content delivery. The clipboard-based extraction cannot work.
92
120
 
93
- **Auto-recovery:** When Bing fails with any extraction error (clipboard, verification, Cloudflare), GreedySearch automatically switches to **visible Chrome**, retries the search, and caches Cloudflare clearance cookies in the Chrome profile. You may need to solve the Cloudflare challenge **once** manually when the visible Chrome window appears. After that, all subsequent headless searches bypass the challenge the cookies persist in the profile.
94
-
95
- If you prefer to skip the auto-recovery delay, launch visible Chrome ahead of time:
121
+ **Auto-recovery:** When Bing or Perplexity fails with a headless-only extraction error (clipboard, verification, timeout, Cloudflare), GreedySearch automatically switches to **visible Chrome** and retries, even in `fast` mode. If manual verification is required, the visible browser is left open and the tool returns instructions to solve the challenge and rerun the same search.
96
122
 
97
- ```bash
98
- node ~/.pi/agent/git/GreedySearch-pi/bin/launch-visible.mjs
99
- ````
123
+ If you prefer to skip the auto-recovery delay, launch visible Chrome ahead of time with `/greedy-visible`, set `GREEDY_SEARCH_ALWAYS_VISIBLE=1`, or pass `visible: true` to `greedy_search`.
100
124
 
101
125
  ## Anti-detection
102
126
 
@@ -118,7 +142,7 @@ When using `depth: "standard"` or `depth: "deep"`, source content is fetched and
118
142
 
119
143
  ## Project layout
120
144
 
121
- - `bin/` — runtime CLIs (`search.mjs`, `launch.mjs`, `launch-visible.mjs`, `visible.mjs`, `cdp.mjs`)
145
+ - `bin/` — runtime CLIs (`search.mjs`, `launch.mjs`, `launch-visible.mjs`, `visible.mjs`, `kill-visible.mjs`, safe CDP wrappers, `cdp.mjs`)
122
146
  - `extractors/` — engine-specific automation + stealth/consent handling
123
147
  - `src/` — search pipeline, chrome management, source fetching, formatting
124
148
  - `skills/` — Pi skill metadata
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env node
2
+ // cdp-greedy.mjs — safe CDP wrapper for the dedicated GreedySearch Chrome.
3
+ //
4
+ // This ALWAYS sets CDP_PROFILE_DIR to the GreedySearch profile so it never
5
+ // falls back to the user's main Chrome DevToolsActivePort.
6
+ //
7
+ // Usage:
8
+ // node bin/cdp-greedy.mjs list
9
+ // node bin/cdp-greedy.mjs --mode visible list
10
+ // node bin/cdp-greedy.mjs --mode headless snap <tab>
11
+
12
+ import { spawn } from "node:child_process";
13
+ import { existsSync, readFileSync } from "node:fs";
14
+ import { tmpdir } from "node:os";
15
+
16
+ const tmp = tmpdir().replaceAll("\\", "/");
17
+ const PROFILE_DIR = `${tmp}/greedysearch-chrome-profile`;
18
+ const ACTIVE_PORT = `${PROFILE_DIR}/DevToolsActivePort`;
19
+ const MODE_FILE = `${tmp}/greedysearch-chrome-mode`;
20
+
21
+ const args = process.argv.slice(2);
22
+ let desiredMode = null;
23
+ const modeIdx = args.indexOf("--mode");
24
+ if (modeIdx !== -1) {
25
+ desiredMode = args[modeIdx + 1] || null;
26
+ args.splice(modeIdx, 2);
27
+ }
28
+
29
+ if (desiredMode && !["visible", "headless"].includes(desiredMode)) {
30
+ console.error(`Invalid --mode ${desiredMode}. Use visible or headless.`);
31
+ process.exit(2);
32
+ }
33
+
34
+ if (!existsSync(ACTIVE_PORT)) {
35
+ console.error(
36
+ `GreedySearch Chrome is not running (missing ${ACTIVE_PORT}). Launch with bin/visible.mjs or bin/launch.mjs.`,
37
+ );
38
+ process.exit(1);
39
+ }
40
+
41
+ if (desiredMode) {
42
+ const actualMode = existsSync(MODE_FILE)
43
+ ? readFileSync(MODE_FILE, "utf8").trim()
44
+ : "unknown";
45
+ if (actualMode !== desiredMode) {
46
+ console.error(
47
+ `GreedySearch Chrome is ${actualMode}, not ${desiredMode}. Refusing to attach.`,
48
+ );
49
+ process.exit(1);
50
+ }
51
+ }
52
+
53
+ const cdpBin = new URL("./cdp.mjs", import.meta.url).pathname.replace(
54
+ /^\/([A-Z]:)/,
55
+ "$1",
56
+ );
57
+
58
+ const proc = spawn(process.execPath, [cdpBin, ...args], {
59
+ stdio: "inherit",
60
+ env: { ...process.env, CDP_PROFILE_DIR: PROFILE_DIR },
61
+ });
62
+ proc.on("close", (code) => process.exit(code ?? 0));
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ // cdp-headless.mjs — safe CDP wrapper for headless GreedySearch Chrome only.
3
+
4
+ import { spawn } from "node:child_process";
5
+
6
+ const cdpGreedyBin = new URL(
7
+ "./cdp-greedy.mjs",
8
+ import.meta.url,
9
+ ).pathname.replace(/^\/([A-Z]:)/, "$1");
10
+
11
+ const proc = spawn(
12
+ process.execPath,
13
+ [cdpGreedyBin, "--mode", "headless", ...process.argv.slice(2)],
14
+ { stdio: "inherit" },
15
+ );
16
+ proc.on("close", (code) => process.exit(code ?? 0));
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ // cdp-visible.mjs — safe CDP wrapper for visible GreedySearch Chrome only.
3
+
4
+ import { spawn } from "node:child_process";
5
+
6
+ const cdpGreedyBin = new URL(
7
+ "./cdp-greedy.mjs",
8
+ import.meta.url,
9
+ ).pathname.replace(/^\/([A-Z]:)/, "$1");
10
+
11
+ const proc = spawn(
12
+ process.execPath,
13
+ [cdpGreedyBin, "--mode", "visible", ...process.argv.slice(2)],
14
+ { stdio: "inherit" },
15
+ );
16
+ proc.on("close", (code) => process.exit(code ?? 0));