Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions src/browser_harness/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,25 @@
stop_remote_daemon,
sync_local_profile,
)
from .helpers import *


def _execution_globals():
"""Globals for stdin scripts, with helpers loaded only for execution.

Maintenance commands like --version and --help must not import
BH_AGENT_WORKSPACE/agent_helpers.py. That file is user-editable and can have
arbitrary side effects, so keep it out of the CLI control path and expose it
only to scripts that explicitly execute through stdin.
"""
from . import helpers

namespace = dict(globals())
namespace.update(
(name, value)
for name, value in vars(helpers).items()
if not name.startswith("_")
)
return namespace

HELP = """Browser Harness

Expand Down Expand Up @@ -122,7 +140,7 @@ def main():
):
start_remote_daemon(NAME)
ensure_daemon()
exec(code, globals())
exec(code, _execution_globals())


if __name__ == "__main__":
Expand Down
47 changes: 46 additions & 1 deletion tests/unit/test_run.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,58 @@
import os
import subprocess
import sys
from io import StringIO
from pathlib import Path
from unittest.mock import patch

import pytest

from browser_harness import run


def test_maintenance_commands_do_not_load_agent_helpers(tmp_path):
(tmp_path / "agent_helpers.py").write_text(
"raise RuntimeError('agent_helpers should not load for maintenance commands')\n"
)
repo_root = Path(__file__).resolve().parents[2]
env = os.environ.copy()
env["BH_AGENT_WORKSPACE"] = str(tmp_path)
env["PYTHONPATH"] = os.pathsep.join(
[str(repo_root / "src"), env.get("PYTHONPATH", "")]
)

for arg in ("--version", "--help"):
result = subprocess.run(
[sys.executable, "-m", "browser_harness.run", arg],
cwd=repo_root,
env=env,
text=True,
capture_output=True,
timeout=10,
)
assert result.returncode == 0, result.stderr
assert "agent_helpers should not load" not in result.stderr


def test_execution_globals_load_agent_workspace_helpers(tmp_path, monkeypatch):
import browser_harness

(tmp_path / "agent_helpers.py").write_text("CUSTOM_AGENT_HELPER = 'loaded'\n")
monkeypatch.setenv("BH_AGENT_WORKSPACE", str(tmp_path))
original_helpers = sys.modules.pop("browser_harness.helpers", None)
original_package_helper = getattr(browser_harness, "helpers", None)
if hasattr(browser_harness, "helpers"):
delattr(browser_harness, "helpers")
try:
namespace = run._execution_globals()
assert namespace["CUSTOM_AGENT_HELPER"] == "loaded"
finally:
sys.modules.pop("browser_harness.helpers", None)
if original_helpers is not None:
sys.modules["browser_harness.helpers"] = original_helpers
if original_package_helper is not None:
browser_harness.helpers = original_package_helper

def test_stdin_executes_code():
stdout = StringIO()
fake_stdin = StringIO("print('hello from stdin')")
Expand Down Expand Up @@ -237,4 +283,3 @@ def test_cli_doctor_rejects_unknown_flags():
run.main()
assert ei.value.code == 2
assert "usage" in err.getvalue().lower()