feat(browser): add cookies action for retrieving browser cookies including HttpOnly#7635
feat(browser): add cookies action for retrieving browser cookies including HttpOnly#7635euisuk-chung wants to merge 3 commits into
Conversation
…elay The Chrome extension relay server needs to be running before the extension can connect. Previously, the browser control service was initialized lazily (on first browser.proxy command), causing a chicken-and-egg problem where the extension couldn't connect because the relay wasn't started yet. This change starts the browser control service eagerly when Node Host starts (if browser proxy is enabled), ensuring the relay server on port 18792 is available for Chrome extension connections. Security: No change to security model - relay remains loopback-only (127.0.0.1) with existing connection validation.
Add a new `cookies` action to the browser tool that allows retrieving browser cookies including HttpOnly cookies via Playwright CDP. Features: - Retrieve all cookies from the browser context - Filter cookies by domain using the `domain` parameter - Support both local browser control and Node Host proxy routing - Include HttpOnly cookies (accessible via CDP, not JavaScript) Use case: Enable MCP server authentication token refresh by extracting session cookies from authenticated browser sessions. Co-Authored-By: Claude Opus 4.5 <[email protected]>
| if (proxyRequest) { | ||
| const result = (await proxyRequest({ | ||
| method: "GET", | ||
| path: "/cookies", | ||
| profile, | ||
| query: { | ||
| targetId, | ||
| }, | ||
| })) as { ok: boolean; targetId: string; cookies: unknown[] }; |
There was a problem hiding this comment.
[P0] domain filter is ignored when calling the node proxy.
In the node-proxy path you only include targetId in query, so domain never reaches the node-host /cookies route. This means browser action="cookies" target="node" domain=... will return unfiltered cookies unless the server happens to filter some other way. (You currently filter client-side after the proxy call, but that assumes the proxy always returns all cookies, which is both inefficient and may not match intended semantics.)
Also appears in src/agents/tools/browser-tool.test.ts:367 (test name implies proxy-side filter but current implementation is client-side).
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/tools/browser-tool.ts
Line: 723:731
Comment:
[P0] `domain` filter is ignored when calling the node proxy.
In the node-proxy path you only include `targetId` in `query`, so `domain` never reaches the node-host `/cookies` route. This means `browser action="cookies" target="node" domain=...` will return *unfiltered* cookies unless the server happens to filter some other way. (You currently filter client-side after the proxy call, but that assumes the proxy always returns *all* cookies, which is both inefficient and may not match intended semantics.)
Also appears in `src/agents/tools/browser-tool.test.ts:367` (test name implies proxy-side filter but current implementation is client-side).
How can I resolve this? If you propose a fix, please make it concise.| const result = await fetchBrowserJson<BrowserCookiesResult>( | ||
| withBaseUrl(baseUrl, `/cookies?${q.toString()}`), | ||
| { timeoutMs: 10000 }, | ||
| ); |
There was a problem hiding this comment.
[P1] /cookies URL always includes a trailing ?, which can break routing.
withBaseUrl(baseUrl, /cookies?${q.toString()}) produces /cookies? when q is empty. Some routers/middlewares treat /cookies and /cookies? differently, which can lead to 404s or cache misses. Safer to omit the ? when there are no query params.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/browser/client.ts
Line: 369:372
Comment:
[P1] `/cookies` URL always includes a trailing `?`, which can break routing.
`withBaseUrl(baseUrl, `/cookies?${q.toString()}`)` produces `/cookies?` when `q` is empty. Some routers/middlewares treat `/cookies` and `/cookies?` differently, which can lead to 404s or cache misses. Safer to omit the `?` when there are no query params.
How can I resolve this? If you propose a fix, please make it concise.| if (opts?.domain) { | ||
| const domainFilter = opts.domain.toLowerCase(); | ||
| result.cookies = result.cookies.filter((c) => | ||
| c.domain.toLowerCase().includes(domainFilter), | ||
| ); | ||
| } |
There was a problem hiding this comment.
[P2] Client-side domain filtering duplicates server/proxy behavior and can hide bugs.
browserCookies() fetches from /cookies then filters result.cookies client-side. If the server later adds domain filtering (or already does for some backends), the client filtering can mask issues and makes it harder to reason about what the API returns. Consider pushing domain down into the request query (so all backends filter consistently) and keeping client-side filtering only as a fallback (or removing it).
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/browser/client.ts
Line: 374:379
Comment:
[P2] Client-side domain filtering duplicates server/proxy behavior and can hide bugs.
`browserCookies()` fetches from `/cookies` then filters `result.cookies` client-side. If the server later adds domain filtering (or already does for some backends), the client filtering can mask issues and makes it harder to reason about what the API returns. Consider pushing `domain` down into the request query (so all backends filter consistently) and keeping client-side filtering only as a fallback (or removing it).
How can I resolve this? If you propose a fix, please make it concise.- Fix URL query string formatting (P1): avoid trailing '?' when no params - Add comment clarifying client-side domain filtering (P0/P2) - Fix lint errors in test file (no-unsafe-optional-chaining) Co-Authored-By: Claude Opus 4.5 <[email protected]>
bfc1ccb to
f92900f
Compare
…mory YouTube Ultimate v2.0.0: - Added yt-dlp integration for video/audio downloads - formats command to list available qualities - 4K/1440p support, FLAC/WAV/OPUS audio - Cookie support for age-restricted content - Robust error handling WhatsApp Full History Sync: - New config option: syncFullHistory (opt-in) - Handles messaging-history.set events from Baileys - Enables reading all past messages (one-time sync at pairing) LanceDB Hybrid Memory: - Cherry-picked openclaw#7695 + openclaw#7636 for better recall - Hybrid search combining vector + keyword Also includes: - openclaw#7747 Zero-latency hot-reload - openclaw#7635 Browser cookies action - openclaw#7600 Secrets injection proxy Trust-first fork: Removing restrictions, enabling full AI access.
Summary
Add a new
cookiesaction to the browser tool that allows retrieving browser cookies includingHttpOnly cookies via Playwright CDP.
browser action="cookies"to retrieve cookies from browser contextdomainparameter to filter cookies by domainUse Case
Enable MCP server authentication token refresh by extracting session cookies from authenticated
browser sessions. For example, NotebookLM MCP authentication requires Google session cookies (HSID,
SSID, SID) which are HttpOnly and cannot be accessed via
document.cookie.Usage
browser action="cookies" profile="chrome" domain="google.com"
Returns:
{ "ok": true, "targetId": "ABC123", "cookies": [ { "name": "SID", "value": "...", "domain": ".google.com", "path": "/", "httpOnly": true, "secure": true, "sameSite": "Lax" } ] } Test plan - Unit tests added for cookies action - Tests for domain filtering - Tests for Node Host proxy routing - All existing browser-tool tests pass