Skip to content

feat(remote): Implement phase 1 for ZooCode remote control#683

Closed
navedmerchant wants to merge 5 commits into
feature/remote-bridgefrom
feature/remote-bridge-phase1
Closed

feat(remote): Implement phase 1 for ZooCode remote control#683
navedmerchant wants to merge 5 commits into
feature/remote-bridgefrom
feature/remote-bridge-phase1

Conversation

@navedmerchant

Copy link
Copy Markdown
Contributor

Related GitHub Issue

Closes: #682

Description

This PR implements Phase 1 of the Remote Control & Approval feature: a forked Node.js bridge process that connects to the Zoo Code extension's IPC API surface over a Unix socket, plus the extension-side wiring to opt into it from the SettingsView.

What Phase 1 delivers:

  • A new @zoo-code/remote-bridge workspace package containing a Bridge class that owns an IpcClient and can issue any TaskCommand (e.g. GetModes, GetCommands, SendMessage) and receive the matching TaskEvent responses.
  • A CLI entry point (main.ts) with two modes: a one-shot mode that runs a single API call and prints the response, and a long-running --serve mode (the mode the extension forks) that stays connected and streams every TaskEvent to stdout as newline-delimited JSON. Phase 2 will replace this stdout line with a WebRTC data channel write.
  • Extension-side lifecycle management via RemoteBridgeHost, which forks the bundled bridge (dist/remote-bridge/main.js) in --serve mode, pipes its output to the Zoo Code output channel, and restarts it with exponential backoff after unexpected crashes (up to maxRestarts).
  • A new Remote Control section in the SettingsView (RemoteControlSettings.tsx) with an enable checkbox and an optional socket path field, persisted through the ContextProxy / GlobalSettings as remoteControlEnabled / remoteControlSocketPath.
  • Hot-toggling without an extension restart: the webviewMessageHandler updateSettings handler persists the new values and fires ClineProvider#onRemoteControlChange, which src/extension.ts subscribes to in order to start/stop/restart the IPC server and forked bridge.
  • The bridge is bundled into the extension VSIX at dist/remote-bridge/main.js by a new esbuild context in src/esbuild.mjs.

Key design choices:

  • The bridge is opt-in: no socket is opened and no process is forked unless remoteControlEnabled is on. The legacy ROO_CODE_IPC_SOCKET_PATH env var still starts the IPC server for headless/CLI use but does not auto-fork the bridge.
  • The bridge reuses the same IpcClient the CLI uses, so it speaks the exact same API contract — no new protocol surface.
  • RemoteBridgeHost's fork implementation is injectable (ForkFn), enabling unit testing of the lifecycle/restart logic without spawning real processes.
  • The package is scoped @zoo-code/remote-bridge to reflect that it's a Zoo Code library.

What Phase 1 does NOT do (later phases): WebRTC data channel, signaling, remote UI, push notifications.

Test Procedure

Unit tests — RemoteBridgeHost lifecycle (src):

cd src && npx vitest run services/remote-bridge/__tests__/RemoteBridgeHost.spec.ts

Covers: fork in --serve mode with socket path, no double-fork when already running, stop() kills child + cancels restarts, crash-restart with exponential backoff, giving up after maxRestarts, restart() re-forks against the same socket, and stdout/stderr piping to the logger.

Integration tests — Bridge IPC round-trip (packages/remote-bridge):

cd packages/remote-bridge && npx vitest run src/__tests__/bridge.test.ts

Stands up a real IpcServer on a temporary socket and verifies the Bridge can: connect and receive its Ack, issue GetModes and receive the ModesResponse event, forward broadcast TaskEvents to onEvent subscribers (and unsubscribe correctly), and reject request() on timeout when no response arrives.

Extension host tests (src):

cd src && npx vitest run core/webview/__tests__/ClineProvider.spec.ts core/webview/__tests__/webviewMessageHandler.spec.ts

Verifies getStateToPostToWebview returns/defaults the remoteControl* values, and that the updateSettings handler persists them and calls notifyRemoteControlChange (and does not notify for unrelated settings).

Webview UI tests (webview-ui):

cd webview-ui && npx vitest run src/components/settings/__tests__/RemoteControlSettings.spec.tsx

Verifies the enable checkbox renders, the socket path input only appears when enabled, and setCachedStateField is called on toggle/input change.

Live demos (manual smoke test):

pnpm --filter @zoo-code/remote-bridge demo        # one-shot API call against a mock server
pnpm --filter @zoo-code/remote-bridge demo:serve  # --serve mode streaming an event from a mock server

