Skip to content

Commit a5abd37

Browse files
deepziyujackwener
andauthored
fix(launcher): graceful degradation and manual CDP override for Windows (#744)
* fix(windows): graceful degradation and manual CDP override for Electron apps * fix: validate OPENCLI_CDP_ENDPOINT with probeCDP before use Fail-fast with a clear error if the manual CDP endpoint is not reachable, instead of passing a bad URL downstream and getting a confusing error. --------- Co-authored-by: jackwener <jakevingoo@gmail.com>
1 parent 8070960 commit a5abd37

2 files changed

Lines changed: 28 additions & 4 deletions

File tree

src/execution.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import { emitHook, type HookContext } from './hooks.js';
2121
import { checkDaemonStatus } from './browser/discover.js';
2222
import { log } from './logger.js';
2323
import { isElectronApp } from './electron-apps.js';
24-
import { resolveElectronEndpoint } from './launcher.js';
24+
import { probeCDP, resolveElectronEndpoint } from './launcher.js';
2525

2626
const _loadedModules = new Set<string>();
2727

@@ -158,8 +158,20 @@ export async function executeCommand(
158158
let cdpEndpoint: string | undefined;
159159

160160
if (electron) {
161-
// Electron apps: auto-detect, prompt restart if needed, launch with CDP
162-
cdpEndpoint = await resolveElectronEndpoint(cmd.site);
161+
// Electron apps: respect manual endpoint override, then try auto-detect
162+
const manualEndpoint = process.env.OPENCLI_CDP_ENDPOINT;
163+
if (manualEndpoint) {
164+
const port = Number(new URL(manualEndpoint).port);
165+
if (!await probeCDP(port)) {
166+
throw new CommandExecutionError(
167+
`CDP not reachable at ${manualEndpoint}`,
168+
'Check that the app is running with --remote-debugging-port and the endpoint is correct.',
169+
);
170+
}
171+
cdpEndpoint = manualEndpoint;
172+
} else {
173+
cdpEndpoint = await resolveElectronEndpoint(cmd.site);
174+
}
163175
} else {
164176
// Browser Bridge: fail-fast when daemon is up but extension is missing.
165177
// 300ms timeout avoids a full 2s wait on cold-start.

src/launcher.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export function probeCDP(port: number, timeoutMs: number = PROBE_TIMEOUT_MS): Pr
4646
* Uses pgrep on macOS/Linux.
4747
*/
4848
export function detectProcess(processName: string): boolean {
49+
if (process.platform === 'win32') return false; // pgrep not available on Windows
4950
try {
5051
execFileSync('pgrep', ['-x', processName], { encoding: 'utf-8', stdio: 'pipe' });
5152
return true;
@@ -58,6 +59,7 @@ export function detectProcess(processName: string): boolean {
5859
* Kill a process by name. Sends SIGTERM first, then SIGKILL after grace period.
5960
*/
6061
export function killProcess(processName: string): void {
62+
if (process.platform === 'win32') return; // pkill not available on Windows
6163
try {
6264
execFileSync('pkill', ['-x', processName], { stdio: 'pipe' });
6365
} catch {
@@ -138,7 +140,17 @@ export async function resolveElectronEndpoint(site: string): Promise<string> {
138140
return endpoint;
139141
}
140142

141-
// Step 2: Running without CDP?
143+
// Step 2: Running without CDP? (process detection requires Unix tools)
144+
if (process.platform !== 'darwin' && process.platform !== 'linux') {
145+
throw new CommandExecutionError(
146+
`${label} is not reachable on CDP port ${port}.`,
147+
`Auto-launch is not yet supported on ${process.platform}.\n` +
148+
`Start ${label} manually with --remote-debugging-port=${port}, then either:\n` +
149+
` • Set OPENCLI_CDP_ENDPOINT=http://127.0.0.1:${port}\n` +
150+
` • Or just re-run the command once ${label} is listening on port ${port}.`,
151+
);
152+
}
153+
142154
const isRunning = detectProcess(processName);
143155
if (isRunning) {
144156
log.debug(`[launcher] ${label} is running but CDP not available`);

0 commit comments

Comments
 (0)