Skip to content

fix: state the browser integration honestly on the edition that has no switch - #383

Merged
KrasimirKralev merged 1 commit into
betafrom
fix/hermes-browser-integration
Aug 12, 2026
Merged

fix: state the browser integration honestly on the edition that has no switch#383
KrasimirKralev merged 1 commit into
betafrom
fix/hermes-browser-integration

Conversation

@KrasimirKralev

@KrasimirKralev KrasimirKralev commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

The bug

On a device with no OpenClaw CLI, the Browser app's second step offered an Activate button that could only ever fail. The action wrote tools.profile: full and tools.web.search.enabled: true through the openclaw binary, so the owner got a red banner —

⚠ The OpenClaw CLI is not available on this edition. [ Fix the error ]

— next to a "Fix the error" prompt that would have sent the on-device agent hunting through journald for a fault that does not exist. Step 1 (Chromium) and step 3 (open the desktop browser) both worked; only the step in between was broken, and it was broken by claiming a capability needed setting up when it was already in place.

Why there is nothing to activate there

Browsing already works on that edition, by design and at every boot:

  • mcp/tools/browser.ts registers browser_open / browser_navigate / browser_screenshot / browser_close for both editions — only the four coordinate tools are OpenClaw-only. They drive the same desktop Chromium over CDP 18800.
  • scripts/register-mcp.sh wires the ClawBox MCP server into the harness config at every web-server boot, then turns the harness's own browser toolset off, logging "built-in browser toolset off; browsing goes through the ClawBox browser_ tools."* That is deliberate: the built-in one drives a browser the owner cannot see, and its engine is not provisioned on this image.
  • There is no tools.profile on that harness to flip, and no separate web-search tool to arm — browsing is the web mechanism.

A per-user toggle would also fight the boot-time reconciliation that script exists to perform, so a "real" Hermes activation was rejected rather than invented.

What changed

src/app/setup-api/browser/manage/route.ts

  • GET answers a new alwaysOn flag, and reports enabled: true on that edition. It skips the OpenClaw config read and the sqlite flag entirely there — neither describes a device that keeps no such files.
  • enable short-circuits after creating the browser profile dir, returning { ok: true, enabled: true, alwaysOn: true } without touching the CLI. It still refuses when Chromium is missing.
  • disable answers plainly that there is nothing to turn off, rather than reporting a success that changed nothing.
  • The two hand-rolled sudo systemctl restart clawbox-gateway calls now go through the existing restartGateway() helper, which already knows which editions have a gateway to bounce (and has the user-unit fallback).

src/components/BrowserApp.tsx

  • Step 2 renders from alwaysOn: satisfied state, no button, and copy that says browsing is built in. ErrorWithFix therefore never fires here.
  • The detail chip names the tools the agent actually holds instead of tools profile: full, which is an OpenClaw config key that does not exist on the other edition.
  • Step 3 checks for Chromium itself rather than inheriting that check from step 2 — step 2 no longer implies it.
  • The success toasts named OpenClaw regardless of the running harness; they now use the harness label the rest of the panel already uses.

Translations — one new key, browser.builtInMessage, in all ten locales.

Not a regression for OpenClaw or dual

The branch is keyed on the edition (openclawIsAbsent()), not the active harness. A dual device still ships the CLI and the gateway, so it keeps the switch, the config writes and the gateway bounce exactly as before. A test asserts the switch shape is still reported on an edition that ships the CLI, and that runOpenclawConfigSet is still called there.

Tests

16 new tests, all failing without the change (verified by mutation: forcing the flag off fails 5 route tests and 3 panel tests).

  • src/tests/routes/browser/manage.test.ts — GET reports on/alwaysOn; GET reads no OpenClaw config; GET stays on even if the old switch was persisted off; enable succeeds with no CLI call and no gateway bounce; enable still refuses without Chromium; disable answers without the CLI and without writing sqlite; the desktop browser controls are untouched; the gateway bounce goes through the shared helper; and the switch edition still reports alwaysOn: false.
  • src/tests/components/browser-app.test.tsx — panel renders the toggle, connected copy and tools-profile chip on a switch device; renders no toggle, built-in copy and the tool-name chip on an always-on device, and still offers the desktop browser controls.

Full suite: 2383 passed, same 32 pre-existing failures as beta on this Windows dev host (path-separator and file-mode assertions). Typecheck and lint clean on every changed file.

Still to verify on hardware

I could not reach the Hermes device to deploy or check the panel. SSH on 192.168.50.190 rejects both the documented password and key auth (Permission denied (publickey,password)), and the device password rotates at each setup. The reasoning above is settled by the code, but the live confirmations are still owed:

  • deploy this branch and confirm step 2 shows the built-in state with no error banner;
  • hermes mcp list / hermes tools list — clawbox server present, built-in browser toolset disabled;
  • drive browser_open from a chat turn and watch the desktop window respond.

Happy to run all three as soon as I have the current credential for that box.