Pre-Submission Checklist

  • Issue Linked: This PR is linked to approved issue Phase 1 add subprocess to zoo for webrtc bridge #682.
  • Scope: My changes are focused on Phase 1 of the Remote Control feature (one major feature per PR).
  • Self-Review: I have performed a thorough self-review of my code.
  • Testing: New unit, integration, and webview tests have been added to cover the bridge, host lifecycle, settings persistence, and UI.
  • Documentation Impact: I have considered documentation updates (see below).
  • Contribution Guidelines: I have read and agree to the Contributor Guidelines.

Screenshots / Videos

Documentation Updates

  • Yes, documentation updates are required. A new packages/remote-bridge/README.md documents the bridge architecture, usage (library + CLI), enabling from Zoo Code preferences, scripts, and tests. User-facing docs for the Remote Control settings tab should be added to the docs repository.

Additional Notes

  • The bridge is bundled as a separate esbuild target (dist/remote-bridge/main.js) and runs as its own Node process, not in the extension host — vscode is marked external for that bundle.
  • knip.json was updated to include remote-bridge in the workspace package group.
  • Socket path resolution precedence: remoteControlSocketPath setting → ROO_CODE_IPC_SOCKET_PATH env var → per-user default /tmp/zoo-code-<uid>.sock.
  • The --serve wire format (ndjson on stdout) is intentionally a Phase 1 placeholder; Phase 2 will swap it for a WebRTC data channel forward.

Get in Touch

…rface (#650)

Add packages/remote-bridge, a standalone forked Node process that connects
to the Zoo Code extension's IPC server (the API surface) over a Unix socket
using the same IpcClient the CLI uses.

Phase 1 scope (issue #650):
- Bridge class wraps IpcClient: connect(), sendCommand(), onEvent(),
  request() (send a TaskCommand and await the matching TaskEvent response),
  plus getModes()/getCommands()/getModels()/sendMessage() helpers.
- main.ts is the forked process entry point (CLI) that connects to a socket
  (defaulting to ROO_CODE_IPC_SOCKET_PATH) and runs a single API call,
  pretty-printing the response.
- Integration test stands up a real IpcServer with a mock command handler
  (mirroring src/extension/api.ts) and proves the round-trip: connect -> Ack ->
  GetModes -> ModesResponse, plus event forwarding and request timeout.
- scripts/demo.ts forks main.ts as a real child process against a mock server
  to demonstrate the live API call end-to-end.

Verified: check-types, lint, and 3/3 vitest tests pass; live demo prints the
modesResponse payload from a forked child process.

Later phases will forward this IPC traffic over a WebRTC data channel.
…rom extension (#650)

Wire the Phase 1 bridge into the extension so it can be toggled from
Zoo Code preferences instead of requiring the ROO_CODE_IPC_SOCKET_PATH env var.

Extension changes:
- Add zoo-code.remoteControl.enabled and zoo-code.remoteControl.socketPath
  settings to src/package.json contributes.configuration (+ package.nls.json).
- src/extension.ts now starts the IpcServer when the setting is on OR the env
  var is set, and forks the bridge in --serve mode via RemoteBridgeHost when the
  setting is on. A config-change listener hot-toggles start/stop without a
  restart; deactivate() disposes the host.
- Add RemoteBridgeHost (src/services/remote-bridge): owns the forked bridge
  lifecycle with crash-restart backoff, pipes child stdout/stderr to the output
  channel. Fork impl is injectable for unit tests (7 tests).
- Bundle the bridge as a separate esbuild entry to dist/remote-bridge/main.js
  so the extension can child_process.fork it from the VSIX.

Bridge changes:
- main.ts gains a long-running --serve mode (the mode the extension forks):
  connects and streams every TaskEvent to stdout as newline-delimited JSON.
  One-shot mode is unchanged.
- scripts/serve-demo.ts forks the bundled bridge against a mock IPC server and
  verifies an event streams end-to-end.

Verified: src + remote-bridge check-types/lint clean; RemoteBridgeHost 7/7 and
bridge 3/3 tests pass; live serve-demo prints the streamed taskStarted ndjson
from a forked bundled bridge process.
@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e8fdfbdf-1b6b-4a6c-9892-a13a1e95ccd8

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/remote-bridge-phase1

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 and usage tips.

@navedmerchant navedmerchant changed the title Roo code remote knip Zoo code remote phase 1 Jun 22, 2026
@navedmerchant navedmerchant marked this pull request as draft June 22, 2026 05:13
@navedmerchant navedmerchant self-assigned this Jun 22, 2026
@navedmerchant navedmerchant changed the title Zoo code remote phase 1 feat(remote): Implement phase 1 for ZooCode remote control Jun 22, 2026
@navedmerchant

Copy link
Copy Markdown
Contributor Author

Closing as I am going to do it in a different way that scales better

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