Docs
/

@leumas/touch-pc

Devices engine — turn a phone/tablet into a LAN remote for the host PC (mouse/keyboard input over a WebSocket) and drive multi-display setups (kiosk scenes on each monitor). Input synthesis comes...

@leumas/touch-pc

The Devices engine. Two hardware surfaces in one package:

  1. Remote control — turn a phone/tablet into a LAN remote for the host PC. A device pairs with a

rotating PIN, gets a session token, then streams mouse/keyboard frames over a WebSocket; the host applies them via an input engine (robotjs, or a noop fallback when native input isn't available). Remote layouts (touchpad, keyboard, controller, joystick, gyro, hybrid) are served to the device.

  1. Multi-display — register the host's monitors and throw full-screen kiosk scenes onto them.

Scenes are saved JSON layouts; the actual browser launch is delegated to an injectable launcher.

Absorbs (DRY-consolidated from): tools/leumas-touch-pc (input engine + WS protocol + PIN pairing / sessions + remotes registry + LAN security) and tools/infinity-displays (multi-display kiosk scenes / slideshows).

LAN-only — gate hard. This engine synthesizes real host input and controls displays. The router runs a lanGuard first and unconditionally rejects non-private callers, and the WS bridge must only be attached to a LAN-bound listener. robotjs is lazy-loaded, so on hosts without it the engine degrades to a safe noop instead of crashing.

Usage (in leumas-api)

import { createTouchPcRouter, createPinPairing, createSessionStore } from '@leumas/touch-pc';
import { createTouchPcWsServer } from '@leumas/touch-pc/ws';
import { requireAuth } from '@leumas/auth';

const pinPairing = createPinPairing();       // rotating 6-digit PIN
const sessionStore = createSessionStore();   // in-memory device sessions

app.use('/api/devices', createTouchPcRouter({ pinPairing, sessionStore, requireAuth }));

// Attach the input WebSocket to the SAME http server. LAN-only.
createTouchPcWsServer({ server, pinPairing, sessionStore, inputEngine: 'auto' });
console.log('Pairing PIN:', pinPairing.getPin());

Routes (/api/devices)

MethodPathPurpose
GET/configclient bootstrap { wsPath, pairingRequired, remotes }
POST/pair{ pin, deviceName }{ sessionToken, sessionId }
GET/sessionspaired devices + pairing status
GET/remotesavailable remote layouts
GET/POST/displayslist / register monitors
POST/displays/:id/sceneassign + launch a scene on a display
GET/POST/sceneslist / upsert scenes
DELETE/scenes/:idremove a scene

The input engine

createInputEngine({ inputEngine }) returns one object implementing moveMouse / moveMouseAbs / mouseDown / mouseUp / click / scroll / keyTap / keyDown / keyUp / typeText.

  • 'auto' (default) → try robotjs, else noop.
  • 'robotjs' → require robotjs (lazy); noop if it can't load.
  • 'noop' → swallow everything (safe default for headless/CI).

The WS protocol

Client frames are JSON { type, payload }; type ∈ { auth, mouse.move, mouse.down, mouse.up, mouse.click, mouse.scroll, key.tap, key.down, key.up, text.type, system.ping }. The server rate-limits (token bucket, 120/s), requires an auth frame with a valid session token when pairing is on, then applies each frame to the input engine. system.pingsystem.pong.

The display launcher contract

launcher.launch({ display, sceneId }) → { ok, launched, ... }

Default is a noop launcher. In a host app (Electron / a kiosk agent) inject one that spawns a full-screen browser at the scene URL positioned on display.bounds.

Source shared/engines/hardware/touch-pc/README.md (no-git)markdownjson
Generated from the Leumas repository. Every page cites the file it came from.leumas.techllms.txt