Summary by CodeRabbit

  • New Features

    • Added built-in browser integration for supported editions, with browser tools shown as always available.
    • Added localized messaging explaining built-in browsing in multiple languages.
    • Browser controls now appear only when integration is enabled and Chromium is installed.
  • Bug Fixes

    • Improved browser status and enable/disable handling for always-on editions.
    • Prevented unsupported disable actions and improved gateway restart behavior.

…o switch

The Browser app's second step offered "Activate", and on a device with no
OpenClaw CLI that button could only ever fail: the action wrote
`tools.profile: full` and `tools.web.search.enabled` through the openclaw
binary, so the owner got "The OpenClaw CLI is not available on this edition."
next to a red "Fix the error" prompt — for a capability that was already
working.

There is nothing to activate there. The four ClawBox browser tools
(browser_open / browser_navigate / browser_screenshot / browser_close) are
registered on that edition unconditionally, scripts/register-mcp.sh wires the
ClawBox MCP server into the harness config at every web-server boot, and it
turns the harness's own browser toolset off so browsing goes through those
tools and therefore through the Chromium window on the desktop. No tools
profile to flip, no separate web-search tool to arm.

So the route now answers `alwaysOn` and the panel renders that state: step 2
reads as already satisfied, names the tools the agent actually holds instead of
an OpenClaw config key, and carries no button — a control with nothing to
control is worse than no control at all. `enable` and `disable` are guarded
server-side too, so a stale page or a direct call gets a plain answer rather
than a CLI that isn't installed.

Keyed on the edition rather than the active harness, so a dual device — which
does have the CLI and the gateway — keeps the switch exactly as it was. The
two hand-rolled `systemctl restart clawbox-gateway` calls now go through the
existing restartGateway() helper, which already knows which editions have a
gateway to bounce. Step 3 checks for Chromium itself rather than inheriting
that check from step 2, which no longer implies it.

New string in all ten locales; success toasts now name the running harness
instead of hardcoding OpenClaw.
@KrasimirKralev
KrasimirKralev requested a review from a team as a code owner August 12, 2026 11:21
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The browser integration now detects always-on editions, skips OpenClaw configuration for them, rejects disable requests, and uses restartGateway(). BrowserApp displays edition-specific controls and messages. Tests cover route and UI behavior. Localized built-in messages were added.

Changes

Browser integration behavior

Layer / File(s) Summary
Edition-aware route behavior
src/app/setup-api/browser/manage/route.ts, src/tests/routes/browser/manage.test.ts
The route reports always-on status for editions without OpenClaw, bypasses configuration reads and writes, validates Chromium, rejects disable requests, and delegates gateway restarts to restartGateway(). Tests cover both edition types and browser controls.
Browser integration UI and localization
src/components/BrowserApp.tsx, src/lib/desktop-translations*.ts, src/tests/components/browser-app.test.tsx
BrowserApp shows built-in browser tools for always-on editions and switch controls for OpenClaw editions. Browser actions require an enabled integration and installed Chromium. Built-in messages are localized across supported languages, with component tests covering both modes.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant BrowserApp
  participant BrowserManageRoute
  participant EditionDetection
  participant OpenClawConfig

  BrowserApp->>BrowserManageRoute: Request browser status
  BrowserManageRoute->>EditionDetection: Check integrationIsAlwaysOn()
  alt Always-on edition
    EditionDetection-->>BrowserManageRoute: alwaysOn = true
    BrowserManageRoute-->>BrowserApp: Enabled built-in browser status
  else OpenClaw edition
    EditionDetection-->>BrowserManageRoute: alwaysOn = false
    BrowserManageRoute->>OpenClawConfig: Read integration configuration
    OpenClawConfig-->>BrowserManageRoute: Configuration state
    BrowserManageRoute-->>BrowserApp: Switch-controlled status
  end
Loading

Possibly related PRs

Suggested labels: area: gateway

Suggested reviewers: georgik77, yalexx

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: accurately representing the browser integration on editions without a switch.
Description check ✅ Passed The description clearly explains the bug, implementation, testing results, affected editions, and pending hardware verification.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/hermes-browser-integration

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown

🦀 ClawReview

Your friendly reef crab, here with the lay of the land.

On Hermes-edition devices (no OpenClaw CLI installed), the Browser app's step 2 previously showed an Activate button that could only produce an error, because the underlying action reached for a binary that isn't there. This PR fixes that by teaching the route to detect the always-on edition via openclawIsAbsent(), returning an alwaysOn flag to the client, and rendering step 2 as a satisfied state with no button rather than a broken switch. As a side-effect, the two hand-rolled systemctl restart clawbox-gateway calls in the route are replaced with the existing restartGateway() helper, which already handles edition differences. All ten translation locales receive the new browser.builtInMessage key.

