Skip to content

feat(browser): 新增 browser cookies 子命令,导出当前页 Cookie(含 HttpOnly)#1583

Open
riba2534 wants to merge 4 commits into
jackwener:mainfrom
riba2534:feat/browser-cookies
Open

feat(browser): 新增 browser cookies 子命令,导出当前页 Cookie(含 HttpOnly)#1583
riba2534 wants to merge 4 commits into
jackwener:mainfrom
riba2534:feat/browser-cookies

Conversation

@riba2534
Copy link
Copy Markdown

这个 PR 解决了什么问题

OpenCLI 内部其实已经实现了完整的 cookie 读取能力——page.getCookies() 在 CDP(src/browser/cdp.ts)和 Chrome Extension(src/browser/page.ts)两个 transport 上都能拿到 cookie,包括 HttpOnly cookie。但这个能力今天没有暴露成 CLI 子命令,唯一的调用点是 browser analyze 内部用来给 site detection 取 cookie 名字(src/cli.ts:1186)。

外部使用者(agent / 适配器作者 / 一次性脚本)想拿当前浏览器登录态的 cookie 时,要么去 fork 一份 daemon 协议自己调,要么换成 `playwright-cli cookie-list` 之类的外部依赖。本 PR 就是把已有的内核能力开出一条最薄的 CLI 通道。

命令形态

```bash
opencli browser cookies [--domain ] [--url ] [--tab ]
```

返回 JSON envelope:

```json
{
"cookies": [
{
"name": "session",
"value": "abc...",
"domain": ".larkoffice.com",
"path": "/",
"secure": true,
"httpOnly": true,
"sameSite": "None",
"expirationDate": 1781234567,
"session": false,
"size": 64,
"priority": "Medium",
"sourceScheme": "Secure",
"sourcePort": 443
}
],
"count": 1
}
```

  • `--domain`:客户端侧过滤,匹配 cookie 的 domain 或其任意父域(沿用现有 `matchesCookieDomain` 逻辑)
  • `--url`:CDP 侧过滤,只返回会发往该 URL 的 cookie(透传给 `Network.getCookies` 的 `urls` 参数)
  • `--tab`:复用 `addBrowserTabOption`,与其他 browser 子命令路由方式一致

这个能力的价值

最直接的应用场景是让 agent 复用已登录的浏览器 session 去驱动外部 HTTP 客户端。一个典型例子:

用户已经在 Chrome 里登录了某个内网平台(飞书 / 公司 SSO / 任意 Web 应用),现在 agent 需要拿这个登录态去打这个平台的内部 RPC。这些 RPC 通常没有公开 SDK,但只要请求里带上 web 客户端的 Cookie 就能直接用。

走 `opencli browser eval "document.cookie"` 不行——`document.cookie` 拿不到 HttpOnly cookie,而几乎所有正经站点的 session/SSO cookie 都是 HttpOnly(这是 OWASP 的安全最低线)。本 PR 的子命令绕开 `document.cookie` 限制,直接走 CDP `Network.getCookies` / chrome.cookies API,把这些登录态 cookie 完整导出。

落到具体使用模式:

```bash

1. 用户已在 Chrome 登录某站点,agent 把当前 tab 绑到 opencli session

opencli browser my bind

2. 一行拿到登录 cookie,喂给任何 HTTP 工具

opencli browser my cookies --domain example.com \
| jq -r '.cookies[] | "\(.name)=\(.value)"' \
| paste -sd '; ' - \
| xargs -I{} curl -H "Cookie: {}" https://example.com/api/...

或者 Python:

opencli browser my cookies --url https://example.com/api/foo > cookies.json
python -c "import json, httpx; jar = httpx.Cookies(); [jar.set(c['name'], c['value'], domain=c['domain'], path=c.get('path','/')) for c in json.load(open('cookies.json'))['cookies']]; print(httpx.get('https://example.com/api/foo', cookies=jar).text)"
```

这个模式之前依赖 `playwright-cli cookie-list`,但需要额外装 Playwright + MCP Bridge 扩展,且其输出格式 `name=value (domain: x, path: y)` 字段比 OpenCLI 这个新命令少(没有 secure / httpOnly / sameSite / expirationDate 等)。本 PR 让 OpenCLI 自身就具备这一能力,对已经在用 OpenCLI 的 agent 工作流是无依赖收益。

