Skip to content

feat(browser,ble): native-messaging stable connect, /browser command, BLE as desktop-only feature#111

Merged
cnjack merged 1 commit into
mainfrom
feat/browser-native-messaging-ble-desktop
Jul 4, 2026
Merged

feat(browser,ble): native-messaging stable connect, /browser command, BLE as desktop-only feature#111
cnjack merged 1 commit into
mainfrom
feat/browser-native-messaging-ble-desktop

Conversation

@cnjack

@cnjack cnjack commented Jul 4, 2026

Copy link
Copy Markdown
Owner

What & why

Follow-up polish on the browser-use + status-channel work, plus a proper fix for the macOS Bluetooth permission prompt.

Browser extension — silent reconnect

  • Stable long-lived token (Bridge.StableToken) reused across restarts instead of minting a fresh token each launch (which accumulated tokens and rewrote the endpoint every time).
  • Desktop sidecar reuses its previous port across launches.
  • Together: the extension's stored (serverUrl, token) stays valid across restarts → it reconnects silently, no re-Auto-connect.

TUI — /browser command

  • Shows browser-use status (backend / Chrome / extension / dev-mode) and toggles it with /browser on|off.
  • Wired through a decoupled tui.BrowserController so the TUI doesn't depend on the browser manager directly.

BLE (Bluetooth status channel) — desktop-only, no startup prompt

Root cause of the prompt: tinygo's bluetooth.DefaultAdapter instantiates a CBCentralManager/CBPeripheralManager at package-init — before any config is read — so a runtime BLEEnabled flag can't prevent the macOS Bluetooth prompt/TCC-abort while the package is linked in.

Fix — separate helper process:

  • The main binary never links CoreBluetooth (0 deps, verified). BLE runs in a separate jcode-ble helper the main process spawns only when BLE is enabled, relaying NotifyEvents over stdio. No prompt when off; the prompt (if any) appears exactly when the user turns BLE on.
  • feature.BLE build flag (-tags desktop): plain jcode web compiles BLE off (its endpoints 404, the settings toggle is hidden via a backend-reported available capability); the desktop sidecar compiles it on.
  • Removed BLE from the terminal/ACP entrypoints; kept on the desktop path.
  • Live toggle (ble.Proxy): the settings switch starts/stops the helper immediately — no app restart needed.
  • Packaging: desktop bundles jcode-ble (externalBin), adds a Bluetooth entitlement, and the Makefile / release CI build the helper per-platform (cgo=1 on macOS only; Linux D-Bus / Windows WinRT are pure Go).

Reviewer notes

  • macOS: for a Developer-ID non-sandboxed build, Bluetooth is gated by the Info.plist usage strings + TCC; the added com.apple.security.device.bluetooth entitlement is a sandbox key (harmless/ignored there) included for a future sandboxed/App-Store path and explicitness.
  • Building jcode-ble with -tags ble but CGO_ENABLED=0 on macOS silently falls back to the spawner stub → the Makefile forces cgo on darwin only (BLE_CGO).
  • Cross-arch desktop bundling needs a matching C toolchain for the macOS helper; release CI builds each platform on its native runner.

Verification

  • go build/vet/test ./... — default and -tags "jcode_headless desktop".
  • Web type-check + build; desktop cargo check.
  • jcode-ble builds for darwin (cgo) / linux / windows.
  • New tests: Bridge.StableToken stability across restart, ble.Proxy state machine.

Generated with Jack AI bot

Summary by CodeRabbit

  • New Features

    • Added desktop BLE support with a dedicated helper process, plus a live enable/disable toggle in the app.
    • Introduced a new /browser command in the TUI to view browser status and turn browser integration on or off.
    • Browser and sidecar startup now reuse saved values more consistently for smoother restarts.
  • Bug Fixes

    • BLE controls are now hidden when unavailable and only shown on supported builds.
    • Improved macOS Bluetooth support by including the required entitlement for desktop builds.

… as desktop-only feature

Browser extension (native messaging):
- Reuse one long-lived token (Bridge.StableToken) instead of minting a fresh
  one each launch, and reuse the desktop sidecar port across restarts, so the
  extension reconnects silently with no re-auth.

TUI:
- Add /browser command: shows browser-use status and toggles it on/off
  (/browser on|off), wired via a decoupled tui.BrowserController.

BLE (Bluetooth status channel) is now desktop-only and cleanly gated:
- Root cause of the macOS Bluetooth prompt at startup: tinygo's bluetooth
  DefaultAdapter instantiates a CBCentralManager at package-init, before any
  config is read — so a runtime flag can't prevent it. Fix: the main binary
  never links CoreBluetooth; BLE runs in a separate jcode-ble helper it spawns
  only when enabled. No prompt when off.
- feature.BLE build flag (-tags desktop): plain `jcode web` compiles BLE off
  (endpoints 404 / hidden in settings); desktop compiles it on. Frontend hides
  the toggle based on the backend-reported capability.
- Remove BLE from the terminal/ACP entrypoints; keep it on the desktop path.
- Live toggle: ble.Proxy lets the settings switch start/stop the helper without
  an app restart.
- Desktop bundles jcode-ble (externalBin) and adds a Bluetooth entitlement;
  release CI and Makefile build the helper per-platform (cgo=1 on macOS only).

Verified: go build/vet/test ./... (both default and -tags "jcode_headless
desktop"), web type-check/build, cargo check, and a real jcode-ble build across
darwin/linux/windows targets.

Generated with Jack AI bot
@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f225e144-3d8b-4724-9fe3-36c7e44507c2

📥 Commits

Reviewing files that changed from the base of the PR and between 65f9fc8 and 4460211.

📒 Files selected for processing (27)
  • .github/workflows/release.yml
  • Makefile
  • cmd/jcode-ble/main_ble.go
  • cmd/jcode-ble/main_stub.go
  • desktop/src-tauri/Entitlements.plist
  • desktop/src-tauri/src/sidecar.rs
  • desktop/src-tauri/tauri.conf.json
  • internal/browser/bridge_test.go
  • internal/browser/tokens.go
  • internal/channel/ble/ble_cgo.go
  • internal/channel/ble/ble_nocgo.go
  • internal/channel/ble/ble_proxy.go
  • internal/channel/ble/ble_proxy_test.go
  • internal/command/interactive.go
  • internal/command/web.go
  • internal/feature/feature.go
  • internal/feature/feature_default.go
  • internal/feature/feature_desktop.go
  • internal/tui/browser_command.go
  • internal/tui/input_views.go
  • internal/tui/tui.go
  • internal/tui/update.go
  • internal/web/browser.go
  • internal/web/channel.go
  • internal/web/server.go
  • web/src/components/SettingsDialog.vue
  • web/src/composables/api.ts

📝 Walkthrough

Walkthrough

This PR extracts BLE functionality into a separate jcode-ble helper process communicating via stdio JSON, introduces a Proxy for runtime enable/disable and a feature.BLE compile-time flag, wires this through web/CLI/TUI and HTTP API, adds macOS Bluetooth entitlements, persists sidecar ports, and adds a stable browser bridge token.

Changes

BLE helper process and channel wiring

Layer / File(s) Summary
Build tooling for jcode-ble helper
.github/workflows/release.yml, Makefile
Adds a build-ble target and CI steps producing a separate jcode-ble binary with ble build tag, alongside the main sidecar built with desktop tag.
jcode-ble helper entrypoint
cmd/jcode-ble/main_ble.go, cmd/jcode-ble/main_stub.go
New ble-tagged binary relays BLE events over stdin/stdout JSON; a non-ble stub exits with an error instructing to build with -tags ble.
Stdio-based BLE Notifier implementation
internal/channel/ble/ble_cgo.go, internal/channel/ble/ble_nocgo.go
Notifier now spawns and manages the external helper process, relaying notify events and received commands via newline-delimited JSON; build tags tightened.
Proxy wrapper for runtime enable/disable
internal/channel/ble/ble_proxy.go, internal/channel/ble/ble_proxy_test.go
New Proxy type installs/detaches an inner Notifier on Enable/Disable, forwards Notify, with state-machine tests.
Desktop-only BLE feature flag
internal/feature/feature.go, internal/feature/feature_default.go, internal/feature/feature_desktop.go
Compile-time feature.BLE constant is true for desktop builds, false otherwise.
Command-level BLE proxy wiring
internal/command/web.go, internal/command/interactive.go
Shared ble.Proxy replaces direct Notifier construction, gated by feature.BLE; interactive/CLI BLE registration removed as desktop-only.
BLE HTTP API and runtime toggle
internal/web/server.go, internal/web/channel.go
New BLEController interface wired into server config; endpoints report availability and apply enable/disable live.
Frontend BLE availability UI
web/src/composables/api.ts, web/src/components/SettingsDialog.vue
BLE toggle visibility now driven by backend-reported available flag instead of an isTauri check.

Desktop sidecar entitlements and port persistence

Layer / File(s) Summary
macOS Bluetooth entitlement and bundle config
desktop/src-tauri/Entitlements.plist, desktop/src-tauri/tauri.conf.json
Adds Bluetooth entitlement plist and bundles jcode-ble with macOS entitlements reference.
Persisted sidecar port selection
desktop/src-tauri/src/sidecar.rs
Sidecar reuses a previously persisted loopback port when free, falling back to a fresh free port.

Browser bridge stable token and TUI/CLI browser status

Layer / File(s) Summary
Stable browser bridge token
internal/browser/tokens.go, internal/browser/bridge_test.go, internal/web/browser.go
Adds StableToken() persisting a long-lived token file, reused across restarts by native messaging setup.
TUI /browser command and controller
internal/tui/tui.go, internal/tui/browser_command.go, internal/tui/update.go, internal/tui/input_views.go, internal/command/interactive.go
Adds /browser slash command showing/toggling browser status via a new BrowserController.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant WebChannelAPI
  participant BLEProxy
  participant BLENotifier
  participant JcodeBleHelper

  WebChannelAPI->>BLEProxy: Enable() / Disable()
  BLEProxy->>BLENotifier: New() / Close()
  BLENotifier->>JcodeBleHelper: spawn process, write wireEvent JSON to stdin
  JcodeBleHelper-->>BLENotifier: write ReceivedCommand JSON to stdout
  BLENotifier-->>BLEProxy: publish on inbound channel
  BLEProxy-->>WebChannelAPI: forward via Notify/Receive
Loading

Possibly related PRs

  • cnjack/jcode#21: Introduces the original BLE notifier integration that this PR refactors into the helper-process/Proxy model.
  • cnjack/jcode#90: Modifies the same /api/channel/ble endpoints and frontend API/response shape for BLE status.
  • cnjack/jcode#107: Touches the same browser bridge/native messaging token plumbing extended by StableToken().
✨ 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 feat/browser-native-messaging-ble-desktop

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.

@cnjack cnjack merged commit eb8c6a2 into main Jul 4, 2026
2 of 3 checks passed
@cnjack cnjack deleted the feat/browser-native-messaging-ble-desktop branch July 4, 2026 06:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant