nexus-browser 2.9.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.
- nexus_browser-2.9.0/.gitignore +20 -0
- nexus_browser-2.9.0/CHANGELOG.md +284 -0
- nexus_browser-2.9.0/LICENSE +21 -0
- nexus_browser-2.9.0/PKG-INFO +211 -0
- nexus_browser-2.9.0/README.md +189 -0
- nexus_browser-2.9.0/VERSION +1 -0
- nexus_browser-2.9.0/client/bridge_client.py +383 -0
- nexus_browser-2.9.0/client/browser_cli.py +374 -0
- nexus_browser-2.9.0/client/emergency_stop.py +219 -0
- nexus_browser-2.9.0/client/mcp_server.py +1050 -0
- nexus_browser-2.9.0/native-host/auth_host.py +117 -0
- nexus_browser-2.9.0/native-host/bridge_auth.py +116 -0
- nexus_browser-2.9.0/native-host/bridge_config.json +6 -0
- nexus_browser-2.9.0/native-host/bridge_security.py +378 -0
- nexus_browser-2.9.0/native-host/bridge_server.py +696 -0
- nexus_browser-2.9.0/packaging/nexus_browser/__init__.py +3 -0
- nexus_browser-2.9.0/packaging/nexus_browser/__main__.py +17 -0
- nexus_browser-2.9.0/pyproject.toml +82 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.log
|
|
6
|
+
.venv/
|
|
7
|
+
|
|
8
|
+
# Windows / OS
|
|
9
|
+
Thumbs.db
|
|
10
|
+
ehthumbs.db
|
|
11
|
+
Desktop.ini
|
|
12
|
+
.DS_Store
|
|
13
|
+
|
|
14
|
+
# Backups
|
|
15
|
+
*.bak
|
|
16
|
+
*.tmp
|
|
17
|
+
|
|
18
|
+
# Development caches (normally stored outside OneDrive by test.ps1)
|
|
19
|
+
.pytest_cache/
|
|
20
|
+
.ruff_cache/
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and versions follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
5
|
+
|
|
6
|
+
## [Unreleased]
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
- PyPI package `nexus-browser` (the MCP server, command `nexus-browser`): the wheel mirrors `client/` and
|
|
10
|
+
`native-host/` unchanged under `nexus_browser/` (hatchling force-include); `install.ps1` and the three agents keep
|
|
11
|
+
running from the repository (`[tool.uv] package = false`). Published by Trusted Publishing
|
|
12
|
+
(`.github/workflows/publish.yml`), checked from the built wheel (initialize, 47 tools).
|
|
13
|
+
- README: animated demo recorded from the real tool; install from PyPI.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
- Bridge not reachable and no token: the error now points to the one-time install (install.ps1).
|
|
17
|
+
|
|
18
|
+
## [Unreleased]
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- `SECURITY.md`: private vulnerability reporting.
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- Repository renamed to `pironjulien/NexusBrowser`; GitHub redirects the old address.
|
|
27
|
+
|
|
28
|
+
## [2.9.0] - 2026-10-18 — Secrets masked in images and console logs
|
|
29
|
+
|
|
30
|
+
Re-run `install.ps1`, then RELOAD the extension (capabilities `capture-field-masking` and `console-masking`
|
|
31
|
+
advertised by ping).
|
|
32
|
+
|
|
33
|
+
### Security
|
|
34
|
+
- Screenshots, PDFs and Set-of-Mark captures no longer show a password revealed by a "show" button, a card number or
|
|
35
|
+
a code: before `Page.captureScreenshot` / `Page.printToPDF` (also through `browser_cdp_send`), sensitive fields of
|
|
36
|
+
every frame (shadow DOM, same-origin and cross-origin iframes) get `-webkit-text-security: disc`, and their own
|
|
37
|
+
inline style is put back right after. A capture whose page cannot be masked (dialog open) is refused. Checked in
|
|
38
|
+
the real Chrome (`test.ps1 -Full`): two values of the same length give the same image, the same field shown as an
|
|
39
|
+
ordinary field gives a different one (screenshot, PDF, cross-origin iframe).
|
|
40
|
+
- Sensitive fields are also recognized by name or id, camelCase included: `cardNumber`, `cc_number`, `cardCvc`,
|
|
41
|
+
`otp`, `pin`, `securityCode`, `iban`... (`cc` alone, `spinner`, `passenger`, `option` are not secrets).
|
|
42
|
+
- `browser_logs` masks console messages like network bodies: secret keys, URL parameters, object previews
|
|
43
|
+
(`{name, value}`), tokens quoted in a text (`Bearer …`, `ghp_…`, JWT).
|
|
44
|
+
- Network log: card number, card code, one-time code, PIN and IBAN keys masked in bodies and URL parameters.
|
|
45
|
+
- `NEXUS_CHROME_EXTRA_ARGS` is now an allowlist (`--lang`, `--accept-lang`, `--variations-override-country`,
|
|
46
|
+
`--disable-features` with `Glic*` features only, `--force-dark-mode`): the former blocklist let through flags that
|
|
47
|
+
weaken Chrome (`--disable-web-security`, `--disable-features=IsolateOrigins`...).
|
|
48
|
+
|
|
49
|
+
### Fixed
|
|
50
|
+
- Element screenshots (`selector`, `mark`) were blank when Chrome's page zoom is not 100% (seen at 90%): the clip is
|
|
51
|
+
now converted with the page zoom reported by `Page.getLayoutMetrics`.
|
|
52
|
+
|
|
53
|
+
### Added
|
|
54
|
+
- Real Chrome test of masking in a cross-origin iframe (reads and capture).
|
|
55
|
+
- Real Chrome check that Julien's tabs are left intact now ignores tabs of other agents (they may close theirs during
|
|
56
|
+
the run).
|
|
57
|
+
|
|
58
|
+
- Every MCP tool has a readable `title` in its annotations, next to its four hints.
|
|
59
|
+
- Startup test: the MCP server answers `initialize` and `tools/list` with no Chrome, bridge or extension, on Windows
|
|
60
|
+
and on Linux (as MCP directories such as Glama check it); outside Windows the emergency stop keeps watching its
|
|
61
|
+
file and skips the global hotkey.
|
|
62
|
+
- GitHub Actions: `test.ps1` on Windows and the startup test on Linux, with a Tests badge; `glama.json`.
|
|
63
|
+
- README: Privacy section.
|
|
64
|
+
|
|
65
|
+
### Changed
|
|
66
|
+
- Published as NexusBrowser in the public repository `pironjulien/chrome-computer-use` (formerly
|
|
67
|
+
`antigravity-chrome-sidepanel`, then `antigravity-browser-use`; GitHub redirects the old URLs). README rewritten in
|
|
68
|
+
English for public use.
|
|
69
|
+
|
|
70
|
+
## [2.8.1] - 2026-10-18 — Screenshots of background tabs no longer freeze
|
|
71
|
+
|
|
72
|
+
Re-run `install.ps1`, then RELOAD the extension (capability `capture-screencast` advertised by ping).
|
|
73
|
+
|
|
74
|
+
### Fixed
|
|
75
|
+
- `browser_screenshot` and `browser_vision_inspect` froze for 12 to 20 s (bridge timeout) in a background tab:
|
|
76
|
+
Chrome stops drawing frames for a hidden tab, so `Page.captureScreenshot` waited after any scroll or change.
|
|
77
|
+
Measured on the real Chrome (October 18, 20 captures alternating with scrolls): 11 to 13 freezes out of 20 for the
|
|
78
|
+
plain capture, `Emulation.setFocusEmulationEnabled` and `captureBeyondViewport` (still 7/12), forcing the device
|
|
79
|
+
metrics (7/12); `fromSurface: false` is refused by Chrome ("Only screenshots from surface are allowed"). A tiny
|
|
80
|
+
`Page.startScreencast` (16×16, never read) held only for the duration of the capture keeps the renderer drawing:
|
|
81
|
+
0 freezes out of 40, about 0.1 s per capture, correct image of the scrolled view; the tab stays in the background.
|
|
82
|
+
If Chrome refuses the screencast, the capture runs as before; it is always stopped, even when the capture fails.
|
|
83
|
+
|
|
84
|
+
### Changed
|
|
85
|
+
- Chrome relaunch (`launch_chrome`): only `--profile-directory=Default` by default. Extra flags are opt-in
|
|
86
|
+
through the `NEXUS_CHROME_EXTRA_ARGS` environment variable (`--name` or `--name=value` separated by spaces);
|
|
87
|
+
anything else, and flags that load or run code (`--load-extension`, `--remote-debugging-*`, command prefixes,
|
|
88
|
+
`--user-data-dir`, `--no-sandbox`...), are ignored.
|
|
89
|
+
- Agent guide and docstrings: repository-relative paths instead of the author's folders.
|
|
90
|
+
|
|
91
|
+
### Removed
|
|
92
|
+
- Live validation: the 3 retries of `browser_vision_inspect` (`gels_de_capture`) are gone, a single capture is
|
|
93
|
+
expected to succeed.
|
|
94
|
+
|
|
95
|
+
## [2.8.0] - 2026-10-18 — October 18 audit: masked passwords, corrected cookie promise, MCP SDK 2
|
|
96
|
+
|
|
97
|
+
Re-run `install.ps1` (with Antigravity closed), then RELOAD the extension in `chrome://extensions` (capabilities
|
|
98
|
+
`sensitive-field-masking`, `cdp-reload-no-script` and `ax-value-masking` advertised by ping), then open a new
|
|
99
|
+
conversation in each agent.
|
|
100
|
+
|
|
101
|
+
### Security
|
|
102
|
+
- Plaintext passwords (severe): `browser_content` (default interactive mode), `browser_vision_inspect`,
|
|
103
|
+
`browser_find` and the read-back of `browser_fill` returned field values, passwords included; `browser_axtree`
|
|
104
|
+
returned card numbers and codes (Chrome only masks passwords there, observed in a real run). The value is never
|
|
105
|
+
returned for a `type=password` field or one whose `autocomplete` is current-password, new-password,
|
|
106
|
+
one-time-code, cc-number, cc-csc or cc-exp (including a "show password" field switched to `type=text`):
|
|
107
|
+
`[valeur masquée]` is returned instead. `browser_content(mode="html")`: the `value` attribute of these fields is
|
|
108
|
+
masked in a copy of the DOM (React copies it from the input), the page itself is untouched. `browser_fill` still
|
|
109
|
+
reads the real value back from the page (`verified` stays accurate); only the output is masked, including the
|
|
110
|
+
expected value and the JavaScript fallback. A single definition of sensitive fields, in the localization kit,
|
|
111
|
+
injected into every page script.
|
|
112
|
+
- Antigravity review (October 18): the rendered text of a sensitive field (textarea, contenteditable) was not
|
|
113
|
+
masked; a "show password" field without a recognized autocomplete is now identified by its name or id (password,
|
|
114
|
+
pwd, mot de passe, mdp, cvc...), never a container; non-input components carrying a sensitive autocomplete are
|
|
115
|
+
masked in the accessibility tree and the HTML.
|
|
116
|
+
- `browser_cdp_send`: `Page.reload` accepted `scriptToEvaluateOnLoad` passed through unchanged (a script injected
|
|
117
|
+
into the page world on load, like `Page.addScriptToEvaluateOnNewDocument`, which is refused). Now refused before
|
|
118
|
+
reaching Chrome, with a clear message; a plain reload is still allowed.
|
|
119
|
+
|
|
120
|
+
### Fixed
|
|
121
|
+
- Incorrect promise: "cookies sealed outside localhost". `browser_eval` and `Runtime.evaluate` (both worlds share the
|
|
122
|
+
document) can read `document.cookie` (non-HttpOnly cookies), localStorage, sessionStorage, IndexedDB and the result
|
|
123
|
+
of a `fetch` sent with the user's session, without masking. Kept as is (maximum autonomy; pattern-based masking
|
|
124
|
+
would be bypassed by the slightest `btoa`) and stated clearly in the tool, the server instructions, the skill and
|
|
125
|
+
the README: masking applies to the dedicated tools, not to a script nor to raw `browser_cdp_send` responses
|
|
126
|
+
(`DOM.getOuterHTML`, `Accessibility.getFullAXTree`).
|
|
127
|
+
|
|
128
|
+
### Changed
|
|
129
|
+
- MCP SDK 2.3.0 (instead of 1.30.0; the 1.x branch only receives security fixes): `MCPServer` (formerly
|
|
130
|
+
`FastMCP`), advertised version read from `VERSION`.
|
|
131
|
+
- MCP annotations on all 47 tools (`readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`): page
|
|
132
|
+
reads, local reads, tab management, navigation, and page actions marked destructive (in the user's real sessions, a
|
|
133
|
+
click, an Enter key press, a checked box or a script can send, delete or confirm). A tool marked "read-only" only
|
|
134
|
+
sends the bridge commands from its read list (tested), with one deliberate exception: `browser_get_cookies`, which
|
|
135
|
+
modifies nothing but is still blocked by the emergency stop.
|
|
136
|
+
- Tools are typed exactly (the decorator no longer turned them into `Any`): mistyped test calls fixed.
|
|
137
|
+
|
|
138
|
+
### Tests
|
|
139
|
+
- Node: content, visual map, search, HTML and accessibility tree contain no sensitive value; typing into a password
|
|
140
|
+
field, into a card field rewritten by the page, and through the JavaScript fallback returns no value;
|
|
141
|
+
`Page.reload` with a script refused before reaching Chrome. Mutation checked: without masking, the three masking
|
|
142
|
+
tests fail.
|
|
143
|
+
- Python: SDK 2, complete annotations consistent with the bridge read list, accurate wording about `browser_eval`
|
|
144
|
+
(tool, instructions, both copies of the skill, README).
|
|
145
|
+
- Real-world validation (`--full-live`): password and card never read (content, HTML, visual map, search, Chrome
|
|
146
|
+
accessibility tree), password entry verified and masked, `Page.reload` with a script refused.
|
|
147
|
+
|
|
148
|
+
### Known issues
|
|
149
|
+
- `Page.captureScreenshot` sometimes hangs for 12 to 20 s in a background tab (both `browser_screenshot` and
|
|
150
|
+
`browser_vision_inspect`, measured on October 18, unrelated to this version): the real-world validation retries the
|
|
151
|
+
visual map up to 3 times and counts the hangs. Root cause to be addressed separately.
|
|
152
|
+
|
|
153
|
+
## [Unpublished] - 2026-10-17 — Antigravity review: regressions fixed
|
|
154
|
+
|
|
155
|
+
Version 2.7.1. Re-run `install.ps1` (with Antigravity closed), then RELOAD the extension in `chrome://extensions`
|
|
156
|
+
(capabilities `orphan-tabs`, `cdp-page-world`, `local-cookie-values`, `body-masking-allowlist` advertised by ping),
|
|
157
|
+
then open a new conversation in each agent.
|
|
158
|
+
|
|
159
|
+
### Fixed
|
|
160
|
+
- Orphaned tabs (severe): after an MCP server restart, its tabs stayed attached to the old session and could not be
|
|
161
|
+
driven, closed, released or adopted. The bridge and the extension now track live sessions: every command and an MCP
|
|
162
|
+
server heartbeat (every 60 / 1.618 s) record the last sign of life; each MCP server announces its process (header
|
|
163
|
+
`X-Nexus-Browser-Pid`), which the bridge checks. A session silent for 1.618 x 60 s, or whose process is gone, is
|
|
164
|
+
dead: its tabs become orphaned. The same agent takes them back (`browser_adopt_tab` without `julien_asked`, and
|
|
165
|
+
automatically on the first heartbeat if it has no tab), and can close or release them. Never another agent; a live
|
|
166
|
+
session of the same agent stays protected (ownership is still held per session). `browser_list_tabs`: `state`
|
|
167
|
+
(`mine`, `orphan`, `other`, `julien`); control panel: "orphelin" badge.
|
|
168
|
+
- `browser_cdp_send`: `Runtime.evaluate` forced into the isolated world prevented reading an application's state.
|
|
169
|
+
Isolated world by default, page world on request (`world="page"`); the context is always enforced, without the
|
|
170
|
+
console API; a dialog opened by the evaluation is reported instead of blocking.
|
|
171
|
+
- Cookies: `include_values=True` is back, ONLY for a local development origin (localhost, 127.0.0.1, [::1],
|
|
172
|
+
*.localhost, *.test) and for cookies of those domains; clear refusal, before any call to Chrome, for any other
|
|
173
|
+
origin. Never a value by default.
|
|
174
|
+
- Body masking: `token` without an anchor masked `nextPageToken`, `pageToken`, `total_tokens`, `prompt_tokens`,
|
|
175
|
+
`max_tokens`, `tokenizer`... Allowlist added (pagination, `token_type`, `expires_*`, `tokenizer`), token counters
|
|
176
|
+
left alone when the value is a number (OpenAI, Anthropic, Gemini), ambiguous keys masked only when the value looks
|
|
177
|
+
like a secret (known formats: `ya29.`, `ghp_`, `sk-`, JWT... or a long opaque string). Unambiguous secret keys
|
|
178
|
+
(`access_token`, `refresh_token`, `id_token`, `token`, `password`, `client_secret`...) always masked.
|
|
179
|
+
|
|
180
|
+
## [Unpublished] - 2026-10-17 — Default CLI identity
|
|
181
|
+
|
|
182
|
+
### Fixed
|
|
183
|
+
- Default CLI identifier `cli` (3 characters) was refused by the bridge (4 minimum): now `cli-local`. Test added.
|
|
184
|
+
|
|
185
|
+
## [Unpublished] - 2026-10-17 — Claude review: isolation and secrets in the extension
|
|
186
|
+
|
|
187
|
+
Version 2.7.0. Re-run `install.ps1` (with Antigravity closed), then RELOAD the extension in `chrome://extensions`
|
|
188
|
+
(the real-world validation and `verify.ps1 -Browser` refuse an outdated extension: version and features advertised by
|
|
189
|
+
ping), then open a new conversation in each agent. Tools that check the installation must read the fingerprint
|
|
190
|
+
through `scripts\empreinte.ps1`.
|
|
191
|
+
|
|
192
|
+
### Security
|
|
193
|
+
- `browser_cdp_send`: closed list of ALLOWED commands (DOM reads, `Accessibility.getFullAXTree`,
|
|
194
|
+
`Runtime.evaluate` forced into the isolated world, screenshots, `Input.dispatch*`, display `Emulation.*`,
|
|
195
|
+
`Network.enable/disable`, Log/Console); everything else refused before reaching Chrome
|
|
196
|
+
(`Storage.clearDataForOrigin`, `Network.getRequestPostData`, `Network.loadNetworkResource`, `IO.read`,
|
|
197
|
+
`Fetch.enable`, `Page.addScriptToEvaluateOnNewDocument`, `Page.navigate`...).
|
|
198
|
+
- Tab ownership enforced by the extension: each bridge client identifies itself (agent + session, headers
|
|
199
|
+
`X-Nexus-Browser-Agent` / `X-Nexus-Browser-Client`, set by the bridge in the signed channel); the tab opened by
|
|
200
|
+
`browser_new_tab` belongs to that session (`chrome.storage.session`, survives service worker restarts); any page
|
|
201
|
+
command on another tab (the user's, another agent's, another session's) is refused. Adopting one of the user's tabs
|
|
202
|
+
only through `browser_adopt_tab(tab_id, julien_asked=True)`, visible in the control panel.
|
|
203
|
+
- `tab_id` required in the schema of every page tool (no more "last tab" on the MCP side).
|
|
204
|
+
- Response bodies: secret fields masked at any depth (JSON, anti-XSSI prefix, forms, truncated JSON, patterns inside
|
|
205
|
+
a script); no cookie value leaves the bridge anymore (`include_values` removed).
|
|
206
|
+
- Uploads also checked by the bridge (any HTTP client); folders added (`~\.gemini`, `~\.android`,
|
|
207
|
+
`~\.git-credentials`, `~\.config\gcloud`, `%APPDATA%\gcloud`, Chrome profile, Credentials/Protect...); paths
|
|
208
|
+
normalized (`\\?\`, `\\.\`, `\\localhost\C$\`, `\\127.0.0.1\C$\`, machine name, junctions and links, trailing
|
|
209
|
+
dots, case).
|
|
210
|
+
- Isolated world for all extension scripts (main frame included, `chrome.scripting` fallback in ISOLATED): markers,
|
|
211
|
+
click probes and AX registry invisible to the page and not replaceable by it. Only `browser_eval` and the YouTube
|
|
212
|
+
transcript (the page's player) run in the page world.
|
|
213
|
+
- Navigation and new tab: http, https, data:, about:blank only (never file://, chrome://, javascript:).
|
|
214
|
+
- Badge and click dot: extension messages only (a page can no longer display fake ones).
|
|
215
|
+
|
|
216
|
+
### Added
|
|
217
|
+
- Control panel (side panel, neutral style): tabs driven per agent, debugger state, STOP (shared stop file
|
|
218
|
+
`~\.nexus\stop` set by the bridge, debuggers detached), "Rendre l'onglet" (release tab), bridge state.
|
|
219
|
+
- Tools `browser_adopt_tab`, `browser_release_tab`, `browser_network_record` (debugger kept attached, 600 s at most),
|
|
220
|
+
`browser_network_stop`.
|
|
221
|
+
- Emergency stop: the bridge cancels in-flight commands (423 response) and notifies the extension, which interrupts
|
|
222
|
+
key-by-key typing, dragging (button released) and waits, detaches all debuggers and refuses any action.
|
|
223
|
+
- `VERSION`; `scripts\empreinte.ps1` (installed.stamp includes `installer-hote-natif.ps1` and `runtime.ps1`);
|
|
224
|
+
`scripts\doublon-antigravity.ps1`, `scripts\ancienne-extension.ps1`.
|
|
225
|
+
|
|
226
|
+
### Changed
|
|
227
|
+
- Debugger: per-tab command counter (replaces the global counter that prevented any detach).
|
|
228
|
+
- Element capture in page coordinates (scrolling added, beyond the viewport); debugging session resumed after a
|
|
229
|
+
service worker restart (iframe tracking restarted).
|
|
230
|
+
- `windows.create` fallback: id of the created tab, never that of the window.
|
|
231
|
+
- Panel shortcut: Alt+Shift+N (Ctrl+Shift+A is Chrome's "Search tabs").
|
|
232
|
+
- Antigravity: nexus-browser declared by its plugin only (the `mcp_config.json` entry is removed, after a backup);
|
|
233
|
+
`verify.ps1` reports a duplicate.
|
|
234
|
+
- Skill also copied for Claude Code (`~\.claude\skills`) and Codex (`~\.codex\skills`).
|
|
235
|
+
- `browser_cli.py`: version read from the code, French help, `--tab-id` required, new commands.
|
|
236
|
+
|
|
237
|
+
### Removed
|
|
238
|
+
- Panel chat (text box, bridge queue, `browser_sidepanel_requests` / `browser_sidepanel_reply`) and its four
|
|
239
|
+
buttons; dead `popup.html/js`; SoM display from the content script that was never triggered.
|
|
240
|
+
|
|
241
|
+
### Migration
|
|
242
|
+
- `install.ps1` removes (log `~\.nexus\nexus-browser\install.log`) the old token `~\.nexusagy\bridge-token` and the
|
|
243
|
+
files of the old native host; `verify.ps1` checks they are gone and reports the old "Antigravity Browser Bridge"
|
|
244
|
+
extension if a Chrome profile loads it (preferences read, never modified).
|
|
245
|
+
- No caches in the source tree: ruff `cache-dir` and pytest `-p no:cacheprovider` in `pyproject.toml`,
|
|
246
|
+
`sys.dont_write_bytecode` in the client and bridge modules; `.ruff_cache`, `client\__pycache__` and
|
|
247
|
+
`native-host\__pycache__` deleted.
|
|
248
|
+
|
|
249
|
+
## [2.6.0] - 2026-10-16 — Neutral repository, shared by the three agents
|
|
250
|
+
|
|
251
|
+
Reload the extension (new folder, new identifier) and re-run `install.ps1` on each machine.
|
|
252
|
+
|
|
253
|
+
### Added
|
|
254
|
+
- Standalone repository, extracted from the former Antigravity package: extension, local bridge, native host,
|
|
255
|
+
`nexus-browser` MCP server, CLI, Antigravity skill and plugin, tests (Python, Node, real-world validation).
|
|
256
|
+
- `install.ps1`: environment outside the source tree (`~\.nexus\nexus-browser`), native host, "Nexus Browser Bridge"
|
|
257
|
+
task with no trigger, registration of `nexus-browser` with Claude Code, Codex and Antigravity (plugin and skill
|
|
258
|
+
included), installation fingerprint.
|
|
259
|
+
- Agent name per registration (`NEXUS_BROWSER_AGENT`: Antigravity, Claude, Codex): default tab group named after it,
|
|
260
|
+
to see at a glance which agent opened what.
|
|
261
|
+
- Fixed extension identifier (`blcgddmjdkobmgakjffjpgiicjlflgjf`), derived from the public `key` in the manifest: the
|
|
262
|
+
same on every machine and whatever folder is loaded; the only origin accepted by the bridge and the native host (six
|
|
263
|
+
identifiers tied to the old folders previously).
|
|
264
|
+
- `verify.ps1`: task, native host, absence of the old task; `-Browser`: bridge reachable and extension loaded and up
|
|
265
|
+
to date.
|
|
266
|
+
|
|
267
|
+
### Changed
|
|
268
|
+
- Neutral names: "Nexus Browser Bridge" extension, native host `com.nexus.browser_auth`, protocol `nexus-browser/1`,
|
|
269
|
+
header `X-Nexus-Browser-Token`, token `~\.nexus\nexus-browser\bridge-token`, exports in
|
|
270
|
+
`Téléchargements\nexus-browser-exports`, default tab group "Nexus" when no registration is set.
|
|
271
|
+
- MCP server reduced to the browser (Computer Use stays in the former Antigravity package); screenshot cleanup with
|
|
272
|
+
no dependency on the desktop client.
|
|
273
|
+
- Uploads also refused under `~\.nexus` (bridge token, emergency stop).
|
|
274
|
+
|
|
275
|
+
### Verified
|
|
276
|
+
- Real-world validation in the user's Chrome: `--mouse` 11/11, `--full-live` 29/29 (`test.ps1 -Full` green four
|
|
277
|
+
times in a row). First three runs right after loading the extension: 27/29, empty network log for the `/api/data`
|
|
278
|
+
request (cause not found, not reproduced since); the failure now prints the log and the click response.
|
|
279
|
+
|
|
280
|
+
### Migration
|
|
281
|
+
- `install.ps1` stops and removes the "NexusAGY Browser Bridge" task (same ports 9224/9225) and the native host
|
|
282
|
+
`com.nexusagy.bridge_auth`. The old "Antigravity Browser Bridge" extension must be removed from Chrome.
|
|
283
|
+
|
|
284
|
+
Versions up to 2.4.0 were published as "Antigravity Browser Bridge" in the same public repository; see its git history.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Julien Piron (https://julienpiron.fr)
|
|
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,211 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: nexus-browser
|
|
3
|
+
Version: 2.9.0
|
|
4
|
+
Summary: Your real Chrome, driven by Claude, Codex and Antigravity in background tabs: the nexus-browser MCP server (needs the NexusBrowser bridge and extension)
|
|
5
|
+
Project-URL: Homepage, https://julienpiron.fr/nexusbrowser/
|
|
6
|
+
Project-URL: Repository, https://github.com/pironjulien/NexusBrowser
|
|
7
|
+
Project-URL: Changelog, https://github.com/pironjulien/NexusBrowser/blob/main/CHANGELOG.md
|
|
8
|
+
Project-URL: Issues, https://github.com/pironjulien/NexusBrowser/issues
|
|
9
|
+
Author: Julien Piron
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: ai-agents,antigravity,browser-use,cdp,chrome,claude,codex,computer-use,mcp,mcp-server
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Browsers
|
|
18
|
+
Requires-Python: >=3.10
|
|
19
|
+
Requires-Dist: mcp<3,>=2.3.0
|
|
20
|
+
Requires-Dist: websockets<17,>=13.0
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
# NexusBrowser
|
|
24
|
+
|
|
25
|
+
<!-- mcp-name: io.github.pironjulien/nexus-browser -->
|
|
26
|
+
|
|
27
|
+
[](https://github.com/pironjulien/NexusBrowser/releases)
|
|
28
|
+
[](https://pypi.org/project/nexus-browser/)
|
|
29
|
+
[](https://github.com/pironjulien/NexusBrowser/actions/workflows/tests.yml)
|
|
30
|
+
[](LICENSE)
|
|
31
|
+
[](extension/manifest.json)
|
|
32
|
+
[](pyproject.toml)
|
|
33
|
+
[](https://modelcontextprotocol.io)
|
|
34
|
+
|
|
35
|
+
[](https://julienpiron.fr/nexusbrowser/)
|
|
36
|
+
|
|
37
|
+

|
|
38
|
+
|
|
39
|
+
**Your real Chrome, driven by AI agents in background tabs.** NexusBrowser lets Antigravity, Claude and Codex use
|
|
40
|
+
the user's own Chrome (their sessions, their extensions) through one shared local bridge and one MCP server,
|
|
41
|
+
`nexus-browser`. Each agent works in its own background tabs, grouped under its name, with real (trusted) mouse
|
|
42
|
+
and keyboard events sent through the Chrome DevTools Protocol, while the user keeps working in their own tabs.
|
|
43
|
+
|
|
44
|
+
Version 2.9.0 · Windows 10/11 · Chrome 116+ · Python 3.14 · MIT licence
|
|
45
|
+
|
|
46
|
+
Part of the Nexus family: [NexusWindows](https://julienpiron.fr/nexuswindows/)
|
|
47
|
+
([Claude](https://github.com/pironjulien/NexusWindows-Claude) ·
|
|
48
|
+
[Codex](https://github.com/pironjulien/NexusWindows-Codex) ·
|
|
49
|
+
[Antigravity](https://github.com/pironjulien/NexusWindows-Antigravity)),
|
|
50
|
+
[NexusBrowser](https://julienpiron.fr/nexusbrowser/) ([repo](https://github.com/pironjulien/NexusBrowser)),
|
|
51
|
+
[NexusAndroid](https://julienpiron.fr/nexusandroid/) ([repo](https://github.com/pironjulien/NexusAndroid)).
|
|
52
|
+
Project page: [julienpiron.fr/nexusbrowser](https://julienpiron.fr/nexusbrowser/).
|
|
53
|
+
|
|
54
|
+
## Why
|
|
55
|
+
|
|
56
|
+
Browser automation usually means a separate headless browser (no sessions, no extensions) or screen-level computer
|
|
57
|
+
use that moves the user's cursor and brings the window to the front. NexusBrowser works inside the browser the user
|
|
58
|
+
already uses, without activating the agent's tab or window, and keeps the guard rails in the extension and the
|
|
59
|
+
bridge rather than in the agent: tab ownership, an allowlist of DevTools commands, masked secrets, an emergency stop.
|
|
60
|
+
|
|
61
|
+
- **Antigravity**: its own browser agent runs in a separate Chrome profile, without the cookies or sign-ins of the
|
|
62
|
+
user's browsing ([Antigravity docs](https://antigravity.google/docs/ide/separate-chrome-profile/)). NexusBrowser
|
|
63
|
+
gives it the user's real Chrome.
|
|
64
|
+
- **Claude and Codex** already have their own Chrome extension (Claude in Chrome, the Codex Chrome extension).
|
|
65
|
+
NexusBrowser complements it where a bridge with the DevTools protocol helps: JavaScript dialogs
|
|
66
|
+
(`browser_handle_dialog`), detailed network capture with response bodies, elements inside cross-origin iframes,
|
|
67
|
+
content and Google Docs/Sheets/Slides exports, and downloads.
|
|
68
|
+
|
|
69
|
+
Until 2.4.0 it was published here as "Antigravity Browser Bridge", for Antigravity only. Since 2.6.0 it is a neutral
|
|
70
|
+
bridge shared by several agents.
|
|
71
|
+
|
|
72
|
+
## Pieces
|
|
73
|
+
|
|
74
|
+
| Folder | Role |
|
|
75
|
+
| --- | --- |
|
|
76
|
+
| `extension/` | Chrome MV3 extension "Nexus Browser Bridge": CDP actions, locators, network, dialogs, cross-origin iframes, exports, tab ownership, control panel |
|
|
77
|
+
| `native-host/` | Local bridge (`bridge_server.py`: WebSocket 9224 for the extension, HTTP 9225 for the agents) and native host `com.nexus.browser_auth` |
|
|
78
|
+
| `client/` | MCP server `nexus-browser` (`mcp_server.py`), authenticated transport (`bridge_client.py`), CLI (`browser_cli.py`), shared emergency stop |
|
|
79
|
+
| `skills/`, `plugins/` | Skill (copied for Antigravity, Claude Code and Codex) and Antigravity plugin, by `install.ps1` |
|
|
80
|
+
|
|
81
|
+
## Tools
|
|
82
|
+
|
|
83
|
+
47 MCP tools. Tool descriptions and messages are in French; tool and parameter names are in English.
|
|
84
|
+
|
|
85
|
+
| Area | Tools |
|
|
86
|
+
| --- | --- |
|
|
87
|
+
| Tabs | `browser_list_tabs`, `browser_active_tab` (read only), `browser_new_tab`, `browser_select_tab`, `browser_adopt_tab`, `browser_release_tab`, `browser_group_tabs`, `browser_close_tab`, `browser_reload`, `browser_navigate`, `browser_wait_load` |
|
|
88
|
+
| Input (trusted CDP events) | `browser_click`, `browser_semantic_click`, `browser_hover`, `browser_fill`, `browser_semantic_fill`, `browser_select_option`, `browser_check`, `browser_press_key`, `browser_type_keys`, `browser_drag_and_drop`, `browser_scroll`, `browser_upload`, `browser_handle_dialog` |
|
|
89
|
+
| Finding elements | `browser_find` (Playwright-style locators: role, label, text, placeholder, test id, in every frame), `browser_interactive_map`, `browser_vision_inspect` and `browser_click_mark` (Set-of-Mark badges), `browser_axtree`, `browser_content`, `browser_check_handoff` (2FA, CAPTCHA) |
|
|
90
|
+
| Network and downloads | `browser_network_record`, `browser_network_stop`, `browser_network_requests`, `browser_network_response`, `browser_wait_network_idle`, `browser_download_status`, `browser_wait_download`, `browser_get_cookies` |
|
|
91
|
+
| Output | `browser_screenshot`, `browser_pdf`, `browser_export` (main content as Markdown, text or HTML), `browser_export_gsuite` (Google Docs, Sheets, Slides through Google's export URLs), `browser_export_youtube_transcript`, `browser_logs` |
|
|
92
|
+
| Page access | `browser_eval`, `browser_cdp_send` (allowlisted commands) |
|
|
93
|
+
|
|
94
|
+
## Install
|
|
95
|
+
|
|
96
|
+
Requirements: Windows 10 or 11, Chrome, [uv](https://docs.astral.sh/uv/), Python 3.14.
|
|
97
|
+
|
|
98
|
+
```powershell
|
|
99
|
+
git clone https://github.com/pironjulien/NexusBrowser.git
|
|
100
|
+
cd NexusBrowser
|
|
101
|
+
pwsh -NoProfile -File install.ps1
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
- Python environment, token, logs, screenshots and exports live in `~\.nexus\nexus-browser` (outside the clone).
|
|
105
|
+
- Scheduled task `Nexus Browser Bridge` with **no trigger**: the bridge starts on demand (first agent call,
|
|
106
|
+
`schtasks /Run`).
|
|
107
|
+
- Native host `com.nexus.browser_auth` declared to Chrome (HKCU): it computes the extension's proofs, the token never
|
|
108
|
+
reaches Chrome.
|
|
109
|
+
- MCP server `nexus-browser` registered for Claude Code, Codex and Antigravity when they are installed, each with
|
|
110
|
+
`NEXUS_BROWSER_AGENT` set to its name (default tab group).
|
|
111
|
+
- When the bridge has to relaunch Chrome, it uses the `Default` profile; extra Chrome flags (language...)
|
|
112
|
+
can be added with the `NEXUS_CHROME_EXTRA_ARGS` environment variable (allowlist: `--lang`, `--accept-lang`,
|
|
113
|
+
`--variations-override-country`, `--disable-features=Glic…`, `--force-dark-mode`; anything else is ignored).
|
|
114
|
+
- Migration from the former "Antigravity Browser Bridge": its task (same ports) and native host
|
|
115
|
+
`com.nexusagy.bridge_auth` are removed.
|
|
116
|
+
|
|
117
|
+
Then load `extension\` once in `chrome://extensions` (developer mode, "Load unpacked") and remove the old
|
|
118
|
+
"Antigravity Browser Bridge" if present. The extension ID is fixed by the public `key` in `manifest.json`
|
|
119
|
+
(`blcgddmjdkobmgakjffjpgiicjlflgjf`), the same on every PC and whatever the folder; it is the only origin the bridge
|
|
120
|
+
and the native host accept.
|
|
121
|
+
|
|
122
|
+
### The MCP server from PyPI
|
|
123
|
+
|
|
124
|
+
Once the bridge and the extension are installed (above), any MCP client can also start the server straight from
|
|
125
|
+
[PyPI](https://pypi.org/project/nexus-browser/), without the clone:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
claude mcp add nexus-browser --scope user -e NEXUS_BROWSER_AGENT=Claude -- uvx nexus-browser
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Without the bridge, the server still starts and lists its 47 tools, and every call answers with a clear error that
|
|
132
|
+
points to the one-time install.
|
|
133
|
+
|
|
134
|
+
## Security (held by the extension and the bridge, not by the agent)
|
|
135
|
+
|
|
136
|
+
- **Mutually authenticated bridge** (`nexus-browser/1`): HMAC proofs over both nonces, then every message signed
|
|
137
|
+
with a per-connection key and a strict sequence number. HTTP requires `X-Nexus-Browser-Token` and refuses any
|
|
138
|
+
browser origin.
|
|
139
|
+
- **Tab ownership**: every bridge client identifies itself (agent + session headers, forwarded by the bridge in the
|
|
140
|
+
signed channel). A tab opened by `browser_new_tab` belongs to that session (`chrome.storage.session`); any page
|
|
141
|
+
command on another tab (the user's, another agent's) is refused. A tab of the user only through
|
|
142
|
+
`browser_adopt_tab(tab_id, julien_asked=True)` (the parameter keeps its historical name). `tab_id` is mandatory on
|
|
143
|
+
every page tool. A session whose MCP process is gone (or silent for 1.618 x 60 s; servers beat every 37 s) leaves
|
|
144
|
+
**orphan** tabs: the same agent can reclaim (`browser_adopt_tab`, also automatically at startup), close or release
|
|
145
|
+
them; never another agent.
|
|
146
|
+
- **CDP allowlist** for `browser_cdp_send`; every extension script runs in an **isolated world** (only
|
|
147
|
+
`browser_eval`, the YouTube transcript and `browser_cdp_send(..., world="page")` touch the page's world).
|
|
148
|
+
- **Secrets**: secret headers, URL parameters and JSON/form body fields masked (pagination tokens and LLM token
|
|
149
|
+
counters kept); cookie values only for local development origins (`include_values=True`); uploads refused by the
|
|
150
|
+
bridge itself from secret folders (normalized paths). The value of a password, one-time code or payment card field
|
|
151
|
+
(`type=password`, `autocomplete` current-password, new-password, one-time-code, cc-number, cc-csc, cc-exp) never
|
|
152
|
+
leaves the page: `browser_content` (html mode included), `browser_vision_inspect`, `browser_find`,
|
|
153
|
+
`browser_axtree` and the value read back by `browser_fill` return `[valeur masquée]` instead.
|
|
154
|
+
- **`browser_eval` is full access to the page, nothing masked**: like `browser_cdp_send(Runtime.evaluate)` (both
|
|
155
|
+
worlds share the document), it reads what the page reads (non-HttpOnly cookies through `document.cookie`,
|
|
156
|
+
localStorage, sessionStorage, IndexedDB, form fields) and its `fetch` carries the user's session. The masking
|
|
157
|
+
above covers the dedicated tools, not scripts nor the raw Chrome replies of `browser_cdp_send`
|
|
158
|
+
(`DOM.getOuterHTML`, `Accessibility.getFullAXTree`...): kept on purpose, said plainly in the tool, the skill and
|
|
159
|
+
the server instructions. `Page.reload` with `scriptToEvaluateOnLoad` is refused (script injected at load).
|
|
160
|
+
- **Emergency stop** (Ctrl+Alt+Shift+Esc, STOP button of the control panel, `~\.nexus\stop`): commands already
|
|
161
|
+
running are cancelled (typing, drag, waits), debuggers detached, every action refused for 120 s. The same file
|
|
162
|
+
protocol is shared with the [NexusWindows](https://julienpiron.fr/nexuswindows/) agents.
|
|
163
|
+
- **Control panel** (side panel, Alt+Shift+N): tabs driven by each agent, debugger state, STOP, give a tab back.
|
|
164
|
+
- Whoever holds the bridge token controls the browser session: keep `~\.nexus\nexus-browser\bridge-token` private.
|
|
165
|
+
|
|
166
|
+
## Checks
|
|
167
|
+
|
|
168
|
+
```powershell
|
|
169
|
+
pwsh -NoProfile -File test.ps1 # offline: ruff, Pyright, pytest (real bridge on ephemeral ports), Node tests
|
|
170
|
+
pwsh -NoProfile -File test.ps1 -Full # plus real validation in the user's Chrome: --mouse then --full-live
|
|
171
|
+
pwsh -NoProfile -File verify.ps1 -Browser # task, native host, bridge reached, loaded extension up to date
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
`test.ps1` uses the environment created by `install.ps1`.
|
|
175
|
+
|
|
176
|
+
## Privacy
|
|
177
|
+
|
|
178
|
+
NexusBrowser runs entirely on your PC: a Chrome extension, a local bridge bound to `127.0.0.1` (authenticated by a
|
|
179
|
+
local token) and an MCP server. It has no account, no telemetry, no analytics and no remote server; nothing is sent
|
|
180
|
+
to the author or to any third party.
|
|
181
|
+
|
|
182
|
+
- **What it reads**: in the tabs the agent opened (or a tab of yours you explicitly let it adopt): page text and
|
|
183
|
+
accessibility tree, screenshots, console logs, network requests (headers and secret fields masked), cookies when
|
|
184
|
+
`browser_get_cookies` is called. Password, code and card values are masked in reads and drawn as dots in
|
|
185
|
+
screenshots and PDFs (every frame, cross-origin ones included); console logs are masked like network bodies;
|
|
186
|
+
`browser_eval` and `Runtime.evaluate` through `browser_cdp_send` do have full access to the page.
|
|
187
|
+
- **Network**: pages are loaded by your own Chrome with your own sessions, as if you browsed them. Exports (Google
|
|
188
|
+
Docs, Sheets, Slides, YouTube transcripts) are fetched from those sites in your session.
|
|
189
|
+
- **Where it goes**: tool results are returned to the AI client you connected (Antigravity, Claude, Codex…), which
|
|
190
|
+
may send them to its model provider under its own privacy policy.
|
|
191
|
+
- **What it stores**, under `~/.nexus/nexus-browser/`: the Python environment, the bridge token, `bridge.log`
|
|
192
|
+
(connection and emergency-stop events, rotated at 1 MB, three backups), `install.log`, screenshots (at most 50,
|
|
193
|
+
each kept under 24 hours) and the exports you ask for. The extension keeps tab ownership in Chrome's session
|
|
194
|
+
storage, cleared when Chrome closes.
|
|
195
|
+
- **Removing it**: remove the extension from Chrome, delete the scheduled task `Nexus Browser Bridge` and the
|
|
196
|
+
registry key `HKCU\Software\Google\Chrome\NativeMessagingHosts\com.nexus.browser_auth`, then delete
|
|
197
|
+
`~/.nexus/nexus-browser/`.
|
|
198
|
+
|
|
199
|
+
Questions or concerns: open an issue on this repository.
|
|
200
|
+
|
|
201
|
+
## Limits
|
|
202
|
+
|
|
203
|
+
- Windows and Chrome only (Manifest V3, `chrome.debugger`). While the debugger is attached, Chrome shows its "is
|
|
204
|
+
being debugged" banner on the agent tab. Protected pages (`chrome://`, the Web Store) cannot be scripted.
|
|
205
|
+
- Tab ownership is per MCP process: sub-agents of one conversation share it.
|
|
206
|
+
- The YouTube transcript reads the page's own player (page world).
|
|
207
|
+
- Tool descriptions, messages and code comments are in French.
|
|
208
|
+
|
|
209
|
+
## Licence
|
|
210
|
+
|
|
211
|
+
MIT, see [LICENSE](LICENSE). Copyright (c) 2026 Julien Piron.
|