Skip to content

refactor(bootstrap): consolidate ACP browser bootstrap into install.{sh,ps1}#26668

Closed
alt-glitch wants to merge 1 commit into
mainfrom
hermes/hermes-8d7d912f
Closed

refactor(bootstrap): consolidate ACP browser bootstrap into install.{sh,ps1}#26668
alt-glitch wants to merge 1 commit into
mainfrom
hermes/hermes-8d7d912f

Conversation

@alt-glitch

@alt-glitch alt-glitch commented May 16, 2026

Copy link
Copy Markdown
Collaborator

Summary

Eliminates 687 lines of duplicated browser bootstrap code by routing all bootstrap paths through dep_ensure.pyinstall.{sh,ps1} --ensure.

Rebased onto main — drops #26620 dependency (upstream stage protocol merged via #27224).

Changes

  • scripts/install.sh: New ensure_browser() function with agent-browser + camofox install, system browser detection + .env writing, per-distro Playwright instructions (apt/arch/fedora/opensuse). macOS app-bundle paths added to find_system_browser(). configure_browser_env_from_system_browser() now creates .env if missing.
  • scripts/install.ps1: New -Ensure and -PostInstall params (coexists with stage protocol). New functions: Resolve-NpmCmd, Resolve-NpxCmd, Find-SystemBrowser (7 Windows Chrome/Edge/Chromium paths), Write-BrowserEnv, Install-AgentBrowser (with -SkipPlaywright). Invoke-EnsureMode dispatches node/browser/ripgrep/ffmpeg. Mutual exclusion guard with -Stage. ErrorActionPreference guards on all native command calls. ASCII-only convention maintained.
  • acp_adapter/entry.py: _run_setup_browser() rewritten — calls ensure_dependency("node") + ensure_dependency("browser") instead of shelling out to standalone scripts.
  • hermes_cli/dep_ensure.py: Windows-aware (_IS_WINDOWS), _find_install_script() returns (path, shell) tuple, PowerShell invocation with powershell/pwsh guard, _has_hermes_agent_browser() checks platform-correct paths, _has_system_browser() checks Windows browser names, env_extra parameter for forwarding install flags, unknown deps return False (not forwarded to script).
  • hermes_cli/config.py: stamp_install_method() writes ~/.hermes/.install_method, detect_install_method() checks stamp first.
  • acp_adapter/bootstrap/ deleted (399 + 288 lines).
  • pyproject.toml: Removed acp_adapter package-data glob.

Validation

Before After
Entry points for browser bootstrap 2 independent paths (ACP scripts + dep_ensure→install.sh) 1 unified path (dep_ensure→install.{sh,ps1})
Lines of browser bootstrap code ~1100 (687 in ACP + ~400 in install.sh ensure_mode) ~500 (install.{sh,ps1} only)
ACP tests 10 passed 10 passed (rewritten for dep_ensure path)
dep_ensure tests 13 passed 25 passed (+12 new)
Full suite 23,834 passed 23,834 passed (8 pre-existing failures, 0 new)
Docker E2E 12/12 bare-minimum tests pass
Windows E2E (vespyr) 7/9 (2 are test-script issues, not code bugs)

E2E Results

Linux Docker (python:3.13-slim, no Node):

  • hermes --version ✅
  • dep_ensure imports ✅
  • dep_ensure browser/node returns False gracefully ✅
  • _has_hermes_agent_browser False ✅
  • bootstrap module deleted ✅
  • _find_install_script returns (path, shell) tuple ✅
  • install.ps1 bundled in wheel ✅
  • stamp_install_method works ✅
  • env_extra parameter exists ✅
  • acp entry imports ✅
  • unknown dep returns False ✅

Windows (sidbin@vespyr, PS 5.1, Win11):

  • _IS_WINDOWS True ✅
  • _find_install_script returns (install.ps1, powershell) ✅
  • stamp_install_method ✅
  • unknown dep returns False ✅
  • env_extra parameter ✅
  • acp entry imports ✅
  • bootstrap module deleted ✅

End state

hermes-acp --setup-browser  ─┐
hermes postinstall           ─┼──► dep_ensure.py ──► install.sh --ensure <dep>
browser_tool.py (runtime)    ─┤                      install.ps1 -Ensure <dep>
hermes_cli (TUI launch)     ─┘

Closes the follow-up noted in #26593 ("Consolidate the two browser-bootstrap paths").

@github-actions

github-actions Bot commented May 16, 2026

Copy link
Copy Markdown
Contributor

🔎 Lint report: hermes/hermes-8d7d912f vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 8746 on HEAD, 8749 on base (✅ -3)

🆕 New issues (18):

Rule Count
unsupported-operator 9
unresolved-attribute 6
invalid-argument-type 2
invalid-return-type 1
First entries
tests/tools/test_web_providers.py:227: [unsupported-operator] unsupported-operator: Operator `in` is not supported between objects of type `Literal["extract_backend"]` and `str | dict[Unknown, Unknown] | list[Unknown] | ... omitted 26 union elements`
hermes_cli/config.py:3139: [invalid-return-type] invalid-return-type: Return type does not match returned value: expected `tuple[int, int]`, found `tuple[Any, str | dict[Unknown, Unknown] | list[Unknown] | ... omitted 26 union elements]`
tests/hermes_cli/test_kanban_core_functionality.py:3176: [unresolved-attribute] unresolved-attribute: Attribute `get` is not defined on `str`, `list[Unknown]`, `list[str]`, `int` in union `str | dict[Unknown, Unknown] | list[Unknown] | ... omitted 26 union elements`
hermes_cli/config.py:3776: [unresolved-attribute] unresolved-attribute: Attribute `get` is not defined on `str`, `list[Unknown]`, `list[str]`, `int` in union `str | dict[Unknown, Unknown] | list[Unknown] | ... omitted 26 union elements`
tests/agent/test_curator.py:855: [unsupported-operator] unsupported-operator: Operator `in` is not supported between objects of type `Literal["curator"]` and `str | dict[Unknown, Unknown] | list[Unknown] | ... omitted 26 union elements`
tests/gateway/test_whatsapp_reply_prefix.py:121: [unsupported-operator] unsupported-operator: Operator `>=` is not supported between objects of type `str | dict[Unknown, Unknown] | list[Unknown] | ... omitted 26 union elements` and `int`
tests/hermes_cli/test_mcp_reload_confirm_gate.py:34: [unresolved-attribute] unresolved-attribute: Attribute `get` is not defined on `str`, `list[Unknown]`, `list[str]`, `int` in union `str | dict[Unknown, Unknown] | list[Unknown] | ... omitted 26 union elements`
tests/hermes_cli/test_aux_config.py:56: [unresolved-attribute] unresolved-attribute: Attribute `keys` is not defined on `str`, `list[Unknown]`, `list[str]`, `int` in union `str | dict[Unknown, Unknown] | list[Unknown] | ... omitted 26 union elements`
tests/hermes_cli/test_aux_config.py:37: [unsupported-operator] unsupported-operator: Operator `in` is not supported between objects of type `Literal["title_generation"]` and `str | dict[Unknown, Unknown] | list[Unknown] | ... omitted 26 union elements`
tests/tools/test_browser_lightpanda.py:242: [unsupported-operator] unsupported-operator: Operator `in` is not supported between objects of type `Literal["engine"]` and `str | dict[Unknown, Unknown] | list[Unknown] | ... omitted 26 union elements`
tests/tools/test_browser_console.py:265: [unsupported-operator] unsupported-operator: Operator `in` is not supported between objects of type `Literal["record_sessions"]` and `str | dict[Unknown, Unknown] | list[Unknown] | ... omitted 26 union elements`
tests/tools/test_web_providers.py:226: [unsupported-operator] unsupported-operator: Operator `in` is not supported between objects of type `Literal["search_backend"]` and `str | dict[Unknown, Unknown] | list[Unknown] | ... omitted 26 union elements`
tests/cli/test_resume_display.py:648: [unsupported-operator] unsupported-operator: Operator `in` is not supported between objects of type `Literal["resume_display"]` and `str | dict[Unknown, Unknown] | list[Unknown] | ... omitted 26 union elements`
tests/hermes_cli/test_destructive_slash_confirm_gate.py:32: [unresolved-attribute] unresolved-attribute: Attribute `get` is not defined on `str`, `list[Unknown]`, `list[str]`, `int` in union `str | dict[Unknown, Unknown] | list[Unknown] | ... omitted 26 union elements`
tests/cli/test_fast_command.py:480: [invalid-argument-type] invalid-argument-type: Argument to bound method `TestCase.assertIn` is incorrect: Expected `Iterable[Any] | Container[Any]`, found `str | dict[Unknown, Unknown] | list[Unknown] | ... omitted 26 union elements`
tests/tools/test_web_providers.py:225: [unsupported-operator] unsupported-operator: Operator `in` is not supported between objects of type `Literal["backend"]` and `str | dict[Unknown, Unknown] | list[Unknown] | ... omitted 26 union elements`
hermes_cli/config.py:3766: [unresolved-attribute] unresolved-attribute: Attribute `items` is not defined on `str`, `list[Unknown]`, `list[str]`, `int` in union `str | dict[Unknown, Unknown] | list[Unknown] | ... omitted 26 union elements`
tests/cli/test_reasoning_command.py:552: [invalid-argument-type] invalid-argument-type: Argument to bound method `TestCase.assertIn` is incorrect: Expected `Iterable[Any] | Container[Any]`, found `str | dict[Unknown, Unknown] | list[Unknown] | ... omitted 26 union elements`

✅ Fixed issues (21):

Rule Count
unsupported-operator 9
unresolved-attribute 6
invalid-argument-type 5
invalid-return-type 1
First entries
tests/cli/test_reasoning_command.py:552: [invalid-argument-type] invalid-argument-type: Argument to bound method `TestCase.assertIn` is incorrect: Expected `Iterable[Any] | Container[Any]`, found `str | dict[Unknown, Unknown] | list[Unknown] | ... omitted 27 union elements`
hermes_cli/config.py:3824: [unresolved-attribute] unresolved-attribute: Attribute `items` is not defined on `str`, `list[Unknown]`, `list[str]`, `int` in union `str | dict[Unknown, Unknown] | list[Unknown] | ... omitted 27 union elements`
hermes_cli/main.py:9207: [invalid-argument-type] invalid-argument-type: Argument to function `get_profile_dir` is incorrect: Expected `str`, found `Any | None`
hermes_cli/main.py:9203: [invalid-argument-type] invalid-argument-type: Argument to function `normalize_profile_name` is incorrect: Expected `str`, found `Any | None`
tests/hermes_cli/test_mcp_reload_confirm_gate.py:34: [unresolved-attribute] unresolved-attribute: Attribute `get` is not defined on `str`, `list[Unknown]`, `list[str]`, `int` in union `str | dict[Unknown, Unknown] | list[Unknown] | ... omitted 27 union elements`
tests/cli/test_fast_command.py:480: [invalid-argument-type] invalid-argument-type: Argument to bound method `TestCase.assertIn` is incorrect: Expected `Iterable[Any] | Container[Any]`, found `str | dict[Unknown, Unknown] | list[Unknown] | ... omitted 27 union elements`
hermes_cli/main.py:9233: [invalid-argument-type] invalid-argument-type: Argument to function `describe_profile` is incorrect: Expected `str`, found `str | Any | None`
tests/hermes_cli/test_destructive_slash_confirm_gate.py:32: [unresolved-attribute] unresolved-attribute: Attribute `get` is not defined on `str`, `list[Unknown]`, `list[str]`, `int` in union `str | dict[Unknown, Unknown] | list[Unknown] | ... omitted 27 union elements`
tests/hermes_cli/test_kanban_core_functionality.py:3176: [unresolved-attribute] unresolved-attribute: Attribute `get` is not defined on `str`, `list[Unknown]`, `list[str]`, `int` in union `str | dict[Unknown, Unknown] | list[Unknown] | ... omitted 27 union elements`
tests/tools/test_browser_console.py:265: [unsupported-operator] unsupported-operator: Operator `in` is not supported between objects of type `Literal["record_sessions"]` and `str | dict[Unknown, Unknown] | list[Unknown] | ... omitted 27 union elements`
tests/tools/test_web_providers.py:225: [unsupported-operator] unsupported-operator: Operator `in` is not supported between objects of type `Literal["backend"]` and `str | dict[Unknown, Unknown] | list[Unknown] | ... omitted 27 union elements`
hermes_cli/config.py:3834: [unresolved-attribute] unresolved-attribute: Attribute `get` is not defined on `str`, `list[Unknown]`, `list[str]`, `int` in union `str | dict[Unknown, Unknown] | list[Unknown] | ... omitted 27 union elements`
tests/tools/test_web_providers.py:226: [unsupported-operator] unsupported-operator: Operator `in` is not supported between objects of type `Literal["search_backend"]` and `str | dict[Unknown, Unknown] | list[Unknown] | ... omitted 27 union elements`
tests/hermes_cli/test_aux_config.py:56: [unresolved-attribute] unresolved-attribute: Attribute `keys` is not defined on `str`, `list[Unknown]`, `list[str]`, `int` in union `str | dict[Unknown, Unknown] | list[Unknown] | ... omitted 27 union elements`
tests/tools/test_web_providers.py:227: [unsupported-operator] unsupported-operator: Operator `in` is not supported between objects of type `Literal["extract_backend"]` and `str | dict[Unknown, Unknown] | list[Unknown] | ... omitted 27 union elements`
hermes_cli/config.py:3197: [invalid-return-type] invalid-return-type: Return type does not match returned value: expected `tuple[int, int]`, found `tuple[Any, str | dict[Unknown, Unknown] | list[Unknown] | ... omitted 27 union elements]`
tests/tools/test_browser_lightpanda.py:242: [unsupported-operator] unsupported-operator: Operator `in` is not supported between objects of type `Literal["engine"]` and `str | dict[Unknown, Unknown] | list[Unknown] | ... omitted 27 union elements`
tests/gateway/test_whatsapp_reply_prefix.py:121: [unsupported-operator] unsupported-operator: Operator `>=` is not supported between objects of type `str | dict[Unknown, Unknown] | list[Unknown] | ... omitted 27 union elements` and `int`
tests/hermes_cli/test_aux_config.py:37: [unsupported-operator] unsupported-operator: Operator `in` is not supported between objects of type `Literal["title_generation"]` and `str | dict[Unknown, Unknown] | list[Unknown] | ... omitted 27 union elements`
tests/cli/test_resume_display.py:648: [unsupported-operator] unsupported-operator: Operator `in` is not supported between objects of type `Literal["resume_display"]` and `str | dict[Unknown, Unknown] | list[Unknown] | ... omitted 27 union elements`
tests/agent/test_curator.py:855: [unsupported-operator] unsupported-operator: Operator `in` is not supported between objects of type `Literal["curator"]` and `str | dict[Unknown, Unknown] | list[Unknown] | ... omitted 27 union elements`

Unchanged: 4587 pre-existing issues carried over.

Diagnostics are surfaced as warnings — this check never fails the build.

@alt-glitch

Copy link
Copy Markdown
Collaborator Author

Self-Review

PR #26668@alt-glitch (consolidation on top of #26620)

What it does: Deletes 687 lines of duplicated browser bootstrap scripts (acp_adapter/bootstrap/) and routes all bootstrap paths through dep_ensure.pyinstall.{sh,ps1} --ensure.

What it solves: Two completely independent browser bootstrap paths that share no code — ACP users got one script, pip users got another, neither had the other's improvements.

How: 11 files, -879/+617 (net -262). New ensure_browser() in install.sh with ACP-quality logic, Find-SystemBrowser/Write-BrowserEnv/-SkipPlaywright in install.ps1, _run_setup_browser() rewritten to call ensure_dependency().

Problems found and fixed during review:

  1. _has_hermes_agent_browser() checked wrong paths — fixed to match actual install locations (~/.hermes/node/bin/ POSIX, ~/.hermes/agent-browser/ Windows, legacy node_modules/.bin/)
  2. PS1 Write-BrowserEnv silently skipped when .env absent — fixed to create dir+file (matches bash side)
  3. PS1 Invoke-EnsureMode downloaded ~400MB Playwright when system browser exists — added -SkipPlaywright flag
  4. install.sh postinstall_mode() had inline browser duplication — replaced with ensure_browser() call
  5. configure_browser_env_from_system_browser() silently skipped when .env absent — fixed to mkdir -p + touch

Pre-existing issues noted (not introduced by this PR):

  • check_node()/Test-Node don't gate on Node ≥ 20 (old ACP bootstrap did)
  • ensure_browser() lacks explicit Termux guard (degrades gracefully, only reachable via explicit --ensure)

Validation: 24 targeted tests pass, 23,318 full suite pass (5 pre-existing flaky), 8 E2E isolation tests pass with real imports + temp HERMES_HOME.

Recommendation: Merge after #26620 lands — rebase to drop the dependency commits, move out of draft.

@alt-glitch alt-glitch added type/refactor Code restructuring, no behavior change P3 Low — cosmetic, nice to have comp/acp Agent Communication Protocol adapter comp/cli CLI entry point, hermes_cli/, setup wizard labels May 16, 2026
@alt-glitch alt-glitch marked this pull request as ready for review May 16, 2026 00:30
@alt-glitch alt-glitch requested a review from a team May 16, 2026 00:30
@alt-glitch alt-glitch marked this pull request as draft May 16, 2026 07:15
@alt-glitch alt-glitch force-pushed the hermes/hermes-8d7d912f branch 2 times, most recently from 1014b72 to b7ea152 Compare May 18, 2026 05:19
…sh,ps1}

Eliminates 687 lines of duplicated browser bootstrap code by routing all
bootstrap paths through dep_ensure.py -> install.{sh,ps1} --ensure.

install.sh:
- New ensure_browser() with agent-browser + camofox install, system browser
  detection + .env writing, per-distro Playwright deps (apt/arch/fedora/suse)
- macOS app-bundle paths added to find_system_browser()
- configure_browser_env_from_system_browser() creates .env if missing
- postinstall_mode() uses ensure_browser() instead of inline duplication

install.ps1:
- New -Ensure and -PostInstall params (coexists with stage protocol)
- New functions: Resolve-NpmCmd, Resolve-NpxCmd, Find-SystemBrowser,
  Write-BrowserEnv, Install-AgentBrowser (with -SkipPlaywright)
- Invoke-EnsureMode dispatches node/browser/ripgrep/ffmpeg
- Invoke-PostInstallMode runs full post-pip-install bootstrap
- ErrorActionPreference guards on all native command calls
- ASCII-only convention maintained (no Unicode)
- Mutual exclusion guard: -Ensure + -Stage = error

dep_ensure.py:
- Windows-aware: _IS_WINDOWS, _find_install_script returns (path, shell) tuple
- PowerShell invocation with powershell/pwsh guard + -ExecutionPolicy Bypass
- _has_hermes_agent_browser() checks platform-correct paths
- _has_system_browser() checks Windows browser names (chrome, msedge, chromium)
- env_extra parameter for forwarding install flags

config.py:
- stamp_install_method() writes ~/.hermes/.install_method
- detect_install_method() checks stamp first (before heuristics)

acp_adapter:
- _run_setup_browser() rewritten: ensure_dependency('node') + ensure_dependency('browser')
- acp_adapter/bootstrap/ deleted (399 + 288 lines)

Rebased onto main -- drops #26620 dependency (upstream stage protocol merged
via #27224). Closes follow-up from #26593.
@alt-glitch

Copy link
Copy Markdown
Collaborator Author

Closing — the squash-rebase silently deleted 7+ features from main (kanban_decomposer, profile_describer, x_search, profile describe, send command, etc.). Splitting into clean atomic PRs from fresh branches off main. See #27826 for tracking.

@alt-glitch alt-glitch closed this May 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/acp Agent Communication Protocol adapter comp/cli CLI entry point, hermes_cli/, setup wizard P3 Low — cosmetic, nice to have type/refactor Code restructuring, no behavior change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant