Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/browser/cdp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ export class CDPBridge {
private _eventListeners = new Map<string, Set<(params: unknown) => void>>();

async connect(opts?: { timeout?: number; workspace?: string }): Promise<IPage> {
if (this._ws) throw new Error('CDPBridge is already connected. Call close() before reconnecting.');

const endpoint = process.env.OPENCLI_CDP_ENDPOINT;
if (!endpoint) throw new Error('OPENCLI_CDP_ENDPOINT is not set');

Expand Down
7 changes: 4 additions & 3 deletions src/daemon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,14 @@ function readBody(req: IncomingMessage): Promise<string> {
return new Promise((resolve, reject) => {
const chunks: Buffer[] = [];
let size = 0;
let aborted = false;
req.on('data', (c: Buffer) => {
size += c.length;
if (size > MAX_BODY) { req.destroy(); reject(new Error('Body too large')); return; }
if (size > MAX_BODY) { aborted = true; req.destroy(); reject(new Error('Body too large')); return; }
chunks.push(c);
});
req.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')));
req.on('error', reject);
req.on('end', () => { if (!aborted) resolve(Buffer.concat(chunks).toString('utf-8')); });
req.on('error', (err) => { if (!aborted) reject(err); });
});
}

Expand Down
7 changes: 5 additions & 2 deletions src/execution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,14 @@ async function runCommand(
}
// After loading, the module's cli() call will have updated the registry.
const updated = getRegistry().get(fullName(cmd));
if (updated?.func) return updated.func(page!, kwargs, debug);
if (updated?.func) {
if (!page) throw new CommandExecutionError(`Command ${fullName(cmd)} requires a browser session but none was provided`);
return updated.func(page, kwargs, debug);
}
if (updated?.pipeline) return executePipeline(page, updated.pipeline, { args: kwargs, debug });
}

if (cmd.func) return cmd.func(page!, kwargs, debug);
if (cmd.func) return cmd.func(page as IPage, kwargs, debug);
if (cmd.pipeline) return executePipeline(page, cmd.pipeline, { args: kwargs, debug });
throw new CommandExecutionError(
`Command ${fullName(cmd)} has no func or pipeline`,
Expand Down
7 changes: 4 additions & 3 deletions src/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,10 @@ function selectCandidate(candidates: SynthesizeResult['candidates'], goal?: stri
}

const lower = (goal ?? '').trim().toLowerCase();
const partial = candidates.find(c =>
c.name?.toLowerCase().includes(lower) || lower.includes(c.name?.toLowerCase())
);
const partial = candidates.find(c => {
const cName = c.name?.toLowerCase() ?? '';
return cName.includes(lower) || lower.includes(cName);
});
return partial ?? candidates[0];
}

Expand Down
9 changes: 6 additions & 3 deletions src/interceptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,18 @@ export function generateInterceptorJs(
const arr = opts.arrayName ?? '__opencli_intercepted';
const guard = opts.patchGuard ?? '__opencli_interceptor_patched';

// Store the current pattern in a separate global so it can be updated
// without re-patching fetch/XHR (the patchGuard only prevents double-patching).
const patternVar = `${guard}_pattern`;

return `
() => {
window.${arr} = window.${arr} || [];
window.${arr}_errors = window.${arr}_errors || [];
const __pattern = ${patternExpr};
window.${patternVar} = ${patternExpr};
const __checkMatch = (url) => window.${patternVar} && url.includes(window.${patternVar});

if (!window.${guard}) {
const __checkMatch = (url) => __pattern && url.includes(__pattern);

// ── Patch fetch ──
const __origFetch = window.fetch;
window.fetch = async function(...args) {
Expand Down
4 changes: 2 additions & 2 deletions src/record.ts
Original file line number Diff line number Diff line change
Expand Up @@ -397,10 +397,9 @@ export async function recordSession(opts: RecordOptions): Promise<RecordResult>
const stop = () => { stopped = true; };

const { promise: enterPromise, cleanup: cleanupEnter } = waitForEnter();
const enterRace = enterPromise.then(stop);
enterPromise.then(stop);
const timeoutPromise = new Promise<void>(r => setTimeout(() => {
stop();
cleanupEnter(); // close readline to prevent process from hanging
r();
}, timeoutMs));

Expand Down Expand Up @@ -428,6 +427,7 @@ export async function recordSession(opts: RecordOptions): Promise<RecordResult>
}, pollMs);

await Promise.race([enterPromise, timeoutPromise]);
cleanupEnter(); // Always clean up readline to prevent process from hanging
clearInterval(pollInterval);

// Final drain from all known tabs
Expand Down
Loading