nekoro-browser 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nekoro_browser-0.1.0/.claude/skills/nekoro-browser/SKILL.md +52 -0
- nekoro_browser-0.1.0/.gitignore +25 -0
- nekoro_browser-0.1.0/AGENTS.md +46 -0
- nekoro_browser-0.1.0/LICENSE +21 -0
- nekoro_browser-0.1.0/PKG-INFO +255 -0
- nekoro_browser-0.1.0/README.md +230 -0
- nekoro_browser-0.1.0/README.zh-CN.md +214 -0
- nekoro_browser-0.1.0/SKILL.md +244 -0
- nekoro_browser-0.1.0/domain-skills/README.md +125 -0
- nekoro_browser-0.1.0/extension/background.js +1098 -0
- nekoro_browser-0.1.0/extension/icons/icon-128.png +0 -0
- nekoro_browser-0.1.0/extension/icons/icon-16.png +0 -0
- nekoro_browser-0.1.0/extension/icons/icon-48.png +0 -0
- nekoro_browser-0.1.0/extension/icons/icon-512.png +0 -0
- nekoro_browser-0.1.0/extension/icons/logo.svg +39 -0
- nekoro_browser-0.1.0/extension/keepalive.js +34 -0
- nekoro_browser-0.1.0/extension/manifest.json +40 -0
- nekoro_browser-0.1.0/extension/options.html +41 -0
- nekoro_browser-0.1.0/extension/options.js +25 -0
- nekoro_browser-0.1.0/pyproject.toml +66 -0
- nekoro_browser-0.1.0/src/nekoro_browser/__init__.py +3 -0
- nekoro_browser-0.1.0/src/nekoro_browser/agent_helpers.py +11 -0
- nekoro_browser-0.1.0/src/nekoro_browser/auth.py +62 -0
- nekoro_browser-0.1.0/src/nekoro_browser/bridge.py +448 -0
- nekoro_browser-0.1.0/src/nekoro_browser/cli.py +387 -0
- nekoro_browser-0.1.0/src/nekoro_browser/config.py +70 -0
- nekoro_browser-0.1.0/src/nekoro_browser/daemon.py +234 -0
- nekoro_browser-0.1.0/src/nekoro_browser/helpers.py +784 -0
- nekoro_browser-0.1.0/src/nekoro_browser/lifecycle.py +219 -0
- nekoro_browser-0.1.0/src/nekoro_browser/mcp_server.py +290 -0
- nekoro_browser-0.1.0/src/nekoro_browser/paths.py +36 -0
- nekoro_browser-0.1.0/src/nekoro_browser/site_notes.py +188 -0
- nekoro_browser-0.1.0/tests/test_auth.py +118 -0
- nekoro_browser-0.1.0/tests/test_cli_timeout.py +74 -0
- nekoro_browser-0.1.0/tests/test_close_tab.py +71 -0
- nekoro_browser-0.1.0/tests/test_daemon.py +92 -0
- nekoro_browser-0.1.0/tests/test_dialog.py +99 -0
- nekoro_browser-0.1.0/tests/test_fill_input.py +80 -0
- nekoro_browser-0.1.0/tests/test_js_values.py +126 -0
- nekoro_browser-0.1.0/tests/test_keepalive.py +74 -0
- nekoro_browser-0.1.0/tests/test_lifecycle.py +143 -0
- nekoro_browser-0.1.0/tests/test_mcp.py +283 -0
- nekoro_browser-0.1.0/tests/test_navigate.py +69 -0
- nekoro_browser-0.1.0/tests/test_netidle.py +72 -0
- nekoro_browser-0.1.0/tests/test_new_tab.py +99 -0
- nekoro_browser-0.1.0/tests/test_paths.py +68 -0
- nekoro_browser-0.1.0/tests/test_pipelining.py +88 -0
- nekoro_browser-0.1.0/tests/test_port_config.py +118 -0
- nekoro_browser-0.1.0/tests/test_press_key.py +102 -0
- nekoro_browser-0.1.0/tests/test_reattach.py +63 -0
- nekoro_browser-0.1.0/tests/test_reload_ext.py +77 -0
- nekoro_browser-0.1.0/tests/test_setup.py +151 -0
- nekoro_browser-0.1.0/tests/test_site_notes.py +207 -0
- nekoro_browser-0.1.0/tests/test_stop.py +93 -0
- nekoro_browser-0.1.0/tests/test_upload.py +123 -0
- nekoro_browser-0.1.0/tests/test_ws_transport.py +230 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: nekoro-browser
|
|
3
|
+
description: 浏览器自动化——打开网页、搜索、点击、截图、执行 JS、填表、上传文件、处理对话框。通过 Chrome 扩展的 chrome.debugger API 操控用户日常浏览器,保留登录态,不开调试端口。触发词:"浏览器"、"打开网页"、"搜索"、"截图"、"点击"、"填表"、"上传文件"、"自动化操作"。
|
|
4
|
+
allowed-tools: Bash(nekoro-browser:*) Bash(python:*) Read Edit Write
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# nekoro-browser
|
|
8
|
+
|
|
9
|
+
通过 Chrome 扩展 + 持久 WebSocket 操控用户日常 Chrome 的 CLI 工具。不需要
|
|
10
|
+
`--remote-debugging-port`(Chrome 136 起该方式无法接默认 profile),保留真实登录态。
|
|
11
|
+
|
|
12
|
+
**完整命令参考、故障排查、领域技能见仓库根目录 [`SKILL.md`](../../../SKILL.md)——先读那份,本文件只补 Claude Code 场景的要点。**
|
|
13
|
+
|
|
14
|
+
## 前置条件
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install -e . # 或 uv pip install -e .,注册 nekoro-browser 命令
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
加载 Chrome 扩展:`chrome://extensions` → 开发者模式 → 加载已解压的扩展程序 → 选
|
|
21
|
+
`extension/` 目录(`nekoro-browser --extension-path` 打印绝对路径)。未打包扩展会被
|
|
22
|
+
Chrome 更新/重启后自动停用,`--doctor` 说 SW 不响应时先去那页确认它还开着。
|
|
23
|
+
|
|
24
|
+
## 快速开始
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
# 终端 1:启动 daemon(前台,保持打开)
|
|
28
|
+
nekoro-browser
|
|
29
|
+
|
|
30
|
+
# 终端 2:验证
|
|
31
|
+
nekoro-browser --doctor
|
|
32
|
+
echo "page_info()" | nekoro-browser
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
daemon 默认监听 `127.0.0.1:28417`(选此端口是为了不与同类工具 `@jackwener/opencli` 的
|
|
36
|
+
19825 撞车,若你机器上也装了 OpenCLI,两者可共存但同一时刻按需只启一个 daemon)。
|
|
37
|
+
|
|
38
|
+
改端口:Python 侧 `nekoro-browser --port 30500` 或环境变量 `NEKORO_PORT`,
|
|
39
|
+
扩展侧在扩展详情页的「扩展程序选项」里设成同一个。客户端不用重复传参——
|
|
40
|
+
daemon 把实际端口写进 `<数据目录>/port`,管道模式自己会读。
|
|
41
|
+
|
|
42
|
+
## 每次调用前
|
|
43
|
+
|
|
44
|
+
检查 daemon 是否存活(`nekoro-browser --doctor`),死了 `nekoro-browser --reload-ext`
|
|
45
|
+
或重启;扩展 service worker 偶尔需要在 `chrome://extensions` 手动重载(尤其 Chrome
|
|
46
|
+
刚重开时)。
|
|
47
|
+
|
|
48
|
+
## 自愈
|
|
49
|
+
|
|
50
|
+
缺少函数时编辑 `src/nekoro_browser/agent_helpers.py` 添加——**只有这个文件**每次
|
|
51
|
+
`/exec` 前会自动 reload,改完立即生效、无需重启 daemon。改 `helpers.py` 本身需要重启
|
|
52
|
+
daemon 才生效(daemon 启动时导入一次)。
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
dist/
|
|
6
|
+
build/
|
|
7
|
+
*.egg
|
|
8
|
+
|
|
9
|
+
# Virtual environments
|
|
10
|
+
.venv/
|
|
11
|
+
venv/
|
|
12
|
+
# 库不锁依赖版本(本来就零依赖),锁文件只是本地噪音
|
|
13
|
+
# 注意:gitignore 不支持行尾注释,注释必须独占一行,否则整行都会被当成 pattern
|
|
14
|
+
uv.lock
|
|
15
|
+
|
|
16
|
+
# IDE
|
|
17
|
+
.vscode/
|
|
18
|
+
.idea/
|
|
19
|
+
|
|
20
|
+
# OS
|
|
21
|
+
.DS_Store
|
|
22
|
+
Thumbs.db
|
|
23
|
+
|
|
24
|
+
# 本地一次性脚本(作者自用,不进仓库;目录本身也不跟踪)
|
|
25
|
+
scripts/
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
nekoro-browser is a CLI that lets an agent drive the user's daily Chrome via a MV3
|
|
2
|
+
extension (`chrome.debugger` API) + a local daemon, keeping the real login state —
|
|
3
|
+
no `--remote-debugging-port` (Chrome 136+ blocks that on the default profile anyway).
|
|
4
|
+
|
|
5
|
+
# Code priorities
|
|
6
|
+
- Clarity
|
|
7
|
+
- Precision
|
|
8
|
+
- Low verbosity
|
|
9
|
+
- Never fabricate success — helpers return `{"ok": false, "error": ...}` on failure,
|
|
10
|
+
not a silently-wrong `{"ok": true}`
|
|
11
|
+
|
|
12
|
+
# Overview
|
|
13
|
+
Two halves, one wire (HTTP + WebSocket, same port):
|
|
14
|
+
- `extension/` — MV3 extension. `background.js` is the service worker: WS transport to
|
|
15
|
+
the daemon, `chrome.debugger` attach/CDP dispatch, dialog auto-handling, tab lifecycle.
|
|
16
|
+
`keepalive.js` is a content script that gives the service worker an independent wake
|
|
17
|
+
vector (MV3 workers get evicted; a plain WS/alarm keep-alive alone isn't reliable).
|
|
18
|
+
- `src/nekoro_browser/` — the daemon + CLI.
|
|
19
|
+
- `daemon.py` — long-lived middleman process between the extension and the agent's `-c` code
|
|
20
|
+
- `bridge.py` — the WS/HTTP transport
|
|
21
|
+
- `lifecycle.py` — pid file, process fingerprint, stale-daemon self-heal
|
|
22
|
+
- `helpers.py` — CDP wrapper functions auto-imported into `-c` scripts, each a thin
|
|
23
|
+
(≤10 line) wrapper over one CDP capability
|
|
24
|
+
- `cli.py` — the `nekoro-browser` command
|
|
25
|
+
- `mcp_server.py` — the `nekoro-browser-mcp` command: stdio JSON-RPC MCP server for
|
|
26
|
+
clients that don't read skill files (Cursor / Cline / Claude Desktop). Tools are
|
|
27
|
+
reflected off `helpers.py` via `inspect.signature`, then forwarded to the same
|
|
28
|
+
daemon `/exec` endpoint the CLI uses — no second execution path to keep in sync.
|
|
29
|
+
|
|
30
|
+
`SKILL.md` tells agents how to use the CLI and lists every helper. `README.md` covers
|
|
31
|
+
install + quick start for humans.
|
|
32
|
+
|
|
33
|
+
An agent operating nekoro-browser edits two places:
|
|
34
|
+
- `src/nekoro_browser/agent_helpers.py` — task-specific browser helpers the agent adds
|
|
35
|
+
at runtime; hot-reloaded via `reload_agent_helpers()`, no daemon restart needed
|
|
36
|
+
- `domain-skills/` — Markdown notes on specific sites, written and read by the agent.
|
|
37
|
+
Empty by default and never imported; workflows go in `agent_helpers.py`.
|
|
38
|
+
|
|
39
|
+
# Testing
|
|
40
|
+
`tests/*.py` are stdlib-style, not pytest: `assert` + a final `print("ALL OK")`, run via
|
|
41
|
+
`uv run python tests/test_X.py`. Extension JS has no unit-test surface — verify syntax
|
|
42
|
+
with `node --check`, behavior needs a live Chrome.
|
|
43
|
+
|
|
44
|
+
# Contributing
|
|
45
|
+
Consider what is really needed. Prefer the smallest diff that fixes the bug. Don't add
|
|
46
|
+
speculative config/flags for scenarios that aren't happening yet.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Nekoro
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nekoro-browser
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Lightweight browser automation CLI + MCP server driving your everyday Chrome via the extension chrome.debugger API — keeps your login state, no --remote-debugging-port
|
|
5
|
+
Project-URL: Homepage, https://github.com/zeshuochen/nekoro-browser
|
|
6
|
+
Project-URL: Repository, https://github.com/zeshuochen/nekoro-browser
|
|
7
|
+
Project-URL: Issues, https://github.com/zeshuochen/nekoro-browser/issues
|
|
8
|
+
Project-URL: Documentation, https://github.com/zeshuochen/nekoro-browser/blob/master/SKILL.md
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: agent,browser-automation,cdp,chrome,chrome-devtools-protocol,llm,mcp,model-context-protocol,playwright-alternative
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: MacOS :: MacOS X
|
|
16
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
17
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Browsers
|
|
22
|
+
Classifier: Topic :: Software Development :: Testing
|
|
23
|
+
Requires-Python: >=3.12
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
<p align="center">
|
|
27
|
+
<img src="extension/icons/icon-128.png" width="80" alt="nekoro-browser">
|
|
28
|
+
</p>
|
|
29
|
+
|
|
30
|
+
<h1 align="center">nekoro-browser</h1>
|
|
31
|
+
|
|
32
|
+
<p align="center">
|
|
33
|
+
<a href="https://github.com/zeshuochen/nekoro-browser/actions/workflows/tests.yml"><img src="https://github.com/zeshuochen/nekoro-browser/actions/workflows/tests.yml/badge.svg" alt="tests"></a>
|
|
34
|
+
<a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.12%2B-blue" alt="Python 3.12+"></a>
|
|
35
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="MIT License"></a>
|
|
36
|
+
<a href="#mcp-cursor--cline--claude-desktop"><img src="https://img.shields.io/badge/MCP-supported-8A2BE2" alt="MCP supported"></a>
|
|
37
|
+
</p>
|
|
38
|
+
|
|
39
|
+
<p align="center">
|
|
40
|
+
Lightweight browser automation CLI + MCP server. Drives your everyday Chrome through an extension — <b>keeps your login state</b>, <b>no debug port</b>, <b>no banners</b>.<br>
|
|
41
|
+
<sub><a href="README.zh-CN.md">中文</a></sub>
|
|
42
|
+
</p>
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Why Not `--remote-debugging-port`?
|
|
47
|
+
|
|
48
|
+
Since Chrome 136, `--remote-debugging-port` / `--remote-debugging-pipe` **refuse the default profile** — you must point Chrome at a non-default `--user-data-dir`, i.e. a clean instance with none of your logins. An extension's `chrome.debugger` is not subject to that restriction, which is why nekoro goes through an extension.
|
|
49
|
+
|
|
50
|
+
| | CDP WebSocket | playwright-cli | opencli | **nekoro-browser** |
|
|
51
|
+
|------|:--:|:--:|:--:|:--:|
|
|
52
|
+
| Approach | `--remote-debugging-port` | Playwright extension | OpenCLI extension | Custom extension + persistent WebSocket |
|
|
53
|
+
| Install | one flag | `npm i -g` (~200MB) | npm / desktop app | `pip install` (stdlib only, zero deps) |
|
|
54
|
+
| Login state | ❌ fresh instance | ✅ | ✅ | ✅ |
|
|
55
|
+
| Modify the extension | — | Edit Playwright source | Edit OpenCLI source | ✅ right in this repo |
|
|
56
|
+
| Self-healing | ❌ | ❌ | ❌ | ✅ Agent edits helpers at runtime |
|
|
57
|
+
| MCP | ❌ | ✅ (separate `@playwright/mcp`) | ❌ | ✅ built in, 45 tools via `nekoro-browser-mcp` |
|
|
58
|
+
|
|
59
|
+
## Quick Start
|
|
60
|
+
|
|
61
|
+
**1 — Install** (Python 3.12+, zero third-party dependencies)
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
git clone https://github.com/zeshuochen/nekoro-browser
|
|
65
|
+
cd nekoro-browser
|
|
66
|
+
pip install -e .
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**2 — Load the extension**
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
nekoro-browser setup
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`setup` copies the extension directory to your clipboard and then waits — up to three
|
|
76
|
+
minutes — until the extension actually connects, so you find out it worked instead of
|
|
77
|
+
guessing. Meanwhile you do the part Chrome reserves for humans: open `chrome://extensions/`,
|
|
78
|
+
turn on **Developer mode**, click **Load unpacked**, paste the directory.
|
|
79
|
+
|
|
80
|
+
**3 — Start the daemon** — give it its own terminal and **leave it open**; it runs in the
|
|
81
|
+
foreground and closing that window stops it
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
nekoro-browser
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**4 — Drive the browser** from anywhere else
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
echo "page_info()" | nekoro-browser
|
|
91
|
+
# → {"ok": true, "result": {"title": "...", "url": "..."}}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
That's it. If step 4 says the daemon isn't running, or a command times out, run
|
|
95
|
+
`nekoro-browser --doctor` — it checks the daemon, the extension and the service worker
|
|
96
|
+
separately and tells you which one is down.
|
|
97
|
+
|
|
98
|
+
## Examples
|
|
99
|
+
|
|
100
|
+
Send a multi-step flow in one shot with a heredoc. Every helper is a top-level `await`:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
nekoro-browser <<'PY'
|
|
104
|
+
await new_tab("https://example.com")
|
|
105
|
+
print((await page_info())["title"]) # Example Domain
|
|
106
|
+
print((await get_markdown(max_chars=200))["result"])
|
|
107
|
+
print((await state(max_items=3))["result"]) # indexed interactive elements, model-ready
|
|
108
|
+
await close_tab()
|
|
109
|
+
PY
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
`state()` numbers the elements and `click_index(n)` clicks by number — the model never has to guess a CSS selector:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
nekoro-browser <<'PY'
|
|
116
|
+
await navigate("https://github.com/search?q=browser+automation&type=repositories")
|
|
117
|
+
await wait_for_load()
|
|
118
|
+
print((await state(max_items=40))["result"])
|
|
119
|
+
await click_index(12)
|
|
120
|
+
PY
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
All helpers are documented in [SKILL.md](SKILL.md).
|
|
124
|
+
|
|
125
|
+
## MCP (Cursor / Cline / Claude Desktop)
|
|
126
|
+
|
|
127
|
+
Every function in `helpers.py` is reflected into an MCP tool (45 today) — no glue code:
|
|
128
|
+
|
|
129
|
+
```json
|
|
130
|
+
{
|
|
131
|
+
"mcpServers": {
|
|
132
|
+
"nekoro-browser": {
|
|
133
|
+
"command": "nekoro-browser-mcp"
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
The daemon still has to be running in another terminal (`nekoro-browser`) — the MCP server just forwards tool calls to it over the same authenticated path as `echo ... | nekoro-browser`. Two escape hatches ship as tools: `cdp` (raw CDP command) and `exec_python` (arbitrary Python in the daemon namespace — a whole multi-step flow in one round trip).
|
|
140
|
+
|
|
141
|
+
Screenshots come back as image content so clients can render them. A helper's own failure (`{"ok": false}`) is surfaced as `isError` rather than being dressed up as success.
|
|
142
|
+
|
|
143
|
+
## API
|
|
144
|
+
|
|
145
|
+
| Category | Commands |
|
|
146
|
+
|----------|----------|
|
|
147
|
+
| Navigation | `navigate(url)`, `new_tab(url)`, `list_tabs()`, `switch_tab(id)`, `close_tab(id)` |
|
|
148
|
+
| Page info | `page_info()`, `page_html()`, `page_text()`, `get_markdown()`, `state()` |
|
|
149
|
+
| JavaScript | `js(code)`, `cdp(method, **p)`, `cdp_batch(*cmds)` |
|
|
150
|
+
| Interaction | `click_selector(sel)`, `click_index(n)`, `click_at_xy(x,y)`, `type_text(t)`, `fill_input(sel,t)`, `press_key(k)`, `upload_file(sel,path)` |
|
|
151
|
+
| Dialogs | `dialog_off()`, `get_last_dialog()` |
|
|
152
|
+
| Waiting | `wait_for_load()`, `wait_selector(sel)`, `wait_for_network_idle()`, `sleep(s)` |
|
|
153
|
+
| Screenshots | `capture_screenshot()`, `capture_screenshot("jpeg", 90)` |
|
|
154
|
+
|
|
155
|
+
## Architecture
|
|
156
|
+
|
|
157
|
+
```
|
|
158
|
+
Chrome extension (background.js) —— chrome.debugger / CDP
|
|
159
|
+
↕ persistent WebSocket
|
|
160
|
+
Python daemon (127.0.0.1:28417)
|
|
161
|
+
↕ HTTP /exec (token auth)
|
|
162
|
+
CLI (nekoro-browser) · MCP server (nekoro-browser-mcp)
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
`helpers.py` (46 thin wrappers) → CDP commands, each ≤10 lines, none of them aware of any particular website.
|
|
166
|
+
|
|
167
|
+
`lifecycle.py` manages the daemon: pid file + process fingerprint (avoids killing a reused pid), self-heal on stale daemon (CDP probe fails → auto cleanup and restart), localhost requests bypass the system proxy.
|
|
168
|
+
|
|
169
|
+
The extension is hardened against MV3 service worker eviction: a `content_scripts` heartbeat (an independent wake vector living in the page, reconnects and wakes the SW even after it's killed) + `onStartup` (reconnects instantly on Chrome cold start) + reattaches the last-driven tab after a restart instead of drifting to a blank tab.
|
|
170
|
+
|
|
171
|
+
## CLI
|
|
172
|
+
|
|
173
|
+
| Command | What it does |
|
|
174
|
+
|---------|---------------|
|
|
175
|
+
| `nekoro-browser` | Start the daemon (foreground) |
|
|
176
|
+
| `nekoro-browser setup` | Guided install: extension path + opens chrome://extensions + waits for it to connect |
|
|
177
|
+
| `nekoro-browser --doctor` | End-to-end diagnostic (daemon + extension + SW all alive?) |
|
|
178
|
+
| `nekoro-browser --stop` | Stop the daemon |
|
|
179
|
+
| `nekoro-browser --restart` | Stop and restart (foreground) |
|
|
180
|
+
| `nekoro-browser --reload-ext` | Reload the extension's service worker — run before a batch job for a clean state |
|
|
181
|
+
| `nekoro-browser --extension-path` | Print the extension directory (for "Load unpacked") |
|
|
182
|
+
| `nekoro-browser --port N` | Run the daemon on port N (default 28417) |
|
|
183
|
+
| `nekoro-browser -c "code"` | Run one snippet, print the result |
|
|
184
|
+
| `nekoro-browser --timeout N` | Seconds to allow a snippet (default 120 — page loads are slow) |
|
|
185
|
+
| `echo "code" \| nekoro-browser` | Pipe mode (daemon must already be running) |
|
|
186
|
+
|
|
187
|
+
## Configuration
|
|
188
|
+
|
|
189
|
+
The daemon listens on **28417** by default. To change it:
|
|
190
|
+
|
|
191
|
+
| Side | How |
|
|
192
|
+
|------|-----|
|
|
193
|
+
| Python (daemon + CLI + MCP) | `nekoro-browser --port 30500`, or set `NEKORO_PORT=30500` |
|
|
194
|
+
| Extension | Extension details → **Extension options** → set the port → Save (reconnects immediately, no reload) |
|
|
195
|
+
|
|
196
|
+
Both sides must agree. Clients don't need the flag repeated: the daemon records its
|
|
197
|
+
actual port in `<data dir>/port`, so a plain `echo ... | nekoro-browser` finds a daemon
|
|
198
|
+
running on a non-default port. Precedence is `--port` > `NEKORO_PORT` > that file > default.
|
|
199
|
+
|
|
200
|
+
## Self-Healing
|
|
201
|
+
|
|
202
|
+
`src/nekoro_browser/agent_helpers.py` is editable at runtime and reloaded on every `/exec`. When an agent hits a gap, it appends the missing function there — effective on the next call, no daemon restart, no extension reload.
|
|
203
|
+
|
|
204
|
+
`domain-skills/` is where site knowledge goes (page structure, selectors, gotchas) — Markdown only, and empty by default since everyone automates different sites. Write a workflow against your notes, drop it into `agent_helpers.py`, same convention (`daemon` as first argument). See [`domain-skills/README.md`](domain-skills/README.md).
|
|
205
|
+
|
|
206
|
+
## Platform Support
|
|
207
|
+
|
|
208
|
+
| Platform | Status |
|
|
209
|
+
|----------|--------|
|
|
210
|
+
| Windows | Primary development platform, exercised end to end |
|
|
211
|
+
| Linux / macOS | The code has the branches (XDG dirs, `chmod 600` token, `/proc` and `ps` liveness probes) and CI runs the unit tests on all three, **but the full "Chrome + extension" loop has never been run on a real macOS/Linux box** — reports welcome |
|
|
212
|
+
|
|
213
|
+
## Known Limitations
|
|
214
|
+
|
|
215
|
+
- **Unpacked extensions get disabled by Chrome.** An extension installed via "Load unpacked" may be switched off automatically after a Chrome update or restart, or hidden behind the "Disable developer mode extensions" prompt. When `--doctor` reports Extension/SW not responding, re-enable it in `chrome://extensions/` first. This project is **not published to the Chrome Web Store**, so the limitation is not going away soon.
|
|
216
|
+
- **Service worker keepalive is not 100%.** MV3 eviction timing is Chrome's call. The heartbeat + `onStartup` + reattach cover the vast majority of cases, but unattended long-running cron jobs should still health-check with `--doctor` and retry.
|
|
217
|
+
- **One active tab at a time.** Tabs can be listed and switched (`list_tabs` / `switch_tab`), but commands always go to the current active tab — there are no parallel sessions.
|
|
218
|
+
- **The MCP server handles requests serially.** During a `wait_selector(timeout=90)` every other request on that connection (including `ping`) queues behind it. Open separate client connections if you need concurrency.
|
|
219
|
+
|
|
220
|
+
## Troubleshooting
|
|
221
|
+
|
|
222
|
+
| Symptom | Cause | Fix |
|
|
223
|
+
|---------|-------|-----|
|
|
224
|
+
| `Daemon not running` | Daemon not started | Run `nekoro-browser` in terminal 1 |
|
|
225
|
+
| CDP timeout | Extension not connected / service worker asleep | `nekoro-browser --doctor` to diagnose; try `--reload-ext` or manually reload in `chrome://extensions` |
|
|
226
|
+
| Extension disabled by Chrome | Unpacked extension + Chrome update | Re-enable it in `chrome://extensions/`, then re-run `--doctor` |
|
|
227
|
+
| Page unchanged | Extension not attached to tab | Open a regular (non-chrome://) page, restart daemon |
|
|
228
|
+
| Port in use | Stale process | Kill the process on port 28417, or just run `nekoro-browser --stop` |
|
|
229
|
+
|
|
230
|
+
## Security
|
|
231
|
+
|
|
232
|
+
The daemon listens on `127.0.0.1` and `/exec` runs arbitrary Python, so the transport is guarded:
|
|
233
|
+
|
|
234
|
+
- **CLI / MCP → daemon** (`/exec`, `/raw`): a per-session token is written to a user-private file (`%LOCALAPPDATA%\nekoro-browser\token`, `chmod 600` on POSIX). Clients read it and send `X-Nekoro-Token`; missing/wrong token → `403`. Web pages and remote hosts can't read local files, so they can't obtain it. `/ping` stays open.
|
|
235
|
+
- **Extension → daemon** (`/ws`): the handshake `Origin` must be `chrome-extension://…`; a web page's `WebSocket` to localhost carries its own origin and is rejected.
|
|
236
|
+
|
|
237
|
+
Same-user local processes can read the token file — that boundary matches the OS user account, as with browser-harness's `chmod 600`.
|
|
238
|
+
|
|
239
|
+
## Feedback
|
|
240
|
+
|
|
241
|
+
Hit a problem, or missing a helper you need? Open an
|
|
242
|
+
[issue](https://github.com/zeshuochen/nekoro-browser/issues).
|
|
243
|
+
For bugs, include the output of `nekoro-browser --doctor`, your Chrome version and OS — saves a round trip.
|
|
244
|
+
|
|
245
|
+
PRs welcome. Run the tests first: `for f in tests/test_*.py; do python "$f"; done` (CI runs them on all three platforms too).
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## Acknowledgments
|
|
250
|
+
|
|
251
|
+
Core architecture derived from:
|
|
252
|
+
|
|
253
|
+
- **[browser-harness](https://github.com/browser-use/browser-harness)** — thin-wrapper philosophy (each function is a CDP alias, ≤10 lines), pipe mode, self-healing `agent_helpers.py`, domain-skills directory structure, `cdp()` raw access
|
|
254
|
+
- **[browser-act](https://github.com/browser-act/skills)** — `state()` indexed element tree, `*[N]` change markers, `waitSelector()` state polling, `getMarkdown()` page extraction
|
|
255
|
+
- **[Playwright](https://github.com/microsoft/playwright)** — CDP `Input.dispatchMouseEvent` real mouse events (`isTrusted:true`), extension + daemon dual-path architecture
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="extension/icons/icon-128.png" width="80" alt="nekoro-browser">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">nekoro-browser</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="https://github.com/zeshuochen/nekoro-browser/actions/workflows/tests.yml"><img src="https://github.com/zeshuochen/nekoro-browser/actions/workflows/tests.yml/badge.svg" alt="tests"></a>
|
|
9
|
+
<a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.12%2B-blue" alt="Python 3.12+"></a>
|
|
10
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="MIT License"></a>
|
|
11
|
+
<a href="#mcp-cursor--cline--claude-desktop"><img src="https://img.shields.io/badge/MCP-supported-8A2BE2" alt="MCP supported"></a>
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
<p align="center">
|
|
15
|
+
Lightweight browser automation CLI + MCP server. Drives your everyday Chrome through an extension — <b>keeps your login state</b>, <b>no debug port</b>, <b>no banners</b>.<br>
|
|
16
|
+
<sub><a href="README.zh-CN.md">中文</a></sub>
|
|
17
|
+
</p>
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Why Not `--remote-debugging-port`?
|
|
22
|
+
|
|
23
|
+
Since Chrome 136, `--remote-debugging-port` / `--remote-debugging-pipe` **refuse the default profile** — you must point Chrome at a non-default `--user-data-dir`, i.e. a clean instance with none of your logins. An extension's `chrome.debugger` is not subject to that restriction, which is why nekoro goes through an extension.
|
|
24
|
+
|
|
25
|
+
| | CDP WebSocket | playwright-cli | opencli | **nekoro-browser** |
|
|
26
|
+
|------|:--:|:--:|:--:|:--:|
|
|
27
|
+
| Approach | `--remote-debugging-port` | Playwright extension | OpenCLI extension | Custom extension + persistent WebSocket |
|
|
28
|
+
| Install | one flag | `npm i -g` (~200MB) | npm / desktop app | `pip install` (stdlib only, zero deps) |
|
|
29
|
+
| Login state | ❌ fresh instance | ✅ | ✅ | ✅ |
|
|
30
|
+
| Modify the extension | — | Edit Playwright source | Edit OpenCLI source | ✅ right in this repo |
|
|
31
|
+
| Self-healing | ❌ | ❌ | ❌ | ✅ Agent edits helpers at runtime |
|
|
32
|
+
| MCP | ❌ | ✅ (separate `@playwright/mcp`) | ❌ | ✅ built in, 45 tools via `nekoro-browser-mcp` |
|
|
33
|
+
|
|
34
|
+
## Quick Start
|
|
35
|
+
|
|
36
|
+
**1 — Install** (Python 3.12+, zero third-party dependencies)
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
git clone https://github.com/zeshuochen/nekoro-browser
|
|
40
|
+
cd nekoro-browser
|
|
41
|
+
pip install -e .
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**2 — Load the extension**
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
nekoro-browser setup
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
`setup` copies the extension directory to your clipboard and then waits — up to three
|
|
51
|
+
minutes — until the extension actually connects, so you find out it worked instead of
|
|
52
|
+
guessing. Meanwhile you do the part Chrome reserves for humans: open `chrome://extensions/`,
|
|
53
|
+
turn on **Developer mode**, click **Load unpacked**, paste the directory.
|
|
54
|
+
|
|
55
|
+
**3 — Start the daemon** — give it its own terminal and **leave it open**; it runs in the
|
|
56
|
+
foreground and closing that window stops it
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
nekoro-browser
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**4 — Drive the browser** from anywhere else
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
echo "page_info()" | nekoro-browser
|
|
66
|
+
# → {"ok": true, "result": {"title": "...", "url": "..."}}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
That's it. If step 4 says the daemon isn't running, or a command times out, run
|
|
70
|
+
`nekoro-browser --doctor` — it checks the daemon, the extension and the service worker
|
|
71
|
+
separately and tells you which one is down.
|
|
72
|
+
|
|
73
|
+
## Examples
|
|
74
|
+
|
|
75
|
+
Send a multi-step flow in one shot with a heredoc. Every helper is a top-level `await`:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
nekoro-browser <<'PY'
|
|
79
|
+
await new_tab("https://example.com")
|
|
80
|
+
print((await page_info())["title"]) # Example Domain
|
|
81
|
+
print((await get_markdown(max_chars=200))["result"])
|
|
82
|
+
print((await state(max_items=3))["result"]) # indexed interactive elements, model-ready
|
|
83
|
+
await close_tab()
|
|
84
|
+
PY
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`state()` numbers the elements and `click_index(n)` clicks by number — the model never has to guess a CSS selector:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
nekoro-browser <<'PY'
|
|
91
|
+
await navigate("https://github.com/search?q=browser+automation&type=repositories")
|
|
92
|
+
await wait_for_load()
|
|
93
|
+
print((await state(max_items=40))["result"])
|
|
94
|
+
await click_index(12)
|
|
95
|
+
PY
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
All helpers are documented in [SKILL.md](SKILL.md).
|
|
99
|
+
|
|
100
|
+
## MCP (Cursor / Cline / Claude Desktop)
|
|
101
|
+
|
|
102
|
+
Every function in `helpers.py` is reflected into an MCP tool (45 today) — no glue code:
|
|
103
|
+
|
|
104
|
+
```json
|
|
105
|
+
{
|
|
106
|
+
"mcpServers": {
|
|
107
|
+
"nekoro-browser": {
|
|
108
|
+
"command": "nekoro-browser-mcp"
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
The daemon still has to be running in another terminal (`nekoro-browser`) — the MCP server just forwards tool calls to it over the same authenticated path as `echo ... | nekoro-browser`. Two escape hatches ship as tools: `cdp` (raw CDP command) and `exec_python` (arbitrary Python in the daemon namespace — a whole multi-step flow in one round trip).
|
|
115
|
+
|
|
116
|
+
Screenshots come back as image content so clients can render them. A helper's own failure (`{"ok": false}`) is surfaced as `isError` rather than being dressed up as success.
|
|
117
|
+
|
|
118
|
+
## API
|
|
119
|
+
|
|
120
|
+
| Category | Commands |
|
|
121
|
+
|----------|----------|
|
|
122
|
+
| Navigation | `navigate(url)`, `new_tab(url)`, `list_tabs()`, `switch_tab(id)`, `close_tab(id)` |
|
|
123
|
+
| Page info | `page_info()`, `page_html()`, `page_text()`, `get_markdown()`, `state()` |
|
|
124
|
+
| JavaScript | `js(code)`, `cdp(method, **p)`, `cdp_batch(*cmds)` |
|
|
125
|
+
| Interaction | `click_selector(sel)`, `click_index(n)`, `click_at_xy(x,y)`, `type_text(t)`, `fill_input(sel,t)`, `press_key(k)`, `upload_file(sel,path)` |
|
|
126
|
+
| Dialogs | `dialog_off()`, `get_last_dialog()` |
|
|
127
|
+
| Waiting | `wait_for_load()`, `wait_selector(sel)`, `wait_for_network_idle()`, `sleep(s)` |
|
|
128
|
+
| Screenshots | `capture_screenshot()`, `capture_screenshot("jpeg", 90)` |
|
|
129
|
+
|
|
130
|
+
## Architecture
|
|
131
|
+
|
|
132
|
+
```
|
|
133
|
+
Chrome extension (background.js) —— chrome.debugger / CDP
|
|
134
|
+
↕ persistent WebSocket
|
|
135
|
+
Python daemon (127.0.0.1:28417)
|
|
136
|
+
↕ HTTP /exec (token auth)
|
|
137
|
+
CLI (nekoro-browser) · MCP server (nekoro-browser-mcp)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
`helpers.py` (46 thin wrappers) → CDP commands, each ≤10 lines, none of them aware of any particular website.
|
|
141
|
+
|
|
142
|
+
`lifecycle.py` manages the daemon: pid file + process fingerprint (avoids killing a reused pid), self-heal on stale daemon (CDP probe fails → auto cleanup and restart), localhost requests bypass the system proxy.
|
|
143
|
+
|
|
144
|
+
The extension is hardened against MV3 service worker eviction: a `content_scripts` heartbeat (an independent wake vector living in the page, reconnects and wakes the SW even after it's killed) + `onStartup` (reconnects instantly on Chrome cold start) + reattaches the last-driven tab after a restart instead of drifting to a blank tab.
|
|
145
|
+
|
|
146
|
+
## CLI
|
|
147
|
+
|
|
148
|
+
| Command | What it does |
|
|
149
|
+
|---------|---------------|
|
|
150
|
+
| `nekoro-browser` | Start the daemon (foreground) |
|
|
151
|
+
| `nekoro-browser setup` | Guided install: extension path + opens chrome://extensions + waits for it to connect |
|
|
152
|
+
| `nekoro-browser --doctor` | End-to-end diagnostic (daemon + extension + SW all alive?) |
|
|
153
|
+
| `nekoro-browser --stop` | Stop the daemon |
|
|
154
|
+
| `nekoro-browser --restart` | Stop and restart (foreground) |
|
|
155
|
+
| `nekoro-browser --reload-ext` | Reload the extension's service worker — run before a batch job for a clean state |
|
|
156
|
+
| `nekoro-browser --extension-path` | Print the extension directory (for "Load unpacked") |
|
|
157
|
+
| `nekoro-browser --port N` | Run the daemon on port N (default 28417) |
|
|
158
|
+
| `nekoro-browser -c "code"` | Run one snippet, print the result |
|
|
159
|
+
| `nekoro-browser --timeout N` | Seconds to allow a snippet (default 120 — page loads are slow) |
|
|
160
|
+
| `echo "code" \| nekoro-browser` | Pipe mode (daemon must already be running) |
|
|
161
|
+
|
|
162
|
+
## Configuration
|
|
163
|
+
|
|
164
|
+
The daemon listens on **28417** by default. To change it:
|
|
165
|
+
|
|
166
|
+
| Side | How |
|
|
167
|
+
|------|-----|
|
|
168
|
+
| Python (daemon + CLI + MCP) | `nekoro-browser --port 30500`, or set `NEKORO_PORT=30500` |
|
|
169
|
+
| Extension | Extension details → **Extension options** → set the port → Save (reconnects immediately, no reload) |
|
|
170
|
+
|
|
171
|
+
Both sides must agree. Clients don't need the flag repeated: the daemon records its
|
|
172
|
+
actual port in `<data dir>/port`, so a plain `echo ... | nekoro-browser` finds a daemon
|
|
173
|
+
running on a non-default port. Precedence is `--port` > `NEKORO_PORT` > that file > default.
|
|
174
|
+
|
|
175
|
+
## Self-Healing
|
|
176
|
+
|
|
177
|
+
`src/nekoro_browser/agent_helpers.py` is editable at runtime and reloaded on every `/exec`. When an agent hits a gap, it appends the missing function there — effective on the next call, no daemon restart, no extension reload.
|
|
178
|
+
|
|
179
|
+
`domain-skills/` is where site knowledge goes (page structure, selectors, gotchas) — Markdown only, and empty by default since everyone automates different sites. Write a workflow against your notes, drop it into `agent_helpers.py`, same convention (`daemon` as first argument). See [`domain-skills/README.md`](domain-skills/README.md).
|
|
180
|
+
|
|
181
|
+
## Platform Support
|
|
182
|
+
|
|
183
|
+
| Platform | Status |
|
|
184
|
+
|----------|--------|
|
|
185
|
+
| Windows | Primary development platform, exercised end to end |
|
|
186
|
+
| Linux / macOS | The code has the branches (XDG dirs, `chmod 600` token, `/proc` and `ps` liveness probes) and CI runs the unit tests on all three, **but the full "Chrome + extension" loop has never been run on a real macOS/Linux box** — reports welcome |
|
|
187
|
+
|
|
188
|
+
## Known Limitations
|
|
189
|
+
|
|
190
|
+
- **Unpacked extensions get disabled by Chrome.** An extension installed via "Load unpacked" may be switched off automatically after a Chrome update or restart, or hidden behind the "Disable developer mode extensions" prompt. When `--doctor` reports Extension/SW not responding, re-enable it in `chrome://extensions/` first. This project is **not published to the Chrome Web Store**, so the limitation is not going away soon.
|
|
191
|
+
- **Service worker keepalive is not 100%.** MV3 eviction timing is Chrome's call. The heartbeat + `onStartup` + reattach cover the vast majority of cases, but unattended long-running cron jobs should still health-check with `--doctor` and retry.
|
|
192
|
+
- **One active tab at a time.** Tabs can be listed and switched (`list_tabs` / `switch_tab`), but commands always go to the current active tab — there are no parallel sessions.
|
|
193
|
+
- **The MCP server handles requests serially.** During a `wait_selector(timeout=90)` every other request on that connection (including `ping`) queues behind it. Open separate client connections if you need concurrency.
|
|
194
|
+
|
|
195
|
+
## Troubleshooting
|
|
196
|
+
|
|
197
|
+
| Symptom | Cause | Fix |
|
|
198
|
+
|---------|-------|-----|
|
|
199
|
+
| `Daemon not running` | Daemon not started | Run `nekoro-browser` in terminal 1 |
|
|
200
|
+
| CDP timeout | Extension not connected / service worker asleep | `nekoro-browser --doctor` to diagnose; try `--reload-ext` or manually reload in `chrome://extensions` |
|
|
201
|
+
| Extension disabled by Chrome | Unpacked extension + Chrome update | Re-enable it in `chrome://extensions/`, then re-run `--doctor` |
|
|
202
|
+
| Page unchanged | Extension not attached to tab | Open a regular (non-chrome://) page, restart daemon |
|
|
203
|
+
| Port in use | Stale process | Kill the process on port 28417, or just run `nekoro-browser --stop` |
|
|
204
|
+
|
|
205
|
+
## Security
|
|
206
|
+
|
|
207
|
+
The daemon listens on `127.0.0.1` and `/exec` runs arbitrary Python, so the transport is guarded:
|
|
208
|
+
|
|
209
|
+
- **CLI / MCP → daemon** (`/exec`, `/raw`): a per-session token is written to a user-private file (`%LOCALAPPDATA%\nekoro-browser\token`, `chmod 600` on POSIX). Clients read it and send `X-Nekoro-Token`; missing/wrong token → `403`. Web pages and remote hosts can't read local files, so they can't obtain it. `/ping` stays open.
|
|
210
|
+
- **Extension → daemon** (`/ws`): the handshake `Origin` must be `chrome-extension://…`; a web page's `WebSocket` to localhost carries its own origin and is rejected.
|
|
211
|
+
|
|
212
|
+
Same-user local processes can read the token file — that boundary matches the OS user account, as with browser-harness's `chmod 600`.
|
|
213
|
+
|
|
214
|
+
## Feedback
|
|
215
|
+
|
|
216
|
+
Hit a problem, or missing a helper you need? Open an
|
|
217
|
+
[issue](https://github.com/zeshuochen/nekoro-browser/issues).
|
|
218
|
+
For bugs, include the output of `nekoro-browser --doctor`, your Chrome version and OS — saves a round trip.
|
|
219
|
+
|
|
220
|
+
PRs welcome. Run the tests first: `for f in tests/test_*.py; do python "$f"; done` (CI runs them on all three platforms too).
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## Acknowledgments
|
|
225
|
+
|
|
226
|
+
Core architecture derived from:
|
|
227
|
+
|
|
228
|
+
- **[browser-harness](https://github.com/browser-use/browser-harness)** — thin-wrapper philosophy (each function is a CDP alias, ≤10 lines), pipe mode, self-healing `agent_helpers.py`, domain-skills directory structure, `cdp()` raw access
|
|
229
|
+
- **[browser-act](https://github.com/browser-act/skills)** — `state()` indexed element tree, `*[N]` change markers, `waitSelector()` state polling, `getMarkdown()` page extraction
|
|
230
|
+
- **[Playwright](https://github.com/microsoft/playwright)** — CDP `Input.dispatchMouseEvent` real mouse events (`isTrusted:true`), extension + daemon dual-path architecture
|