Skip to main content
Glama

Moonlighting

你把号建好,AI agent 替你管。 一个桌面 = 一个平台 = 一个账号:agent 在属于自己的隐藏 Windows 桌面里登录、操作、切换标签页,账号密码存进该桌面独立的凭据库,永远不进模型上下文。 邮箱也能交给它——用它去各平台把号申请下来,验证码自己收、自己填。

站点 PyPI License: MIT Platform

English · 中文

┌─ 你的桌面 ─────────────────────────────────┐
│  Hermes / Claude / Codex / 任何 MCP 客户端  │
└───────────────┬────────────────────────────┘
                │ MCP (stdio)  或  本地 HTTP
┌───────────────▼────────────────────────────┐
│  隐藏桌面 "HermesDesk"                      │
│  ┌──────────────────────────────────────┐   │
│  │ 真实 Chrome / Edge + 持久化 profile   │   │
│  │ (登录态、钱包、扩展都在里面)          │   │
│  └──────────────────────────────────────┘   │
│  凭据代理(DPAPI 加密、绑定 origin)         │
└─────────────────────────────────────────────┘

你看不到任何窗口闪出来,agent 也看不到任何密码。

它解决什么

让 agent 操作浏览器,通常死在三个地方:

翻车点

常见做法

这里的做法

找元素

截图 + 猜坐标,差 11px 就点空

一次无障碍树快照,按 [ref=e42] 精确点击

丢状态

每次都是全新 profile,登录态归零

私有桌面上的持久 profile,关掉重开还在

传密码

把密码贴进 prompt

你自己在掩码窗里输 → DPAPI 加密 → 代理注入,模型只拿到长度

42KB 的页面快照压到 6KB 再喂模型,20 步不会烧掉 20 万 token。

Related MCP server: Browser Control

快速开始

需要 Windows 10/11、Python 3.10+、Node 18+,以及任意 Chromium 系浏览器(Chrome 或 Edge)—— Windows 自带 Edge,所以干净的机器直接能用。浏览器不打包,用你系统里那个。

pip install moonlighting
moonlighting-setup --home "D:\moonlighting"   # 浏览器数据放哪
moonlighting                                  # 起推流 / 凭据代理 / 浏览器驱动

启动后会打印预览地址并自动打开窗口:

preview: http://127.0.0.1:8765/ctl

画面是实时的,可以直接在上面点、在上面打字——输入会反向送进隐藏桌面。

浏览器查找顺序:HERMES_CHROME → 注册表 App Paths → 常见安装路径 → PATH; 一个都没有时给一句看得懂的话,不抛 traceback。

接进 agent

一行配置(pip 安装后)

{ "mcpServers": { "browser": { "command": "moonlighting-mcp" } } }

moonlighting-mcp、moonlighting-setup、moonlighting-session、moonlighting 都是装包时生成的命令,没有任何绝对路径要填。

暴露 27 个工具:25 个 browser_*(快照、点击、输入、导航、标签页、求值…) 加 cred_list / cred_fill。Codex CLI 用 ~/.codex/config.toml:

[mcp_servers.browser]
command = "moonlighting-mcp"
startup_timeout_sec = 60
tool_timeout_sec = 120

从源码跑

git clone https://github.com/xinyuzjj/moonlighting.git
cd moonlighting && pip install -r requirements.txt
python scripts/svc.py

python scripts/install_mcp.py                 # 打印带真实路径的配置
python scripts/install_mcp.py --write all     # 直接写进 Claude / Codex(自动备份原文件)

凭据怎么走

用户 ──掩码弹窗──► DPAPI 加密存储 ──handle──► agent
                                              │
                             cred_fill(handle) │ origin 校验 + 读回校验
                                              ▼
                                           填进页面
  • 密码不进模型上下文。 agent 只拿到 {ok, verified, submitted, id_len, pwd_len}。

  • 绑定 origin。 给 https://github.com 存的凭据,在别的 origin 上会被直接拒绝。

  • 每次注入都有审计。 往 <home>/audit.jsonl 追加时间、动作、origin、handle、结果码,永不记明文。

  • token 门禁。 首个请求前生成 token.txt;/health、/status 保持开放,预览不会被卡住。

  • 不绕人机识别。 驱动的是真实浏览器,不改指纹、不打码、不硬闯风控。

