会话与登录 / Sessions & Auth

会话与持久化

走 Chrome 扩展连接时,所有 AI 会话共用一个后台窗口,每个会话使用独立标签组;人使用自己的窗口。登录态由同一 profile 共享。用 --launch 单独启动的浏览器才有独立的 cookies 和存储。

归属都能确认是 AI 的已有窗口会继续复用,全屏也不另开窗口;窗口记录丢失时先寻找已有 AI 窗口。不会搬走或删除用户页来回收窗口。AI 窗口混入用户页后这个窗口就归用户:里面已有的 AI 标签照常工作,新的 AI 标签开到一个新的 AI 窗口,之后的会话共用它。空白占位页清理失败时保留归属记录并报错,避免后续会话继续开窗口。tab list --all 首次查询也不启动 daemon、不开窗口或标签、不 attach 页面。

首次 open 被拒绝后,仍可用 tab list --all 通过会话绑定的 profile 或已配置的 CDP 地址只读查询。查询不建立页面驱动连接,也不绕过混合窗口保护。

ab-connect 0.5.50+ 在 native host 断线后,首次重试定时为 1 秒;连续失败按 2、4、8、16 秒退避,最多 30 秒,确认连接后重置。Chrome alarm 仍用于恢复重启的 worker。旧 socket 关闭时,命令与事件等待会结束,session 或 inspector 保留旧 client 也不会维持失效的事件流。旧回复不重放到新 host,重连时重建当前已核验的标签记录。发送失败或回复丢失报告 action_outcome_unknown 和 retryable: false。中断的写操作须先检查页面,再决定是否重复。

旧归属记录只有 targetId、没有标签身份,导致 close 一直无法完成时,可显式运行 close --forget-untracked。它仅放弃两次有效 Chrome registry 查询都未列出的无身份记录,不猜测或关闭其他标签。JSON 的 ownershipDropped 列出放弃的记录,verifiedAbsent: false 和 tabClosureUnverified: true 表示原来的标签未被确认关闭。普通 close 仍保留严格核验。该选项仅用于扩展中继,不能与单个标签或 --all 合用。

ℹ️ 先分清哪条路径
state save / --state / --session-name 是给 chrome-use 自己启动的浏览器和 --session 隔离浏览器用的——它们开局没有登录态,得靠状态文件跨运行续上。走扩展连接驱动你真实 Chrome 时用不到这些:登录态本来就在你真实 profile 里、原生持久化,重启也还在。见 真实 Chrome。

跨运行保存登录态

登录一次,把 cookies 和 localStorage 存进一个文件,之后的运行加载它就直接是登录态—— 不用每次都重新走一遍登录表单。用 state save 保存;启动时用 --state 带上, 或用 state load 把状态注入当前会话再继续:

state save / load / 复用
# 登录一次,保存 cookies + localStorage + 登录状态
$ chrome-use state save ./auth.json

# 启动时带上状态,开局就是登录态
$ chrome-use --state ./auth.json open https://app.example.com

# 或把已保存的状态注入当前会话,再继续操作
$ chrome-use state load ./auth.json
$ chrome-use open https://app.example.com/dashboard

怎么先干净地登录、再把会话存下来,见 登录与凭证——那一页从登录流程的角度讲了同一个 state save。

状态文件是一份普通 JSON,包含 cookies、两种 storage 和来源列表:

auth-state.json
{
  "cookies": [...],
  "localStorage": {...},
  "sessionStorage": {...},
  "origins": [...]
}
⚠️ 状态文件是明文凭证
auth.json 里装着可登录的 cookies 和 token——别提交进 Git (echo "*.auth-state.json" >> .gitignore),用完及时删除,别放进任何会被打包分发的目录。

自动存取:--session-name

不想手动 save/load,就用 --session-name(或环境变量 AGENT_BROWSER_SESSION_NAME)——同名会话的状态会自动保存、并在后续运行时自动恢复:

自动保存 / 恢复
$ AGENT_BROWSER_SESSION_NAME=my-app chrome-use open https://app.example.com
# 之后用同一个 name 再跑,状态自动恢复
ℹ️ Cloudflare 清关也能这样复用
通过 Cloudflare 挑战拿到的 cf_clearance cookie 绑定在你的 IP + User-Agent 上。 对隔离会话,--session-name 的存取会连同它一起保存/恢复,只要出口 IP 和 UA 不变,就能跳过重复挑战。 更多登录与凭证细节见 登录与凭证。

用 --session 并行跑多个浏览器

每个 --session <name> 是一个相互隔离的浏览器——各自独立的 cookies、 LocalStorage / SessionStorage、IndexedDB、缓存、历史记录和标签页。适合测多用户流程或并行抓取:

两个隔离会话
$ chrome-use --session a open https://app.example.com
$ chrome-use --session b open https://app.example.com
$ chrome-use --session a fill @e1 "alice@test.com"
$ chrome-use --session b fill @e1 "bob@test.com"

省略 --session 时,chrome-use 会优先从受支持的运行器 ID 派生稳定的独立会话。 Codex 任务会自动使用 CODEX_THREAD_ID,Claude Code 用 CLAUDE_PID, 其他运行器可以提供 AGENT_BROWSER_SESSION_ID;没在名单里的运行器,只要导出 <产品名>_SESSION_ID / _THREAD_ID / _CONVERSATION_ID 这类变量也会被自动识别。都没有时退回到终端 ID(WT_SESSION、 TERM_SESSION_ID 等),仍然什么都没有的普通 shell 才用 default。 想知道当前 shell 到底会落到哪个会话、是靠哪个变量派生的,跑 chrome-use doctor 看 Default session 那一行。想显式设置当前 shell 的默认会话,用 AGENT_BROWSER_SESSION=myapp。会话不需要时用 close 关掉,用 session list 看还有哪些活跃会话:

ℹ️ 换目录不会丢会话
派生出的名字是 cu-<repo>-<tag>:<tag> 来自 agent / 终端 id, <repo> 只是当前目录的 basename。以前换个目录执行就会造出另一个名字、打到一个没有任何 ref 的新守护进程 (报 「Unknown ref」)。现在只要有一个活着的守护进程带着同一个 agent tag(前缀不同),就会复用它, agent 的标签和 ref 跨 cd 保留。显式 --session <name> / AGENT_BROWSER_SESSION 仍然优先。 「Unknown ref」报错现在会说明是哪个会话在应答、它有没有 ref:一个都没有时提示「此会话尚未运行过 snapshot;如果你在别的目录或终端做的快照, 用 session list 找到它并用 --session 钉住」,否则列出它实际持有的 ref 范围。
默认会话 & 清理
$ AGENT_BROWSER_SESSION=myapp chrome-use open https://example.com

# 关掉指定会话
$ chrome-use --session auth close

# 列出活跃会话
$ chrome-use session list
并发 agent 必须各有独立会话
一个会话内,命令被钉在你打开的那个标签(按 target_id 锁定,外来标签不会把你的 eval/screenshot 带偏)。两个 agent 共用同一个会话 (比如都用裸默认会话)会共享一个守护进程和一个活跃标签,互相覆盖。Codex 任务现在自动隔离; 其他环境请给每个 agent 一个独立的 --session <name> 或 AGENT_BROWSER_SESSION_ID。
ℹ️ 真正的多 agent 隔离走扩展连接
真正的多 agent 隔离需要扩展连接路径:每个 --session 拿到自己彩色的 Chrome 标签组, 会话之间从不碰彼此的标签。裸 --cdp <port> 不隔离——每个会话都挂到同一个浏览器的现有目标上, 第二个会话的首次 open 可能导航到兄弟会话的标签。同一台真实 Chrome 上跑并发 agent,用扩展(各带一个不同的 --session),别用裸 --cdp。见 真实 Chrome。

跨会话拿指定标签:targetId 收养

每个会话有自己的标签组、各自分配 t<N> 序号(同一个物理标签在 A 会话是 t8、 在 B 会话是 t1),所以 t<N> 不是跨会话稳定的句柄。要从另一个会话拿到某个具体标签 (比如某会话把表单填了一半、句柄却死了),用稳定的 CDP targetId:

按 targetId 收养标签
# 重新同步实时标签,每行打印 target: <id>
$ chrome-use tab list --full --session B

# 收养那个确切的标签,不刷新,状态保留
$ chrome-use tab adopt <targetId> --session B

tab list 每次调用都重新发现实时标签集,所以新会话能看到别的会话开的标签(以及被重新挂上的标签), 而不只是自己的。按 targetId 收养会让 B 会话落到那个搁浅的标签上而不重新加载它, 半填的表单因此得以保留。

tab list --all 列出 profile 里的全部标签和各自归属:本会话、session <名字> (并标明那个会话是否在运行)或用户,其他会话按名字分组。它只做观察。要操作别的会话或用户的标签,需要 --tab <handle> --force,且只在用户要求时使用;那个会话正在运行时,结果会警告它可能正在操作这个标签。 见命令。

✅ 用 --reuse-tab 避免重复标签
重新绑定时如果又 open 同一个入口 URL,加 --reuse-tab:若已有标签正显示那个 URL (按 origin+path 匹配),就切过去,而不是再开一个新标签堆起来。

给标签组起个人看得懂的名字

在你的真 Chrome 里,一个会话开的标签会被收进一个标签组。默认组名是 会话 id(cu-myproject-fb0742)——那是路由键, 对着自己浏览器的人完全看不出 agent 在干什么。

session name
$ chrome-use session name "🔎 查快递单号"
✓ tab group for session 'cu-myproject-fb0742' is now 🔎 查快递单号

$ chrome-use session name            # 显示当前的
$ chrome-use session name --clear    # 恢复显示会话 id

在开标签之前设。组名是建组时定的,所以设晚了只影响之后新开的标签; 已经打开的那些要靠扩展去改名,装的扩展版本够新才行。两种情况命令都会明说是哪一种, 不会让你以为全都改好了。

名字短一点、和任务相关、用一个中性友好的 emoji 开头,在标签栏里最好认;拿不准就用 🔎。 改名不影响任何路由——会话 id 才是身份,组名纯粹是给人看的。


复位卡住的守护进程状态

每个会话跑一个后台守护进程 worker 来持有页面句柄。这个守护进程也是 boa JS 引擎的落脚点—— 引擎跑在页面外面,script 才能扛住整页硬跳转不丢状态。 如果某个会话开始出问题——命令打到错的标签、 refs/句柄看起来陈旧,或者你在会话中途升级了 chrome-use 导致旧 worker 残留—— 直接重启守护进程,别去 pgrep/kill 手动找 PID:

daemon status / restart
# 列出运行中的会话守护进程(含 relay 状态)
$ chrome-use daemon status

# 优雅停止一个守护进程(默认当前会话)
$ chrome-use session stop [name]

# 优雅停止所有活动会话守护进程
$ chrome-use session prune

# 杀掉所有会话守护进程 worker
$ chrome-use daemon restart
✅ restart 不动你的 Chrome 和标签
daemon restart 不碰扩展的原生消息桥(__nm-host),所以到你实时 Chrome 的 relay 保持在线——下一条命令只是对同一个浏览器起一个干净的新守护进程。它不会关掉任何标签。
会话 socket 丢失时自动恢复
如果已登记的 daemon 进程仍然存活、但本地 socket 已消失,下一条浏览器命令会停止这个不可达 worker, 并为同一会话启动干净的新 daemon。若 socket 在命令执行途中消失,chrome-use 会在错误中给出缺失的 endpoint、清理陈旧状态,并提示重跑该命令。

极少数情况:把会话交接给用户(session handoff / resume)

✅ 先自主登录——这是默认,也是目标
chrome-use 的设计就是让 agent 自己登录:从凭证库 / Bitwarden 取账号密码(bwu get … | chrome-use fill … --stdin)、读 TOTP/2FA 验证码、走 OAuth、用 state save / --session-name 存会话让下次直接登录态。别因为有密码或 2FA 就把登录交给用户——自己解决。见 登录与凭证。

session handoff 是逃生口,不是登录方式。只在某一步 agent 真的做不到时才用——解不了的图形/行为验证码、拿不到的短信/验证器码、硬件密钥轻触、银行"在手机上确认"。先尝试自主完成,实在不行才交接:

交接 → 用户操作 → 取回
$ chrome-use session handoff   # 最后手段:标记会话归用户;告诉用户具体要做什么
# …… 用户做那件 agent 真做不了的事 ……
$ chrome-use session resume    # 取回控制——务必等用户确认做完再执行
⚠️ 交接期间 agent 被挡在门外
会话交接后,agent 对它的任何驱动命令都会被拒绝(大声报错并给出 session resume 的原命令),所以 agent 不会和用户抢标签页。默认零影响——不调 handoff 时 agent 一直拥有并自主驱动每个会话(含自主登录)。 session status 看归属,session list 列出每个会话及其归属。别自己 resume 抢回来——等用户说做完了。

相关页面