Skip to content

feat: implement Rozenite as a built-in plugin#29

Open
burczu wants to merge 7 commits into
callstackincubator:masterfrom
burczu:feat/rozenite-plugin
Open

feat: implement Rozenite as a built-in plugin#29
burczu wants to merge 7 commits into
callstackincubator:masterfrom
burczu:feat/rozenite-plugin

Conversation

@burczu
Copy link
Copy Markdown
Contributor

@burczu burczu commented May 21, 2026

Closes #27

Summary

Implements the Rozenite plugin on top of the generic plugin system from #26. The plugin lets agent-cdp interoperate with Rozenite's React Native agent tooling without leaking any Rozenite-specific details into the core daemon or CLI.

Rozenite plugin

  • Lives entirely under packages/agent-cdp/src/plugins/rozenite/* — core code is unaware of Fusebox globals, Rozenite domains, or tool registry internals
  • Activates only for React Native targets; fails cleanly on unsupported targets
  • On target selection, connects to @rozenite/metro's HTTP agent API (POST /rozenite/agent/sessions) using the target's sourceUrl and logicalDeviceId
  • Exposes a fixed CLI surface: rozenite status, rozenite tools, rozenite tool-schema <name>, rozenite call <name> [--input JSON]

Playground integration

  • Adds @rozenite/metro (dev) and @rozenite/agent-bridge to the playground
  • metro.config.js wraps the Expo Metro config with withRozenite (opt-in via WITH_ROZENITE=true)
  • useRozeniteBridge hook registers three test tools (echo, getTimestamp, getPlaygroundInfo) for end-to-end testing

Architectural note: CDP → HTTP

The issue assumed the CDP path (Runtime.addBinding + Runtime.bindingCalled). During implementation it was discovered that this does not work in the Hermes/Fusebox New Architecture: binding events for the native __CHROME_DEVTOOLS_FRONTEND_BINDING__ are routed to the DevTools frontend only, not to debugging sessions. The plugin therefore uses @rozenite/metro's HTTP agent API, which was designed exactly for this case. CDP session lifecycle is used only to trigger the initial connection — the HTTP session is independent of the CDP transport.

Testing

# start Metro with Rozenite enabled
cd playground && WITH_ROZENITE=true pnpm start

# run the app, then:
node packages/agent-cdp/dist/bin.js start
node packages/agent-cdp/dist/bin.js target list
node packages/agent-cdp/dist/bin.js target select <id>
node packages/agent-cdp/dist/bin.js rozenite status        # state: ready, toolCount > 0
node packages/agent-cdp/dist/bin.js rozenite tools
node packages/agent-cdp/dist/bin.js rozenite call echo --input '{"text":"hello"}'

@burczu burczu changed the title feat: implement Rozenite as a built-in plugin (closes #27) feat: implement Rozenite as a built-in plugin May 21, 2026
burczu added 7 commits May 21, 2026 10:27
Constants and message type definitions for the Rozenite CDP binding
protocol — binding payload envelope, app↔agent message union types,
and AgentTool shape.
In-memory registry for dynamically registered Rozenite tools —
supports register by domain (qualifies names as domain.toolName),
unregister by qualified name, and clear on disconnect.
Async poll → bind → domain-init sequence for establishing the Rozenite
CDP binding. Polls for __FUSEBOX_REACT_DEVTOOLS_DISPATCHER__ at 500ms
intervals, times out after 30s with a descriptive error, and supports
AbortSignal cancellation for clean target-cleared teardown.
- Add alwaysExecutable flag to AgentPluginCommand so status-style
  commands can run regardless of plugin state; honour it in dispatch
- Implement RozenitePlugin: fire-and-forget bootstrap, onDisconnected
  wiring, tool registry lifecycle, in-flight call rejection on
  disconnect, and four commands: status, tools, tool-schema, call
Add rozenite CLI subcommand family (status, tools, tool-schema, call)
and register RozenitePlugin in the PluginOrchestrator alongside the
runtime bridge plugin.
18 tests covering bootstrap lifecycle, tool registry, and all four
commands — including alwaysExecutable status, tool-level vs transport
failure distinction, and disconnect rejection of in-flight calls.
…gration

The CDP binding approach (Runtime.addBinding + Runtime.bindingCalled) does not
work in Hermes/Fusebox New Architecture — binding events are routed to the
DevTools frontend only, not to debugging sessions. Switch to @rozenite/metro's
HTTP agent API (/rozenite/agent/*) which is designed for this use case.

Remove bootstrap.ts and tool-registry.ts; the HTTP session owns tool state.
CDP session disconnects no longer tear down the Rozenite session since the
HTTP session is independent of the CDP transport.

Add playground integration: metro.config.js wraps Expo config with withRozenite,
use-rozenite-bridge.ts registers three test tools (echo, getTimestamp,
getPlaygroundInfo), and index.tsx renders a Rozenite status section when the
Fusebox dispatcher global is present.
@burczu burczu force-pushed the feat/rozenite-plugin branch from 4bd4d97 to 538f24a Compare May 21, 2026 08:27
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.

Implement Rozenite as a built-in plugin on top of the plugin system

1 participant