文件历史

提交图

16 次代码提交

作者 SHA1 备注 提交日期
Sarath S Menon fb1a51dd9b refactor: move to src layout, agent-workspace, and fix SKILL.md invocation format (#229)
* refactor(tests): reorganize into tests/unit and tests/integration

Moves all root-level test_*.py files into a structured tests/ directory:
- tests/unit/ — admin, helpers (was test_screenshot), run
- tests/integration/ — js expression tests
- tests/conftest.py — shared fake_png pytest fixture, eliminating duplication

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor: move to src layout, agent-workspace, and fix SKILL.md invocation format

- Move package to src/browser_harness/ and domain-skills/interaction-skills to agent-workspace/
- Fix all browser-harness <<'PY' heredoc examples in SKILL.md and run.py HELP string to use the correct -c '...' flag format (heredoc was never supported by the CLI)
- Update SKILL.md path references from domain-skills/ to agent-workspace/domain-skills/

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 15:38:11 +05:30
Alezander9 b05be93d60 Slim ipc.py and rename to _ipc.py to signal internal
Cut ipc.py from 127 to 68 lines (-46%) by removing restated docstrings and
keeping only load-bearing inline comments (path-traversal guard, uv-Python
AF_UNIX gating, Windows .port-file role). Same logic, same call sites.

Rename ipc -> _ipc per Python convention for internal modules. The IPC
plumbing is only called by daemon/admin/helpers; agents reading helpers.py
should not be pulled into transport details. Callers do 'import _ipc as ipc'
so internal ipc.foo references stay unchanged.
2026-04-27 18:01:11 -07:00
Alezander9 71cf3a87a0 Merge remote-tracking branch 'origin/main' into fix/windows-ipc
# Conflicts:
#	SKILL.md
#	admin.py
#	helpers.py
2026-04-27 17:41:08 -07:00
Alezander9 991ab21f80 Windows support: route IPC through ipc.py (TCP on Windows, AF_UNIX on POSIX)
The harness was Linux/macOS-only because daemon IPC hardcoded AF_UNIX sockets
at /tmp/bu-*.sock paths and asyncio.start_unix_server, all of which are
unavailable or invalid on Windows. Worse, uv-managed Python on Windows
(python-build-standalone) ships without socket.AF_UNIX entirely (#124).

New ipc.py centralizes the platform fork:
  - POSIX: AF_UNIX socket at <tempdir>/bu-<NAME>.sock (chmod 0600), unchanged
    semantics from the prior /tmp-hardcoded path.
  - Windows: TCP loopback on 127.0.0.1:<ephemeral>, with the chosen port
    written to <tempdir>/bu-<NAME>.port so clients can find the daemon.
    Uses asyncio.start_server (stdlib, no obscure APIs, no third-party deps).

Path discipline: log/pid/port files all sit under tempfile.gettempdir() so
they land in /tmp on Linux, $TMPDIR on macOS, %TEMP% on Windows. helpers.py
screenshot() default also moves from /tmp/shot.png to tempfile.gettempdir().

subprocess detach uses start_new_session=True on POSIX and
DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP on Windows via ipc.spawn_kwargs().

run.py reconfigures stdout to UTF-8 on Windows so print(page_info()) doesn't
UnicodeEncodeError on the 🟢 marker that helpers prepend to tab titles
(#124 item 4). cp1252 (PowerShell default) can't encode it.

Verified end-to-end on Windows 11 with Chrome remote debugging:
  - daemon spawns, allocates port, writes .port file
  - goto + page_info + screenshot round-trip through TCP loopback
  - restart_daemon cleans up .port and .pid

POSIX path is logically equivalent to the prior code (same AF_UNIX call,
same socket-file semantics, same chmod 0600), routed through ipc.py.

Closes #124 items 1, 2, 4. Item 3 (Chrome 147 user-data-dir) is a separate
concern not addressed here.
2026-04-27 16:40:56 -07:00
Saurav Panda 7d314748ee Merge branch 'main' into feat/screenshot-max-dim 2026-04-27 12:11:01 -07:00
Saurav Panda ffadfe6c51 fix: declare pillow dependency and use TemporaryDirectory in tests
Bot review caught two issues:

1. capture_screenshot's max_dim path imports PIL but Pillow wasn't in
   pyproject.toml dependencies, so a clean install would ImportError as
   soon as the option was used.

2. NamedTemporaryFile + writing to f.name while the handle is open is
   not portable on Windows (file lock). Switched the screenshot tests to
   tempfile.TemporaryDirectory and folded the three near-identical bodies
   into a small _run() helper.
2026-04-27 12:08:40 -07:00
Matt Van Horn 4faf1d7414 fix(deps): pin websockets==15.0.1 to unblock Chrome 147 CDP handshake 2026-04-23 07:46:36 -07:00
reformedot 8a0c981082 feat: self-update CLI, release workflow, and fetch-use routing
- Add --version, --doctor, --setup, --update[-y] commands in run.py;
  logic lives in admin.py (install-mode detection, GitHub-releases cache
  with 24h TTL, dirty-worktree guard, interactive Chrome-attach flow).
- Print a once-per-day startup banner telling agents to run
  `browser-harness --update -y` when a newer release is available.
- Rename project to browser-harness in pyproject.toml so PyPI installs
  (uv tool install browser-harness) work via the public package name.
- Add .github/workflows/release.yml: on v* tag push, verify the tag
  matches pyproject.toml, uv build, and publish to PyPI via trusted
  publishing.
- Wire helpers.http_get through fetch_use.fetch_sync(...).text when
  BROWSER_USE_API_KEY is set; falls back to the original urllib path
  otherwise, preserving the existing str return contract.
- Document the new commands and the agent's self-update duty in
  SKILL.md and install.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 12:49:07 -07:00
Saurav Panda 8bb719b05b pyproject: pin direct dependencies to exact versions
pyproject.toml used >= ranges for browser-harness, cdp-use, and
websockets, so a resolver could pull newer minors/patches on any
fresh install. Move them to == at the currently-resolved versions so
bumps have to be explicit and show up in review.

uv.lock stays gitignored; transitives float by design.
2026-04-18 21:51:07 -07:00
Magnus Müller becec42ca4 [codex] Rename CLI to browser-harness run (#35)
* Rename CLI to browser-harness run

* Simplify browser-harness CLI
2026-04-17 22:33:05 -07:00
Magnus Müller acda8c72a0 Simplify helper surface (#23)
* Simplify helper surface

* Trim common module and restore dispatch key
2026-04-17 21:37:58 -07:00
Gregor Žunič 29c4dfa8e7 changed name 2026-04-17 20:50:33 -07:00
Magnus Müller 35ae22bdec [codex] Make setup work from anywhere (#17)
* Make setup work from anywhere

* Rename launcher to bh

* Remove extra helper commands

* Document global skill setup
2026-04-17 19:32:18 -07:00
Magnus Müller fc3be06d78 add global bu launcher and shared skill guidance (#14) 2026-04-17 18:21:50 -07:00
Gregor Žunič 5ab5a958bb add remote browser support (Browser Use cloud) + multi-daemon + rename to bu (#1)
* add remote browser support via Browser Use cloud + multi-daemon

HARNESLESS_NAME suffixes socket/pid/log — daemons are independent, no
supervisor. start_remote_daemon() creates a Browser Use cloud browser and
launches a daemon attached to it; kill_daemon() stops both. Local Chrome
path is unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* rename env vars to BU_ prefix (shorter, less noisy in tool calls)

HARNESLESS_NAME → BU_NAME
HARNESLESS_CDP_WS → BU_CDP_WS
HARNESLESS_REMOTE_BROWSER_ID → BU_BROWSER_ID

Socket/pid/log files keep the harnesless- prefix on disk so they're
recognizable in /tmp. BROWSER_USE_API_KEY unchanged (external convention).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* rename project from harnesless to bu

Socket/pid/log paths now /tmp/bu-<name>.{sock,pid,log}. pyproject package
name updated, uv.lock regenerated. Slash command now /bu.

Note: the repo directory itself is still named harnesless on disk. Rename
manually (mv harnesless bu) so the absolute paths in docs line up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 00:28:02 -07:00
Gregor Žunič 2b8be642e0 initial commit: harnesless — LLM-first browser control via CDP
Three-process architecture: daemon.py holds one persistent CDP WebSocket
to the user's running Chrome (via chrome://inspect), short-lived run.py
processes talk to it over a Unix socket, helpers.py is the transparent
layer the LLM reads and edits at will.

Philosophy: no CLI, no fixed API surface. The LLM writes Python blocks
against ~13 tiny helpers (cdp, click, type_text, screenshot, get_dom,
etc.) and edits helpers.py on the fly when a pattern repeats. Coordinate
clicks default because they pass through iframes/shadow DOM/cross-origin
at the compositor level.

Uses cdp-use internally for send_raw only (ignores its 36k lines of
typed wrappers — raw CDP strings tokenize better than typed calls).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-16 17:51:31 -07:00