-
Notifications
You must be signed in to change notification settings - Fork 210
Description
Description
All commands using Strategy.INTERCEPT (search, notifications, followers, following) fail to capture API responses. The XHR/fetch interceptor is never actually installed because the generated JavaScript is never executed.
Root Cause
1. Arrow function expression not wrapped as IIFE
installInterceptor() and getInterceptedRequests() in src/browser/page.ts:261-279 call sendCommand('exec', ...) directly, bypassing wrapForEval().
The generated JS from src/interceptor.ts is a bare arrow function expression (() => { ... }), not an IIFE. When CDP Runtime.evaluate receives a bare function expression, it evaluates it as a function object but never executes the function body. The monkey-patch is never installed.
In contrast, page.evaluate() wraps code through wrapForEval() (page.ts:290-304) which detects arrow functions and wraps them as IIFEs: (() => { ... })().
The YAML pipeline's intercept step (src/pipeline/steps/intercept.ts:19) correctly uses page.evaluate() and is NOT affected.
2. Regression introduced by daemon migration
Git history confirms this is a regression from commit b2fa7da ("replace @playwright/mcp with lightweight daemon + Chrome Extension"). Before that commit, installInterceptor used this.evaluate() (with wrapping). The migration changed it to direct sendCommand(), losing the wrapForEval wrapping.
| Commit | Method |
|---|---|
68840fc / cfad003 (Playwright era) |
this.evaluate(generateInterceptorJs(...)) — correct |
b2fa7da (daemon migration) and after |
sendCommand('exec', { code: ... }) — broken |
3. No wrapping anywhere in the chain
The daemon (daemon.ts:120) transparently forwards the command. The extension (background.ts:176) passes cmd.code directly to cdp.evaluateAsync(). CDP (cdp.ts:41) passes it directly to Runtime.evaluate. Zero wrapping at any layer.
4. Additional bug in followers.ts / following.ts
followers.ts:61 filters intercepted data with r?.url?.includes('Followers'), but getInterceptedRequests() returns response body JSON (from response.clone().json()), NOT request objects with a url field. Even after fixing the IIFE issue, this filter would discard all captured data. following.ts has the same problem.
Affected commands
twitter search(twitter search returns empty results (INTERCEPT strategy not catching SearchTimeline) #86, [Bug]: opencli twitter search --query "马斯克" 返回 (no data) #79)twitter notifications([Bug]: opencli twitter notifications #87)twitter followerstwitter following- Any future TS adapter using
Strategy.INTERCEPT
Suggested Fix
page.ts — change both methods to use this.evaluate():
async installInterceptor(pattern: string): Promise<void> {
const { generateInterceptorJs } = await import('../interceptor.js');
await this.evaluate(generateInterceptorJs(JSON.stringify(pattern), {
arrayName: '__opencli_xhr',
patchGuard: '__opencli_interceptor_patched',
}));
}
async getInterceptedRequests(): Promise<any[]> {
const { generateReadInterceptedJs } = await import('../interceptor.js');
const result = await this.evaluate(generateReadInterceptedJs('__opencli_xhr'));
return (result as any[]) || [];
}followers.ts / following.ts — remove the incorrect url.includes() filter (intercepted data is response JSON, not request objects).
Relationship to PR #91
PR #91 fixed a real timing issue (interceptor wiped by goto() navigation resetting JS context). That fix is necessary but insufficient — even with correct timing, the interceptor code was never being executed due to this IIFE bug. Both fixes are needed together.