拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Camofox Browser Persistence 插件实战:基于 Playwright storageState 的用户会话持久化与恢复

Camofox Browser Persistence 插件实战基于 Playwright storageState 的用户会话持久化与恢复【免费下载链接】camofox-browserStealth headless browser for AI agents — bypass Cloudflare, bot detection, and anti-scraping. Drop-in Puppeteer/Playwright replacement.项目地址: https://gitcode.com/GitHub_Trending/ca/camofox-browser本指南围绕 Camofox Browser 内置的 persistence 插件展开讲解它如何借助 Playwright 的storageStateAPI为每个 userId 保存并恢复 cookies、localStorage可选 IndexedDB从而让 AI Agent 的浏览器会话跨重启、跨容器部署、跨空闲超时保持登录态。读完本文你将掌握该插件的生命周期钩子机制、存储目录布局、完整配置方式含环境变量与 Docker 卷挂载、原子化落盘实现原理以及如何通过DELETE /sessions/:userId/storage_state安全重置用户存储状态。插件定位与核心能力persistence 插件是 Camofox Browser 中默认启用的会话持久化方案其权威说明位于 plugins/persistence/AGENTS.md详细配置可参考 plugins/persistence/README.md。核心能力概括如下按用户维度隔离每个userId对应一个确定性的 SHA256 哈希子目录保证任意 userId即使包含/、:等字符都映射为路径安全的目录名会话全生命周期覆盖从session:creating恢复到session:created首次引导、session:cookies:import/session:destroying/server:shutdown落盘检查点再到DELETE /sessions/:userId/storage_state无检查点重置基于 Playwright 原生 API恢复时注入contextOptions.storageState落盘时调用context.storageState()与 Puppeteer/Playwright 生态的 storageState 文件格式完全兼容IndexedDB 可选支持默认不采集开启后可保留存放在 IndexedDB 中的登录态如 Firebase Auth 等 SSO 流程但会显著增大快照体积、拖慢检查点。工作原理四个生命周期钩子与一个重置端点所有钩子均通过emitAsync()异步等待保证「存储状态已加载」先于「上下文创建」完成。这一保证的实现见 lib/plugins.js 中的emitAsync它会Promise.all等待所有监听器包括异步监听器执行完毕因此session:creating中对contextOptions的修改按引用传递一定在核心调用b.newContext(contextOptions)之前生效。时机钩子 / 端点行为会话创建前session:creating若存在已持久化的storage_state.json将其路径写入contextOptions.storageStatePlaywright 在创建上下文时自动恢复会话创建后session:created若没有已持久化状态则尝试从CAMOFOX_COOKIES_DIR/cookies.txt导入引导 cookies导入成功后立即检查点落盘运行时session:cookies:import用户主动导入 cookies 后将当前上下文状态检查点到磁盘其他插件导出session:storage:export若其他插件导出了 storageState直接持久化该精确快照只序列化一次导出与落盘数据一致会话关闭前session:destroying上下文仍存活时执行检查点storage_reset原因除外随后从活跃会话表移除服务器关闭server:shutdown遍历所有活跃会话逐个检查点然后清空活跃会话表存储重置DELETE /sessions/:userId/storage_state关闭活跃上下文但不检查点等待进行中的写操作完成删除持久化文件下次会话全新开始上述逻辑全部实现在 plugins/persistence/index.js该文件不含路由之外的副作用、不使用child_process是纯生命周期钩子与一个路由的轻量组合。存储布局确定性哈希目录默认持久化根目录为~/.camofox/profiles/由全局配置profileDir决定见 lib/config.js每个用户对应一个 SHA256 哈希子目录~/.camofox/profiles/ └── sha256(userId)/ ├── storage-state.json # Playwright storageState 快照 └── meta.json # 元数据userId、updatedAt、storageStatePath路径计算逻辑位于 lib/persistence.js 的getUserPersistencePaths使用crypto.createHash(sha256)对String(userId)求哈希并截取前 32 个十六进制字符作为目录名。这一点有两点工程价值确定性同一 userId 每次计算出的路径一致重启后能精确找到对应快照由 plugins/persistence/persistence.test.js 中getUserPersistencePaths is deterministic and stays under root用例验证路径安全任意 userId测试中甚至使用agent/profile:default这类含特殊字符的输入都会被映射为不含/、:的纯十六进制目录名杜绝路径穿越与文件名注入。配置指南开关、目录与 IndexedDB1. camofox.config.json 配置persistence 插件默认启用仓库根目录 camofox.config.json 中persistence: { enabled: true }。完整配置项如下{ plugins: { persistence: { enabled: true, profileDir: /data/profiles, indexedDB: true } } }2. 环境变量覆盖profileDir的解析优先级为环境变量 插件配置 全局配置默认值实现在 plugins/persistence/index.js 的register函数中const profileDir process.env.CAMOFOX_PROFILE_DIR || pluginConfig.profileDir || config.profileDir; if (!profileDir) { log(warn, persistence plugin: no profileDir configured, plugin disabled); return; }即启动时设置CAMOFOX_PROFILE_DIR/data/profiles若三者均未提供profileDir 为空插件会记录 warn 日志并自动禁用——这一行为由 plugins/persistence/plugin.test.js 的skips registration when no profileDir configured用例覆盖。环境变量覆盖插件配置的优先级由env var CAMOFOX_PROFILE_DIR overrides pluginConfig用例验证。相关环境变量汇总默认值见 lib/config.js环境变量默认值作用CAMOFOX_PROFILE_DIR~/.camofox/profiles持久化根目录插件配置可覆盖环境变量优先CAMOFOX_COOKIES_DIR~/.camofox/cookies引导 cookies 目录首次运行时从中读取cookies.txtNetscape 格式3. IndexedDB可选的登录态深水区默认情况下indexedDB为falsecontext.storageState()调用时不携带indexedDB选项仅保存 cookies 与 localStorage。当配置indexedDB: true后插件会设置ctx.persistenceStorageStateOptions { indexedDB: true }落盘时调用context.storageState({ path: tmpPath, indexedDB: true })采集所有可序列化的 IndexedDB 记录而不只是认证数据。这种做法的收益与代价并存README 与源码注释均有明确警告收益是能保留存放在 IndexedDB 中的登录态例如 Firebase Auth 与其他 SSO 流程代价是快照体积可能显著增大、检查点明显变慢。选择是否开启前应评估目标站点认证数据的实际存放位置。该开关的两种行为分别由does not persist IndexedDB by default与indexedDB: true opts in to IndexedDB persistence两个测试用例锁定。底层实现原子写入与并发控制原子落盘tmp 写入 renamepersistStorageStatelib/persistence.js采用「临时文件写入 原子 rename」策略保证任何时刻磁盘上都存在一份完整快照const suffix .tmp-${process.pid}-${Date.now()}; const tmpStoragePath ${storageStatePath}${suffix}; const tmpMetaPath ${metaPath}${suffix}; // 1. mkdir -p 用户目录 // 2. 写入 tmp 文件JSON.stringify(storageState, null, 2) 或 context.storageState({ path: tmp })) // 3. fs.rename(tmp, storageStatePath) —— 原子替换 // 4. 同样流程写 meta.json若写入过程中抛错catch分支会清理残留的.tmp-*文件并返回{ persisted: false }。测试用例a failed persist leaves the previous storage-state intact and cleans up tmp files验证了两个关键性质失败时旧快照完好无损、没有 tmp 残留。检查点串行化inflight 合并同一用户的多个检查点如 cookie 导入与会话关闭几乎同时触发通过checkpointPromisesMap 串行化——每个 userId 只保留「最新排队中的检查点」新检查点挂在上一个 Promise 之后执行避免并发写同一文件const previous checkpointPromises.get(userId) || Promise.resolve(); const current previous.catch(() {}).then(async () { /* persist */ }); checkpointPromises.set(userId, current);读取校验损坏文件不致命loadPersistedStorageState读取并校验快照必须是合法 JSON 对象、cookies必须为数组、origins若存在必须为数组否则视为无状态并返回undefined。损坏的 JSON如{not-json会被静默忽略并回退到「无持久化状态」路径由loadPersistedStorageState ignores invalid JSON files用例验证——这意味着单个用户快照损坏不会影响其他用户也不会导致会话创建失败。首次运行的引导 cookies当某个 userId 没有任何持久化状态时典型场景全新部署或刚执行过存储重置session:created钩子会调用importBootstrapCookies实现位于 mcp/lib/cookies.mjs由 lib/cookies.js 兼容性再导出。它从CAMOFOX_COOKIES_DIR/cookies.txtNetscape 格式读取引导 cookies 注入新上下文导入成功后立即触发一次bootstrap_cookies检查点。若 cookies 文件不存在则视为 no-opimported: 0不影响会话启动。存储状态重置DELETE /sessions/:userId/storage_state当需要让某用户「从零开始」如清除登录态、切换账号时调用DELETE /sessions/:userId/storage_state该端点注册于 plugins/persistence/index.js的语义与普通会话关闭有本质区别其内部流程为若该 userId 的重置已在进行中返回409 { error: storage state reset already in progress }将 userId 加入resettingUsers集合——期间所有检查点请求都会被跳过if (resettingUsers.has(userId)) return;同时守卫在checkpoint与各钩子中以reason: storage_reset调用ctx.destroySession关闭活跃上下文不触发检查点await checkpointPromises.get(userId)等待可能正在进行的落盘写操作完成避免「边写边删」的竞态由DELETE storage_state waits for an in-flight checkpoint before deleting用例验证删除storage-state.json与meta.json返回{ ok: true, userId, clearedLive, removedPersisted }。该端点具备幂等性即使该用户没有活跃会话也没有持久化文件仍返回{ ok: true, clearedLive: false, removedPersisted: false }见DELETE storage_state is idempotent without a live session or persisted file用例。重置后的下一次会话将走session:creating→ 无状态 →session:created→ 引导 cookies 的完整首启流程。Docker 部署卷挂载持久化目录在容器环境中~/.camofox位于容器内重启容器即丢失。正确做法是将 profile 目录挂载为宿主机卷docker run -d \ -p 9377:9377 \ -v /host/profiles:/data/profiles \ camofox-browser配合配置profileDir: /data/profiles或环境变量CAMOFOX_PROFILE_DIR/data/profiles即可实现容器重建、重新部署后登录态不丢失。同理若希望引导 cookies 也跨部署保留可将CAMOFOX_COOKIES_DIR指向持久化卷。测试覆盖与质量保障persistence 插件的正确性由两层测试保障plugins/persistence/persistence.test.js针对 lib/persistence.js 三个辅助函数的单元测试覆盖路径确定性、状态加载、落盘元数据、损坏文件容错、失败清理等plugins/persistence/plugin.test.js针对插件生命周期钩子与 DELETE 端点的集成测试覆盖状态恢复注入、cookie 导入检查点、session:storage:export精确持久化、session:destroying检查点、重置语义含并发竞态与幂等性、环境变量覆盖、IndexedDB 开关等 10 个场景。运行测试npx jest plugins/persistence/persistence.test.js plugins/persistence/plugin.test.js小结persistence 插件用极少的代码一个纯钩子文件 三个辅助函数解决了 Agent 浏览器场景中最关键的「会话连续性」问题通过 PlaywrightstorageState原生能力实现 cookies/localStorage/IndexedDB 的按用户持久化通过emitAsync保证恢复时机正确通过原子 rename 与 inflight 串行化保证落盘安全通过哈希目录与路径校验保证多租户隔离最终以DELETE端点提供干净的重置出口。对于需要长时间运行、频繁重启或弹性部署的 AI Agent 浏览器服务它是开箱即用的会话保活基础组件。【免费下载链接】camofox-browserStealth headless browser for AI agents — bypass Cloudflare, bot detection, and anti-scraping. Drop-in Puppeteer/Playwright replacement.项目地址: https://gitcode.com/GitHub_Trending/ca/camofox-browser创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门