Development setup
Requirements: Node 24+ and pnpm (corepack enable is enough). Run commands from the repository root.
pnpm installRepository layout
packages/
protocol/ @himmetkaan/remote-devtools-protocol: message types, field DSL, JSON patch helpers (no dependencies)
relay/ @himmetkaan/remote-devtools-relay: Node + ws; library (createRelay) and CLI
sdk-js/ @himmetkaan/remote-devtools: the JS SDK and its modules; scripts/fake-device.ts
ui/ @himmetkaan/remote-devtools-ui: Vite + React; one folder per tab in src/modules
apps/
desktop/ Electron app: embedded relay + UI
server/ Docker image (UI build + bundled relay), compose file with Caddy
examples/
web/ A page that uses every SDK module
docs/ This site (VitePress)TypeScript runs directly in Node through type stripping during development, so the relay, tests and scripts need no build step. Only "erasable" TypeScript is used (no enums or namespaces).
Everyday commands
pnpm dev:relay # relay on :8080 with CONNECT_REQUESTS=local
pnpm dev:ui # UI on :5173, proxies /ws and /health to the relay
pnpm dev:example # example page on :5174
pnpm dev:desktop # build and open the Electron app
pnpm docs:dev # this documentation sitepnpm typecheck && pnpm test # every package
pnpm build # every package, the desktop app bundle and the docsTrying things without a phone
The fake device uses the SDK itself:
node packages/sdk-js/scripts/fake-device.ts dev 8080 # find the local relay, ask to connect
node packages/sdk-js/scripts/fake-device.ts code 123456 ws://127.0.0.1:8080
node packages/sdk-js/scripts/fake-device.ts qr '<QR content>'Type commands on stdin: bg, fg, log|debug|warn|err <message>, name <text>, net <url>, set <key> <value>, inc, flags, drop, resume, unpair, quit. The fake device registers sample functions, a storage adapter, a state store, two flags and a panel.
The relay smoke test runs the whole pairing flow against a running relay:
node packages/relay/scripts/smoke.ts ws://127.0.0.1:8080Landing page screenshots
The home page (docs/.vitepress/theme/LandingPage.vue) uses real UI screenshots from docs/public/landing/. After UI changes, regenerate them:
pnpm docs:screenshotsThe script builds the UI, starts a relay, opens it in headless Chrome (dark scheme, 2× pixel density), pairs a demo device with realistic data (docs/scripts/demo-device.ts), lets a second device ask to connect and captures every tab. It needs Google Chrome; set CHROME_PATH for another Chromium build. Spotlight positions on the landing page are percentages of the screenshot, so check them when a tab's layout changes.
Adding a UI tab for a module
- Create
packages/ui/src/modules/<name>/<Name>Tab.tsx; it receives{deviceId}. - Register it in
packages/ui/src/modules/registry.tswith the capability key that turns it on. - Read the capability from
device.hello.capabilities[<name>], send requests withrelayClient.request(...)and listen to events withrelayClient.subscribe(...)(seemodules/network/ingest.ts).
Changing the protocol
packages/protocol/src/index.ts and the protocol page must change together. Bump PROTOCOL_VERSION for incompatible changes: the relay rejects other versions with UNSUPPORTED_VERSION.
