@apmantza/greedysearch-pi 1.0.13 → 1.0.15
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/cdp.mjs +10 -0
- package/coding-task.mjs +18 -3
- package/launch.mjs +199 -199
- package/package.json +1 -1
- package/search.mjs +38 -6
package/cdp.mjs
CHANGED
|
@@ -38,6 +38,16 @@ function getDevToolsActivePortPath() {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
function getWsUrl() {
|
|
41
|
+
// If CDP_PROFILE_DIR is set (by search.mjs), prefer that profile's port file
|
|
42
|
+
// so GreedySearch targets its own Chrome, not the user's main session.
|
|
43
|
+
const profileDir = process.env.CDP_PROFILE_DIR;
|
|
44
|
+
if (profileDir) {
|
|
45
|
+
const p = profileDir.replace(/\\/g, '/') + '/DevToolsActivePort';
|
|
46
|
+
if (existsSync(p)) {
|
|
47
|
+
const lines = readFileSync(p, 'utf8').trim().split('\n');
|
|
48
|
+
return `ws://127.0.0.1:${lines[0]}${lines[1]}`;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
41
51
|
const portFile = getDevToolsActivePortPath();
|
|
42
52
|
const lines = readFileSync(portFile, 'utf8').trim().split('\n');
|
|
43
53
|
return `ws://127.0.0.1:${lines[0]}${lines[1]}`;
|
package/coding-task.mjs
CHANGED
|
@@ -281,7 +281,8 @@ async function main() {
|
|
|
281
281
|
'',
|
|
282
282
|
'Examples:',
|
|
283
283
|
' node coding-task.mjs "write a debounce function in JS" --engine gemini',
|
|
284
|
-
' node coding-task.mjs "review this module" --mode review --engine all --
|
|
284
|
+
' node coding-task.mjs "review this module" --mode review --engine all --file src/myfile.mjs',
|
|
285
|
+
' node coding-task.mjs "debug this" --mode debug --engine all --file a.mjs --file b.mjs',
|
|
285
286
|
' node coding-task.mjs "I want to build X, here is my plan: ..." --mode plan --engine all',
|
|
286
287
|
].join('\n') + '\n');
|
|
287
288
|
process.exit(1);
|
|
@@ -290,7 +291,6 @@ async function main() {
|
|
|
290
291
|
const engineFlagIdx = args.indexOf('--engine');
|
|
291
292
|
const engineArg = engineFlagIdx !== -1 ? args[engineFlagIdx + 1] : 'gemini';
|
|
292
293
|
const contextFlagIdx = args.indexOf('--context');
|
|
293
|
-
const context = contextFlagIdx !== -1 ? args[contextFlagIdx + 1] : null;
|
|
294
294
|
const outIdx = args.indexOf('--out');
|
|
295
295
|
const outFile = outIdx !== -1 ? args[outIdx + 1] : null;
|
|
296
296
|
const tabFlagIdx = args.indexOf('--tab');
|
|
@@ -299,16 +299,31 @@ async function main() {
|
|
|
299
299
|
const mode = modeFlagIdx !== -1 ? args[modeFlagIdx + 1] : 'code';
|
|
300
300
|
|
|
301
301
|
if (!MODE_PROMPTS.hasOwnProperty(mode)) {
|
|
302
|
-
process.stderr.write(`Error: unknown mode "${mode}". Use: code, review, plan\n`);
|
|
302
|
+
process.stderr.write(`Error: unknown mode "${mode}". Use: code, review, plan, test, debug\n`);
|
|
303
303
|
process.exit(1);
|
|
304
304
|
}
|
|
305
305
|
|
|
306
|
+
// --file can be repeated: --file a.mjs --file b.mjs
|
|
307
|
+
const fileIndices = [];
|
|
308
|
+
const filePaths = [];
|
|
309
|
+
for (let i = 0; i < args.length; i++) {
|
|
310
|
+
if (args[i] === '--file' && args[i + 1]) {
|
|
311
|
+
fileIndices.push(i, i + 1);
|
|
312
|
+
filePaths.push(args[i + 1]);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
const fileContext = filePaths.length > 0
|
|
316
|
+
? filePaths.map(p => `// FILE: ${p}\n${readFileSync(p, 'utf8')}`).join('\n\n')
|
|
317
|
+
: null;
|
|
318
|
+
const context = fileContext || (contextFlagIdx !== -1 ? args[contextFlagIdx + 1] : null);
|
|
319
|
+
|
|
306
320
|
const skipFlags = new Set([
|
|
307
321
|
...(engineFlagIdx >= 0 ? [engineFlagIdx, engineFlagIdx + 1] : []),
|
|
308
322
|
...(contextFlagIdx >= 0 ? [contextFlagIdx, contextFlagIdx + 1] : []),
|
|
309
323
|
...(outIdx >= 0 ? [outIdx, outIdx + 1] : []),
|
|
310
324
|
...(tabFlagIdx >= 0 ? [tabFlagIdx, tabFlagIdx + 1] : []),
|
|
311
325
|
...(modeFlagIdx >= 0 ? [modeFlagIdx, modeFlagIdx + 1] : []),
|
|
326
|
+
...fileIndices,
|
|
312
327
|
]);
|
|
313
328
|
const task = args.filter((_, i) => !skipFlags.has(i)).join(' ');
|
|
314
329
|
|
package/launch.mjs
CHANGED
|
@@ -1,199 +1,199 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// launch.mjs — start a dedicated Chrome instance for GreedySearch
|
|
3
|
-
//
|
|
4
|
-
// This Chrome instance uses --disable-features=DevToolsPrivacyUI which suppresses
|
|
5
|
-
// the "Allow remote debugging?" dialog entirely. It runs on port
|
|
6
|
-
// conflict with your main Chrome session.
|
|
7
|
-
//
|
|
8
|
-
// On launch, it overwrites the DevToolsActivePort file that cdp.mjs reads so all
|
|
9
|
-
// extractors automatically target the GreedySearch Chrome, with no code changes.
|
|
10
|
-
// The original file is restored on --kill.
|
|
11
|
-
//
|
|
12
|
-
// Usage:
|
|
13
|
-
// node launch.mjs — launch (or report if already running)
|
|
14
|
-
// node launch.mjs --kill — stop and restore original DevToolsActivePort
|
|
15
|
-
// node launch.mjs --status — check if running
|
|
16
|
-
|
|
17
|
-
import { spawn } from 'child_process';
|
|
18
|
-
import { existsSync, writeFileSync, readFileSync, copyFileSync, mkdirSync, unlinkSync } from 'fs';
|
|
19
|
-
import { tmpdir, homedir, platform } from 'os';
|
|
20
|
-
import { join } from 'path';
|
|
21
|
-
import http from 'http';
|
|
22
|
-
|
|
23
|
-
const PORT =
|
|
24
|
-
const PROFILE_DIR = join(tmpdir(), 'greedysearch-chrome-profile');
|
|
25
|
-
const ACTIVE_PORT = join(PROFILE_DIR, 'DevToolsActivePort');
|
|
26
|
-
const PID_FILE = join(tmpdir(), 'greedysearch-chrome.pid');
|
|
27
|
-
|
|
28
|
-
function findChrome() {
|
|
29
|
-
const os = platform();
|
|
30
|
-
const candidates = os === 'win32' ? [
|
|
31
|
-
'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe',
|
|
32
|
-
'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe',
|
|
33
|
-
] : os === 'darwin' ? [
|
|
34
|
-
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
|
|
35
|
-
'/Applications/Chromium.app/Contents/MacOS/Chromium',
|
|
36
|
-
] : [
|
|
37
|
-
'/usr/bin/google-chrome',
|
|
38
|
-
'/usr/bin/google-chrome-stable',
|
|
39
|
-
'/usr/bin/chromium-browser',
|
|
40
|
-
'/usr/bin/chromium',
|
|
41
|
-
'/snap/bin/chromium',
|
|
42
|
-
];
|
|
43
|
-
return candidates.find(existsSync) || null;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function systemPortPath() {
|
|
47
|
-
const os = platform();
|
|
48
|
-
if (os === 'win32') return join(homedir(), 'AppData', 'Local', 'Google', 'Chrome', 'User Data', 'DevToolsActivePort');
|
|
49
|
-
if (os === 'darwin') return join(homedir(), 'Library', 'Application Support', 'Google', 'Chrome', 'DevToolsActivePort');
|
|
50
|
-
return join(homedir(), '.config', 'google-chrome', 'DevToolsActivePort');
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const SYSTEM_PORT = systemPortPath();
|
|
54
|
-
const SYSTEM_BACKUP = SYSTEM_PORT + '.bak';
|
|
55
|
-
|
|
56
|
-
const CHROME_FLAGS = [
|
|
57
|
-
`--remote-debugging-port=${PORT}`,
|
|
58
|
-
'--disable-features=DevToolsPrivacyUI', // suppresses "Allow remote debugging?" dialog
|
|
59
|
-
'--no-first-run',
|
|
60
|
-
'--no-default-browser-check',
|
|
61
|
-
'--disable-default-apps',
|
|
62
|
-
`--user-data-dir=${PROFILE_DIR}`,
|
|
63
|
-
'--profile-directory=Default',
|
|
64
|
-
'about:blank',
|
|
65
|
-
];
|
|
66
|
-
|
|
67
|
-
// ---------------------------------------------------------------------------
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
function isRunning() {
|
|
71
|
-
if (!existsSync(PID_FILE)) return false;
|
|
72
|
-
const pid = parseInt(readFileSync(PID_FILE, 'utf8').trim());
|
|
73
|
-
if (!pid) return false;
|
|
74
|
-
try { process.kill(pid, 0); return pid; } catch { return false; }
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function httpGet(url, timeoutMs = 1000) {
|
|
78
|
-
return new Promise(resolve => {
|
|
79
|
-
const req = http.get(url, res => {
|
|
80
|
-
let body = '';
|
|
81
|
-
res.on('data', d => body += d);
|
|
82
|
-
res.on('end', () => resolve({ ok: res.statusCode === 200, body }));
|
|
83
|
-
});
|
|
84
|
-
req.on('error', () => resolve({ ok: false }));
|
|
85
|
-
req.setTimeout(timeoutMs, () => { req.destroy(); resolve({ ok: false }); });
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
async function writePortFile(timeoutMs = 15000) {
|
|
91
|
-
// Chrome on Windows doesn't write DevToolsActivePort — we build it from the HTTP API.
|
|
92
|
-
const deadline = Date.now() + timeoutMs;
|
|
93
|
-
while (Date.now() < deadline) {
|
|
94
|
-
const { ok, body } = await httpGet(`http://localhost:${PORT}/json/version`, 1500);
|
|
95
|
-
if (ok) {
|
|
96
|
-
try {
|
|
97
|
-
const { webSocketDebuggerUrl } = JSON.parse(body);
|
|
98
|
-
// webSocketDebuggerUrl = "ws://localhost:9223/devtools/browser/..."
|
|
99
|
-
const wsPath = new URL(webSocketDebuggerUrl).pathname;
|
|
100
|
-
// Write in DevToolsActivePort format: port on line 1, path on line 2
|
|
101
|
-
const content = `${PORT}\n${wsPath}`;
|
|
102
|
-
writeFileSync(ACTIVE_PORT, content, 'utf8');
|
|
103
|
-
return true;
|
|
104
|
-
} catch { /* malformed response, retry */ }
|
|
105
|
-
}
|
|
106
|
-
await new Promise(r => setTimeout(r, 400));
|
|
107
|
-
}
|
|
108
|
-
return false;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
function redirectCdpToGreedySearch() {
|
|
112
|
-
// Back up system DevToolsActivePort (user's main Chrome)
|
|
113
|
-
if (existsSync(SYSTEM_PORT) && !existsSync(SYSTEM_BACKUP)) {
|
|
114
|
-
copyFileSync(SYSTEM_PORT, SYSTEM_BACKUP);
|
|
115
|
-
}
|
|
116
|
-
// Point cdp.mjs to our dedicated Chrome's port
|
|
117
|
-
copyFileSync(ACTIVE_PORT, SYSTEM_PORT);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
function restoreCdpToMainChrome() {
|
|
121
|
-
if (existsSync(SYSTEM_BACKUP)) {
|
|
122
|
-
copyFileSync(SYSTEM_BACKUP, SYSTEM_PORT);
|
|
123
|
-
console.log('Restored DevToolsActivePort to main Chrome.');
|
|
124
|
-
} else if (existsSync(SYSTEM_PORT)) {
|
|
125
|
-
// No backup means main Chrome wasn't using CDP — remove our file
|
|
126
|
-
try { unlinkSync(SYSTEM_PORT); } catch {}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// ---------------------------------------------------------------------------
|
|
131
|
-
|
|
132
|
-
async function main() {
|
|
133
|
-
const arg = process.argv[2];
|
|
134
|
-
|
|
135
|
-
if (arg === '--kill') {
|
|
136
|
-
const pid = isRunning();
|
|
137
|
-
if (pid) {
|
|
138
|
-
try { process.kill(pid, 'SIGTERM'); console.log(`Stopped Chrome (pid ${pid}).`); }
|
|
139
|
-
catch (e) { console.error(`Failed: ${e.message}`); }
|
|
140
|
-
} else {
|
|
141
|
-
console.log('GreedySearch Chrome is not running.');
|
|
142
|
-
}
|
|
143
|
-
restoreCdpToMainChrome();
|
|
144
|
-
return;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
if (arg === '--status') {
|
|
148
|
-
const pid = isRunning();
|
|
149
|
-
if (pid) console.log(`Running — pid ${pid}, port ${PORT}, DevToolsActivePort redirected.`);
|
|
150
|
-
else console.log('Not running.');
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// Already running?
|
|
155
|
-
const existing = isRunning();
|
|
156
|
-
if (existing) {
|
|
157
|
-
const ready = await writePortFile(5000);
|
|
158
|
-
if (ready) {
|
|
159
|
-
console.log(`GreedySearch Chrome already running (pid ${existing}, port ${PORT}).`);
|
|
160
|
-
redirectCdpToGreedySearch();
|
|
161
|
-
console.log('DevToolsActivePort redirected.');
|
|
162
|
-
return;
|
|
163
|
-
}
|
|
164
|
-
// Stale PID — process alive but not Chrome on port 9223. Fall through to fresh launch.
|
|
165
|
-
console.log(`Stale PID ${existing} detected (not Chrome on port ${PORT}) — launching fresh.`);
|
|
166
|
-
try { unlinkSync(PID_FILE); } catch {}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
const CHROME_EXE = process.env.CHROME_PATH || findChrome();
|
|
170
|
-
if (!CHROME_EXE) {
|
|
171
|
-
console.error('Chrome not found. Tried standard paths for your OS.');
|
|
172
|
-
console.error('Set the CHROME_PATH environment variable to point to your Chrome binary.');
|
|
173
|
-
process.exit(1);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
mkdirSync(PROFILE_DIR, { recursive: true });
|
|
177
|
-
|
|
178
|
-
console.log(`Launching GreedySearch Chrome on port ${PORT}...`);
|
|
179
|
-
const proc = spawn(CHROME_EXE, CHROME_FLAGS, {
|
|
180
|
-
detached: true,
|
|
181
|
-
stdio: 'ignore',
|
|
182
|
-
windowsHide: false,
|
|
183
|
-
});
|
|
184
|
-
proc.unref();
|
|
185
|
-
writeFileSync(PID_FILE, String(proc.pid));
|
|
186
|
-
|
|
187
|
-
// Wait for Chrome HTTP endpoint, build DevToolsActivePort file, redirect cdp.mjs
|
|
188
|
-
const portFileReady = await writePortFile();
|
|
189
|
-
if (!portFileReady) {
|
|
190
|
-
console.error('Chrome did not become ready within 15s.');
|
|
191
|
-
process.exit(1);
|
|
192
|
-
}
|
|
193
|
-
redirectCdpToGreedySearch();
|
|
194
|
-
|
|
195
|
-
console.log(`Ready. No more "Allow remote debugging?" dialogs.`);
|
|
196
|
-
console.log(`Run "node launch.mjs --kill" when done to restore your main Chrome's CDP.`);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
main();
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// launch.mjs — start a dedicated Chrome instance for GreedySearch
|
|
3
|
+
//
|
|
4
|
+
// This Chrome instance uses --disable-features=DevToolsPrivacyUI which suppresses
|
|
5
|
+
// the "Allow remote debugging?" dialog entirely. It runs on port 9222 so it doesn't
|
|
6
|
+
// conflict with your main Chrome session (which may use port 9223).
|
|
7
|
+
//
|
|
8
|
+
// On launch, it overwrites the DevToolsActivePort file that cdp.mjs reads so all
|
|
9
|
+
// extractors automatically target the GreedySearch Chrome, with no code changes.
|
|
10
|
+
// The original file is restored on --kill.
|
|
11
|
+
//
|
|
12
|
+
// Usage:
|
|
13
|
+
// node launch.mjs — launch (or report if already running)
|
|
14
|
+
// node launch.mjs --kill — stop and restore original DevToolsActivePort
|
|
15
|
+
// node launch.mjs --status — check if running
|
|
16
|
+
|
|
17
|
+
import { spawn } from 'child_process';
|
|
18
|
+
import { existsSync, writeFileSync, readFileSync, copyFileSync, mkdirSync, unlinkSync } from 'fs';
|
|
19
|
+
import { tmpdir, homedir, platform } from 'os';
|
|
20
|
+
import { join } from 'path';
|
|
21
|
+
import http from 'http';
|
|
22
|
+
|
|
23
|
+
const PORT = 9222;
|
|
24
|
+
const PROFILE_DIR = join(tmpdir(), 'greedysearch-chrome-profile');
|
|
25
|
+
const ACTIVE_PORT = join(PROFILE_DIR, 'DevToolsActivePort');
|
|
26
|
+
const PID_FILE = join(tmpdir(), 'greedysearch-chrome.pid');
|
|
27
|
+
|
|
28
|
+
function findChrome() {
|
|
29
|
+
const os = platform();
|
|
30
|
+
const candidates = os === 'win32' ? [
|
|
31
|
+
'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe',
|
|
32
|
+
'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe',
|
|
33
|
+
] : os === 'darwin' ? [
|
|
34
|
+
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
|
|
35
|
+
'/Applications/Chromium.app/Contents/MacOS/Chromium',
|
|
36
|
+
] : [
|
|
37
|
+
'/usr/bin/google-chrome',
|
|
38
|
+
'/usr/bin/google-chrome-stable',
|
|
39
|
+
'/usr/bin/chromium-browser',
|
|
40
|
+
'/usr/bin/chromium',
|
|
41
|
+
'/snap/bin/chromium',
|
|
42
|
+
];
|
|
43
|
+
return candidates.find(existsSync) || null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function systemPortPath() {
|
|
47
|
+
const os = platform();
|
|
48
|
+
if (os === 'win32') return join(homedir(), 'AppData', 'Local', 'Google', 'Chrome', 'User Data', 'DevToolsActivePort');
|
|
49
|
+
if (os === 'darwin') return join(homedir(), 'Library', 'Application Support', 'Google', 'Chrome', 'DevToolsActivePort');
|
|
50
|
+
return join(homedir(), '.config', 'google-chrome', 'DevToolsActivePort');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const SYSTEM_PORT = systemPortPath();
|
|
54
|
+
const SYSTEM_BACKUP = SYSTEM_PORT + '.bak';
|
|
55
|
+
|
|
56
|
+
const CHROME_FLAGS = [
|
|
57
|
+
`--remote-debugging-port=${PORT}`,
|
|
58
|
+
'--disable-features=DevToolsPrivacyUI', // suppresses "Allow remote debugging?" dialog
|
|
59
|
+
'--no-first-run',
|
|
60
|
+
'--no-default-browser-check',
|
|
61
|
+
'--disable-default-apps',
|
|
62
|
+
`--user-data-dir=${PROFILE_DIR}`,
|
|
63
|
+
'--profile-directory=Default',
|
|
64
|
+
'about:blank',
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
function isRunning() {
|
|
71
|
+
if (!existsSync(PID_FILE)) return false;
|
|
72
|
+
const pid = parseInt(readFileSync(PID_FILE, 'utf8').trim());
|
|
73
|
+
if (!pid) return false;
|
|
74
|
+
try { process.kill(pid, 0); return pid; } catch { return false; }
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function httpGet(url, timeoutMs = 1000) {
|
|
78
|
+
return new Promise(resolve => {
|
|
79
|
+
const req = http.get(url, res => {
|
|
80
|
+
let body = '';
|
|
81
|
+
res.on('data', d => body += d);
|
|
82
|
+
res.on('end', () => resolve({ ok: res.statusCode === 200, body }));
|
|
83
|
+
});
|
|
84
|
+
req.on('error', () => resolve({ ok: false }));
|
|
85
|
+
req.setTimeout(timeoutMs, () => { req.destroy(); resolve({ ok: false }); });
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
async function writePortFile(timeoutMs = 15000) {
|
|
91
|
+
// Chrome on Windows doesn't write DevToolsActivePort — we build it from the HTTP API.
|
|
92
|
+
const deadline = Date.now() + timeoutMs;
|
|
93
|
+
while (Date.now() < deadline) {
|
|
94
|
+
const { ok, body } = await httpGet(`http://localhost:${PORT}/json/version`, 1500);
|
|
95
|
+
if (ok) {
|
|
96
|
+
try {
|
|
97
|
+
const { webSocketDebuggerUrl } = JSON.parse(body);
|
|
98
|
+
// webSocketDebuggerUrl = "ws://localhost:9223/devtools/browser/..."
|
|
99
|
+
const wsPath = new URL(webSocketDebuggerUrl).pathname;
|
|
100
|
+
// Write in DevToolsActivePort format: port on line 1, path on line 2
|
|
101
|
+
const content = `${PORT}\n${wsPath}`;
|
|
102
|
+
writeFileSync(ACTIVE_PORT, content, 'utf8');
|
|
103
|
+
return true;
|
|
104
|
+
} catch { /* malformed response, retry */ }
|
|
105
|
+
}
|
|
106
|
+
await new Promise(r => setTimeout(r, 400));
|
|
107
|
+
}
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function redirectCdpToGreedySearch() {
|
|
112
|
+
// Back up system DevToolsActivePort (user's main Chrome)
|
|
113
|
+
if (existsSync(SYSTEM_PORT) && !existsSync(SYSTEM_BACKUP)) {
|
|
114
|
+
copyFileSync(SYSTEM_PORT, SYSTEM_BACKUP);
|
|
115
|
+
}
|
|
116
|
+
// Point cdp.mjs to our dedicated Chrome's port
|
|
117
|
+
copyFileSync(ACTIVE_PORT, SYSTEM_PORT);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function restoreCdpToMainChrome() {
|
|
121
|
+
if (existsSync(SYSTEM_BACKUP)) {
|
|
122
|
+
copyFileSync(SYSTEM_BACKUP, SYSTEM_PORT);
|
|
123
|
+
console.log('Restored DevToolsActivePort to main Chrome.');
|
|
124
|
+
} else if (existsSync(SYSTEM_PORT)) {
|
|
125
|
+
// No backup means main Chrome wasn't using CDP — remove our file
|
|
126
|
+
try { unlinkSync(SYSTEM_PORT); } catch {}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ---------------------------------------------------------------------------
|
|
131
|
+
|
|
132
|
+
async function main() {
|
|
133
|
+
const arg = process.argv[2];
|
|
134
|
+
|
|
135
|
+
if (arg === '--kill') {
|
|
136
|
+
const pid = isRunning();
|
|
137
|
+
if (pid) {
|
|
138
|
+
try { process.kill(pid, 'SIGTERM'); console.log(`Stopped Chrome (pid ${pid}).`); }
|
|
139
|
+
catch (e) { console.error(`Failed: ${e.message}`); }
|
|
140
|
+
} else {
|
|
141
|
+
console.log('GreedySearch Chrome is not running.');
|
|
142
|
+
}
|
|
143
|
+
restoreCdpToMainChrome();
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (arg === '--status') {
|
|
148
|
+
const pid = isRunning();
|
|
149
|
+
if (pid) console.log(`Running — pid ${pid}, port ${PORT}, DevToolsActivePort redirected.`);
|
|
150
|
+
else console.log('Not running.');
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Already running?
|
|
155
|
+
const existing = isRunning();
|
|
156
|
+
if (existing) {
|
|
157
|
+
const ready = await writePortFile(5000);
|
|
158
|
+
if (ready) {
|
|
159
|
+
console.log(`GreedySearch Chrome already running (pid ${existing}, port ${PORT}).`);
|
|
160
|
+
redirectCdpToGreedySearch();
|
|
161
|
+
console.log('DevToolsActivePort redirected.');
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
// Stale PID — process alive but not Chrome on port 9223. Fall through to fresh launch.
|
|
165
|
+
console.log(`Stale PID ${existing} detected (not Chrome on port ${PORT}) — launching fresh.`);
|
|
166
|
+
try { unlinkSync(PID_FILE); } catch {}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const CHROME_EXE = process.env.CHROME_PATH || findChrome();
|
|
170
|
+
if (!CHROME_EXE) {
|
|
171
|
+
console.error('Chrome not found. Tried standard paths for your OS.');
|
|
172
|
+
console.error('Set the CHROME_PATH environment variable to point to your Chrome binary.');
|
|
173
|
+
process.exit(1);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
mkdirSync(PROFILE_DIR, { recursive: true });
|
|
177
|
+
|
|
178
|
+
console.log(`Launching GreedySearch Chrome on port ${PORT}...`);
|
|
179
|
+
const proc = spawn(CHROME_EXE, CHROME_FLAGS, {
|
|
180
|
+
detached: true,
|
|
181
|
+
stdio: 'ignore',
|
|
182
|
+
windowsHide: false,
|
|
183
|
+
});
|
|
184
|
+
proc.unref();
|
|
185
|
+
writeFileSync(PID_FILE, String(proc.pid));
|
|
186
|
+
|
|
187
|
+
// Wait for Chrome HTTP endpoint, build DevToolsActivePort file, redirect cdp.mjs
|
|
188
|
+
const portFileReady = await writePortFile();
|
|
189
|
+
if (!portFileReady) {
|
|
190
|
+
console.error('Chrome did not become ready within 15s.');
|
|
191
|
+
process.exit(1);
|
|
192
|
+
}
|
|
193
|
+
redirectCdpToGreedySearch();
|
|
194
|
+
|
|
195
|
+
console.log(`Ready. No more "Allow remote debugging?" dialogs.`);
|
|
196
|
+
console.log(`Run "node launch.mjs --kill" when done to restore your main Chrome's CDP.`);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
main();
|
package/package.json
CHANGED
package/search.mjs
CHANGED
|
@@ -23,13 +23,15 @@ import { spawn } from 'child_process';
|
|
|
23
23
|
import { fileURLToPath } from 'url';
|
|
24
24
|
import { join, dirname } from 'path';
|
|
25
25
|
import { readFileSync, existsSync, writeFileSync } from 'fs';
|
|
26
|
-
import { tmpdir } from 'os';
|
|
26
|
+
import { tmpdir, homedir } from 'os';
|
|
27
27
|
import http from 'http';
|
|
28
28
|
|
|
29
29
|
const __dir = dirname(fileURLToPath(import.meta.url));
|
|
30
|
-
const CDP = join(
|
|
30
|
+
const CDP = join(homedir(), '.claude', 'skills', 'chrome-cdp', 'scripts', 'cdp.mjs');
|
|
31
31
|
const PAGES_CACHE = `${tmpdir().replace(/\\/g, '/')}/cdp-pages.json`;
|
|
32
32
|
|
|
33
|
+
const GREEDY_PORT = 9222;
|
|
34
|
+
|
|
33
35
|
const ENGINES = {
|
|
34
36
|
perplexity: 'perplexity.mjs',
|
|
35
37
|
pplx: 'perplexity.mjs',
|
|
@@ -182,9 +184,16 @@ function writeOutput(data, outFile) {
|
|
|
182
184
|
}
|
|
183
185
|
}
|
|
184
186
|
|
|
185
|
-
|
|
187
|
+
const GREEDY_PROFILE_DIR = `${tmpdir().replace(/\\/g, '/')}/greedysearch-chrome-profile`;
|
|
188
|
+
const ACTIVE_PORT_FILE = `${GREEDY_PROFILE_DIR}/DevToolsActivePort`;
|
|
189
|
+
|
|
190
|
+
// Tell cdp.mjs to prefer the GreedySearch Chrome profile's DevToolsActivePort,
|
|
191
|
+
// so searches never accidentally attach to the user's main Chrome session.
|
|
192
|
+
process.env.CDP_PROFILE_DIR = GREEDY_PROFILE_DIR;
|
|
193
|
+
|
|
194
|
+
function probeGreedyChrome(timeoutMs = 3000) {
|
|
186
195
|
return new Promise(resolve => {
|
|
187
|
-
const req = http.get(
|
|
196
|
+
const req = http.get(`http://localhost:${GREEDY_PORT}/json/version`, res => {
|
|
188
197
|
res.resume();
|
|
189
198
|
resolve(res.statusCode === 200);
|
|
190
199
|
});
|
|
@@ -193,14 +202,37 @@ function probePort9223(timeoutMs = 3000) {
|
|
|
193
202
|
});
|
|
194
203
|
}
|
|
195
204
|
|
|
205
|
+
// Write (or refresh) the DevToolsActivePort file for the GreedySearch Chrome so
|
|
206
|
+
// cdp.mjs always connects to the right port rather than the user's main Chrome.
|
|
207
|
+
async function refreshPortFile() {
|
|
208
|
+
try {
|
|
209
|
+
const body = await new Promise((res, rej) => {
|
|
210
|
+
const req = http.get(`http://localhost:${GREEDY_PORT}/json/version`, r => {
|
|
211
|
+
let b = '';
|
|
212
|
+
r.on('data', d => b += d);
|
|
213
|
+
r.on('end', () => res(b));
|
|
214
|
+
});
|
|
215
|
+
req.on('error', rej);
|
|
216
|
+
req.setTimeout(3000, () => { req.destroy(); rej(new Error('timeout')); });
|
|
217
|
+
});
|
|
218
|
+
const { webSocketDebuggerUrl } = JSON.parse(body);
|
|
219
|
+
const wsPath = new URL(webSocketDebuggerUrl).pathname;
|
|
220
|
+
writeFileSync(ACTIVE_PORT_FILE, `${GREEDY_PORT}\n${wsPath}`, 'utf8');
|
|
221
|
+
} catch { /* best-effort — launch.mjs already wrote the file on first start */ }
|
|
222
|
+
}
|
|
223
|
+
|
|
196
224
|
async function ensureChrome() {
|
|
197
|
-
const ready = await
|
|
225
|
+
const ready = await probeGreedyChrome();
|
|
198
226
|
if (!ready) {
|
|
199
|
-
process.stderr.write(
|
|
227
|
+
process.stderr.write(`GreedySearch Chrome not running on port ${GREEDY_PORT} — auto-launching...\n`);
|
|
200
228
|
await new Promise((resolve, reject) => {
|
|
201
229
|
const proc = spawn('node', [join(__dir, 'launch.mjs')], { stdio: ['ignore', process.stderr, process.stderr] });
|
|
202
230
|
proc.on('close', code => code === 0 ? resolve() : reject(new Error('launch.mjs failed')));
|
|
203
231
|
});
|
|
232
|
+
} else {
|
|
233
|
+
// Chrome already running — refresh the port file so cdp.mjs always picks
|
|
234
|
+
// up the right port, even if the file was stale from a previous session.
|
|
235
|
+
await refreshPortFile();
|
|
204
236
|
}
|
|
205
237
|
}
|
|
206
238
|
|