At a glance

  • 🔧 Fix · touches BrowserApp UI + browser/manage route, edition detection, all translation files
  • Base branch: beta · +140 source / +227 tests across 8 files
  • ✅ base beta matches the beta-first convention
  • ✅ conventional PR title
  • ✅ source changes come with test changes

Good to know

  • ℹ️ Adds 16 new tests, mutation-verified (forcing the flag off breaks 8 of them), so the fix has solid coverage before it ships to customer devices.
  • 🟡 The restartGateway() consolidation is a quiet bonus: removes two raw sudo systemctl calls that bypassed the helper's edition-aware no-op logic.
  • ℹ️ The alwaysOn flag is driven by the edition (not the active harness), so a dual device still gets the toggle path — worth confirming if any dual hardware is in the test pool.

— ClawReview 🦀, scuttling off. General info only — see CodeRabbit for the detailed review. Conventions: docs.

@github-actions github-actions Bot added the area: ui Auto-triage area label Aug 12, 2026
@github-actions

github-actions Bot commented Aug 12, 2026

Copy link
Copy Markdown

CI Summary

✅ Tests

  • Result: passed
  • View run
  • Coverage: statements 65.17%, branches 54.15%, functions 63.12%, lines 67.25%

✅ E2E

✅ E2E Install

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/BrowserApp.tsx`:
- Around line 267-269: Update the alwaysOn label in BrowserApp to include
browser_close alongside the other available browser tools, while leaving the
non-always-on “tools profile: full” label unchanged.

In `@src/tests/routes/browser/manage.test.ts`:
- Around line 261-264: Update the systemctlCalls filter in the relevant browser
management test to match the exact gateway service argument,
“clawbox-gateway.service”, so direct restart calls are detected while preserving
the existing zero-call assertion.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 93aa3241-55fb-4db3-93f6-ec820f915280

📥 Commits

Reviewing files that changed from the base of the PR and between 8b1ed5c and 8c8706a.

📒 Files selected for processing (8)
  • src/app/setup-api/browser/manage/route.ts
  • src/components/BrowserApp.tsx
  • src/lib/desktop-translations-part1.ts
  • src/lib/desktop-translations-part2.ts
  • src/lib/desktop-translations-part3.ts
  • src/lib/desktop-translations.ts
  • src/tests/components/browser-app.test.tsx
  • src/tests/routes/browser/manage.test.ts

Comment on lines +267 to +269
<span className={`text-xs text-white/40${alwaysOn ? " font-mono" : ""}`}>
{alwaysOn ? "browser_open · browser_navigate · browser_screenshot" : "tools profile: full"}
</span>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

List every available browser tool.

The always-on label omits browser_close. The route describes it as an available browser tool. The panel therefore reports an incomplete capability set.

Proposed fix
-                      {alwaysOn ? "browser_open · browser_navigate · browser_screenshot" : "tools profile: full"}
+                      {alwaysOn ? "browser_open · browser_navigate · browser_screenshot · browser_close" : "tools profile: full"}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<span className={`text-xs text-white/40${alwaysOn ? " font-mono" : ""}`}>
{alwaysOn ? "browser_open · browser_navigate · browser_screenshot" : "tools profile: full"}
</span>
<span className={`text-xs text-white/40${alwaysOn ? " font-mono" : ""}`}>
{alwaysOn ? "browser_open · browser_navigate · browser_screenshot · browser_close" : "tools profile: full"}
</span>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/BrowserApp.tsx` around lines 267 - 269, Update the alwaysOn
label in BrowserApp to include browser_close alongside the other available
browser tools, while leaving the non-always-on “tools profile: full” label
unchanged.

Comment on lines +261 to +264
const systemctlCalls = mockExec.mock.calls.filter(
([, args]) => Array.isArray(args) && args.includes("clawbox-gateway"),
);
expect(systemctlCalls).toHaveLength(0);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Match the gateway service argument exactly.

args.includes("clawbox-gateway") does not match the direct service argument, clawbox-gateway.service. The test can pass if a direct systemctl restart clawbox-gateway.service call returns.

Proposed fix
-        ([, args]) => Array.isArray(args) && args.includes("clawbox-gateway"),
+        ([, args]) => Array.isArray(args) && args.includes("clawbox-gateway.service"),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const systemctlCalls = mockExec.mock.calls.filter(
([, args]) => Array.isArray(args) && args.includes("clawbox-gateway"),
);
expect(systemctlCalls).toHaveLength(0);
const systemctlCalls = mockExec.mock.calls.filter(
([, args]) =>
Array.isArray(args) && args.includes("clawbox-gateway.service"),
);
expect(systemctlCalls).toHaveLength(0);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/tests/routes/browser/manage.test.ts` around lines 261 - 264, Update the
systemctlCalls filter in the relevant browser management test to match the exact
gateway service argument, “clawbox-gateway.service”, so direct restart calls are
detected while preserving the existing zero-call assertion.

@KrasimirKralev
KrasimirKralev merged commit 66266f1 into beta Aug 12, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: ui Auto-triage area

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant