关于 sitelooper
sitelooper 是一款 CLI,可用 LLM 记录浏览器任务并编译为 Playwright 测试。已记录步骤可在不调用模型的情况下重放,发生变化的步骤则可通过恢复流程重新处理。
主要功能
- 01根据指令记录浏览器任务
- 02重放稳定且经过效果检查的步骤
- 03使用模型恢复发生变化的步骤
- 04编译独立的 Playwright 测试
- 05对生成的测试执行就绪检查
适用场景
- 01将重复的浏览器任务转为 CI 测试
- 02无需调用模型即可重放工作流
- 03应用页面变化后维护测试
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
npx -y sitelooperOPENROUTER_API_KEY密钥API key for the default model provider (OpenRouter). Not needed if another provider's key variable is set; run `sitelooper doctor` to see which one is used.本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
工具
17收录的工具列表 · 更新于 6 天前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| cwd | string | 否 | 命令运行所在的项目目录(绝对路径);从此处查找 sitelooper.config.json。默认值:服务器的工作目录。 |
| url | string | 是 | 要打开的 URL。 |
| learn | boolean | 否 | 在此会话中记录流程(--learn)。仅在会话启动时生效。 |
| headed | boolean | 否 | 显示浏览器窗口(--headed)。仅在会话启动时生效。 |
| session | string | 否 | 浏览器会话名称(默认值为 "default")。每个会话都有独立的浏览器和守护进程。 |
详细介绍
sitelooper
用通俗英语编写浏览器测试。在 CI 中以原生 Playwright 运行。应用发生变化时自动修复测试。
sitelooper 是一个内置轻量 LLM Agent 的 CLI。每次给它一条指令即可,例如:
“以 [email protected] 登录,创建标题为 'k7 Bench' 的工单,并报告其 ID”。它会操作
实时浏览器并返回一个已验证的结果。每个成功完成的步骤都会被编译成一个
无需模型、无需 Token、零成本的流程,可导出为独立的 @playwright/test 规范。

首次在仓库内的维修服务台应用中录制(由 Agent 驱动时,耗时 24 秒、28 秒和 42 秒)。 应用重置后,相同流程会在 16 秒内以零模型调用重放,随后查询应用自身的状态。
工作原理
- 录制。 一个低成本模型会遵循你的指令。工作过程中,sitelooper 会记录持久化的 定位器、每个值的来源,以及每个步骤对页面所做的更改。
- 重放。 录制内容会以确定性方式重放。如果应用在某个步骤执行前发生变化,只有 该步骤会交由模型处理;验证通过的恢复方案会被固定到流程中,使流程在多次运行中自行修复。
- 编译。
sitelooper build会生成一个 Playwright 规范,并为每个步骤添加效果检查。 它可以在 CI 中运行,无需 sitelooper 守护进程,也无需模型。
结果
在十个可自托管的应用中,每项任务先执行一次并记录,然后在不使用模型的情况下回放两次,最后编译为 Playwright 规范。对比工具是由 LLM 驱动的 agent-browser,它会在每次运行时重新执行任务。每次运行都根据应用自身的数据库或 API 进行评分,而非依据工具自己的报告。2026 年 9 月:
| app | sitelooper,首次运行 | sitelooper 回放 ×2 | 编译后的规范 | agent-browser,GPT-6 Luna | agent-browser,GPT-6.1 Sol |
|---|---|---|---|---|---|
| repair-desk | 6/6 · $0.02 · 187s | 6/6 · $0 · 52s | 6/6 | 6/6 · $0.01 | 6/6 · $0.29 |
| Odoo | 6/6 · $0.07 · 589s | 6/6 · $0 · 193s | 6/6 | 3/6 · $0.02 | 6/6 · $0.37 |
| Grafana | 6/6 · $0.09 · 615s | 6/6 · $0 · 145s | 通过 | 5/6,随后 6/6 · $0.04 | 4/6 · $0.33 |
| Kanboard | 6/6 · $0.05 · 357s | 6/6 · $0 · 56s | 4/4 + 2 项仅报告 | 6/6,评论重复 · $0.005 | 6/6 · $0.23 |
| OpenProject | 7/7 · $0.05 · 377s | 7/7 · $0 · 93-132s | 5/5 + 2 项仅报告 | 7/7,评论重复 · $0.02 | 7/7 · $0.38 |
| Gitea | 7/7 · $0.04 · 324s | 7/7 · $0 · 76s | 5/5 + 2 项仅报告 | 6/7 · $0.09 | 7/7 · $0.25 |
| Vikunja | 7/7 · $0.04 · 270s | 7/7 · $0 · 76s | 5/5 + 2 项仅报告 | 7/7,评论重复 · $0.01 | 7/7 · $0.28 |
| EspoCRM | 7/7 · $0.06 · 454s | 7/7 · $0 · 165s | 拒绝编译 | 1/7 · $0.02 | 7/7 · $0.23 |
| Snipe-IT | 7/7 · $0.04 · 298s | 7/7 · $0 · 78s | 5/5 + 2 项仅报告 | 1/7 · $0.02 | 7/7 · $0.25 |
| Ghost | 7/7 · $0.02 · 142s | 7/7 · $0 · 46s | 5/5 + 2 项仅报告 | 7/7 · $0.005 | 7/7 · $0.10 |
- sitelooper 在全部 30 次运行中验证了每一项目标,指令由 GPT-6 Luna 编写,浏览器由 DeepSeek V4.1 Flash 驱动。每次回放都未调用模型。十个编译后的规范中有九个通过;EspoCRM 的规范拒绝编译,而不是猜测。
- **agent-browser 使用相同模型时,11 次运行中只有 3 次无误。**它在 Odoo、EspoCRM 和 Snipe-IT 上彻底失败,在三个应用中发布了重复评论,并有两次报告任务已完成,但应用并未保存。
- **使用单个 token 价格约高出 20 倍的 GPT-6.1 Sol 时,agent-browser 在 10 次运行中有 9 次无误。**它每次运行仍需支付这一价格;在 Grafana 上,它再次将未保存的设置报告为已验证。
仅报告目标要求运行在最终报告中陈述某个值,而编译后的规范不会写入该值。
- bench/RESULTS.md:协议、模型、成本,以及基准测试无法说明的内容
- bench/MATRIX-2026-09.md:每次运行的详情,包括运行 ID、提交和结果分支
- bench/SWEEPS.md:每轮扫描一行,并说明每次未达标的原因
快速开始
你需要 Node 20+,以及用于编写的 OpenRouter API 密钥。编译后的测试只需要 @playwright/test。
npm install -g sitelooper
npm install -D @playwright/test && npx playwright install chromium
export OPENROUTER_API_KEY=sk-or-...
sitelooper doctor # shows the provider and models it will use, and why
sitelooper init # writes sitelooper.config.json
每条指令记录一个逻辑结果。凭据请使用 {{env:NAME}},并用 var 声明应在不同运行间变化的值。这里,demo 会变成一个 {{runid}} 占位符:
sitelooper --session ticket --learn open http://localhost:3000
sitelooper --session ticket var runid=demo
sitelooper --session ticket do "Sign in as {{env:TEST_USER}} using {{env:TEST_PASSWORD}} and verify the dashboard opens"
sitelooper --session ticket do "Create a ticket titled 'demo Test'; verify it appears and report its id"
sitelooper --session ticket assert "the ticket list shows 'demo Test' with status Open"
sitelooper --session ticket stop --save-flow ticket
sitelooper flow export ticket --out .sitelooper/procedures.json
构建测试并运行:
sitelooper build ticket --var runid=test-{n} --reset-cmd "npm run reset:e2e"
# or, if your Playwright fixtures already prepare fresh data:
# sitelooper build ticket --var runid=test-{n} --fixture-isolation
# with a reset command, build converges by default (2 rounds): it re-records the steps a refusal or
# a failing spec names (the model works those steps only). Tune or opt out:
# sitelooper build ticket --var runid=test-{n} --reset-cmd "npm run reset:e2e" --converge 3 [--rerecord-runs 2] [--json]
# sitelooper build ticket --var runid=test-{n} --reset-cmd "npm run reset:e2e" --no-converge
npx playwright test tests/sitelooper/ticket.spec.ts
ticket.flow.ts 会自动生成:其中包含流程、带类型的输入/输出 API、具名步骤和效果检查。ticket.spec.ts 由你维护。在其中添加业务断言和测试夹具,重新编译也不会覆盖它。
为什么 Agent 自己写的脚本无法重跑
让 Agent 根据它执行过的操作编写脚本,会因结构性原因而失败:
- 运行时的值被写死了。 脚本中引用了今天的记录 ID,因此明天会打开昨天的记录,或者把错误记录处理完后报告成功。
- 选择器指向的是位置,而非元素本身。
tr:nth-of-type(3)、生成的类名,以及名称包含当前分钟数的文本框。 - 等待步骤消失了。 Agent 每次观察时的停顿,都是应用所需的等待时间。
- 无人检查操作效果。 点击可能在错误的元素上“成功”,保存也可能被脚本未能发现的对话框拒绝。
sitelooper 将录制内容视为编译依据,而不是要重放的文本:
- 定位器链,包括角色和名称、标签、测试 ID 以及结构路径。通过测量每次重放时哪些候选定位器能够匹配,逐步淘汰不合适的候选项。
- 使用参数,而非字面值。 输入的值会变成槽位,声明的值会变成
{{runid}}。某个步骤读取、后续步骤使用的值,会在它们之间实时传递。应用生成的 ID 会在每次运行时重新读取,而没有来源的值则留待恢复处理,不会被猜测。 - 效果门控。 如果某个步骤已运行,但未产生录制时记录的效果,就会停止重放,避免下一步在错误状态下操作。
- 恢复阶梯。 每个步骤首先在不调用模型的情况下重放。若失败,则由轻量模型恢复;若受阻,则由更强的模型尝试;若仍然失败,运行就会停止,并报告各步骤的状态。
- 工具中不包含任何特定于应用的内容。 应用知识会放在可选的会话级说明中,因此针对某个应用的修复不会变成该应用的专用变通方案。
在项目中使用
就绪性意味着运行生成的代码
build <flow-or-bundle> 会编译工作流并运行就绪性门禁。check <name.flow.ts> --ready 会对现有产物运行相同的门禁。普通的 compile 可离线运行,普通的
check 则运行一次规范。
默认情况下,如果有可用的重置命令(--reset-cmd 或项目配置中的
resetCommand),build 还会进行收敛:运行一次已编译的规范;如果编译被拒绝、规范失败,或规范只有通过阻塞性定位器回退(见下文)才通过,就重新记录它指出的步骤(2 轮;--converge N 用于设置
轮数),然后对通过的规范运行就绪性门禁。如果没有重置命令,就无法在干净的应用上检查,因此会像之前一样编译并运行就绪性检查,同时输出一行说明(使用 --json 时,会包含 convergence: { skipped: true, why } 字段)收敛已跳过。重新记录会调用模型:如果没有配置 API 密钥,build 会编译并检查一次;如果需要重新记录,则以 unavailable 状态停止。--no-converge 会恢复为仅编译并运行就绪性检查。
收敛还会补做录制时执行、但工作流中遗漏的操作。更改了应用却未报告成功的指令不属于工作流步骤(在工作流文件中标记为 omitted,编译时会发出 omitted-work 警告);如果后续工作流步骤没有重新输入这些值,build 会将该指令作为步骤插回录制时的位置(在 04-… 后插入 04b-…,因此现有步骤 ID 不会改变),再用模型重新录制;步骤固定后,遗漏项便会移除。不开启收敛时,build 会在就绪性检查前停止,并指出缺少哪条指令:没有这项操作也能通过的规范,并不能证明任务已完成。普通的 compile 仍只会发出警告。
该门禁要求在禁用重试的情况下,连续干净地执行 3 次。每次运行都从重置命令或已声明的固定数据准备好的状态开始,并至少使用 2 组不同的数据集({n} 会依次替换为 1、2、3)。不得跳过任何步骤,也不得有未解决的步骤;并且不能依靠任何定位器回退来决定运行操作的对象或所用内容:操作上的回退,或后续步骤或断言会使用其值的读取操作上的回退,都会导致检查不通过。仅为最终报告提供数据的读取操作上的回退会列在证据的 warnings 下,不会导致检查不通过。收敛也采用相同规则,因此经其判定为已收敛的规范不会因相同的回退而被就绪性检查拒绝。该门禁使用你的 Playwright 配置和项目(--config、--project)。证据会写入 <name>.readiness.json,也可通过 --report <file.json> 指定文件。连续 3 次干净运行是执行门禁,并不代表能从统计意义上保证没有不稳定问题。
--negative-spec <file> 会添加一项由用户编写的故障注入测试,该测试必须无法通过结果断言,并报告 failureDetection: verified。请参阅
项目固定数据与负向检查。
断言:每次运行都必须满足的条件
do 记录 Agent 做了什么。assert 记录你希望为真的内容,并确保这项检查不会被跳过。它接受一个句子,且不会对页面执行任何操作:
sitelooper --session ticket assert "the order total is 370.00"
sitelooper --session ticket assert "no error banner is showing"
系统会立即根据当前页面检查该句子。退出码 0 表示条件成立,1 表示条件不成立,2 表示检查无法运行。在 --learn 会话中,通过的断言也会作为独立步骤记录到已保存的工作流中。模型会在录制时查找一次元素。之后,该步骤会在不使用模型的情况下重放并编译。
断言可以记录七种条件:元素可见、元素隐藏、元素文本等于某个值、元素文本包含某个值、匹配元素的数量、表单字段的值,以及页面 URL 包含某个值。
预期值必须写在句子中。它可以是字面值、已声明变量的值,或先前步骤报告的值。Agent 仅从页面读取的值不算:如果写成“总数正确”,就会变成“总数等于它显示的任何值”,这样页面每次运行都会与自身一致。如果句子没有指定要比较的值,断言就会失败并说明原因。
录制的断言是严格的。如果条件不成立,或找不到对应元素,就不会尝试逐级恢复、调用模型或重新固定定位器,也不能将其视为已完成并跳过:
- 在
sitelooper run中,该步骤的状态为assert-failed,且工作流会停止。 - 在已编译的规范中,测试会失败,并显示
assertion failed: <your sentence> — <detail>;如果没有已记录的定位器找到该元素,则会显示assertion could not be checked: <your sentence> — <detail>。
assert 适用于可以用句子描述的条件。对于其他情况,例如计算后的比较,或针对应用自身 API 的检查,请在 .spec.ts 中编写 expect 调用。该文件由你管理,重新编译不会覆盖它。请参阅如何在两者之间选择。
项目配置
sitelooper init 会创建 sitelooper.config.json。最近的祖先目录中的配置文件提供默认值,CLI 标志会覆盖这些默认值。切勿在此文件中放置凭据。
{
"targetUrl": "http://localhost:3000",
"vars": { "runid": "test-{n}" },
"requiredVars": ["runid"],
"resetCommand": "npm run reset:e2e",
"playwright": { "config": "playwright.config.ts", "project": "chromium" },
"verificationRuns": 3,
"outputDir": "tests/sitelooper",
"snapshotFile": ".sitelooper/procedures.json"
}
targetUrl 可以相对于 Playwright 的 baseURL。fixtureIsolation: true 会替代重置命令。flow export 会将工作流及其已固定的过程打包,以便其他机器可以编译它。请提交打包文件、配置文件和生成的文件。
一次性验证码
将测试账户的 TOTP 秘钥(base32 格式,或完整的 otpauth:// URI)放入环境变量,并在验证码填写位置写入 {{totp:NAME}}。输入时才会生成验证码。录制内容和已编译的规范只保留标记,不会保留秘钥或验证码。
export TEST_TOTP=JBSWY3DPEHPK3PXP # a test user's seed, never a real person's
sitelooper --session ticket do "Enter the authentication code {{totp:TEST_TOTP}} and verify the dashboard opens"
修复一次,审核后应用
sitelooper repair tests/sitelooper/ticket.flow.ts --propose ticket-repair.json \
--var runid=repair-{n} --reset-cmd "npm run reset:e2e"
sitelooper repair apply ticket-repair.json
提案会执行一次实时分诊运行和多次收敛运行,然后在你的原始规范旁边,用纯 Playwright 检查暂存的候选版本。请先审核,再应用。apply 会准确写入已检查的源代码;如果此后有任何更改,它就会拒绝应用。你的 .spec.ts 永远不会被重写。如果诊断指出某条录制有问题,请运行 rerecord <flow> <step>,仅隔离并重新录制该步骤。
覆盖标志彼此独立:--allow-demoted 接受经过诊断并降级的过程,--overwrite-spec 则会替换用户脚手架。
从另一个 Agent 调用
--json 会返回带版本信息的结果(schemaVersion、stage、outcome、nextActions,其中 nextActions 为参数数组)。--report <file.json> 会将同一份文档写入固定路径。进度信息会输出到 stderr。对于多行指令,请使用 do --instruction-file <file> 或 do --stdin。
do 会返回 {report: {status, summary, details?, evidence?}, turns, usage, model}。达到轮数或时间上限时,它还会返回 actions,即已运行的工具调用,让调用方在恢复前检查状态。快照、重试和工具交互内容都保留在守护进程内部。
退出代码:0 成功,1 Agent/记录失败,2 输入无效,3 重放收敛失败,4 编译后的规范/就绪性检查失败。
Claude Code 插件
sitelooper 插件可指导 Claude 编写浏览器测试、修复测试并通过此 CLI 操作浏览器。它包含三个技能(write-browser-test、fix-browser-test 和 sitelooper),且不包含钩子或服务:
/plugin marketplace add restspace/sitelooper
/plugin install sitelooper@sitelooper
这些技能位于 plugin/skills/,也随 npm 包一同发布。若要在不使用插件的情况下使用它们,请将每个文件夹复制到 ~/.claude/skills/。关于该插件运行和发送的内容,请参阅 plugin/README.md。
MCP 服务
sitelooper mcp 是一个 stdio MCP 服务。它向 MCP 客户端提供工具,用于打开页面、运行 do 和 assert、构建并检查流程,以及修复或重新录制步骤。它会在底层运行 CLI,因此会话与终端共享。请在自己的环境中设置模型密钥(例如 OPENROUTER_API_KEY)。建议通过继承环境变量或提示输入的方式提供密钥,而不是将其写入 JSON 文件。
Claude Code:
claude mcp add --transport stdio sitelooper -- npx -y [email protected] mcp
添加 --scope project 可通过 .mcp.json 共享配置,添加 --scope user 可将配置用于所有项目。密钥会从启动 Claude Code 的 shell 环境中继承。在原生 Windows 上,请使用 -- cmd /c npx -y [email protected] mcp。
Cursor(.cursor/mcp.json,或用于所有项目的 ~/.cursor/mcp.json)、Claude Desktop(claude_desktop_config.json)和 Windsurf(mcp_config.json)使用相同的格式:
{
"mcpServers": {
"sitelooper": {
"command": "npx",
"args": ["-y", "[email protected]", "mcp"],
"env": { "OPENROUTER_API_KEY": "${env:OPENROUTER_API_KEY}" }
}
}
}
在 Cursor 和 Windsurf 中,${env:NAME} 会读取环境变量。在 Claude Desktop 中,请省略 env,并在启动应用前将密钥设置为用户环境变量。
VS Code(.vscode/mcp.json)使用 servers,并可提示输入密钥而不将其存储:
{
"inputs": [
{ "type": "promptString", "id": "sl-key", "description": "Model API key", "password": true }
],
"servers": {
"sitelooper": {
"type": "stdio",
"command": "npx",
"args": ["-y", "[email protected]", "mcp"],
"env": { "OPENROUTER_API_KEY": "${input:sl-key}" }
}
}
}
工具:open、do、assert、set_var、peek、screenshot、save_flow、list_flows、build_start、check、repair_propose、repair_apply、rerecord、job_status、sessions、stop、doctor。构建和其他长时间运行的任务会返回一个 job_id,可用 job_status 查询。有关输入、安全规则和故障排除,请参阅 docs/mcp.md。
参考
提供商
LLM 层是带预设的 OpenAI 兼容适配器。
| 预设 | 基础 URL | 默认模型 | 升级模型 | 密钥环境变量 |
|---|---|---|---|---|
openrouter(默认) | https://openrouter.ai/api/v1 | deepseek/deepseek-v4.1-flash(DeepSeek 后端) | z-ai/glm-5.3 | OPENROUTER_API_KEY |
zhipu | https://api.z.ai/api/paas/v4 | glm-5.2 | — | GLM_API_KEY / ZHIPU_API_KEY |
novita | https://api.novita.ai/openai | deepseek/deepseek-v4-flash | zai-org/glm-5.3 | NOVITA_API_KEY |
openai | https://api.openai.com/v1 | gpt-5-mini | — | OPENAI_API_KEY |
anthropic | https://api.anthropic.com(原生 Messages API) | claude-sonnet-5 | — | ANTHROPIC_API_KEY |
每个字段都按 标志 > 环境变量 > 配置文件 > 预设 的顺序解析:--provider、
--model、--base-url、--fallback-model;SITELOOPER_PROVIDER、SITELOOPER_MODEL、
SITELOOPER_FALLBACK_MODEL、SITELOOPER_BASE_URL、SITELOOPER_API_KEY;以及
sitelooper config set <key> <value>,它会写入 ~/.sitelooper/config.json。密钥优先使用环境变量。
- 未指定提供商。 Z.ai 密钥会使用
zhipu。否则,OPENROUTER_API_KEY会选择openrouter,通用的SITELOOPER_API_KEY会使用zhipu,若完全没有密钥,则默认使用openrouter。sitelooper doctor会打印所选项及原因。 - 路由固定。 使用默认模型时,
openrouter预设会发送{"provider":{"only":["DeepSeek"]}},这是基准测试定价所假定的后端。SITELOOPER_EXTRA_BODY会替换此设置,'{}'则会将其关闭。SITELOOPER_FALLBACK_EXTRA_BODY可为升级模型设置相同配置。 - 升级。 被报告为
blocked的指令会在同一个浏览器中使用升级模型重试一次,并要求该模型在重复任何操作前重新检查状态。已验证的failure不会重试。--no-escalate可关闭此功能。
配置
| 环境变量 / 标志 | 默认值 | |
|---|---|---|
SITELOOPER_CHANNEL | chrome → msedge → bundled | 浏览器通道 |
SITELOOPER_EXECUTABLE | — | 指定浏览器二进制文件 |
SITELOOPER_HEADED=1、--headed | 无头模式 | 可见窗口(会话中的首次调用) |
SITELOOPER_HOME | ~/.sitelooper | 会话、技能、流程、配置 |
SITELOOPER_SKILLS=1、--learn | 关闭 | 学习模式;SITELOOPER_SKILLS_DIR 可更改存储位置 |
SITELOOPER_FLOWS_DIR | ~/.sitelooper/flows | 流程文件 |
SITELOOPER_RECORD=1、--record | 关闭 | 每个标签页录制为 webm;路径由 stop 打印 |
SITELOOPER_SCRIPT=1、--script | 关闭 | 将每个操作录制为原始 Playwright 步骤(探索性) |
--max-turns | 30 | 每条指令的 Agent 轮次上限 |
--timeout | 300 | 每条指令的墙钟时间(秒) |
--turn-timeout | 90 | 单次 LLM 调用中止并发出提醒前等待的秒数 |
运行 sitelooper --help 查看完整命令参考。
它不会做什么
- Canvas 渲染的内容没有可读取或验证的 DOM。Agent 会报告为 blocked。
- 规避反机器人措施、破解 CAPTCHA 和爬取不在范围内。sitelooper 用于你运营或获准测试的应用。
- 视觉识别。 Agent 仅处理文本,并读取无障碍树和 DOM。
- 猜测凭据。 缺失或被拒绝的凭据会立即报告为 blocked,不会重试。
开发
npm run build # tsc -> dist/
npm test # unit tests
BP_BROWSER_TESTS=1 npx vitest run # + browser-backed replay tests (needs Chrome/Edge)
test/rebuild.test.ts 会重新编译真实发布的录制内容,并固定其编译结果。它会运行已构建的引擎,因此测试前请先构建。基准测试流程和目标位于
bench/ 下。
sitelooper 之前曾以
sleep-walker的名称发布到 npm。早期名称中的环境变量前缀和主目录 仍可作为别名使用;旧命令名称则不再可用。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。