Commit Graph

45 Commits

Author SHA1 Message Date
jakevin 812f27a05e chore(release): 1.5.5 (#565) 2026-03-28 22:19:59 +08:00
Cjy-CN f9857f8c7b fix: remove invalid state: 'normal' from chrome.windows.create() (#559)
* fix: remove invalid `state: 'normal'` from chrome.windows.create()

Chrome 146+ rejects 'normal' as an invalid value for the `state` parameter
in chrome.windows.create(). This causes the error:

    Error: Invalid value for state

Root cause analysis:
- The Chrome Extensions API documentation states that `state` parameter
  only accepts 'minimized', 'maximized', and 'fullscreen' as input values
- While WindowState enum includes 'normal', it's meant for reading window
  state, not for setting it during creation
- Chrome 146 enforces stricter validation on the `state` parameter
- When `state` is omitted, the window defaults to 'normal' state anyway

Fix: Remove the `state: 'normal'` parameter entirely. The window will
default to normal state without explicitly setting it.

Tested: `opencli doctor` and `opencli bilibili hot` now work correctly
on Chrome 146.0.7680.165.

* build: rebuild dist after removing state: 'normal'

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-28 21:25:31 +08:00
jakevin c9e29d9f22 chore(release): 1.5.4 (#558) 2026-03-28 20:22:24 +08:00
AstroHan 75ddb6319b fix(extension): probe daemon before WebSocket to eliminate console noise (#534)
* fix(extension): probe daemon via HTTP before WebSocket to eliminate console noise

When the daemon is offline, `new WebSocket()` logs uncatchable
ERR_CONNECTION_REFUSED errors to Chrome's extension error page.
Add `probeAndConnect()` that checks daemon reachability with a
silent `fetch(HEAD)` before attempting WebSocket connection.

All three auto-connect paths (initialize, keepalive alarm, eager
reconnect) now go through the probe, eliminating the error noise
entirely.

Closes #505

* refactor(extension): inline probe into connect(), add /ping to daemon

Instead of a separate probeAndConnect() wrapper that all call sites had
to remember to use, bake the HTTP probe directly into connect() itself.
This makes the guard impossible to accidentally skip when adding new
connection paths in the future.

Also adds a dedicated GET /ping endpoint to the daemon (no X-OpenCLI
header required) so the probe has a clear semantic contract instead of
relying on a 403 side-effect from the root path.

- daemon: GET /ping → 200 {ok:true}, no auth needed, placed before the
  X-OpenCLI header check; only chrome-extension:// and no-origin
  requests reach it (origin check is still enforced above)
- background: connect() is now async; probes /ping with a 1 s timeout
  before new WebSocket(); all call sites (initialize, keepalive alarm,
  scheduleReconnect) remain unchanged
- probeAndConnect() removed — no longer needed

* fix(extension/daemon): address review feedback on probe refactor

- protocol.ts: replace DAEMON_HTTP_URL with DAEMON_PING_URL (clearer
  semantics, single source of truth for the health-check URL)
- background.ts: import DAEMON_PING_URL from protocol instead of
  defining a local constant; check res.ok so an unexpected non-200
  response doesn't fall through to WebSocket; annotate all fire-and-
  forget connect() call sites with `void` to make intent explicit
- daemon.ts: add security comment on /ping documenting the timing
  side-channel tradeoff (loopback-only, accepted risk)

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-28 20:15:51 +08:00
jakevin 4f13484aa3 chore(release): 1.5.3 (#533) 2026-03-28 11:02:09 +08:00
AstroHan 5ab3f5d7d5 fix(extension): change automation window state from minimized to normal (#531)
chrome.windows.create rejects state:'minimized' when combined with
width/height (Chrome API constraint). Revert to state:'normal' to fix
the "Invalid value for state" error. The 30s idle timeout from #521
is preserved.

Fixes #526
2026-03-28 10:57:40 +08:00
jakevin 9919f03aed chore(release): 1.5.2 (#523)
* chore(release): 1.5.2

* test(e2e): stabilize output format checks
2026-03-28 00:48:27 +08:00
jakevin 6e90356649 chore(extension): bump version to 1.5.1 (#519) 2026-03-28 00:09:30 +08:00
AstroHan 0085d63fb8 fix(weread): resolve shelf auth fallback (#518)
* fix(weread): resolve shelf auth fallback

* chore(docs): move local issue notes out of pr

* fix(weread): classify session expiry as auth required

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-28 00:09:28 +08:00
jakevin a1561e5361 fix(extension): minimize automation window + reduce idle timeout to 30s (#521)
- Create automation window with `state: 'minimized'` so it never
  appears in the user's taskbar or steals visual attention
- Reduce idle timeout from 120s to 30s — window closes quickly after
  the last command finishes, instead of lingering for 2 minutes
- CDP debugger works fine on minimized windows, no functional impact

Fixes the user-visible issue of a blank data:text/html tab appearing
during command execution.
2026-03-28 00:03:44 +08:00
jakevin 7617dff262 feat: zero onboarding, extension version check, and update notifier (#479)
* feat: zero onboarding, extension version check, and update notifier

- Fail-fast guard in execution.ts: when daemon is running but extension
  is not connected, immediately surface a setup guide instead of waiting
  for the 30s connect timeout

- Extension version handshake: extension sends `hello` with its version
  on WebSocket connect; daemon stores it and exposes via /status; CLI
  warns on mismatch in both execution path and `opencli doctor`

- `opencli doctor` now shows extension version inline and reports
  version mismatch as an actionable issue

- Non-blocking npm update checker: registers a process exit hook so the
  update notice appears after command output (same pattern as npm/gh/yarn);
  background fetch writes to ~/.opencli/update-check.json for next run

- postinstall: print Browser Bridge setup instructions after shell
  completion install for first-time global install users

Bug fixes caught in review:
- discover.ts: add AbortController timeout to checkDaemonStatus() fetch,
  move clearTimeout after res.json() to cover body streaming
- daemon.ts: clear extensionVersion and reject pending requests in
  ws.on('error') handler, not just ws.on('close')
- update-check.ts: skip update notice when process exits with non-zero
  code; read cache once at module load to avoid double disk I/O;
  guard isNewer() against NaN from pre-release version strings

* fix: reduce fail-fast timeout to 300ms and guard stderr.write in exit hook
2026-03-27 02:14:37 +08:00
zzf fe87fc7b87 fix(extension): fail release packaging when manifest entry files are missing (#470) 2026-03-26 19:08:47 +08:00
Xeron ed89157804 fix(jd): filter avif images only from pcpubliccms CDN (#453)
* feat(jd): add item adapter for JD.com product details

Support fetching:
- Product title, price, shop
- Product specifications (品牌, 型号, 规格参数 etc.)
- Main product images
- Detail images from product page

Usage: opencli jd item <sku>

* fix(jd): update test to expect avifImages column

* review: tighten jd item image contract

* fix: stabilize extension packaging and Chinese-site e2e

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-26 15:12:41 +08:00
HzTTT 0705d38b40 fix(ci): include popup assets in extension release zip (#444)
* fix(ci): include popup assets in extension release

Copy popup assets into the packaged Chrome extension zip and validate that manifest-referenced files exist before publishing the artifact.

Co-authored-by: Codex <noreply@openai.com>

* fix: restore executable permission on bin entries after tsc build (#446) (#452)

tsc does not preserve the +x bit when compiling, so after clean-dist
removes dist/ and tsc regenerates it, dist/main.js loses its executable
permission. This causes 'Permission denied' when users run 'npm run build'
in the installed directory.

Fix: read bin entries from package.json at the end of build-manifest and
chmod 0o755 them (skipped on Windows). Wrapped in try/catch so it never
breaks the build.

Closes #446

* fix: correct positional arg usage in tests (#449)

* fix yahoo-finance quote e2e invocation

* fix positional args in v2ex topic tests

* fix(ci): script extension release packaging

---------

Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: jakevin <jakevingoo@gmail.com>
Co-authored-by: pi-dal <hi@pi-dal.com>
2026-03-26 12:27:57 +08:00
jakevin 2bfd3eeeb3 chore: release v1.4.1 (#425) 2026-03-25 16:21:14 +08:00
jakevin bf5f327775 fix(extension): improve UX when daemon is not running (#424)
- Show helpful hint in popup when disconnected: "This is normal. The
  extension connects automatically when you run any opencli command."
- Stop eager reconnect after 6 attempts (reaching 60s backoff) to
  reduce ERR_CONNECTION_REFUSED noise in console; keepalive alarm
  still retries every ~24s at low frequency.
2026-03-25 16:19:02 +08:00
jakevin 4812486482 feat(extension): add popup UI, privacy policy, and CSP for Chrome Web Store (#415)
- Add popup.html/popup.js showing daemon connection status
  (Connected / Reconnecting / No daemon connected)
- Add message listener in background.ts to expose WebSocket state
- Add PRIVACY.md with full privacy policy covering all permissions
- Add content_security_policy to manifest.json
- Update description to be clearer for CWS reviewers
2026-03-25 15:07:40 +08:00
jakevin 15369fa23c chore: release v1.4.0 (#413)
* chore: release v1.4.0

* docs: sync command references across SKILL.md, README, and docs

SKILL.md:
- Add 12 missing sites: apple-podcasts, arxiv, bloomberg, coupang,
  dictionary, doubao, jd, linkedin, pixiv, web, weixin, xiaoyuzhou
- Add 36 missing commands across 6 existing sites (twitter, hackernews,
  yollomi, xueqiu, linux-do, v2ex)

README (EN + zh-CN):
- Add linkedin timeline command

docs/:
- Add 13 missing adapters to vitepress sidebar navigation
- Add 6 missing adapters to docs/adapters/index.md overview table
- Update xueqiu commands with fund-holdings, fund-snapshot
2026-03-25 14:48:46 +08:00
jakevin 594ad50949 fix: pre-release cleanup — bugs, version sync, and error handling (#412)
* fix: pre-release cleanup — bugs, version sync, and error handling

Bug fixes:
- Fix hasLimit using wrong Set (SEARCH_PARAMS → LIMIT_PARAMS) in
  analysis.ts classifyQueryParams
- Remove phantom scroll step from BROWSER_STEPS and KNOWN_STEP_NAMES
  (declared but never registered, causes runtime crash if used in YAML)
- Add missing download step to KNOWN_STEP_NAMES (was producing
  false-positive validation warnings)

Docs:
- Sync version numbers: SKILL.md, extension/package.json,
  extension/manifest.json → 1.3.3
- Add jd, web to README command tables (both EN and zh-CN)
- Update xueqiu commands with fund-holdings, fund-snapshot

Code quality:
- Replace all 22 catch (err: any) with typed error handling using
  existing getErrorMessage() utility across 13 files

* fix: remove (err as any) casts in error handling

- antigravity/serve.ts: use typed Error.cause instead of (err as any).cause
- external.ts: move instanceof guard into shouldRetryWithCmdShim,
  accept unknown instead of forcing NodeJS.ErrnoException cast at call site
2026-03-25 14:32:29 +08:00
Saeed Al Mansouri 0ff28aa0d8 fix(extension): security hardening — tab isolation, URL validation, cookie scope (#409)
* fix(extension): security hardening — tab isolation, URL validation, cookie scope

Addresses issues raised in #399 (Astro-Han's community triage):

1. Tab isolation bypass: resolveTabId now verifies that an explicit tabId
   belongs to the automation window (tab.windowId === session.windowId)
   before accepting it. Tabs from the user's browsing session are rejected.

2. URL scheme allowlist: isDebuggableUrl switched from a blocklist
   (chrome://, chrome-extension://) to an allowlist (http://, https:// only).
   handleNavigate and tabs.new also reject non-http(s) URLs early, blocking
   file://, javascript:, and data: scheme abuse.

3. Cookie scope restriction: handleCookies now requires domain or url.
   Requests with neither are rejected instead of dumping all browser cookies.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(extension): resolve data: URI vs allowlist conflict, plug tabs.select bypass

- Add BLANK_PAGE constant and whitelist it in isDebuggableUrl so
  internal blank tabs are not treated as non-debuggable after the
  blocklist-to-allowlist change.
- Add isSafeNavigationUrl for user-facing URL validation (http/https
  only), keeping it separate from internal isDebuggableUrl.
- Fix tabs.select to verify tab belongs to automation window before
  activating, closing a tab isolation bypass.
- Normalize error message style (-- instead of em dash).

* fix(extension): add try-catch for tabs.select with explicit tabId

Gracefully handle the case where cmd.tabId points to a closed tab
instead of letting the unhandled exception bubble up.

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-25 14:03:18 +08:00
jakevin 3b2a51fcd3 fix(extension): revert #377 and cleanly fix same-url navigation timeout (#380)
* Revert "fix(extension): avoid same-url navigation timeout (#377)"

This reverts commit b7ada0e38c.

* fix(extension): avoid same-url navigation timeout

- Add normalizeUrlForComparison for minimal URL canonicalization
  (root slash + default port only; preserves hash and non-root paths)
- Fast-path: skip navigation when tab is already at the target URL
- Rewrite wait logic with finish() pattern to prevent double-resolve
- Handle both same-URL and redirect scenarios in navigation listener
- Add regression tests for same-URL and hash-route distinction
2026-03-24 22:25:29 +08:00
ayotme b7ada0e38c fix(extension): avoid same-url navigation timeout (#377)
* fix(extension): avoid same-url navigation timeout

* review: preserve hash-aware extension navigation

---------

Co-authored-by: huruichen <huruichen@kanzhun.com>
Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-24 22:05:22 +08:00
jakevin 56d4326646 refactor(extension): reuse async detach() in registerListeners (#328)
Replace inline sync chrome.debugger.detach() in onUpdated listener
with the shared async detach() function for consistent cleanup behavior
across all detach paths.
2026-03-24 02:18:34 +08:00
QSam2023 4c9a2b1fde fix: detach debugger before navigation in browser bridge (#322)
* fix: detach debugger before navigation in browser bridge

* refactor: make detach() async, await all detach calls

- cdp.ts: detach() now async, awaits chrome.debugger.detach()
- background.ts: await detach() in handleNavigate and handleTabs close
- Eliminates theoretical race between detach and subsequent tab operations

Co-authored-by: jackwener <jackwener@gmail.com>

---------

Co-authored-by: bluey_heeler <fragwang231@gmail.com>
Co-authored-by: jackwener <jakevingoo@gmail.com>
Co-authored-by: jackwener <jackwener@gmail.com>
2026-03-24 02:14:31 +08:00
jakevin c44bc62b60 chore: code cleanup + extension conflict troubleshooting (#260)
Cleanup:
- Remove redundant double-retry in resolveTabId (was retrying data: URI
  with the same data: URI)
- Fix stale comment (30s → 120s idle timeout)
- Remove verbose debug logging in resolveTabId
- Built extension is now smaller (16.66kB vs 17.18kB)

Extension conflict:
- Add hint to attach-failed error when chrome-extension:// URL is detected
- Add troubleshooting entry for extension conflicts (e.g. youmind, New Tab
  Override) to both README.md and README.zh-CN.md

Ref: #249
2026-03-22 22:18:22 +08:00
jakevin 7f57e76485 fix: treat empty tab URL as debuggable (fixes first-run doctor --live failure) (#259)
When a new automation window is created, the initial tab URL may be
empty briefly while Chrome loads the data: URI. isDebuggableUrl('') was
returning false, causing ensureAttached to reject the tab.

Fix: only reject known non-debuggable URLs (chrome://, chrome-extension://).
Empty/undefined URLs are now treated as debuggable since they represent
tabs still loading.

Also adds 200ms delay after window creation to let Chrome populate the
tab URL.
2026-03-22 22:10:30 +08:00
jakevin 3e91876d13 fix: replace all about:blank with data: URI to prevent New Tab Override interception (#257)
Root cause: getAutomationWindow and resolveTabId used about:blank which
New Tab Override extensions intercept immediately, replacing it with
chrome-extension:// URLs that cannot be debugged.

Changes:
- Window creation: about:blank → data:text/html
- reuseTab fallback: about:blank → data:text/html
- newTab handler: about:blank → data:text/html
- Added diagnostic logging to resolveTabId for debugging
- Synced extension version to 1.2.4

Ref: #249
2026-03-22 22:04:25 +08:00
jakevin 112fdefa8d fix: harden resolveTabId against New Tab Override extension interception (#255)
resolveTabId's reuseTab path now verifies the URL is actually debuggable
after navigating to about:blank. If a New Tab Override extension intercepts
it (setting it back to chrome-extension://), falls back to a data: URI,
then creates a fresh tab as last resort.

This fixes the persistent 'attach failed: Cannot access chrome-extension://'
error for users with New Tab Override extensions installed.

Ref: #249
2026-03-22 21:47:44 +08:00
jakevin 71b2c3961b fix: harden browser automation pipeline (resolves #249) (#251)
- resolveTabId: validate URL even for explicit tabId, fall through to
  auto-resolve when tab is not debuggable or has been closed
- handleNavigate: wait for URL change before checking 'complete' status
  to avoid race condition with stale about:blank
- ensureAttached: pre-check tab URL, verify cached attach with probe,
  invalidate cache on URL change via onUpdated listener
- daemon-client: recognize transient extension errors (disconnected,
  attach failed) as retryable with 1500ms delay; fresh command ID per attempt
- pipeline executor: add per-step retry for browser steps (up to 2 retries
  on transient errors); cleanup automation window on pipeline failure
- page.ts: selectTab/newTab/closeTab properly update/invalidate _tabId
- daemon.ts: add WebSocket ping/pong heartbeat (15s interval, 2-miss disconnect)
- Increase automation window idle timeout from 30s to 120s
- Fix timeout param edge cases in BrowserBridge._ensureDaemon
- Remove unused chalk import; fix trailing import placement

Closes #249
2026-03-22 21:23:21 +08:00
AstroHan fbf051d539 fix(extension): skip chrome-extension:// tabs in resolveTabId fallback (#198)
* fix(extension): skip chrome-extension:// tabs in resolveTabId fallback

Remove the unsafe fallback that returned `tabs[0]` regardless of URL
type. When no web-accessible tab exists in the automation window (e.g.
a New Tab Override extension replaced about:blank with its own
chrome-extension:// page), we now always create a fresh about:blank
tab instead. This prevents chrome.debugger.attach from failing with
"Cannot access a chrome-extension:// URL of different extension".

Fixes #195, fixes #197

* refactor(extension): rename isWebUrl → isDebuggableUrl & reuse tabs in resolveTabId

Improvements over the original fix:

1. Rename isWebUrl() → isDebuggableUrl(): better reflects the intent —
   the function determines whether a URL can be attached via CDP, not
   just whether it's a "web" URL (about:blank is debuggable but not
   really a web URL).

2. Reuse existing non-debuggable tabs: when a New Tab Override extension
   replaces about:blank with chrome-extension://, use chrome.tabs.update()
   to navigate the existing tab to about:blank instead of creating a new
   one. This prevents orphan tab accumulation since chrome.tabs.create()
   may also get intercepted by the same extension.

3. Only fall back to chrome.tabs.create() when the window has zero tabs,
   which is the truly empty-window edge case.

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-21 23:05:06 +08:00
jackwener 943e286815 chore: track package-lock.json for CI reproducibility 2026-03-20 14:14:01 +08:00
jackwener 44f0bbe94d feat: add workspace-aware browser sessions 2026-03-20 13:41:04 +08:00
jackwener 0ea6e4a15c fix: address review findings and docs cleanup 2026-03-20 12:28:30 +08:00
jackwener f5f7a9500e chore: rename extension to OpenCLI 2026-03-20 11:03:02 +08:00
jackwener b5c1b242e2 fix(extension): use idle-timeout for automation window lifecycle
Replace eager close-window (which caused race conditions when
parallel commands shared the window) with an idle-based timer:

- Window auto-closes 30s after the last command completes
- Each incoming command resets the idle timer
- Consecutive commands reuse the same window (faster)
- No race conditions with parallel execution
- Close-window action kept for explicit cleanup if needed
2026-03-20 00:05:48 +08:00
jackwener 2f6d28a3e9 feat(extension): auto-close automation window after command completes
- Add 'close-window' action to extension protocol and background.ts
- Add Page.closeWindow() method to send close-window command
- browserSession() now closes automation window in cleanup
- Remove domain pre-navigation + 2s wait from main.ts (CDP handles
  cross-domain cookies natively, no same-origin workaround needed)
- Net effect: commands run faster, no stale windows left behind
2026-03-19 23:59:31 +08:00
jackwener fde618063f chore: pre-release cleanup
- Delete unused extension/src/executor.ts (chrome.scripting experiment)
- Remove 15 no-op backward-compat exports from doctor.ts
- Remove getTokenFingerprint no-op from browser/index.ts
- Rename PlaywrightMCP → BrowserBridge across all source files
  (backward-compat alias kept in mcp.ts and browser/index.ts)
- Remove unnecessary host_permissions from extension manifest
- Sync extension package.json version to 0.2.0
- All 14 tests pass
2026-03-19 23:51:36 +08:00
jackwener 89947fee50 feat(extension): isolated automation window
All opencli operations now run in a dedicated Chrome window instead
of hijacking the user's active tab. The automation window:
- Created on first command via chrome.windows.create({ focused: false })
- 1280x900 viewport, auto-cleaned up when closed
- All tabs resolved within this window only
- User's main browsing session is never touched

Tested: twitter trending ✅, zhihu hot ✅
2026-03-19 23:34:27 +08:00
jackwener 2e962b2e7c chore: pre-release cleanup
- Fix daemon per-command timeout: 30s → 120s (was shorter than CLI-layer timeouts)
- Remove debug command: grok/debug.ts
- Sync extension version to 1.0.0
- Rename PlaywrightMCP → BrowserBridge (keep backward-compat alias)
- Add accept/reply-dm to README command tables
- Clean up consoleMessages() JSDoc in page.ts
- Update runtime.ts comment
2026-03-19 22:49:54 +08:00
jackwener f7c7230854 fix: include pre-built extension dist/ in repo for zero-step install 2026-03-19 20:53:45 +08:00
jackwener 59c0d639a5 refactor: fix 9 issues from round 2 code review
Bug fixes:
- #1 /logs?level=error returned 404 — use pathname for route matching
- #2 Duplicate initialization — added 'initialized' guard flag

Should fix:
- #4 Added screenshot() to IPage interface
- #5 Graceful shutdown rejects pending requests before exit
- #6 Use process.execPath instead of 'npx tsx' for faster daemon spawn

Cleanup:
- #7 Removed duplicate 'browser' keyword in package.json
- #8 Removed unused normalizeEvaluateSource import from browser.ts
- #9 Changed dynamic import to static import in intercept.ts
- #10 Added explicit throw at end of sendCommand for clarity

61 tests pass (4 test files). Extension: 10.55KB.
2026-03-19 16:36:06 +08:00
jackwener 3d1f9640ea feat: forward extension console logs to daemon
Extension side:
- Hook console.log/warn/error → forward via WS as { type: 'log', level, msg, ts }
- Original console output preserved (for chrome://extensions debug)

Daemon side:
- Ring buffer (200 entries) stores extension logs
- Logs printed to daemon stderr with emoji prefix (📋/⚠️/❌)
- GET /logs — returns buffered logs (optional ?level= filter)
- DELETE /logs — clears log buffer

Usage:
  curl localhost:19825/logs              # view all logs
  curl localhost:19825/logs?level=error  # errors only
  curl -X DELETE localhost:19825/logs    # clear buffer

Extension build: 10.48KB
2026-03-19 16:21:17 +08:00
jackwener 8e8c4a0229 feat: add exponential backoff reconnect + CDP screenshot support
Exponential backoff:
- Reconnect delay: 2s, 4s, 8s, 16s, ..., capped at 60s
- Resets to base delay on successful connection
- Reduces idle CPU waste vs fixed 3s reconnect

Screenshot via CDP Page.captureScreenshot:
- New 'screenshot' action in protocol (5th action)
- Supports format (png/jpeg), quality, fullPage
- Full-page: uses Emulation.setDeviceMetricsOverride for scroll height
- CLI-side: page.screenshot() with optional file save
- Extension build: 9.81KB (+1.7KB from 8.11KB)

Inspired by bb-browser's architecture patterns.
2026-03-19 16:21:17 +08:00
jackwener 01b8b6b5bf refactor: fix 14 issues from deep code review
P0 Critical:
- #1 Fix double IIFE wrapping: unified wrapForEval() replaces
  normalizeEvaluateSource + ad-hoc wrap in page.evaluate()
- #2 Fix navigate race: check tab.status before addListener,
  reduced timeout 30s→15s

P1 Should Fix:
- #8 Remove unused permissions (scripting, host_permissions, content_scripts)
- #10 Add retry (3x, 500ms) + timeout (30s) to sendCommand()

P2 Cleanup:
- #3 Extract isWebUrl() to safely handle undefined tab.url
- #4 Sanitize maxDepth with Math.max/min bounds
- #6 Delete empty src/daemon/ directory
- #7 Remove dead createJsonRpcRequest + its test
- #9 Remove stale IIFE-mode comment
- #11 Validate body.id in daemon request handler
- #12 Guard ensureAttached: detach+re-attach on 'already attached'
- #14 Extract _tabOpt() helper (removes 13x spread duplication)
- #15 Add verbose warning for unsupported consoleMessages()

All 35 unit tests pass.
2026-03-19 16:21:17 +08:00
jackwener b2fa7daf57 feat: replace @playwright/mcp with lightweight daemon + Chrome Extension
Architecture:
- Micro-daemon (HTTP + WebSocket bridge, ~190 lines, auto-start/idle-exit)
- Chrome MV3 Extension using chrome.debugger CDP (10KB build)
- 5 protocol actions: exec, navigate, tabs, cookies, screenshot
- All DOM ops via JS evaluate — no extension update needed for new features

Key features:
- CDP Runtime.evaluate for JS execution in page context
- Tab management, cookie access via Chrome APIs
- Auto-start daemon on cold boot, idle auto-exit (5min)
- Minimal permissions: debugger, tabs, cookies, activeTab, alarms

Tested: zhihu hot (14.3s), twitter timeline (9.3s)
2026-03-19 16:21:17 +08:00