Roku Cloud Emulator support - #399
Draft
chrisdp wants to merge 12 commits into
Draft
Conversation
…they work on Cloud Emulator devices
…ECP2 for Cloud Emulator devices
…p, and rendezvous RokuECP and RendezvousTracker no longer own transports or XML parsing for these commands; roku-deploy's device-aware wrappers handle both local and Cloud Emulator devices. Also fixes rendezvous trackingEnabled being a truthy string instead of a boolean.
roku-deploy now enforces the canonical RemoteKey names at the type level, so the home keypress calls use 'Home'.
…ocket
DebugProtocolClient received the device config but never used it - both
sockets connected raw tcp to options.host, which for an RCE device is
the unresolved ${promptForHost} placeholder. They now route through
roku-deploy's createTelnetSocket like the compile client already does,
so an RCE device reaches the control port (8081) and io port over the
instance api's /api/v0/ports/<port> WebSocket routes. Also drops the
removed channel option from the existing createTelnetSocket call sites.
…able When the launch config's device option is an RCE config without an rceToken, fill it from the env var (a config-supplied token always wins). Hydration happens at the device-option accessors rather than in normalizeLaunchConfig so the stored launch config stays tokenless and never echoes the token back through logs or custom events.
The deprecated launch config host field is now read in exactly one place: normalizeLaunchConfig converts it into the device config and returns a ResolvedLaunchConfiguration (typed without host, device required), which everything downstream uses. AdapterOptions, DebugProtocolClient, RokuECP, PerfettoManager, and RendezvousTracker all take a concrete DeviceConfig; the scattered host fallbacks and device getters are gone. The RCE api token is hydrated from ROKU_RCE_TOKEN during normalize, and the launch config copies echoed in LaunchStartEvent/ChannelPublishedEvent scrub it back out. Log and error messages use a new util.deviceLabel helper instead of host, and RokuECP routes raw requests through rokuDeploy's ecp transport instead of hand-building host urls.
- untrack RokuAppLaunchConfiguration.ts (stays as a local file) - SceneGraphDebugCommandController: accept only a DeviceConfig, keep an error listener attached for the socket's whole life, and replace the quiet-period greeting drain with a prompt wait bounded by the connect timeout - fail the launch fast when the config supplies no device addressing - delete the deprecated host field from the config during normalize
| * includes credentials like the rceToken). A local device is identified by its host and an RCE | ||
| * device by its instanceUrl, id, or esn. | ||
| */ | ||
| public deviceLabel(device: DeviceConfig): string { |
Member
There was a problem hiding this comment.
maybe call it getDeviceLabel instead?
| const options = { | ||
| controlPort: undefined as number, | ||
| host: '127.0.0.1' | ||
| host: '127.0.0.1', |
Member
There was a problem hiding this comment.
Shouldn't take host at root level anymore.
| */ | ||
| private get device(): DeviceOption { | ||
| return { host: this.launchConfiguration.host }; | ||
| private get isLocalDevice(): boolean { |
Member
There was a problem hiding this comment.
Let's call them physical and cloud devices. (local isn't great...)
| return this.shutdown(`Could not resolve ip address for host '${this.launchConfiguration.host}'`); | ||
| //do a DNS lookup for the host to fix issues with roku rejecting ECP. | ||
| //only applies to local devices; other device types (like the Roku Cloud Emulator) are not addressed by host | ||
| if (this.isLocalDevice) { |
Member
There was a problem hiding this comment.
I think you were going to create a rokuDeploy (resolveDns) function or something for this, so we can pass the whole device in, and it resolves the device if it's a local one (but skips for rce devices).
TwitchBronBron
requested changes
Jul 31, 2026
roku-deploy renamed its device-agnostic console transport: TelnetSocket is now RokuDeploySocket, created by createRokuDeploySocket taking SocketOptions. Rename the imports, the protected createRokuDeploySocket test seams, and the spec fakes to match. No behavior changes.
- Rename util.deviceLabel to util.getDeviceLabel.
- DebugProtocolClient's spec no longer passes a root-level host to the
client; the shared options literal is split into the server's
{controlPort, host} (bind address) and the client's
{controlPort, device}.
- The launch DNS lookup goes through the new rokuDeploy.resolveDns,
which takes the whole device config and only resolves local devices,
so the session's isLocalDevice branch (and the getter) are gone.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds Roku Cloud Emulator (RCE) support to roku-debug. Stacked on #398 (the roku-deploy v4 migration) so this diff is purely the RCE work. Builds against a local checkout of roku-deploy (unified-device-option + RCE support, rokucommunity/roku-deploy#331).
The core change is a new
deviceoption on the launch configuration: a roku-deployDeviceConfigis the canonical way to address the target device, supporting local network devices ({host}) and Cloud Emulator devices ({instanceUrl|id|esn, rceToken}). The deprecated top-levelhostfield is read in exactly one place:normalizeLaunchConfigconverts whatever addressing the client supplied into a concrete device config and returns aResolvedLaunchConfiguration(typed withouthost,devicerequired), which everything inside the debugger works against, switching between local and cloud transports based on the device config.Done
device?: DeviceConfigonLaunchConfiguration, withhostdeprecated and converted innormalizeLaunchConfig- the only place the debugger reads it, which then deletes the field from the object. The session stores aResolvedLaunchConfiguration, whose type has nohostat all, so no internal reader can regress. A launch config with neitherdevicenorhostfails the launch immediately with a clear messageAdapterOptions, the debug protocol client,RokuECP,PerfettoManager, andRendezvousTrackerall take a concreteDeviceConfig(their host fields are removed entirely); log and error messages identify the device by a token-safe label (host, instance URL, id, or esn) that never includes therceTokenresolveDns, which takes the whole device config, resolves the host for local devices, and passes cloud devices through unchangedROKU_RCE_TOKENenvironment variable instead of riding the launch config:normalizeLaunchConfighydrates a tokenless RCE device config from it, and the launch configs echoed back inLaunchStartEvent/ChannelPublishedEventscrub the token out, so it never travels over DAP in either directionRemoteKeycasing (Home)createRokuDeploySocket: a real tcp socket for local devices, the instance api's/api/v0/ports/<port>websocket for cloud devices (Instance API v0.3.0 replaced the named telnet routes with numeric port routes), behind the same socket surface, soTelnetAdapterandTelnetRequestPipelineare unchanged apart from construction. Live-verified on both device types, including breaking into and resuming the micro debugger over the cloud transportcreateRokuDeploySockettoo, reaching a cloud device over the instance api's ports routes (8081 for control, plus the io port the device opens) - unblocked by Instance API v0.3.0. The transport is wired and unit-tested; the full protocol-mode session still needs a live end-to-end pass>prompt (bounded by the connect timeout) before handing the socket to telnet-client, and an error listener stays attached for the socket's whole life so a transport error can never crash the adapter process. Live-verified against a cloud device on the v0.3.0 ports routes - the old telnet proxy's line-buffering swallowed the un-terminated>prompt and forced per-command timeouts, and the byte-unchanged ports routes resolved thatqueryRegistry/queryAppState/exitApp/queryRendezvous/setRendezvousTrackingAPIs, andRokuECP's remaining raw requests (the perfetto ECP commands) ride roku-deploy'secp()transport, which addresses local and cloud devices through one HTTP ECP path (the instance'secp1proxy).RokuECPandRendezvousTrackerno longer own transports or XML parsing, and device-reported errors (not keyed, limited-mode refusals) surface verbatim. Live-verified against a cloud device, including in limited ECP modecreateRokuDeploySocket, so no raw sockets remain in the telnet paths. Live-verified connecting and settling over the cloud transportTo do
ecp()(200 OK,devin enabled-channels), and the instance's/ecp1proxy even forwards theperfetto-sessionwebsocket upgrade to the device - but the device firmware's own source-IP check rejects the connection (403 Source IP 127.0.0.6 not on a private/local network, the service-mesh sidecar address; the check reads the TCP peer, so forwarded-IP headers do nothing). Ask Roku to allow the mesh/loopback source for perfetto-session on RCE firmware; once that lands, capture needs only a URL change in PerfettoManager (the ports routes are a fixed whitelist without 8060, so the ecp1 upgrade path is the viable one)