多套实例并存

默认值(HermesDesk、8765/8897/8899/18763、一个全局指针文件)是按「一台机器一套」设计的。 机器上已经有一套在跑时,第二套给个自己的身份——全是环境变量:

export HERMES_BROWSER_HOME="E:\\browser\\junjunni"    # 端口表 / token / 凭据库(每套必须独立)
export HERMES_PROFILES="D:\\Hermes浏览器"             # profile 根,可共用,每个桌面一个子目录
export HERMES_DESK=junjunni
export HERMES_STREAM_PORT=8766 HERMES_BROKER_PORT=8900 HERMES_DRIVER_PORT=8898

代码靠这几条把它们隔开:

  • home 每套独立。 ports.json 和 token.txt 是按 home 命名的单文件,共用会互相覆盖端口表。

  • 认领端口要验身份。 服务在 /health、/status 里报 home + desk;端口上的服务报的是另一套身份,就当别人的跳过,而不是「它能回答所以是我的」。

  • 指针文件绝不静默改写。 setup.py --home 发现 settings.json 已指向别处会直接拒绝,--force 先备份再覆盖。

  • 环境变量穿得过 detach。 服务用 WMI 起(为了活过 agent 收尾),而 WMI 不继承调用者环境,所以 svc.py 把隔离变量写进 .cmd 包装脚本。缺这一步,隔离配置会静默丢失,数据写回默认目录。

  • 选过 home 就不回退旧路径。 profiles/、vault/ 都在它下面解析。

  • 凭据库跟着 home 走。 A 桌面的 handle 在 B 桌面的 broker 上会返回 not found,跨实例填不进去。

账号台账

一个桌面 = 一个平台 = 一个号。台账把这件事记下来,省得号多了靠人脑记:

python scripts/accounts.py add --desktop junjunni --platform github        --account me@example.com --cdp 18764 --actions read,post
python scripts/accounts.py list
python scripts/accounts.py remove --desktop junjunni
桌面            平台        账号                  权限      状态      新增
junjunni        github      me@example.com        read,post unknown   2026-10-18 11:15

台账只存元数据,不存密码——密码在各桌面自己的 vault 里。状态列现在是 unknown, 等登录态体检做出来才会填 ok / expired。文件默认在 %LOCALAPPDATA%\moonlightingccounts.json(MOONLIGHTING_ACCOUNTS 可覆盖)。

Agent 闭环(实验,agent/)

scripts/ 是手,agent/ 是套在手上的闭环——不在 wheel 里,从 clone 里跑。

cd agent
python run.py ../tasks/github-login-replay.json     # 步骤 + 断言 + 重试 + events.jsonl

文件

作用

obs.py

/snap → 压到 ≤6KB(只留可操作节点),shot() 抓像素兜底

verify.py

断言 DSL:url / text / eval / count

act.py

11 个原子动作,硬白名单

run.py

每步 3 次重试、连败截图、milestone 回滚、forbid / ask_user / done_when

mem.py

站点记忆:host+path → 成功动作序列

每条步骤都必须带断言,没有 check 的任务会被拒跑——这是「点了没反应还继续走」的唯一根治点。 完成判定只认 done_when,不认模型的自然语言声明。还没有 planner:任务是显式步骤列表。

实测数字

项

数字

快照压缩

GitHub 仓库页 56.2KB → 6.0KB(-89%);MDN 42.4KB → 6.0KB(-86%)

单步耗时

快照 83–250ms、click 39–1136ms、goto 586–1797ms

空点击检出

故意点不存在的元素 → 动作 FAIL + 断言 FAIL,10/10 命中

失败恢复

重试 3 次 → 第 2 次自动截图 → 断言失败 → 回滚 milestone(URL + 表单值)

内核

Chrome 155 与 Edge 148 均实测通过,CDP 同一套协议

隔离

两套实例并存:8765/8899/8897 与 8766/8900/8898,/health 各报各的 home+desk

目录结构

scripts/
  mcp_server.py      标准 MCP stdio server(browser_* + cred_*)
  browser_daemon.py  常驻驱动:快照 / 点击 / 输入 / 标签页 / 求值
  desk_stream.py     隐藏桌面实时画面(CDP screencast)+ 点击键盘反向通道
  vault_broker.py    凭据代理:DPAPI、origin 校验、选择器填充 + 读回校验
  cred_dialog.py     用户输入用的掩码弹窗
  login_flow.py      完整登录闭环:弹窗 → 填充 → 校验 → 提交 → 失败重弹
  desk_chrome.py     在指定私有桌面启动/关闭浏览器(自动找 Chrome 或 Edge)
  private_desktop.py Win32 桌面原语(CreateDesktopW、WM_SETTEXT、PrintWindow)
  svc.py             静默启停三个服务,启动后自动打开预览
  ports.py           端口分配 + 身份校验(默认端口被占就自动换)
  auth.py            token 文件与请求头
  install_mcp.py     给 Claude / Codex / Hermes 写 MCP 配置
  templates/         预览页(/ctl 可操作、/view 只读)
agent/               闭环层(obs / act / verify / run / mem)
tasks/               示例任务,含故意失败用例
docs/SKILL.md        长篇实战手册:每个坑都带实测数字

平台要求

只支持 Windows,这是设计选择:私有桌面(CreateDesktopW)、DPAPI、静默启动都是 Win32 原语。 浏览器控制这一层本身是可移植的——移植等于重写 private_desktop.py 和 DPAPI 那几处。

鸣谢

站在这些项目上:

项目

用在哪

microsoft/playwright-mcp

无障碍树快照引擎。browser_* 工具全部转发给它;没有它的 [ref=...] 模型,就不可能做到不碰坐标的点击。(Apache-2.0)

microsoft/playwright

playwright-mcp 底层的浏览器驱动。(Apache-2.0)

python-websockets/websockets

驱动、推流、凭据代理三处的 CDP 传输。

python-pillow/Pillow

实时画面的 JPEG 处理,以及画在画面上的光标。

借鉴了设计思路(未拷贝代码):

支持本项目

Moonlighting 免费、无广告、不收集任何数据,也不会因为没打赏就少更新一次。

如果它帮你省下了来回登录、切换平台、复制验证码的功夫,可以请作者喝杯咖啡 👇

许可证

MIT。playwright-mcp 与 Playwright 的 Apache-2.0 许可适用于对应组件。

Available Tools

27 tools
browser_clickB
Destructive

Perform click on a web page

Inputs: target (required), button, element, modifiers, +1 more.

browser_closeB
Destructive

Close the page

Inputs: no named parameters.

browser_console_messagesC
Read-only

Returns all console messages

Inputs: level (required), all, filename.

browser_dragA
Destructive

Perform drag and drop between two elements

Inputs: endTarget (required), startTarget (required), endElement, startElement.

browser_dropA
Destructive

Drop files or MIME-typed data onto an element, as if dragged from outside the page. At least one of "paths" or "data" must be provided.

Inputs: target (required), data, paths, element.

browser_emulate_mediaA
Destructive

Emulate CSS media features for the page, for example switch between the light and dark color scheme. Omitted parameters are left unchanged; null clears an override.

Inputs: media, contrast, colorScheme, forcedColors, +1 more.

browser_evaluateC
Destructive

Evaluate JavaScript expression on page or element

Inputs: function (required), target, element, filename.

browser_file_uploadB
Destructive

Upload one or multiple files

Inputs: paths.

browser_fill_formB
Destructive

Fill multiple form fields

Inputs: fields (required).

browser_findA
Read-only

Search the accessibility snapshot of the current page for text or a regular expression. Returns matching snapshot nodes with a few lines of surrounding context (like search snippets), each shown under its path from the root of the tree, which is cheaper than capturing the whole snapshot when you only need to locate an element and its ref.

Inputs: text, regex, filename.

browser_handle_dialogC
Destructive

Handle a dialog

Inputs: accept (required), promptText.

browser_hoverA
Destructive

Hover over element on page

Inputs: target (required), element.

browser_navigateC
Destructive

Navigate to a URL

Inputs: url (required).

browser_navigate_backA
Destructive

Go back to the previous page in the history

Inputs: no named parameters.

browser_network_requestA
Read-only

Returns full details (headers and body) of a single network request, or a single part if part is set. Use the number from browser_network_requests.

Inputs: index (required), part, filename.

browser_network_requestsA
Read-only

Returns a numbered list of network requests since loading the page. Use browser_network_request with the number to get full details.

Inputs: static (required), filter, filename.

browser_press_keyB
Destructive

Press a key on the keyboard

Inputs: key (required).

browser_resizeB
Destructive

Resize the browser window

Inputs: width (required), height (required).

browser_run_code_unsafeA
Destructive

Run a Playwright code snippet. Unsafe: executes arbitrary JavaScript in the Playwright server process and is RCE-equivalent.

Inputs: code, filename.

browser_select_optionB
Destructive

Select an option in a dropdown

Inputs: target (required), values (required), element.

browser_snapshotA
Read-only

Capture accessibility snapshot of the current page, this is better than screenshot

Inputs: boxes, depth, target, filename.

browser_tabsA
Destructive

List, create, close, or select a browser tab.

Inputs: action (required), url, index.

browser_take_screenshotA
Read-only

Take a screenshot of the current page. You can't perform actions based on the screenshot, use browser_snapshot for actions.

Inputs: scale (required), type, target, element, +2 more.

browser_typeB
Destructive

Type text into editable element

Inputs: text (required), target (required), slowly, submit, +1 more.

browser_wait_forB
Read-only

Wait for text to appear or disappear or a specified time to pass

Inputs: text, time, textGone.

cred_fillA

把已保存的账号密码填进当前页面并可选提交。密码由本机凭据代理解密并注入,不会出现在返回值或模型上下文里。origin 必须与保存时绑定的一致。

Inputs: origin (required), url, handle, cdp_port, +3 more.

cred_listA

列出已保存的凭据(只有 handle/label/origin,没有密码)。

Inputs: no named parameters.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    B3.2/5.0

    Scored across 27 tools

    Disambiguation4/5

    Most tools map to a distinct browser action (navigate, click, type, hover, drag, screenshot), and paired tools like browser_network_requests/browser_network_request and browser_snapshot/browser_find are clearly differentiated by description. However, some boundaries blur: browser_evaluate vs browser_run_code_unsafe both execute JS, and browser_drop vs browser_drag both involve drag semantics, which could cause occasional misselection.

    Naming Consistency4/5

    Names follow a consistent snake_case browser_<verb|noun> pattern (browser_navigate, browser_click, browser_take_screenshot, browser_network_requests) with predictable prefixes. The only deviation is the two cred_* tools, which use a different domain prefix but still keep the same verb_noun style, so it remains readable.

    Tool Count3/5

    At 27 tools the set sits above the 25-tool 'heavy' threshold, and a few pairs (network_requests/network_request, evaluate/run_code_unsafe) could be consolidated. Still, browser automation is a genuinely broad domain where most tools earn their place, so it is heavy rather than bloated.

    Completeness4/5

    The surface covers the full browser lifecycle: navigation, element interaction, forms, tabs, dialogs, file upload, network/console inspection, snapshots, screenshots, media emulation, and credential filling. Minor gaps exist (no explicit cookie/storage-state management or download handling), but core workflows are well covered.

    Maintenance

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      C
      maintenance
      Enables AI agents to drive a real headful Chrome browser using vision-first computer interactions, with support for multi-agent tab groups, password-manager-filled logins, and human handoff.
      6 npm
      MIT
    • A
      license
      Not graded
      quality
      B
      maintenance
      Enables a language model to see and act in the operator's own Chromium-based browser session through the Chrome DevTools Protocol, using the same screenshots and accessibility tree while keeping credentials encrypted and requiring human confirmation for payments, irreversible deletions, and secret changes.
      MIT