@elizaos/plugin-remote-desktop 2.0.3-beta.5 → 2.0.3-beta.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,15 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/actions/remote-desktop.ts", "../src/lifeops/remote-desktop.ts", "../src/remote/remote-session-service.ts", "../src/remote/pairing-code.ts", "../src/plugin.ts", "../src/index.ts"],
4
+ "sourcesContent": [
5
+ "import type {\n Action,\n ActionExample,\n ActionResult,\n IAgentRuntime,\n Memory,\n} from \"@elizaos/core\";\nimport {\n requireConfirmation,\n resolveActionArgs,\n type SubactionsMap,\n} from \"@elizaos/core\";\nimport {\n detectRemoteDesktopBackend,\n endRemoteSession as endStoredRemoteSession,\n getSessionStatus as getStoredSessionStatus,\n type RemoteDesktopSession,\n} from \"../lifeops/remote-desktop.js\";\nimport {\n getRemoteSessionService,\n RemoteSessionError,\n} from \"../remote/remote-session-service.js\";\nimport type {\n RemoteDesktopActionParams,\n RemoteDesktopSubaction,\n} from \"../types.js\";\n\nconst ACTION_NAME = \"REMOTE_DESKTOP\";\n\nconst SUBACTIONS: SubactionsMap<RemoteDesktopSubaction> = {\n start: {\n description:\n \"Open remote-control session via RemoteSessionService. Requires confirmed:true. \" +\n \"Local ELIZA_REMOTE_LOCAL_MODE=1 skips pairingCode; cloud requires 6-digit pairingCode.\",\n descriptionCompressed:\n \"open remote session confirmed-true 6-digit-pairing local-mode-skips\",\n required: [\"confirmed\"],\n optional: [\"pairingCode\"],\n },\n status: {\n description: \"Lookup remote session by sessionId via stored backend.\",\n descriptionCompressed:\n \"lookup remote session sessionId stored-session-backend\",\n required: [\"sessionId\"],\n },\n end: {\n description: \"Close remote session by sessionId via stored backend.\",\n descriptionCompressed:\n \"close remote session sessionId stored-session-backend\",\n required: [\"sessionId\"],\n },\n list: {\n description:\n \"List active remote sessions via RemoteSessionService: ids, status, ingress URLs, local-mode hints.\",\n descriptionCompressed:\n \"list active remote sessions ids+status+ingress+local-mode-hint\",\n required: [],\n },\n revoke: {\n description:\n \"Revoke active remote session by sessionId via RemoteSessionService.\",\n descriptionCompressed: \"revoke active remote session sessionId\",\n required: [\"sessionId\"],\n },\n};\n\nfunction coerceString(value: unknown): string | undefined {\n if (typeof value !== \"string\") return undefined;\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n\nfunction formatLegacySession(session: RemoteDesktopSession): string {\n const lines = [\n `Session ${session.id}`,\n ` backend: ${session.backend}`,\n ` status: ${session.status}`,\n ];\n if (session.accessUrl) lines.push(` url: ${session.accessUrl}`);\n if (session.accessCode) lines.push(` code: ${session.accessCode}`);\n if (session.expiresAt) lines.push(` expires: ${session.expiresAt}`);\n if (session.error) lines.push(` error: ${session.error}`);\n return lines.join(\"\\n\");\n}\n\nasync function handleStart(\n runtime: IAgentRuntime,\n message: Memory,\n params: RemoteDesktopActionParams,\n): Promise<ActionResult> {\n const backend = await detectRemoteDesktopBackend();\n const startPrompt = `Starting a remote desktop session will expose this machine to the network via ${backend}.`;\n const decision = await requireConfirmation({\n runtime,\n message,\n actionName: ACTION_NAME,\n pendingKey: `remote-start:${backend}`,\n prompt: startPrompt,\n });\n if (decision.status !== \"confirmed\") {\n return {\n text:\n decision.status === \"pending\"\n ? `${startPrompt} Reply yes to confirm or no to cancel.`\n : \"Remote desktop start cancelled.\",\n success: decision.status === \"pending\",\n values: {\n success: false,\n error:\n decision.status === \"pending\" ? \"CONFIRMATION_REQUIRED\" : \"CANCELLED\",\n requiresConfirmation: decision.status === \"pending\",\n backend,\n },\n data: {\n actionName: ACTION_NAME,\n subaction: \"start\",\n requiresConfirmation: decision.status === \"pending\",\n backend,\n intent: params.intent ?? null,\n },\n };\n }\n\n const requesterIdentity =\n coerceString(params.requesterIdentity) ?? String(message.entityId);\n\n try {\n const result = await getRemoteSessionService().startSession({\n requesterIdentity,\n pairingCode: coerceString(params.pairingCode),\n confirmed: true,\n });\n\n if (result.status === \"denied\") {\n return {\n text: \"Pairing code was invalid or expired. Request a fresh code and retry.\",\n success: false,\n values: {\n success: false,\n error: \"PAIRING_DENIED\",\n sessionId: result.sessionId,\n },\n data: { actionName: ACTION_NAME, subaction: \"start\", session: result },\n };\n }\n\n if (result.ingressUrl === null) {\n return {\n text: `Remote session ${result.sessionId} is authorized but the data plane is not configured (${result.reason ?? \"unknown\"}). Configure Tailscale (T9b) or the Eliza Cloud tunnel to complete pixel transport.`,\n success: false,\n values: {\n success: false,\n error: \"DATA_PLANE_NOT_CONFIGURED\",\n requiresConfirmation: true,\n sessionId: result.sessionId,\n status: result.status,\n ingressUrl: null,\n reason: result.reason,\n localMode: result.localMode,\n },\n data: {\n actionName: ACTION_NAME,\n subaction: \"start\",\n requiresConfirmation: true,\n session: result,\n },\n };\n }\n\n return {\n text: `Remote session ${result.sessionId} active. Connect via ${result.ingressUrl}.`,\n success: true,\n values: {\n success: true,\n sessionId: result.sessionId,\n status: result.status,\n ingressUrl: result.ingressUrl,\n localMode: result.localMode,\n },\n data: { actionName: ACTION_NAME, subaction: \"start\", session: result },\n };\n } catch (error) {\n if (error instanceof RemoteSessionError) {\n return {\n text: error.message,\n success: false,\n values: { success: false, error: error.code },\n data: { actionName: ACTION_NAME, subaction: \"start\" },\n };\n }\n throw error;\n }\n}\n\nasync function handleStatus(\n params: RemoteDesktopActionParams,\n): Promise<ActionResult> {\n const sessionId = coerceString(params.sessionId);\n if (!sessionId) {\n return {\n text: \"Missing sessionId.\",\n success: false,\n values: { success: false, error: \"MISSING_SESSION_ID\" },\n data: { actionName: ACTION_NAME, subaction: \"status\" },\n };\n }\n const session = await getStoredSessionStatus(sessionId);\n if (!session) {\n return {\n text: `No session found with id ${sessionId}.`,\n success: false,\n values: { success: false, error: \"SESSION_NOT_FOUND\" },\n data: { actionName: ACTION_NAME, subaction: \"status\", sessionId },\n };\n }\n return {\n text: formatLegacySession(session),\n success: true,\n values: { success: true, status: session.status },\n data: { actionName: ACTION_NAME, subaction: \"status\", session },\n };\n}\n\nasync function handleEnd(\n params: RemoteDesktopActionParams,\n): Promise<ActionResult> {\n const sessionId = coerceString(params.sessionId);\n if (!sessionId) {\n return {\n text: \"Missing sessionId.\",\n success: false,\n values: { success: false, error: \"MISSING_SESSION_ID\" },\n data: { actionName: ACTION_NAME, subaction: \"end\" },\n };\n }\n const existing = await getStoredSessionStatus(sessionId);\n if (!existing) {\n return {\n text: `No session found with id ${sessionId}.`,\n success: false,\n values: { success: false, error: \"SESSION_NOT_FOUND\" },\n data: { actionName: ACTION_NAME, subaction: \"end\", sessionId },\n };\n }\n await endStoredRemoteSession(sessionId);\n return {\n text: `Remote session ${sessionId} ended.`,\n success: true,\n values: { success: true, sessionId },\n data: { actionName: ACTION_NAME, subaction: \"end\", sessionId },\n };\n}\n\nasync function handleList(): Promise<ActionResult> {\n const sessions = await getRemoteSessionService().listActiveSessions();\n if (sessions.length === 0) {\n return {\n text: \"No active remote sessions.\",\n success: true,\n values: { success: true, count: 0 },\n data: { actionName: ACTION_NAME, subaction: \"list\", sessions: [] },\n };\n }\n const lines = sessions.map(\n (s) =>\n `• ${s.id} — status=${s.status}${\n s.ingressUrl\n ? ` ingress=${s.ingressUrl}`\n : ` ingress=<none:${s.reason ?? \"unknown\"}>`\n }${s.localMode ? \" (local)\" : \"\"}`,\n );\n return {\n text: `Active remote sessions (${sessions.length}):\\n${lines.join(\"\\n\")}`,\n success: true,\n values: { success: true, count: sessions.length },\n data: { actionName: ACTION_NAME, subaction: \"list\", sessions },\n };\n}\n\nasync function handleRevoke(\n params: RemoteDesktopActionParams,\n): Promise<ActionResult> {\n const sessionId = coerceString(params.sessionId);\n if (!sessionId) {\n return {\n text: \"Missing sessionId.\",\n success: false,\n values: { success: false, error: \"MISSING_SESSION_ID\" },\n data: { actionName: ACTION_NAME, subaction: \"revoke\" },\n };\n }\n try {\n await getRemoteSessionService().revokeSession(sessionId);\n return {\n text: `Remote session ${sessionId} revoked.`,\n success: true,\n values: { success: true, sessionId },\n data: { actionName: ACTION_NAME, subaction: \"revoke\", sessionId },\n };\n } catch (error) {\n if (error instanceof RemoteSessionError) {\n return {\n text: error.message,\n success: false,\n values: { success: false, error: error.code, sessionId },\n data: { actionName: ACTION_NAME, subaction: \"revoke\", sessionId },\n };\n }\n throw error;\n }\n}\n\n// Suppresses the planner's post-action continuation prompt. Opening a remote\n// session is consumed out-of-band (a VNC viewer / SSH client), so the planner\n// should not chain another turn.\ntype RemoteDesktopAction = Action & {\n suppressPostActionContinuation?: boolean;\n};\n\nexport const remoteDesktopAction: RemoteDesktopAction = {\n name: ACTION_NAME,\n similes: [\n \"REMOTE_SESSION\",\n \"VNC_SESSION\",\n \"REMOTE_CONTROL\",\n \"PHONE_REMOTE_ACCESS\",\n \"CONNECT_FROM_PHONE\",\n ],\n description:\n \"Remote-desktop sessions; owner connects to this machine from another device. \" +\n \"Subactions start confirmed:true cloud pairingCode; status|end|revoke sessionId; list active.\",\n descriptionCompressed:\n \"REMOTE_DESKTOP start|status|end|list|revoke; start confirmed:true; cloud pairingCode\",\n tags: [\n \"domain:meta\",\n \"capability:read\",\n \"capability:write\",\n \"capability:execute\",\n \"capability:delete\",\n \"surface:device\",\n \"surface:internal\",\n \"risk:irreversible\",\n ],\n contexts: [\"browser\", \"automation\", \"settings\", \"admin\", \"terminal\"],\n roleGate: { minRole: \"OWNER\" },\n suppressPostActionContinuation: true,\n\n validate: async () => true,\n\n parameters: [\n {\n name: \"action\",\n description: \"start | status | end | list | revoke.\",\n descriptionCompressed:\n \"remote-desktop action: start|status|end|list|revoke\",\n required: false,\n schema: {\n type: \"string\" as const,\n enum: [\"start\", \"status\", \"end\", \"list\", \"revoke\"],\n },\n examples: [\"start\", \"list\", \"revoke\"],\n },\n {\n name: \"sessionId\",\n description: \"Session id. Required status|end|revoke.\",\n descriptionCompressed: \"session id (status|end|revoke)\",\n required: false,\n schema: { type: \"string\" as const },\n examples: [\"rs_abc123\"],\n },\n {\n name: \"confirmed\",\n description: \"true required for start; security gate.\",\n descriptionCompressed: \"true required for start (security)\",\n required: false,\n schema: { type: \"boolean\" as const },\n },\n {\n name: \"pairingCode\",\n description:\n \"6-digit pairingCode for start. Required unless ELIZA_REMOTE_LOCAL_MODE=1.\",\n descriptionCompressed:\n \"6-digit pairing code (start; skipped in local mode)\",\n required: false,\n schema: { type: \"string\" as const, pattern: \"^[0-9]{6}$\" },\n examples: [\"482193\"],\n },\n {\n name: \"requesterIdentity\",\n description: \"Requester id/name/device. Audit start.\",\n descriptionCompressed: \"audit: requester id (start)\",\n required: false,\n schema: { type: \"string\" as const },\n },\n {\n name: \"intent\",\n description: \"Owner intent/reason. Audit.\",\n descriptionCompressed: \"audit: owner reason\",\n required: false,\n schema: { type: \"string\" as const },\n },\n ],\n\n examples: [\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Start a remote session with pairing code 482193, confirmed.\",\n },\n },\n {\n name: \"{{agentName}}\",\n content: {\n text: \"Remote session active. Connect via vnc://host:5900.\",\n action: ACTION_NAME,\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: { text: \"Are any remote sessions open right now?\" },\n },\n {\n name: \"{{agentName}}\",\n content: {\n text: \"No active remote sessions.\",\n action: ACTION_NAME,\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: { text: \"End the remote session rs_abc123.\" },\n },\n {\n name: \"{{agentName}}\",\n content: {\n text: \"Remote session rs_abc123 revoked.\",\n action: ACTION_NAME,\n },\n },\n ],\n ] as ActionExample[][],\n\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n state,\n options,\n ): Promise<ActionResult> => {\n const resolved = await resolveActionArgs<\n RemoteDesktopSubaction,\n RemoteDesktopActionParams\n >({\n runtime,\n message,\n ...(state ? { state } : {}),\n ...(options ? { options } : {}),\n actionName: ACTION_NAME,\n subactions: SUBACTIONS,\n });\n if (!resolved.ok) {\n return {\n success: false,\n text: resolved.clarification,\n values: {\n success: false,\n error: \"MISSING_REMOTE_DESKTOP_ARGUMENTS\",\n missing: resolved.missing,\n },\n data: {\n actionName: ACTION_NAME,\n reason: \"missing_arguments\",\n missing: resolved.missing,\n },\n };\n }\n\n const { subaction, params } = resolved;\n switch (subaction) {\n case \"start\":\n return handleStart(runtime, message, params);\n case \"status\":\n return handleStatus(params);\n case \"end\":\n return handleEnd(params);\n case \"list\":\n return handleList();\n case \"revoke\":\n return handleRevoke(params);\n }\n },\n};\n\n// Re-exported for callers that want to reach for the action name as a const.\nexport const REMOTE_DESKTOP_ACTION_NAME = ACTION_NAME;\n\n// Re-export the action's parameter types so plugin consumers can type-check\n// the params they pass when invoking the action programmatically.\nexport type { RemoteDesktopActionParams, RemoteDesktopSubaction };\n",
6
+ "/**\n * Remote desktop session framework.\n *\n * Provides a narrow façade over Tailscale / ngrok so the owner can view or\n * control their computer from a phone while the agent is working. Sessions are\n * ephemeral, in-memory, and auto-expire. No secrets are persisted to disk and\n * no passwords are passed on the command line.\n */\n\nimport { type ChildProcess, execFile, spawn } from \"node:child_process\";\nimport { randomInt, randomUUID } from \"node:crypto\";\nimport { promisify } from \"node:util\";\nimport { logger } from \"@elizaos/core\";\nimport type {\n RemoteDesktopBackend,\n RemoteDesktopConfig,\n RemoteDesktopSession,\n} from \"../types.js\";\n\nexport type {\n RemoteDesktopBackend,\n RemoteDesktopConfig,\n RemoteDesktopSession,\n} from \"../types.js\";\n\nconst execFileAsync = promisify(execFile);\n\nexport class RemoteDesktopError extends Error {\n readonly backend: RemoteDesktopBackend;\n constructor(message: string, backend: RemoteDesktopBackend) {\n super(message);\n this.name = \"RemoteDesktopError\";\n this.backend = backend;\n }\n}\n\n// ---------------------------------------------------------------------------\n// In-process session store\n// ---------------------------------------------------------------------------\n\ninterface SessionRuntimeState {\n session: RemoteDesktopSession;\n expiryTimer?: NodeJS.Timeout | undefined;\n ngrokProcess?: ChildProcess | undefined;\n}\n\nconst sessions = new Map<string, SessionRuntimeState>();\n\nconst DEFAULT_VNC_PORT = 5900;\nconst DEFAULT_SESSION_MINUTES = 60;\n\nfunction isMockRemoteDesktopEnabled(): boolean {\n const explicit = process.env.ELIZA_TEST_REMOTE_DESKTOP_BACKEND?.trim();\n if (explicit) {\n const normalized = explicit.toLowerCase();\n return (\n normalized === \"1\" ||\n normalized === \"true\" ||\n normalized === \"yes\" ||\n normalized === \"on\" ||\n normalized === \"fixture\"\n );\n }\n return false;\n}\n\n// ---------------------------------------------------------------------------\n// Env / config resolution\n// ---------------------------------------------------------------------------\n\nfunction resolveConfig(\n config?: RemoteDesktopConfig,\n env: NodeJS.ProcessEnv = process.env,\n): Required<Pick<RemoteDesktopConfig, \"vncPort\" | \"sessionDurationMinutes\">> & {\n preferredBackend?: RemoteDesktopBackend | undefined;\n tailscaleNodeName?: string | undefined;\n ngrokAuthToken?: string | undefined;\n} {\n return {\n preferredBackend: config?.preferredBackend,\n tailscaleNodeName:\n config?.tailscaleNodeName ??\n (env.ELIZA_TAILSCALE_NODE?.trim() || undefined),\n ngrokAuthToken:\n config?.ngrokAuthToken ??\n (env.ELIZA_NGROK_AUTH_TOKEN?.trim() || undefined),\n vncPort: config?.vncPort ?? DEFAULT_VNC_PORT,\n sessionDurationMinutes:\n config?.sessionDurationMinutes ?? DEFAULT_SESSION_MINUTES,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Backend detection probes\n// ---------------------------------------------------------------------------\n\ninterface TailscaleState {\n authenticated: boolean;\n hostname?: string | undefined;\n}\n\nasync function probeTailscale(): Promise<TailscaleState> {\n try {\n const { stdout } = await execFileAsync(\"tailscale\", [\"status\", \"--json\"], {\n timeout: 3_000,\n });\n const parsed = JSON.parse(stdout) as {\n BackendState?: string;\n Self?: { HostName?: string; DNSName?: string };\n };\n const authenticated = parsed.BackendState === \"Running\";\n const hostname =\n parsed.Self?.DNSName?.replace(/\\.$/, \"\") || parsed.Self?.HostName;\n return { authenticated, hostname };\n } catch {\n return { authenticated: false };\n }\n}\n\nasync function probeLocalVncServer(): Promise<boolean> {\n if (process.platform === \"darwin\") {\n // macOS: screensharing is launchd-managed. Look for the plist as a signal.\n try {\n const { stdout } = await execFileAsync(\n \"launchctl\",\n [\"list\", \"com.apple.screensharing\"],\n { timeout: 2_000 },\n );\n return stdout.includes(\"com.apple.screensharing\");\n } catch {\n return false;\n }\n }\n if (process.platform === \"linux\") {\n try {\n await execFileAsync(\"which\", [\"x11vnc\"], { timeout: 2_000 });\n return true;\n } catch {\n return false;\n }\n }\n return false;\n}\n\nasync function probeNgrok(token?: string): Promise<boolean> {\n if (!token) return false;\n try {\n await execFileAsync(\"ngrok\", [\"version\"], { timeout: 2_000 });\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function detectRemoteDesktopBackend(\n config?: RemoteDesktopConfig,\n): Promise<RemoteDesktopBackend> {\n if (isMockRemoteDesktopEnabled()) {\n return \"tailscale-vnc\";\n }\n const resolved = resolveConfig(config);\n\n if (resolved.preferredBackend === \"none\") return \"none\";\n\n if (resolved.preferredBackend) {\n const available = await backendAvailable(\n resolved.preferredBackend,\n resolved.ngrokAuthToken,\n );\n return available ? resolved.preferredBackend : \"none\";\n }\n\n const tailscale = await probeTailscale();\n if (tailscale.authenticated) {\n if (await probeLocalVncServer()) return \"tailscale-vnc\";\n return \"tailscale-ssh\";\n }\n\n if (await probeNgrok(resolved.ngrokAuthToken)) return \"ngrok-vnc\";\n\n return \"none\";\n}\n\nasync function backendAvailable(\n backend: RemoteDesktopBackend,\n ngrokToken?: string,\n): Promise<boolean> {\n if (backend === \"none\") return true;\n if (backend === \"tailscale-vnc\") {\n const ts = await probeTailscale();\n return ts.authenticated && (await probeLocalVncServer());\n }\n if (backend === \"tailscale-ssh\") {\n const ts = await probeTailscale();\n return ts.authenticated;\n }\n if (backend === \"ngrok-vnc\") {\n return probeNgrok(ngrokToken);\n }\n return false;\n}\n\n// ---------------------------------------------------------------------------\n// Session helpers\n// ---------------------------------------------------------------------------\n\nfunction generatePairingCode(): string {\n // 6-digit numeric code, zero-padded. Sourced from crypto.randomInt.\n return randomInt(0, 1_000_000).toString().padStart(6, \"0\");\n}\n\nfunction scheduleExpiry(id: string, durationMs: number): NodeJS.Timeout {\n return setTimeout(() => {\n void endRemoteSession(id).catch((error) => {\n logger.warn(\n {\n boundary: \"lifeops\",\n integration: \"remote-desktop\",\n sessionId: id,\n err: error instanceof Error ? error : undefined,\n },\n `[remote-desktop] auto-expire failed: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n });\n }, durationMs);\n}\n\n// ---------------------------------------------------------------------------\n// Backend starters\n// ---------------------------------------------------------------------------\n\nasync function startTailscaleVncSession(args: {\n id: string;\n pairingCode: string;\n vncPort: number;\n tailscaleNodeOverride?: string | undefined;\n}): Promise<{ accessUrl: string }> {\n const ts = await probeTailscale();\n if (!ts.authenticated) {\n throw new RemoteDesktopError(\n \"Tailscale is not authenticated\",\n \"tailscale-vnc\",\n );\n }\n const host = args.tailscaleNodeOverride || ts.hostname;\n if (!host) {\n throw new RemoteDesktopError(\n \"Tailscale hostname not discoverable\",\n \"tailscale-vnc\",\n );\n }\n\n if (process.platform === \"darwin\") {\n // Do NOT attempt to toggle Screen Sharing via sudo. If the service is not\n // already enabled we surface that clearly to the owner instead of racing a\n // sudo prompt in a background process.\n const vncUp = await probeLocalVncServer();\n if (!vncUp) {\n throw new RemoteDesktopError(\n \"macOS Screen Sharing is not enabled. Enable it in System Settings → General → Sharing → Screen Sharing, then retry.\",\n \"tailscale-vnc\",\n );\n }\n }\n\n // The pairing code is intentionally NOT placed into the URL userinfo — VNC\n // viewers would treat it as a connection password and we have no way to\n // inject it into the running VNC server without changing user credentials.\n // The owner uses it as an out-of-band check when opening the session.\n return {\n accessUrl: `vnc://${host}:${args.vncPort}`,\n };\n}\n\nasync function startTailscaleSshSession(args: {\n tailscaleNodeOverride?: string | undefined;\n}): Promise<{ accessUrl: string }> {\n const ts = await probeTailscale();\n if (!ts.authenticated) {\n throw new RemoteDesktopError(\n \"Tailscale is not authenticated\",\n \"tailscale-ssh\",\n );\n }\n const host = args.tailscaleNodeOverride || ts.hostname;\n if (!host) {\n throw new RemoteDesktopError(\n \"Tailscale hostname not discoverable\",\n \"tailscale-ssh\",\n );\n }\n return { accessUrl: `ssh://${host}` };\n}\n\nasync function startNgrokVncSession(args: {\n vncPort: number;\n authToken: string;\n}): Promise<{ accessUrl: string; child: ChildProcess }> {\n // Feed the auth token via env, never on argv.\n const child = spawn(\n \"ngrok\",\n [\"tcp\", String(args.vncPort), \"--log=stdout\", \"--log-format=json\"],\n {\n env: { ...process.env, NGROK_AUTHTOKEN: args.authToken },\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n },\n );\n\n const accessUrl = await new Promise<string>((resolve, reject) => {\n const timer = setTimeout(() => {\n reject(\n new RemoteDesktopError(\n \"ngrok did not report a public URL within 10s\",\n \"ngrok-vnc\",\n ),\n );\n }, 10_000);\n\n const onStdout = (chunk: Buffer) => {\n const text = chunk.toString(\"utf8\");\n for (const line of text.split(\"\\n\")) {\n if (!line.trim()) continue;\n try {\n const evt = JSON.parse(line) as { url?: string; msg?: string };\n if (evt.url?.startsWith(\"tcp://\")) {\n clearTimeout(timer);\n child.stdout.off(\"data\", onStdout);\n resolve(evt.url);\n return;\n }\n } catch {\n // ngrok writes non-JSON lines occasionally; ignore them.\n }\n }\n };\n child.stdout.on(\"data\", onStdout);\n child.once(\"error\", (err) => {\n clearTimeout(timer);\n reject(\n new RemoteDesktopError(`ngrok failed: ${err.message}`, \"ngrok-vnc\"),\n );\n });\n child.once(\"exit\", (code) => {\n clearTimeout(timer);\n reject(\n new RemoteDesktopError(\n `ngrok exited prematurely with code ${code}`,\n \"ngrok-vnc\",\n ),\n );\n });\n });\n\n return { accessUrl, child };\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\nexport async function startRemoteSession(\n config?: RemoteDesktopConfig,\n): Promise<RemoteDesktopSession> {\n const resolved = resolveConfig(config);\n const mockEnabled = isMockRemoteDesktopEnabled();\n const backend = await detectRemoteDesktopBackend(config);\n\n const now = new Date();\n const durationMs = resolved.sessionDurationMinutes * 60_000;\n const id = randomUUID();\n const pairingCode = generatePairingCode();\n\n const initialSession: RemoteDesktopSession = {\n id,\n backend,\n status: \"starting\",\n accessCode: pairingCode,\n startedAt: now.toISOString(),\n expiresAt: new Date(now.getTime() + durationMs).toISOString(),\n };\n sessions.set(id, { session: initialSession });\n\n if (backend === \"none\") {\n const failed: RemoteDesktopSession = {\n ...initialSession,\n status: \"failed\",\n error:\n \"No remote-desktop backend available. Configure Tailscale or ngrok.\",\n endedAt: new Date().toISOString(),\n };\n sessions.set(id, { session: failed });\n return failed;\n }\n\n try {\n if (mockEnabled) {\n const activeSession: RemoteDesktopSession = {\n ...initialSession,\n backend,\n status: \"active\",\n accessUrl: `vnc://127.0.0.1:${resolved.vncPort}/mock/${id}`,\n mockMode: true,\n };\n const expiryTimer = scheduleExpiry(id, durationMs);\n sessions.set(id, {\n session: activeSession,\n expiryTimer,\n });\n return activeSession;\n }\n\n let accessUrl: string;\n let ngrokProcess: ChildProcess | undefined;\n\n if (backend === \"tailscale-vnc\") {\n const result = await startTailscaleVncSession({\n id,\n pairingCode,\n vncPort: resolved.vncPort,\n tailscaleNodeOverride: resolved.tailscaleNodeName,\n });\n accessUrl = result.accessUrl;\n } else if (backend === \"tailscale-ssh\") {\n const result = await startTailscaleSshSession({\n tailscaleNodeOverride: resolved.tailscaleNodeName,\n });\n accessUrl = result.accessUrl;\n } else {\n if (!resolved.ngrokAuthToken) {\n throw new RemoteDesktopError(\n \"ngrok auth token not configured (ELIZA_NGROK_AUTH_TOKEN)\",\n \"ngrok-vnc\",\n );\n }\n const result = await startNgrokVncSession({\n vncPort: resolved.vncPort,\n authToken: resolved.ngrokAuthToken,\n });\n accessUrl = result.accessUrl;\n ngrokProcess = result.child;\n }\n\n const activeSession: RemoteDesktopSession = {\n ...initialSession,\n status: \"active\",\n accessUrl,\n };\n const expiryTimer = scheduleExpiry(id, durationMs);\n sessions.set(id, {\n session: activeSession,\n expiryTimer,\n ngrokProcess,\n });\n\n logger.info(\n {\n boundary: \"lifeops\",\n integration: \"remote-desktop\",\n sessionId: id,\n backend,\n },\n `[remote-desktop] session ${id} active via ${backend}`,\n );\n\n return activeSession;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n const failed: RemoteDesktopSession = {\n ...initialSession,\n status: \"failed\",\n error: message,\n endedAt: new Date().toISOString(),\n };\n sessions.set(id, { session: failed });\n logger.warn(\n {\n boundary: \"lifeops\",\n integration: \"remote-desktop\",\n sessionId: id,\n backend,\n },\n `[remote-desktop] session ${id} failed: ${message}`,\n );\n return failed;\n }\n}\n\nexport async function getSessionStatus(\n id: string,\n): Promise<RemoteDesktopSession | null> {\n const entry = sessions.get(id);\n return entry ? entry.session : null;\n}\n\nexport async function endRemoteSession(id: string): Promise<void> {\n const entry = sessions.get(id);\n if (!entry) return;\n\n if (entry.expiryTimer) {\n clearTimeout(entry.expiryTimer);\n }\n if (entry.ngrokProcess && entry.ngrokProcess.exitCode === null) {\n entry.ngrokProcess.kill(\"SIGTERM\");\n }\n\n const ended: RemoteDesktopSession = {\n ...entry.session,\n status: \"ended\",\n endedAt: new Date().toISOString(),\n };\n sessions.set(id, { session: ended });\n\n logger.info(\n {\n boundary: \"lifeops\",\n integration: \"remote-desktop\",\n sessionId: id,\n },\n `[remote-desktop] session ${id} ended`,\n );\n}\n\nexport async function listActiveSessions(): Promise<RemoteDesktopSession[]> {\n return Array.from(sessions.values())\n .map((entry) => entry.session)\n .filter(\n (session) => session.status === \"active\" || session.status === \"starting\",\n );\n}\n",
7
+ "/**\n * Control-plane service for remote VNC / remote-control sessions (T9a).\n *\n * This module owns:\n * - Session lifecycle (pending → active → denied/revoked).\n * - Pairing-code issuance and verification.\n * - Local-mode bypass (ELIZA_REMOTE_LOCAL_MODE=1 skips the code requirement\n * but still requires explicit `confirmed: true`).\n * - Data-plane handoff point (`ingressUrl`).\n *\n * It does NOT own pixel transport. The data plane (VNC / Tailscale / Eliza\n * Cloud tunnel) is separate infrastructure. When no data plane is configured,\n * `startSession` returns an explicit `ingressUrl: null` with a structured\n * `reason: \"data-plane-not-configured\"` — this is deliberate absence, not a\n * fallback that pretends the session is usable.\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { logger, resolveStateDir } from \"@elizaos/core\";\nimport type {\n DataPlaneUnavailableReason,\n RemoteSession,\n RemoteSessionStatus,\n StartSessionParams,\n StartSessionResult,\n} from \"../types.js\";\nimport { PairingCodeStore } from \"./pairing-code.js\";\n\nexport type {\n DataPlaneUnavailableReason,\n RemoteSession,\n RemoteSessionStatus,\n StartSessionParams,\n StartSessionResult,\n} from \"../types.js\";\n\nexport interface DataPlaneResolution {\n ingressUrl: string | null;\n reason: DataPlaneUnavailableReason | null;\n}\n\nexport interface DataPlaneResolver {\n /**\n * Returns the ingress URL for this session, or an explicit reason absence.\n * Implementations live in T9b (Tailscale) and the Eliza Cloud tunnel track.\n */\n resolve(session: {\n sessionId: string;\n requesterIdentity: string;\n localMode: boolean;\n }): Promise<DataPlaneResolution> | DataPlaneResolution;\n}\n\nexport interface RemoteSessionLogger {\n debug: (\n value: string | Error | Record<string, unknown>,\n message?: string,\n ...args: unknown[]\n ) => void;\n info: (\n value: string | Error | Record<string, unknown>,\n message?: string,\n ...args: unknown[]\n ) => void;\n warn: (\n value: string | Error | Record<string, unknown>,\n message?: string,\n ...args: unknown[]\n ) => void;\n}\n\nexport interface RemoteSessionServiceOptions {\n pairingCodes?: PairingCodeStore;\n dataPlane?: DataPlaneResolver;\n /** Read once per `startSession` call; overridable for tests. */\n isLocalMode?: () => boolean;\n now?: () => Date;\n logger?: RemoteSessionLogger;\n storagePath?: string;\n}\n\nexport class RemoteSessionError extends Error {\n readonly code: string;\n constructor(code: string, message: string) {\n super(message);\n this.name = \"RemoteSessionError\";\n this.code = code;\n }\n}\n\nconst PAIRING_SUBJECT = \"agent\";\n\nfunction defaultIsLocalMode(): boolean {\n return process.env.ELIZA_REMOTE_LOCAL_MODE === \"1\";\n}\n\nconst nullDataPlane: DataPlaneResolver = {\n resolve: () => ({\n ingressUrl: null,\n reason: \"data-plane-not-configured\",\n }),\n};\n\nfunction defaultStoragePath(): string {\n return path.join(resolveStateDir(), \"lifeops\", \"remote-sessions.json\");\n}\n\nexport class RemoteSessionService {\n private readonly sessions = new Map<string, RemoteSession>();\n private readonly pairingCodes: PairingCodeStore;\n private readonly dataPlane: DataPlaneResolver;\n private readonly isLocalMode: () => boolean;\n private readonly now: () => Date;\n private readonly log: RemoteSessionLogger;\n private readonly storagePath: string;\n\n constructor(options: RemoteSessionServiceOptions = {}) {\n this.pairingCodes = options.pairingCodes ?? new PairingCodeStore();\n this.dataPlane = options.dataPlane ?? nullDataPlane;\n this.isLocalMode = options.isLocalMode ?? defaultIsLocalMode;\n this.now = options.now ?? (() => new Date());\n this.log = options.logger ?? logger;\n this.storagePath = options.storagePath ?? defaultStoragePath();\n this.loadSessions();\n }\n\n /**\n * Issue a fresh pairing code. Each call rotates the code (one-time use).\n * Returns the code so it can be displayed to the owner out-of-band.\n */\n issuePairingCode(): { code: string; expiresAt: string } {\n const entry = this.pairingCodes.issue(PAIRING_SUBJECT);\n return {\n code: entry.code,\n expiresAt: new Date(entry.expiresAt).toISOString(),\n };\n }\n\n async startSession(params: StartSessionParams): Promise<StartSessionResult> {\n if (!params.confirmed) {\n throw new RemoteSessionError(\n \"NOT_CONFIRMED\",\n \"Remote sessions require explicit confirmation (confirmed: true).\",\n );\n }\n const requester = params.requesterIdentity.trim();\n if (!requester) {\n throw new RemoteSessionError(\n \"MISSING_REQUESTER\",\n \"requesterIdentity is required.\",\n );\n }\n\n const localMode = this.isLocalMode();\n let status: RemoteSessionStatus = \"pending\";\n\n if (!localMode) {\n const code = params.pairingCode?.trim();\n if (!code) {\n throw new RemoteSessionError(\n \"PAIRING_CODE_REQUIRED\",\n \"Pairing code required for non-local sessions. Issue one with issuePairingCode() first.\",\n );\n }\n const ok = this.pairingCodes.consume(PAIRING_SUBJECT, code);\n if (!ok) {\n const denied = this.recordSession({\n requesterIdentity: requester,\n status: \"denied\",\n ingressUrl: null,\n reason: null,\n localMode,\n });\n this.log.warn(\n { boundary: \"remote\", sessionId: denied.id, requester },\n \"[RemoteSessionService] pairing-code rejected\",\n );\n return {\n sessionId: denied.id,\n status: \"denied\",\n ingressUrl: null,\n reason: null,\n localMode,\n };\n }\n status = \"active\";\n } else {\n status = \"active\";\n }\n\n const resolved = await this.dataPlane.resolve({\n sessionId: \"pending\",\n requesterIdentity: requester,\n localMode,\n });\n\n const session = this.recordSession({\n requesterIdentity: requester,\n status,\n ingressUrl: resolved.ingressUrl,\n reason: resolved.reason,\n localMode,\n });\n\n if (resolved.ingressUrl === null) {\n this.log.info(\n {\n boundary: \"remote\",\n sessionId: session.id,\n reason: resolved.reason,\n localMode,\n },\n `[RemoteSessionService] session ${session.id} active but no data plane ingress (reason=${resolved.reason ?? \"unknown\"})`,\n );\n } else {\n this.log.info(\n {\n boundary: \"remote\",\n sessionId: session.id,\n localMode,\n },\n `[RemoteSessionService] session ${session.id} active`,\n );\n }\n\n return {\n sessionId: session.id,\n status,\n ingressUrl: session.ingressUrl,\n reason: session.reason,\n localMode,\n };\n }\n\n async revokeSession(sessionId: string): Promise<void> {\n const session = this.sessions.get(sessionId);\n if (!session) {\n throw new RemoteSessionError(\n \"SESSION_NOT_FOUND\",\n `No session found with id ${sessionId}.`,\n );\n }\n if (session.status === \"revoked\" || session.status === \"denied\") {\n return;\n }\n const endedAt = this.now().toISOString();\n const revoked: RemoteSession = {\n ...session,\n status: \"revoked\",\n updatedAt: endedAt,\n endedAt,\n };\n this.sessions.set(sessionId, revoked);\n this.persistSessions();\n this.log.info(\n { boundary: \"remote\", sessionId },\n `[RemoteSessionService] session ${sessionId} revoked`,\n );\n }\n\n async listActiveSessions(): Promise<RemoteSession[]> {\n return Array.from(this.sessions.values()).filter(\n (s) => s.status === \"active\" || s.status === \"pending\",\n );\n }\n\n async getSession(sessionId: string): Promise<RemoteSession | undefined> {\n return this.sessions.get(sessionId);\n }\n\n private recordSession(input: {\n requesterIdentity: string;\n status: RemoteSessionStatus;\n ingressUrl: string | null;\n reason: DataPlaneUnavailableReason | null;\n localMode: boolean;\n }): RemoteSession {\n const id = randomUUID();\n const nowIso = this.now().toISOString();\n const session: RemoteSession = {\n id,\n requesterIdentity: input.requesterIdentity,\n status: input.status,\n ingressUrl: input.ingressUrl,\n reason: input.reason,\n localMode: input.localMode,\n createdAt: nowIso,\n updatedAt: nowIso,\n endedAt:\n input.status === \"denied\" || input.status === \"revoked\" ? nowIso : null,\n };\n this.sessions.set(id, session);\n this.persistSessions();\n return session;\n }\n\n private loadSessions(): void {\n if (!fs.existsSync(this.storagePath)) {\n return;\n }\n try {\n const parsed = JSON.parse(\n fs.readFileSync(this.storagePath, \"utf8\"),\n ) as unknown;\n if (!Array.isArray(parsed)) {\n return;\n }\n for (const entry of parsed) {\n if (!entry || typeof entry !== \"object\") {\n continue;\n }\n const session = entry as Partial<RemoteSession>;\n if (\n typeof session.id !== \"string\" ||\n typeof session.requesterIdentity !== \"string\" ||\n typeof session.status !== \"string\" ||\n typeof session.localMode !== \"boolean\" ||\n typeof session.createdAt !== \"string\" ||\n typeof session.updatedAt !== \"string\"\n ) {\n continue;\n }\n this.sessions.set(session.id, {\n id: session.id,\n requesterIdentity: session.requesterIdentity,\n status:\n session.status === \"active\" ||\n session.status === \"pending\" ||\n session.status === \"denied\" ||\n session.status === \"revoked\"\n ? session.status\n : \"revoked\",\n ingressUrl:\n typeof session.ingressUrl === \"string\" ? session.ingressUrl : null,\n reason:\n session.reason === \"data-plane-not-configured\" ||\n session.reason === \"local-mode-no-ingress\"\n ? session.reason\n : null,\n localMode: session.localMode,\n createdAt: session.createdAt,\n updatedAt: session.updatedAt,\n endedAt: typeof session.endedAt === \"string\" ? session.endedAt : null,\n });\n }\n } catch {\n // Invalid persisted state is ignored so a new process can recreate it.\n }\n }\n\n private persistSessions(): void {\n fs.mkdirSync(path.dirname(this.storagePath), { recursive: true });\n fs.writeFileSync(\n this.storagePath,\n JSON.stringify(Array.from(this.sessions.values()), null, 2),\n {\n encoding: \"utf8\",\n mode: 0o600,\n },\n );\n }\n}\n\nlet singleton: RemoteSessionService | undefined;\n\nexport function getRemoteSessionService(): RemoteSessionService {\n if (!singleton) {\n singleton = new RemoteSessionService();\n }\n return singleton;\n}\n\n/** Test-only hook for resetting the singleton between tests. */\nexport function __resetRemoteSessionServiceForTests(): void {\n singleton = undefined;\n}\n",
8
+ "/**\n * 6-digit rolling pairing codes for the remote-control control plane (T9a).\n *\n * Codes are one-time use with a 5-minute TTL. They are held in process memory\n * — the control plane is intended to issue a fresh code on every `startSession`\n * call rather than persist codes across restarts.\n */\n\nimport { randomInt } from \"node:crypto\";\n\nexport const PAIRING_CODE_TTL_MS = 5 * 60 * 1000;\nexport const PAIRING_CODE_LENGTH = 6;\n\nexport interface PairingCodeEntry {\n code: string;\n issuedAt: number;\n expiresAt: number;\n}\n\nexport interface PairingCodeStoreOptions {\n ttlMs?: number;\n now?: () => number;\n}\n\n/**\n * In-process rolling pairing-code store. Each issuance replaces the previous\n * code for that subject. `consume` both validates and invalidates the code.\n */\nexport class PairingCodeStore {\n private readonly ttlMs: number;\n private readonly now: () => number;\n private readonly entries = new Map<string, PairingCodeEntry>();\n\n constructor(options: PairingCodeStoreOptions = {}) {\n this.ttlMs = options.ttlMs ?? PAIRING_CODE_TTL_MS;\n this.now = options.now ?? Date.now;\n }\n\n issue(subject: string): PairingCodeEntry {\n const issuedAt = this.now();\n const entry: PairingCodeEntry = {\n code: generatePairingCode(),\n issuedAt,\n expiresAt: issuedAt + this.ttlMs,\n };\n this.entries.set(subject, entry);\n return entry;\n }\n\n /**\n * Validates and consumes the code for `subject`. Returns true if the code\n * matched and was unexpired. Always removes the entry after a match (one-time\n * use) and after any expiry.\n */\n consume(subject: string, code: string): boolean {\n const entry = this.entries.get(subject);\n if (!entry) return false;\n\n const now = this.now();\n if (entry.expiresAt <= now) {\n this.entries.delete(subject);\n return false;\n }\n if (entry.code !== code) {\n return false;\n }\n this.entries.delete(subject);\n return true;\n }\n\n peek(subject: string): PairingCodeEntry | undefined {\n const entry = this.entries.get(subject);\n if (!entry) return undefined;\n if (entry.expiresAt <= this.now()) {\n this.entries.delete(subject);\n return undefined;\n }\n return entry;\n }\n\n clear(subject?: string): void {\n if (subject) {\n this.entries.delete(subject);\n } else {\n this.entries.clear();\n }\n }\n}\n\nexport function generatePairingCode(): string {\n return randomInt(0, 10 ** PAIRING_CODE_LENGTH)\n .toString()\n .padStart(PAIRING_CODE_LENGTH, \"0\");\n}\n",
9
+ "import type { Plugin } from \"@elizaos/core\";\n\nimport { remoteDesktopAction } from \"./actions/remote-desktop.js\";\n\n/**\n * @elizaos/plugin-remote-desktop\n *\n * Owner-only remote desktop session control. Provides a single\n * `REMOTE_DESKTOP` umbrella action with subactions:\n * - start — open a session (requires confirmation; pairing code in cloud mode)\n * - status — look up a session by id\n * - end — close a session by id\n * - list — list active sessions\n * - revoke — revoke an active session\n *\n * Backends: Tailscale VNC, Tailscale SSH, and ngrok TCP. Pairing-code gating\n * lives in the underlying RemoteSessionService (`src/remote/`); backend\n * detection and the in-process session store live in `src/lifeops/`.\n */\nexport const remoteDesktopPlugin: Plugin = {\n name: \"remote-desktop\",\n description:\n \"Remote desktop session control for Eliza agents. REMOTE_DESKTOP umbrella action (start/status/end/list/revoke) over Tailscale VNC/SSH and ngrok backends with pairing-code confirmation. Extracted from @elizaos/plugin-personal-assistant.\",\n actions: [remoteDesktopAction],\n};\n\nexport default remoteDesktopPlugin;\n",
10
+ "import { remoteDesktopPlugin } from \"./plugin.js\";\n\nexport { remoteDesktopPlugin } from \"./plugin.js\";\nexport default remoteDesktopPlugin;\n\nexport {\n REMOTE_DESKTOP_ACTION_NAME,\n remoteDesktopAction,\n} from \"./actions/remote-desktop.js\";\n\nexport {\n detectRemoteDesktopBackend,\n endRemoteSession,\n getSessionStatus,\n listActiveSessions,\n RemoteDesktopError,\n startRemoteSession,\n} from \"./lifeops/remote-desktop.js\";\n\nexport {\n __resetRemoteSessionServiceForTests,\n getRemoteSessionService,\n RemoteSessionError,\n RemoteSessionService,\n type DataPlaneResolution,\n type DataPlaneResolver,\n type RemoteSessionServiceOptions,\n} from \"./remote/remote-session-service.js\";\n\nexport {\n generatePairingCode,\n PAIRING_CODE_LENGTH,\n PAIRING_CODE_TTL_MS,\n PairingCodeStore,\n type PairingCodeEntry,\n type PairingCodeStoreOptions,\n} from \"./remote/pairing-code.js\";\n\nexport type {\n DataPlaneUnavailableReason,\n RemoteDesktopActionParams,\n RemoteDesktopBackend,\n RemoteDesktopConfig,\n RemoteDesktopSession,\n RemoteDesktopSessionStatus,\n RemoteDesktopSubaction,\n RemoteSession,\n RemoteSessionStatus,\n StartSessionParams,\n StartSessionResult,\n} from \"./types.js\";\n"
11
+ ],
12
+ "mappings": "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",
13
+ "debugId": "0352AD5350E719D164756E2164756E21",
14
+ "names": []
15
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Remote desktop session framework.
3
+ *
4
+ * Provides a narrow façade over Tailscale / ngrok so the owner can view or
5
+ * control their computer from a phone while the agent is working. Sessions are
6
+ * ephemeral, in-memory, and auto-expire. No secrets are persisted to disk and
7
+ * no passwords are passed on the command line.
8
+ */
9
+ import type { RemoteDesktopBackend, RemoteDesktopConfig, RemoteDesktopSession } from "../types.js";
10
+ export type { RemoteDesktopBackend, RemoteDesktopConfig, RemoteDesktopSession, } from "../types.js";
11
+ export declare class RemoteDesktopError extends Error {
12
+ readonly backend: RemoteDesktopBackend;
13
+ constructor(message: string, backend: RemoteDesktopBackend);
14
+ }
15
+ export declare function detectRemoteDesktopBackend(config?: RemoteDesktopConfig): Promise<RemoteDesktopBackend>;
16
+ export declare function startRemoteSession(config?: RemoteDesktopConfig): Promise<RemoteDesktopSession>;
17
+ export declare function getSessionStatus(id: string): Promise<RemoteDesktopSession | null>;
18
+ export declare function endRemoteSession(id: string): Promise<void>;
19
+ export declare function listActiveSessions(): Promise<RemoteDesktopSession[]>;
20
+ //# sourceMappingURL=remote-desktop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote-desktop.d.ts","sourceRoot":"","sources":["../../src/lifeops/remote-desktop.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,KAAK,EACV,oBAAoB,EACpB,mBAAmB,EACnB,oBAAoB,EACrB,MAAM,aAAa,CAAC;AAErB,YAAY,EACV,oBAAoB,EACpB,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,aAAa,CAAC;AAIrB,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,OAAO,EAAE,oBAAoB,CAAC;gBAC3B,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,oBAAoB;CAK3D;AAwHD,wBAAsB,0BAA0B,CAC9C,MAAM,CAAC,EAAE,mBAAmB,GAC3B,OAAO,CAAC,oBAAoB,CAAC,CAyB/B;AAqLD,wBAAsB,kBAAkB,CACtC,MAAM,CAAC,EAAE,mBAAmB,GAC3B,OAAO,CAAC,oBAAoB,CAAC,CA2H/B;AAED,wBAAsB,gBAAgB,CACpC,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAGtC;AAED,wBAAsB,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA0BhE;AAED,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAM1E"}
@@ -0,0 +1,19 @@
1
+ import type { Plugin } from "@elizaos/core";
2
+ /**
3
+ * @elizaos/plugin-remote-desktop
4
+ *
5
+ * Owner-only remote desktop session control. Provides a single
6
+ * `REMOTE_DESKTOP` umbrella action with subactions:
7
+ * - start — open a session (requires confirmation; pairing code in cloud mode)
8
+ * - status — look up a session by id
9
+ * - end — close a session by id
10
+ * - list — list active sessions
11
+ * - revoke — revoke an active session
12
+ *
13
+ * Backends: Tailscale VNC, Tailscale SSH, and ngrok TCP. Pairing-code gating
14
+ * lives in the underlying RemoteSessionService (`src/remote/`); backend
15
+ * detection and the in-process session store live in `src/lifeops/`.
16
+ */
17
+ export declare const remoteDesktopPlugin: Plugin;
18
+ export default remoteDesktopPlugin;
19
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAI5C;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,mBAAmB,EAAE,MAKjC,CAAC;AAEF,eAAe,mBAAmB,CAAC"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * 6-digit rolling pairing codes for the remote-control control plane (T9a).
3
+ *
4
+ * Codes are one-time use with a 5-minute TTL. They are held in process memory
5
+ * — the control plane is intended to issue a fresh code on every `startSession`
6
+ * call rather than persist codes across restarts.
7
+ */
8
+ export declare const PAIRING_CODE_TTL_MS: number;
9
+ export declare const PAIRING_CODE_LENGTH = 6;
10
+ export interface PairingCodeEntry {
11
+ code: string;
12
+ issuedAt: number;
13
+ expiresAt: number;
14
+ }
15
+ export interface PairingCodeStoreOptions {
16
+ ttlMs?: number;
17
+ now?: () => number;
18
+ }
19
+ /**
20
+ * In-process rolling pairing-code store. Each issuance replaces the previous
21
+ * code for that subject. `consume` both validates and invalidates the code.
22
+ */
23
+ export declare class PairingCodeStore {
24
+ private readonly ttlMs;
25
+ private readonly now;
26
+ private readonly entries;
27
+ constructor(options?: PairingCodeStoreOptions);
28
+ issue(subject: string): PairingCodeEntry;
29
+ /**
30
+ * Validates and consumes the code for `subject`. Returns true if the code
31
+ * matched and was unexpired. Always removes the entry after a match (one-time
32
+ * use) and after any expiry.
33
+ */
34
+ consume(subject: string, code: string): boolean;
35
+ peek(subject: string): PairingCodeEntry | undefined;
36
+ clear(subject?: string): void;
37
+ }
38
+ export declare function generatePairingCode(): string;
39
+ //# sourceMappingURL=pairing-code.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pairing-code.d.ts","sourceRoot":"","sources":["../../src/remote/pairing-code.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,eAAO,MAAM,mBAAmB,QAAgB,CAAC;AACjD,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAErC,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,uBAAuB;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuC;gBAEnD,OAAO,GAAE,uBAA4B;IAKjD,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB;IAWxC;;;;OAIG;IACH,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO;IAgB/C,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;IAUnD,KAAK,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI;CAO9B;AAED,wBAAgB,mBAAmB,IAAI,MAAM,CAI5C"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Control-plane service for remote VNC / remote-control sessions (T9a).
3
+ *
4
+ * This module owns:
5
+ * - Session lifecycle (pending → active → denied/revoked).
6
+ * - Pairing-code issuance and verification.
7
+ * - Local-mode bypass (ELIZA_REMOTE_LOCAL_MODE=1 skips the code requirement
8
+ * but still requires explicit `confirmed: true`).
9
+ * - Data-plane handoff point (`ingressUrl`).
10
+ *
11
+ * It does NOT own pixel transport. The data plane (VNC / Tailscale / Eliza
12
+ * Cloud tunnel) is separate infrastructure. When no data plane is configured,
13
+ * `startSession` returns an explicit `ingressUrl: null` with a structured
14
+ * `reason: "data-plane-not-configured"` — this is deliberate absence, not a
15
+ * fallback that pretends the session is usable.
16
+ */
17
+ import type { DataPlaneUnavailableReason, RemoteSession, StartSessionParams, StartSessionResult } from "../types.js";
18
+ import { PairingCodeStore } from "./pairing-code.js";
19
+ export type { DataPlaneUnavailableReason, RemoteSession, RemoteSessionStatus, StartSessionParams, StartSessionResult, } from "../types.js";
20
+ export interface DataPlaneResolution {
21
+ ingressUrl: string | null;
22
+ reason: DataPlaneUnavailableReason | null;
23
+ }
24
+ export interface DataPlaneResolver {
25
+ /**
26
+ * Returns the ingress URL for this session, or an explicit reason absence.
27
+ * Implementations live in T9b (Tailscale) and the Eliza Cloud tunnel track.
28
+ */
29
+ resolve(session: {
30
+ sessionId: string;
31
+ requesterIdentity: string;
32
+ localMode: boolean;
33
+ }): Promise<DataPlaneResolution> | DataPlaneResolution;
34
+ }
35
+ export interface RemoteSessionLogger {
36
+ debug: (value: string | Error | Record<string, unknown>, message?: string, ...args: unknown[]) => void;
37
+ info: (value: string | Error | Record<string, unknown>, message?: string, ...args: unknown[]) => void;
38
+ warn: (value: string | Error | Record<string, unknown>, message?: string, ...args: unknown[]) => void;
39
+ }
40
+ export interface RemoteSessionServiceOptions {
41
+ pairingCodes?: PairingCodeStore;
42
+ dataPlane?: DataPlaneResolver;
43
+ /** Read once per `startSession` call; overridable for tests. */
44
+ isLocalMode?: () => boolean;
45
+ now?: () => Date;
46
+ logger?: RemoteSessionLogger;
47
+ storagePath?: string;
48
+ }
49
+ export declare class RemoteSessionError extends Error {
50
+ readonly code: string;
51
+ constructor(code: string, message: string);
52
+ }
53
+ export declare class RemoteSessionService {
54
+ private readonly sessions;
55
+ private readonly pairingCodes;
56
+ private readonly dataPlane;
57
+ private readonly isLocalMode;
58
+ private readonly now;
59
+ private readonly log;
60
+ private readonly storagePath;
61
+ constructor(options?: RemoteSessionServiceOptions);
62
+ /**
63
+ * Issue a fresh pairing code. Each call rotates the code (one-time use).
64
+ * Returns the code so it can be displayed to the owner out-of-band.
65
+ */
66
+ issuePairingCode(): {
67
+ code: string;
68
+ expiresAt: string;
69
+ };
70
+ startSession(params: StartSessionParams): Promise<StartSessionResult>;
71
+ revokeSession(sessionId: string): Promise<void>;
72
+ listActiveSessions(): Promise<RemoteSession[]>;
73
+ getSession(sessionId: string): Promise<RemoteSession | undefined>;
74
+ private recordSession;
75
+ private loadSessions;
76
+ private persistSessions;
77
+ }
78
+ export declare function getRemoteSessionService(): RemoteSessionService;
79
+ /** Test-only hook for resetting the singleton between tests. */
80
+ export declare function __resetRemoteSessionServiceForTests(): void;
81
+ //# sourceMappingURL=remote-session-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote-session-service.d.ts","sourceRoot":"","sources":["../../src/remote/remote-session-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Public types for @elizaos/plugin-remote-desktop.
3
+ *
4
+ * Canonical home for the remote-desktop type contract. The engine
5
+ * (`src/lifeops/remote-desktop.ts`) and the control-plane service
6
+ * (`src/remote/remote-session-service.ts`) import and re-export these.
7
+ */
8
+ export type RemoteDesktopBackend = "tailscale-vnc" | "tailscale-ssh" | "ngrok-vnc" | "none";
9
+ export type RemoteDesktopSessionStatus = "starting" | "active" | "ended" | "failed";
10
+ export interface RemoteDesktopSession {
11
+ id: string;
12
+ backend: RemoteDesktopBackend;
13
+ status: RemoteDesktopSessionStatus;
14
+ accessUrl?: string;
15
+ accessCode?: string;
16
+ startedAt: string;
17
+ endedAt?: string;
18
+ expiresAt?: string;
19
+ error?: string;
20
+ mockMode?: boolean;
21
+ }
22
+ export interface RemoteDesktopConfig {
23
+ preferredBackend?: RemoteDesktopBackend;
24
+ tailscaleNodeName?: string;
25
+ ngrokAuthToken?: string;
26
+ vncPort?: number;
27
+ sessionDurationMinutes?: number;
28
+ }
29
+ export type RemoteSessionStatus = "pending" | "active" | "denied" | "revoked";
30
+ export type DataPlaneUnavailableReason = "data-plane-not-configured" | "local-mode-no-ingress";
31
+ export interface RemoteSession {
32
+ id: string;
33
+ requesterIdentity: string;
34
+ status: RemoteSessionStatus;
35
+ ingressUrl: string | null;
36
+ reason: DataPlaneUnavailableReason | null;
37
+ localMode: boolean;
38
+ createdAt: string;
39
+ updatedAt: string;
40
+ endedAt: string | null;
41
+ }
42
+ export interface StartSessionParams {
43
+ requesterIdentity: string;
44
+ pairingCode?: string | undefined;
45
+ confirmed: boolean;
46
+ }
47
+ export interface StartSessionResult {
48
+ sessionId: string;
49
+ status: RemoteSessionStatus;
50
+ ingressUrl: string | null;
51
+ reason: DataPlaneUnavailableReason | null;
52
+ localMode: boolean;
53
+ }
54
+ /**
55
+ * Subaction names accepted by the REMOTE_DESKTOP umbrella action.
56
+ */
57
+ export type RemoteDesktopSubaction = "start" | "status" | "end" | "list" | "revoke";
58
+ /**
59
+ * Parameter envelope passed to the REMOTE_DESKTOP handler.
60
+ */
61
+ export interface RemoteDesktopActionParams {
62
+ sessionId?: string;
63
+ confirmed?: boolean;
64
+ pairingCode?: string;
65
+ requesterIdentity?: string;
66
+ intent?: string;
67
+ }
68
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,MAAM,oBAAoB,GAC5B,eAAe,GACf,eAAe,GACf,WAAW,GACX,MAAM,CAAC;AAEX,MAAM,MAAM,0BAA0B,GAClC,UAAU,GACV,QAAQ,GACR,OAAO,GACP,QAAQ,CAAC;AAEb,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,oBAAoB,CAAC;IAC9B,MAAM,EAAE,0BAA0B,CAAC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,gBAAgB,CAAC,EAAE,oBAAoB,CAAC;IACxC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE9E,MAAM,MAAM,0BAA0B,GAClC,2BAA2B,GAC3B,uBAAuB,CAAC;AAE5B,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,iBAAiB,EAAE,MAAM,CAAC;IAC1B,MAAM,EAAE,mBAAmB,CAAC;IAC5B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,MAAM,EAAE,0BAA0B,GAAG,IAAI,CAAC;IAC1C,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,MAAM,WAAW,kBAAkB;IACjC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,mBAAmB,CAAC;IAC5B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,MAAM,EAAE,0BAA0B,GAAG,IAAI,CAAC;IAC1C,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAC9B,OAAO,GACP,QAAQ,GACR,KAAK,GACL,MAAM,GACN,QAAQ,CAAC;AAEb;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elizaos/plugin-remote-desktop",
3
- "version": "2.0.3-beta.5",
3
+ "version": "2.0.3-beta.7",
4
4
  "description": "Remote desktop session control for Eliza agents. REMOTE_DESKTOP umbrella action (start/status/end/list/revoke) over Tailscale VNC/SSH and ngrok backends with pairing-code confirmation. Extracted from @elizaos/plugin-personal-assistant.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -50,7 +50,7 @@
50
50
  "author": "elizaOS",
51
51
  "license": "MIT",
52
52
  "dependencies": {
53
- "@elizaos/core": "2.0.3-beta.5"
53
+ "@elizaos/core": "2.0.3-beta.7"
54
54
  },
55
55
  "devDependencies": {
56
56
  "@types/node": "^25.0.3",
@@ -83,5 +83,5 @@
83
83
  "node": "Default export (Node.js)"
84
84
  }
85
85
  },
86
- "gitHead": "ff6157011c9459670021cc28a6797592a78b8817"
86
+ "gitHead": "61094f10458d11055c75b3dd0bae374e3f66bac5"
87
87
  }