实现要点

  1. src/types.ts — 扩展 `BrowserCookie` 类型,增加可选字段:`sameSite / session / size / priority / sourceScheme / sourcePort / hostOnly / storeId / sameParty / partitionKey`。纯加法、向后兼容。
  2. src/browser/cdp.ts — 把 `Network.getCookies` 的字段全部映射到 `BrowserCookie`,并把 CDP 字段名 `expires` 归一化为 `expirationDate`,与 chrome.cookies API 风格一致。这修复了一个早就存在的隐性不一致:CDP 路径之前从未真正返回过 `expirationDate`(因为 raw 字段叫 `expires`),但类型签名声明它会返回。Session cookies(`expires <= 0`)省略 `expirationDate`、并设 `session: true`。
  3. src/cli.ts — 在 `browser frames` 后面加 `browser cookies` 子命令,复用 `browserAction` 错误处理 wrapper 和 `addBrowserTabOption`。空字符串过滤值不下传,避免 CDP 把 `domain=""` 当字面量匹配返回零结果。
  4. src/browser/page.ts — Extension 实现没改,因为 `chrome.cookies.getAll` 原生返回的就是 `expirationDate` 风格,透传即可。

测试

```
Test Files 363 passed (363)
Tests 3636 passed | 1 skipped (3637)
```

新增 5 个测试:

  • `src/cli.test.ts` × 3:默认列出、`--domain`/`--url` 透传、空字符串过滤值不下传
  • `src/browser/cdp.test.ts` × 2:CDP 字段完整归一化(`expires` → `expirationDate` + 各 CDP-only 字段保留)、Session cookie 省略 `expirationDate` 并标记 `session: true`

无 breaking change:

  • `browser analyze` 唯一现存的 `getCookies()` 调用只取 `c.name`,归一化字段不影响它
  • `BrowserCookie` 新字段全部 optional

文档

`skills/opencli-browser/SKILL.md` 的 Inspect 命令表里加了 `browser cookies` 行,说明使用场景、过滤选项、HttpOnly 能力,以及和 `document.cookie` / `playwright-cli cookie-list` 的差异。

兼容性 / 风险

  • 命令名 `cookies` 在 `browser` 命名空间下未被占用
  • 仅新增 CLI surface 与可选类型字段,无 breaking change
  • CDP 归一化对内部调用者(`browser analyze`)无可见影响

@riba2534 riba2534 force-pushed the feat/browser-cookies branch from a0d085b to 4993395 Compare May 15, 2026 08:17
riba2534 and others added 2 commits May 20, 2026 23:50
Expose `page.getCookies()` as a top-level browser subcommand so agents
and adapters can read live browser cookies — including HttpOnly ones —
without depending on `playwright-cli cookie-list` or hand-rolled CDP
plumbing.

  opencli browser <session> cookies [--domain <d>] [--url <u>]

Returns `{cookies, count}` where each cookie carries the full set of
fields the underlying transport exposes (name, value, domain, path,
secure, httpOnly, sameSite, expirationDate, session, plus CDP-only
size/priority/sourceScheme/sourcePort/sameParty/partitionKey).

CDP `Network.getCookies` reports the expiry as `expires`; the chrome
cookies extension API calls the same field `expirationDate`. We
normalize CDP records to the chrome.cookies shape so consumers don't
have to branch on transport. Session cookies (CDP `expires` <= 0)
omit `expirationDate` and surface `session: true` instead.

Why this exists: a recurring agent pattern is to log into a site once
in Chrome and then drive an external HTTP client (Python httpx, curl,
etc.) using the resulting session — typically for internal RPCs that
don't have a public API but do honor the web client's cookies. The
in-page `document.cookie` path can't see the session/SSO cookies that
make this work because they're HttpOnly. Both transports already in
opencli (CDP and the Chrome extension) can read HttpOnly cookies; this
PR just wires that capability through the CLI.
@jackwener jackwener force-pushed the feat/browser-cookies branch from 4993395 to ef00e0d Compare May 20, 2026 15:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants