import { assign, createActor, fromPromise, setup, waitFor } from '@moonshot-ai/agent-core-v2/human/xstate2'; import { startRemoteControl, type RemoteControlHandle } from './remote-control'; export type RemoteControlState = 'off' | 'starting' | 'on' | 'stopping'; export interface RemoteControlStatusInfo { readonly enabled: boolean; readonly state: RemoteControlState; readonly url?: string; readonly deviceId?: string; readonly deviceName?: string; readonly error?: string; } export interface RemoteControlManager { status(): RemoteControlStatusInfo; enable(): Promise; disable(): Promise; close(): Promise; } export interface RemoteControlManagerOptions { readonly homeDir: string; readonly localOrigin: () => string; readonly localServerToken: () => string; readonly clientVersion: string; readonly relayOrigin?: string; readonly stderr?: Pick; } interface RemoteControlMachineContext { readonly handle?: RemoteControlHandle; readonly error?: unknown; } type RemoteControlMachineEvent = { type: 'enable' } | { type: 'disable' } | { type: 'tunnel.exited' }; function createRemoteControlMachine( options: RemoteControlManagerOptions, onTunnelStarted: (handle: RemoteControlHandle) => void, ) { return setup({ types: { context: {} as RemoteControlMachineContext, events: {} as RemoteControlMachineEvent, }, actors: { startTunnel: fromPromise(async () => { const handle = await startRemoteControl({ homeDir: options.homeDir, localOrigin: options.localOrigin(), localServerToken: options.localServerToken, clientVersion: options.clientVersion, relayOrigin: options.relayOrigin, stderr: options.stderr, }); onTunnelStarted(handle); return handle; }), stopTunnel: fromPromise(async ({ input }) => { await input?.close(); }), }, }).createMachine({ id: 'remoteControl', initial: 'off', context: { handle: undefined, error: undefined }, states: { off: { on: { enable: 'starting', 'tunnel.exited': {} }, }, starting: { on: { enable: {} }, invoke: { src: 'startTunnel', onDone: { target: 'on', actions: assign({ handle: ({ event }) => event.output, error: undefined }), }, onError: { target: 'off', actions: assign({ handle: undefined, error: ({ event }) => event.error }), }, }, }, on: { on: { disable: 'stopping', 'tunnel.exited': { target: 'off', actions: assign({ handle: undefined }) }, }, }, stopping: { on: { disable: {}, 'tunnel.exited': {} }, invoke: { src: 'stopTunnel', input: ({ context }) => context.handle, onDone: { target: 'off', actions: assign({ handle: undefined }) }, onError: { target: 'off', actions: assign({ handle: undefined, error: ({ event }) => event.error }), }, }, }, }, }); } export function createRemoteControlManager( options: RemoteControlManagerOptions, ): RemoteControlManager { const actor = createActor( createRemoteControlMachine(options, (handle) => { void handle.closed.then( () => { actor.send({ type: 'tunnel.exited' }); }, (error) => { options.stderr?.write(`remote-control lock release failed: ${errorMessage(error)}\n`); actor.send({ type: 'tunnel.exited' }); }, ); }), ); actor.start(); const status = (): RemoteControlStatusInfo => { const snap = actor.getSnapshot(); const state = snap.value as RemoteControlState; const handle = snap.context.handle; const error = snap.context.error; return { enabled: state === 'on', state, url: handle?.url, deviceId: handle?.deviceId, deviceName: handle?.deviceName, error: error === undefined ? undefined : errorMessage(error), }; }; const settle = () => waitFor(actor, (snap) => snap.value === 'on' || snap.value === 'off'); const enable = async (): Promise => { const snap = actor.getSnapshot(); if (snap.value === 'on') return status(); if (snap.value === 'stopping') { await waitFor(actor, (s) => s.value === 'off'); } else if (snap.value === 'starting') { const settled = await settle(); if (settled.value === 'on') return status(); throw settled.context.error; } actor.send({ type: 'enable' }); const settled = await settle(); if (settled.value === 'on') return status(); throw settled.context.error; }; const disable = async (): Promise => { const snap = actor.getSnapshot(); if (snap.value === 'off') return status(); if (snap.value === 'starting') { const settled = await settle(); if (settled.value === 'off') return status(); } else if (snap.value === 'stopping') { await waitFor(actor, (s) => s.value === 'off'); return status(); } actor.send({ type: 'disable' }); await waitFor(actor, (s) => s.value === 'off'); return status(); }; const close = async (): Promise => { try { await disable(); } catch (error) { options.stderr?.write(`remote-control shutdown failed: ${errorMessage(error)}\n`); } finally { actor.stop(); } }; return { status, enable, disable, close }; } function errorMessage(error: unknown): string { return error instanceof Error ? error.message : String(error); }