@elizaos/plugin-screenshare 2.0.3-beta.6 → 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.
Files changed (47) hide show
  1. package/dist/components/ScreenshareSpatialView.d.ts +61 -0
  2. package/dist/components/ScreenshareSpatialView.d.ts.map +1 -0
  3. package/dist/components/ScreenshareSpatialView.js +206 -0
  4. package/dist/components/ScreenshareSpatialView.js.map +1 -0
  5. package/dist/components/ScreenshareView.d.ts +13 -0
  6. package/dist/components/ScreenshareView.d.ts.map +1 -0
  7. package/dist/components/ScreenshareView.js +263 -0
  8. package/dist/components/ScreenshareView.js.map +1 -0
  9. package/dist/index.d.ts +9 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +58 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/register-terminal-view.d.ts +15 -0
  14. package/dist/register-terminal-view.d.ts.map +1 -0
  15. package/dist/register-terminal-view.js +27 -0
  16. package/dist/register-terminal-view.js.map +1 -0
  17. package/dist/routes.d.ts +8 -0
  18. package/dist/routes.d.ts.map +1 -0
  19. package/dist/routes.js +639 -0
  20. package/dist/routes.js.map +1 -0
  21. package/dist/session-store.d.ts +44 -0
  22. package/dist/session-store.d.ts.map +1 -0
  23. package/dist/session-store.js +200 -0
  24. package/dist/session-store.js.map +1 -0
  25. package/dist/ui/ScreenshareOperatorSurface.d.ts +5 -0
  26. package/dist/ui/ScreenshareOperatorSurface.d.ts.map +1 -0
  27. package/dist/ui/ScreenshareOperatorSurface.helpers.d.ts +40 -0
  28. package/dist/ui/ScreenshareOperatorSurface.helpers.d.ts.map +1 -0
  29. package/dist/ui/ScreenshareOperatorSurface.helpers.js +47 -0
  30. package/dist/ui/ScreenshareOperatorSurface.helpers.js.map +1 -0
  31. package/dist/ui/ScreenshareOperatorSurface.interact.d.ts +2 -0
  32. package/dist/ui/ScreenshareOperatorSurface.interact.d.ts.map +1 -0
  33. package/dist/ui/ScreenshareOperatorSurface.interact.js +100 -0
  34. package/dist/ui/ScreenshareOperatorSurface.interact.js.map +1 -0
  35. package/dist/ui/ScreenshareOperatorSurface.js +746 -0
  36. package/dist/ui/ScreenshareOperatorSurface.js.map +1 -0
  37. package/dist/ui/index.d.ts +3 -0
  38. package/dist/ui/index.d.ts.map +1 -0
  39. package/dist/ui/index.js +10 -0
  40. package/dist/ui/index.js.map +1 -0
  41. package/dist/ui/screenshare-view-bundle.d.ts +3 -0
  42. package/dist/ui/screenshare-view-bundle.d.ts.map +1 -0
  43. package/dist/ui/screenshare-view-bundle.js +7 -0
  44. package/dist/ui/screenshare-view-bundle.js.map +1 -0
  45. package/dist/views/bundle.js +507 -0
  46. package/dist/views/bundle.js.map +1 -0
  47. package/package.json +8 -8
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/routes.ts"],"sourcesContent":["import type http from \"node:http\";\nimport type { AppPackageRouteContext as RouteContext } from \"@elizaos/core\";\nimport {\n captureDesktopScreenshot,\n type DesktopInputButton,\n type DesktopScreenshotRegion,\n listDesktopWindows,\n performDesktopClick,\n performDesktopDoubleClick,\n performDesktopKeypress,\n performDesktopMouseMove,\n performDesktopScroll,\n performDesktopTextInput,\n} from \"@elizaos/plugin-computeruse\";\nimport type {\n AppLaunchDiagnostic,\n AppLaunchPreparation,\n AppLaunchSessionContext,\n AppRunSessionContext,\n AppSessionState,\n} from \"@elizaos/shared\";\nimport {\n buildScreenshareAppSession,\n canAccessScreenshareSession,\n createScreenshareSession,\n getOrCreateLocalScreenshareSession,\n getScreenshareCapabilities,\n getScreenshareSession,\n listScreenshareSessions,\n recordScreenshareFrame,\n recordScreenshareInput,\n type ScreenshareSession,\n stopScreenshareSession,\n toPublicSession,\n} from \"./session-store.js\";\n\ninterface StartSessionBody {\n label?: unknown;\n}\n\ninterface ScreenshareInputBody {\n token?: unknown;\n type?: unknown;\n x?: unknown;\n y?: unknown;\n button?: unknown;\n text?: unknown;\n keys?: unknown;\n deltaX?: unknown;\n deltaY?: unknown;\n}\n\nconst BASE_PATH = \"/api/apps/screenshare\";\nconst VIEWER_SANDBOX =\n \"allow-scripts allow-same-origin allow-forms allow-pointer-lock\";\nconst MAX_TEXT_INPUT_LENGTH = 4096;\n\n// Simple in-process rate limiter for session-creation requests.\nconst SESSION_CREATE_LIMIT = 10; // max requests per window\nconst SESSION_CREATE_WINDOW_MS = 60_000; // 1 minute\nconst sessionCreateCounts = new Map<\n string,\n { count: number; resetAt: number }\n>();\n\nfunction getRemoteIp(req: http.IncomingMessage | undefined): string {\n const addr = req?.socket?.remoteAddress ?? req?.headers?.[\"x-forwarded-for\"];\n return (Array.isArray(addr) ? addr[0] : (addr ?? \"unknown\"))\n .split(\",\")[0]\n .trim();\n}\n\nfunction sessionCreateRateLimitExceeded(\n req: http.IncomingMessage | undefined,\n): boolean {\n const ip = getRemoteIp(req);\n const now = Date.now();\n const entry = sessionCreateCounts.get(ip);\n if (!entry || entry.resetAt <= now) {\n sessionCreateCounts.set(ip, {\n count: 1,\n resetAt: now + SESSION_CREATE_WINDOW_MS,\n });\n return false;\n }\n entry.count += 1;\n return entry.count > SESSION_CREATE_LIMIT;\n}\nconst MAX_KEYPRESS_LENGTH = 128;\nconst SAFE_KEYPRESS_PATTERN = /^[A-Za-z0-9+_.,: -]+$/;\n\nexport async function prepareLaunch(\n _ctx: AppLaunchSessionContext,\n): Promise<AppLaunchPreparation> {\n const session = getOrCreateLocalScreenshareSession();\n const viewerUrl = buildViewerUrl(session);\n return {\n launchUrl: viewerUrl,\n viewer: {\n url: viewerUrl,\n sandbox: VIEWER_SANDBOX,\n },\n skipRuntimePluginRegistration: true,\n diagnostics: collectCapabilityDiagnostics(),\n };\n}\n\nexport async function resolveLaunchSession(\n _ctx: AppLaunchSessionContext,\n): Promise<AppSessionState> {\n return buildScreenshareAppSession(getOrCreateLocalScreenshareSession());\n}\n\nexport async function refreshRunSession(\n ctx: AppRunSessionContext,\n): Promise<AppSessionState | null> {\n const sessionId = ctx.session?.sessionId;\n if (!sessionId) {\n return buildScreenshareAppSession(getOrCreateLocalScreenshareSession());\n }\n const session = getScreenshareSession(sessionId);\n if (!session || session.status !== \"active\") {\n return null;\n }\n return buildScreenshareAppSession(session);\n}\n\nexport async function stopRun(ctx: AppRunSessionContext): Promise<void> {\n const sessionId = ctx.session?.sessionId;\n if (sessionId) {\n stopScreenshareSession(sessionId);\n }\n}\n\nexport async function handleAppRoutes(ctx: RouteContext): Promise<boolean> {\n if (!ctx.pathname.startsWith(BASE_PATH)) {\n return false;\n }\n\n if (ctx.method === \"GET\" && ctx.pathname === `${BASE_PATH}/viewer`) {\n sendHtml(ctx.res, renderViewerHtml());\n return true;\n }\n\n if (ctx.method === \"GET\" && ctx.pathname === `${BASE_PATH}/capabilities`) {\n ctx.json(ctx.res, {\n platform: process.platform,\n capabilities: getScreenshareCapabilities(),\n });\n return true;\n }\n\n if (ctx.method === \"GET\" && ctx.pathname === `${BASE_PATH}/windows`) {\n try {\n ctx.json(ctx.res, { windows: listDesktopWindows() });\n } catch (error) {\n ctx.error(\n ctx.res,\n error instanceof Error\n ? error.message\n : \"Desktop window listing failed.\",\n 500,\n );\n }\n return true;\n }\n\n if (ctx.method === \"GET\" && ctx.pathname === `${BASE_PATH}/sessions`) {\n ctx.json(ctx.res, { sessions: listScreenshareSessions() });\n return true;\n }\n\n if (ctx.method === \"POST\" && ctx.pathname === `${BASE_PATH}/session`) {\n if (sessionCreateRateLimitExceeded(ctx.req)) {\n ctx.error(\n ctx.res,\n \"Too many session creation requests. Please wait before trying again.\",\n 429,\n );\n return true;\n }\n const body = await ctx.readJsonBody<StartSessionBody>();\n if (body === null) {\n return true;\n }\n const session = createScreenshareSession(readLabel(body.label));\n ctx.json(ctx.res, {\n session: toPublicSession(session),\n token: session.token,\n viewerUrl: buildViewerUrl(session),\n });\n return true;\n }\n\n const match = ctx.pathname.match(\n /^\\/api\\/apps\\/screenshare\\/session\\/([^/]+)(?:\\/([^/]+))?$/,\n );\n if (!match?.[1]) {\n return false;\n }\n\n const sessionId = decodeURIComponent(match[1]);\n const subroute = match[2] ? decodeURIComponent(match[2]) : \"\";\n const session = getScreenshareSession(sessionId);\n if (!session) {\n ctx.error(\n ctx.res,\n `Screen share session \"${sessionId}\" was not found.`,\n 404,\n );\n return true;\n }\n\n if (ctx.method === \"GET\" && !subroute) {\n const token = readRequestToken(ctx);\n if (!canAccessScreenshareSession(session, token)) {\n ctx.error(ctx.res, \"Invalid screen share token.\", 403);\n return true;\n }\n ctx.json(ctx.res, { session: toPublicSession(session) });\n return true;\n }\n\n if (ctx.method === \"GET\" && subroute === \"frame\") {\n const token = readRequestToken(ctx);\n if (!canAccessScreenshareSession(session, token)) {\n ctx.error(ctx.res, \"Invalid screen share token.\", 403);\n return true;\n }\n if (session.status !== \"active\") {\n ctx.error(ctx.res, \"Screen share session is stopped.\", 409);\n return true;\n }\n const region = readFrameRegion(ctx.url);\n try {\n const screenshot = captureDesktopScreenshot(region);\n recordScreenshareFrame(session.id);\n sendPng(ctx.res, screenshot);\n } catch (error) {\n ctx.error(\n ctx.res,\n error instanceof Error ? error.message : \"Screenshot failed.\",\n 500,\n );\n }\n return true;\n }\n\n if (ctx.method === \"POST\" && subroute === \"input\") {\n const body = await ctx.readJsonBody<ScreenshareInputBody>();\n if (body === null) {\n return true;\n }\n const token = readBodyToken(body) ?? readRequestToken(ctx);\n if (!canAccessScreenshareSession(session, token)) {\n ctx.error(ctx.res, \"Invalid screen share token.\", 403);\n return true;\n }\n if (session.status !== \"active\") {\n ctx.error(ctx.res, \"Screen share session is stopped.\", 409);\n return true;\n }\n\n let result: { success: boolean; message: string };\n try {\n result = executeInput(body);\n } catch (error) {\n ctx.error(\n ctx.res,\n error instanceof Error ? error.message : \"Desktop input failed.\",\n 500,\n );\n return true;\n }\n if (!result.success) {\n ctx.error(ctx.res, result.message, 400);\n return true;\n }\n const updated = recordScreenshareInput(session.id) ?? session;\n ctx.json(ctx.res, {\n success: true,\n message: result.message,\n session: toPublicSession(updated),\n });\n return true;\n }\n\n if (ctx.method === \"POST\" && subroute === \"stop\") {\n const body = await ctx.readJsonBody<{ token?: unknown }>();\n if (body === null) {\n return true;\n }\n const token = readBodyToken(body) ?? readRequestToken(ctx);\n if (!canAccessScreenshareSession(session, token)) {\n ctx.error(ctx.res, \"Invalid screen share token.\", 403);\n return true;\n }\n const stopped = stopScreenshareSession(session.id) ?? session;\n ctx.json(ctx.res, { session: toPublicSession(stopped) });\n return true;\n }\n\n return false;\n}\n\nfunction collectCapabilityDiagnostics(): AppLaunchDiagnostic[] {\n const capabilities = getScreenshareCapabilities();\n return Object.entries(capabilities)\n .filter(([, capability]) => !capability.available)\n .map(([code, capability]) => ({\n code: `screenshare-${code}-unavailable`,\n severity: \"warning\" as const,\n message: `${code} unavailable: ${capability.tool}`,\n }));\n}\n\nfunction buildViewerUrl(session: ScreenshareSession): string {\n const params = new URLSearchParams({\n sessionId: session.id,\n token: session.token,\n mode: \"host\",\n });\n return `${BASE_PATH}/viewer?${params.toString()}`;\n}\n\nfunction readLabel(value: unknown): string {\n return typeof value === \"string\" && value.trim()\n ? value.trim().slice(0, 80)\n : \"This machine\";\n}\n\nfunction readRequestToken(ctx: RouteContext): string | null {\n const queryToken = ctx.url.searchParams.get(\"token\");\n if (queryToken?.trim()) {\n return queryToken.trim();\n }\n\n const req = ctx.req;\n const headerToken = req.headers[\"x-screenshare-token\"];\n if (typeof headerToken === \"string\" && headerToken.trim()) {\n return headerToken.trim();\n }\n\n const authorization = req.headers.authorization;\n if (typeof authorization === \"string\") {\n const match = authorization.match(/^Bearer\\s+(.+)$/i);\n const token = match?.[1]?.trim();\n if (token) {\n return token;\n }\n }\n\n return null;\n}\n\nfunction readBodyToken(body: { token?: unknown }): string | null {\n return typeof body.token === \"string\" && body.token.trim()\n ? body.token.trim()\n : null;\n}\n\nfunction readFrameRegion(url: URL): DesktopScreenshotRegion | undefined {\n const x = readIntegerParam(url, \"x\");\n const y = readIntegerParam(url, \"y\");\n const width = readIntegerParam(url, \"width\");\n const height = readIntegerParam(url, \"height\");\n if (x === null || y === null || width === null || height === null) {\n return undefined;\n }\n if (width <= 0 || height <= 0) {\n return undefined;\n }\n return { x, y, width, height };\n}\n\nfunction readIntegerParam(url: URL, key: string): number | null {\n const raw = url.searchParams.get(key);\n if (raw === null || raw.trim() === \"\") {\n return null;\n }\n const parsed = Number(raw);\n return Number.isInteger(parsed) ? parsed : null;\n}\n\nfunction executeInput(body: ScreenshareInputBody): {\n success: boolean;\n message: string;\n} {\n const type = typeof body.type === \"string\" ? body.type.trim() : \"\";\n if (type === \"click\" || type === \"double-click\" || type === \"move\") {\n const point = readPoint(body);\n if (!point) {\n return { success: false, message: \"Input requires integer x and y.\" };\n }\n if (type === \"move\") {\n performDesktopMouseMove(point.x, point.y);\n return { success: true, message: \"Pointer moved.\" };\n }\n const button = readButton(body.button);\n if (!button) {\n return { success: false, message: \"button must be left or right.\" };\n }\n if (type === \"double-click\") {\n performDesktopDoubleClick(point.x, point.y, button);\n return { success: true, message: \"Double-click sent.\" };\n }\n performDesktopClick(point.x, point.y, button);\n return { success: true, message: \"Click sent.\" };\n }\n\n if (type === \"type\") {\n if (typeof body.text !== \"string\" || body.text.length === 0) {\n return { success: false, message: \"text is required.\" };\n }\n if (body.text.length > MAX_TEXT_INPUT_LENGTH) {\n return {\n success: false,\n message: `text exceeds maximum length (${MAX_TEXT_INPUT_LENGTH}).`,\n };\n }\n performDesktopTextInput(body.text);\n return { success: true, message: \"Text sent.\" };\n }\n\n if (type === \"keypress\") {\n if (typeof body.keys !== \"string\" || !body.keys.trim()) {\n return { success: false, message: \"keys is required.\" };\n }\n const keys = body.keys.trim();\n if (keys.length > MAX_KEYPRESS_LENGTH) {\n return {\n success: false,\n message: `keys exceeds maximum length (${MAX_KEYPRESS_LENGTH}).`,\n };\n }\n if (!SAFE_KEYPRESS_PATTERN.test(keys)) {\n return {\n success: false,\n message:\n \"keys contains unsupported characters; allowed: letters, numbers, space, +, _, ., ,, :, -\",\n };\n }\n performDesktopKeypress(keys);\n return { success: true, message: \"Keypress sent.\" };\n }\n\n if (type === \"scroll\") {\n const deltaX = readInteger(body.deltaX) ?? 0;\n const deltaY = readInteger(body.deltaY) ?? 0;\n performDesktopScroll(deltaX, deltaY);\n return { success: true, message: \"Scroll sent.\" };\n }\n\n return {\n success: false,\n message:\n \"type must be one of: click, double-click, move, type, keypress, scroll.\",\n };\n}\n\nfunction readPoint(\n body: ScreenshareInputBody,\n): { x: number; y: number } | null {\n const x = readInteger(body.x);\n const y = readInteger(body.y);\n return x === null || y === null ? null : { x, y };\n}\n\nfunction readInteger(value: unknown): number | null {\n return typeof value === \"number\" && Number.isInteger(value) ? value : null;\n}\n\nfunction readButton(value: unknown): DesktopInputButton | null {\n if (value === undefined || value === null || value === \"left\") {\n return \"left\";\n }\n return value === \"right\" ? \"right\" : null;\n}\n\nfunction sendPng(response: unknown, png: Buffer): void {\n const res = response as http.ServerResponse;\n res.writeHead(200, {\n \"Content-Type\": \"image/png\",\n \"Content-Length\": png.byteLength,\n \"Cache-Control\": \"no-store\",\n });\n res.end(png);\n}\n\nfunction sendHtml(response: unknown, html: string): void {\n const data = Buffer.from(html, \"utf8\");\n const res = response as http.ServerResponse;\n res.writeHead(200, {\n \"Content-Type\": \"text/html; charset=utf-8\",\n \"Content-Length\": data.byteLength,\n \"Cache-Control\": \"no-store\",\n });\n res.end(data);\n}\n\nfunction renderViewerHtml(): string {\n return `<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\" />\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\" />\n<title>Screen Share</title>\n<style>\n:root{color-scheme:dark;--bg:#0b0d0f;--panel:#15181c;--line:#2a3036;--txt:#edf1f4;--muted:#9aa7ae;--accent:#d4b45e;--ok:#70d6a7;--danger:#f17a7a}\n*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--txt);font:13px/1.45 \"Poppins\",Arial,system-ui,sans-serif}\nmain{display:grid;grid-template-rows:auto 1fr auto;min-height:100vh}\n.bar{display:flex;gap:10px;align-items:center;border-bottom:1px solid var(--line);background:var(--panel);padding:10px 12px}\n.status{display:inline-flex;align-items:center;gap:8px;min-width:0;white-space:nowrap}.dot{width:8px;height:8px;border-radius:999px;background:var(--muted)}.dot.live{background:var(--ok)}.dot.err{background:var(--danger)}\n.spacer{flex:1}.btn,.input{height:32px;border:1px solid var(--line);border-radius:8px;background:#0f1215;color:var(--txt);padding:0 10px}.btn{cursor:pointer}.btn:hover{border-color:var(--accent)}.btn:disabled{cursor:not-allowed;opacity:.55}.input{min-width:0}\n.stage{display:grid;place-items:center;min-height:0;background:#050607;overflow:hidden;position:relative}.frame{max-width:100%;max-height:100%;object-fit:contain;user-select:none;outline:none}.empty{color:var(--muted);text-align:center;padding:24px}\n.controls{display:grid;grid-template-columns:1fr auto auto auto;gap:8px;border-top:1px solid var(--line);background:var(--panel);padding:10px 12px}.keys{display:flex;gap:8px;min-width:0}\n@media(max-width:720px){.bar,.controls{grid-template-columns:1fr;flex-wrap:wrap}.controls{display:flex}.keys{width:100%}.input{flex:1}}\n</style>\n</head>\n<body>\n<main>\n <div class=\"bar\">\n <div class=\"status\"><span id=\"dot\" class=\"dot\"></span><span id=\"status\">Connecting</span></div>\n <div class=\"spacer\"></div>\n <input id=\"base\" class=\"input\" placeholder=\"Server URL\" />\n <input id=\"session\" class=\"input\" placeholder=\"Session\" />\n <input id=\"token\" class=\"input\" placeholder=\"Token\" />\n <button id=\"connect\" class=\"btn\" type=\"button\">Connect</button>\n </div>\n <div id=\"stage\" class=\"stage\" tabindex=\"0\">\n <img id=\"frame\" class=\"frame\" alt=\"Remote desktop stream\" draggable=\"false\" />\n <div id=\"empty\" class=\"empty\">No stream selected.</div>\n </div>\n <div class=\"controls\">\n <div class=\"keys\">\n <input id=\"text\" class=\"input\" placeholder=\"Text\" />\n <button id=\"type\" class=\"btn\" type=\"button\">Type</button>\n </div>\n <button class=\"btn\" data-key=\"Enter\" type=\"button\">Enter</button>\n <button class=\"btn\" data-key=\"Escape\" type=\"button\">Esc</button>\n <button id=\"stop\" class=\"btn\" type=\"button\">Stop</button>\n </div>\n</main>\n<script>\n(() => {\n const params = new URLSearchParams(location.search);\n const state = {\n base: params.get(\"remoteBase\") || \"\",\n sessionId: params.get(\"sessionId\") || \"\",\n token: params.get(\"token\") || \"\",\n running: false,\n timer: 0,\n frameObjectUrl: \"\"\n };\n const dot = document.getElementById(\"dot\");\n const status = document.getElementById(\"status\");\n const frame = document.getElementById(\"frame\");\n const empty = document.getElementById(\"empty\");\n const stage = document.getElementById(\"stage\");\n const base = document.getElementById(\"base\");\n const session = document.getElementById(\"session\");\n const token = document.getElementById(\"token\");\n const text = document.getElementById(\"text\");\n base.value = state.base;\n session.value = state.sessionId;\n token.value = state.token;\n\n function endpoint(path) {\n const root = state.base.replace(/\\\\/$/, \"\");\n return root + path;\n }\n\n function setStatus(label, tone) {\n status.textContent = label;\n dot.className = \"dot\" + (tone === \"live\" ? \" live\" : tone === \"err\" ? \" err\" : \"\");\n }\n\n function applyConnection() {\n state.base = base.value.trim();\n state.sessionId = session.value.trim();\n state.token = token.value.trim();\n state.running = Boolean(state.sessionId && state.token);\n empty.style.display = state.running ? \"none\" : \"block\";\n frame.style.display = state.running ? \"block\" : \"none\";\n clearTimeout(state.timer);\n if (state.running) {\n setStatus(\"Streaming\", \"live\");\n loadFrame();\n } else {\n setStatus(\"Idle\", \"\");\n }\n }\n\n function disconnect(label) {\n state.running = false;\n clearTimeout(state.timer);\n if (state.frameObjectUrl) {\n URL.revokeObjectURL(state.frameObjectUrl);\n state.frameObjectUrl = \"\";\n }\n frame.removeAttribute(\"src\");\n frame.style.display = \"none\";\n empty.style.display = \"block\";\n setStatus(label, \"\");\n }\n\n async function readErrorMessage(response) {\n const body = await response.clone().json().catch(() => null);\n if (body && typeof body.error === \"string\" && body.error.trim()) {\n return body.error.trim();\n }\n const text = await response.text().catch(() => \"\");\n return text.trim() || \"Frame unavailable\";\n }\n\n async function loadFrame() {\n if (!state.running) return;\n const src = endpoint(\"/api/apps/screenshare/session/\" + encodeURIComponent(state.sessionId) + \"/frame?token=\" + encodeURIComponent(state.token) + \"&t=\" + Date.now());\n try {\n const response = await fetch(src, {\n headers: { \"X-Screenshare-Token\": state.token }\n });\n if (!response.ok) {\n throw new Error(await readErrorMessage(response));\n }\n const blob = await response.blob();\n if (!state.running) return;\n if (state.frameObjectUrl) {\n URL.revokeObjectURL(state.frameObjectUrl);\n }\n state.frameObjectUrl = URL.createObjectURL(blob);\n frame.src = state.frameObjectUrl;\n setStatus(\"Streaming\", \"live\");\n state.timer = window.setTimeout(loadFrame, 500);\n } catch (err) {\n if (!state.running) return;\n setStatus(err instanceof Error ? err.message : \"Frame unavailable\", \"err\");\n state.timer = window.setTimeout(loadFrame, 1500);\n }\n }\n\n async function sendInput(payload) {\n if (!state.sessionId || !state.token) return;\n const response = await fetch(endpoint(\"/api/apps/screenshare/session/\" + encodeURIComponent(state.sessionId) + \"/input\"), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\", \"X-Screenshare-Token\": state.token },\n body: JSON.stringify({ ...payload, token: state.token })\n });\n if (!response.ok) {\n const body = await response.json().catch(() => null);\n throw new Error(body?.error || body?.message || \"Input failed\");\n }\n }\n\n function imagePoint(event) {\n const rect = frame.getBoundingClientRect();\n if (!frame.naturalWidth || !frame.naturalHeight || rect.width <= 0 || rect.height <= 0) return null;\n return {\n x: Math.round((event.clientX - rect.left) * frame.naturalWidth / rect.width),\n y: Math.round((event.clientY - rect.top) * frame.naturalHeight / rect.height)\n };\n }\n\n frame.addEventListener(\"click\", (event) => {\n const point = imagePoint(event);\n if (!point) return;\n stage.focus();\n void sendInput({ type: \"click\", ...point, button: \"left\" }).catch((err) => setStatus(err.message, \"err\"));\n });\n frame.addEventListener(\"dblclick\", (event) => {\n const point = imagePoint(event);\n if (!point) return;\n stage.focus();\n void sendInput({ type: \"double-click\", ...point, button: \"left\" }).catch((err) => setStatus(err.message, \"err\"));\n });\n frame.addEventListener(\"contextmenu\", (event) => {\n event.preventDefault();\n const point = imagePoint(event);\n if (!point) return;\n stage.focus();\n void sendInput({ type: \"click\", ...point, button: \"right\" }).catch((err) => setStatus(err.message, \"err\"));\n });\n frame.addEventListener(\"mousemove\", (event) => {\n if (event.buttons === 0) return;\n const point = imagePoint(event);\n if (!point) return;\n void sendInput({ type: \"move\", ...point }).catch(() => {});\n });\n frame.addEventListener(\"wheel\", (event) => {\n event.preventDefault();\n void sendInput({\n type: \"scroll\",\n deltaX: Math.max(-10, Math.min(10, Math.round(event.deltaX / 80))),\n deltaY: Math.max(-10, Math.min(10, Math.round(event.deltaY / 80)))\n }).catch((err) => setStatus(err.message, \"err\"));\n }, { passive: false });\n\n document.getElementById(\"connect\").addEventListener(\"click\", applyConnection);\n document.getElementById(\"type\").addEventListener(\"click\", () => {\n const value = text.value;\n if (!value) return;\n void sendInput({ type: \"type\", text: value }).then(() => { text.value = \"\"; }).catch((err) => setStatus(err.message, \"err\"));\n });\n for (const button of document.querySelectorAll(\"[data-key]\")) {\n button.addEventListener(\"click\", () => {\n void sendInput({ type: \"keypress\", keys: button.getAttribute(\"data-key\") }).catch((err) => setStatus(err.message, \"err\"));\n });\n }\n stage.addEventListener(\"keydown\", (event) => {\n const keyMap = { Enter: \"Enter\", Escape: \"Escape\", Tab: \"Tab\", ArrowUp: \"Up\", ArrowDown: \"Down\", ArrowLeft: \"Left\", ArrowRight: \"Right\", Backspace: \"Backspace\" };\n const mapped = keyMap[event.key];\n if (!mapped) return;\n event.preventDefault();\n void sendInput({ type: \"keypress\", keys: mapped }).catch((err) => setStatus(err.message, \"err\"));\n });\n document.getElementById(\"stop\").addEventListener(\"click\", async () => {\n if (!state.sessionId || !state.token) return;\n const response = await fetch(endpoint(\"/api/apps/screenshare/session/\" + encodeURIComponent(state.sessionId) + \"/stop\"), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\", \"X-Screenshare-Token\": state.token },\n body: JSON.stringify({ token: state.token })\n });\n if (response.ok) {\n disconnect(\"Stopped\");\n }\n });\n applyConnection();\n})();\n</script>\n</body>\n</html>`;\n}\n"],"mappings":"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","names":["session"]}
@@ -0,0 +1,44 @@
1
+ import { type DesktopControlCapabilities } from "@elizaos/plugin-computeruse";
2
+ import type { AppSessionState } from "@elizaos/shared";
3
+ export declare const SCREENSHARE_APP_NAME = "@elizaos/plugin-screenshare";
4
+ export declare const SCREENSHARE_DISPLAY_NAME = "Screen Share";
5
+ export type ScreenshareSessionStatus = "active" | "stopped";
6
+ export interface ScreenshareSession {
7
+ id: string;
8
+ token: string;
9
+ label: string;
10
+ status: ScreenshareSessionStatus;
11
+ createdAt: string;
12
+ updatedAt: string;
13
+ stoppedAt: string | null;
14
+ platform: NodeJS.Platform;
15
+ frameCount: number;
16
+ inputCount: number;
17
+ lastFrameAt: string | null;
18
+ lastInputAt: string | null;
19
+ }
20
+ export interface ScreensharePublicSession {
21
+ id: string;
22
+ label: string;
23
+ status: ScreenshareSessionStatus;
24
+ createdAt: string;
25
+ updatedAt: string;
26
+ stoppedAt: string | null;
27
+ platform: NodeJS.Platform;
28
+ frameCount: number;
29
+ inputCount: number;
30
+ lastFrameAt: string | null;
31
+ lastInputAt: string | null;
32
+ }
33
+ export declare function createScreenshareSession(label?: string): ScreenshareSession;
34
+ export declare function getOrCreateLocalScreenshareSession(): ScreenshareSession;
35
+ export declare function getScreenshareSession(sessionId: string): ScreenshareSession | null;
36
+ export declare function listScreenshareSessions(): ScreensharePublicSession[];
37
+ export declare function stopScreenshareSession(sessionId: string): ScreenshareSession | null;
38
+ export declare function recordScreenshareFrame(sessionId: string): ScreenshareSession | null;
39
+ export declare function recordScreenshareInput(sessionId: string): ScreenshareSession | null;
40
+ export declare function canAccessScreenshareSession(session: ScreenshareSession, token: string | null | undefined): boolean;
41
+ export declare function toPublicSession(session: ScreenshareSession): ScreensharePublicSession;
42
+ export declare function getScreenshareCapabilities(): DesktopControlCapabilities;
43
+ export declare function buildScreenshareAppSession(session: ScreenshareSession): AppSessionState;
44
+ //# sourceMappingURL=session-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-store.d.ts","sourceRoot":"","sources":["../src/session-store.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,0BAA0B,EAGhC,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEvD,eAAO,MAAM,oBAAoB,gCAAgC,CAAC;AAClE,eAAO,MAAM,wBAAwB,iBAAiB,CAAC;AAEvD,MAAM,MAAM,wBAAwB,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE5D,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,wBAAwB,CAAC;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,wBAAwB;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,wBAAwB,CAAC;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AA0BD,wBAAgB,wBAAwB,CACtC,KAAK,SAAiB,GACrB,kBAAkB,CAwBpB;AAED,wBAAgB,kCAAkC,IAAI,kBAAkB,CAUvE;AAED,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,MAAM,GAChB,kBAAkB,GAAG,IAAI,CAE3B;AAED,wBAAgB,uBAAuB,IAAI,wBAAwB,EAAE,CAIpE;AAED,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,MAAM,GAChB,kBAAkB,GAAG,IAAI,CAW3B;AA2BD,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,MAAM,GAChB,kBAAkB,GAAG,IAAI,CAc3B;AAED,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,MAAM,GAChB,kBAAkB,GAAG,IAAI,CAc3B;AAED,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,kBAAkB,EAC3B,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC/B,OAAO,CAET;AAED,wBAAgB,eAAe,CAC7B,OAAO,EAAE,kBAAkB,GAC1B,wBAAwB,CAc1B;AAeD,wBAAgB,0BAA0B,IAAI,0BAA0B,CAEvE;AAED,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,kBAAkB,GAC1B,eAAe,CAsCjB"}
@@ -0,0 +1,200 @@
1
+ import { randomBytes, randomUUID } from "node:crypto";
2
+ import {
3
+ detectDesktopControlCapabilities,
4
+ getDesktopPlatformName
5
+ } from "@elizaos/plugin-computeruse";
6
+ const SCREENSHARE_APP_NAME = "@elizaos/plugin-screenshare";
7
+ const SCREENSHARE_DISPLAY_NAME = "Screen Share";
8
+ const STORE_KEY = /* @__PURE__ */ Symbol.for("elizaos.app-screenshare.session-store");
9
+ function getStore() {
10
+ const globalObject = globalThis;
11
+ const existing = globalObject[STORE_KEY];
12
+ if (existing) {
13
+ return existing;
14
+ }
15
+ const created = {
16
+ sessions: /* @__PURE__ */ new Map(),
17
+ localSessionId: null
18
+ };
19
+ globalObject[STORE_KEY] = created;
20
+ return created;
21
+ }
22
+ function createScreenshareSession(label = "This machine") {
23
+ const now = (/* @__PURE__ */ new Date()).toISOString();
24
+ const store = getStore();
25
+ if (store.localSessionId) {
26
+ stopSessionInStore(store, store.localSessionId, now);
27
+ }
28
+ const session = {
29
+ id: randomUUID(),
30
+ token: randomBytes(24).toString("base64url"),
31
+ label,
32
+ status: "active",
33
+ createdAt: now,
34
+ updatedAt: now,
35
+ stoppedAt: null,
36
+ platform: getDesktopPlatformName(),
37
+ frameCount: 0,
38
+ inputCount: 0,
39
+ lastFrameAt: null,
40
+ lastInputAt: null
41
+ };
42
+ store.sessions.set(session.id, session);
43
+ store.localSessionId = session.id;
44
+ return session;
45
+ }
46
+ function getOrCreateLocalScreenshareSession() {
47
+ const store = getStore();
48
+ const localId = store.localSessionId;
49
+ if (localId) {
50
+ const existing = store.sessions.get(localId);
51
+ if (existing?.status === "active") {
52
+ return existing;
53
+ }
54
+ }
55
+ return createScreenshareSession();
56
+ }
57
+ function getScreenshareSession(sessionId) {
58
+ return getStore().sessions.get(sessionId) ?? null;
59
+ }
60
+ function listScreenshareSessions() {
61
+ return Array.from(getStore().sessions.values()).sort((left, right) => right.updatedAt.localeCompare(left.updatedAt)).map(toPublicSession);
62
+ }
63
+ function stopScreenshareSession(sessionId) {
64
+ const store = getStore();
65
+ const session = store.sessions.get(sessionId);
66
+ if (!session) {
67
+ return null;
68
+ }
69
+ if (session.status === "stopped") {
70
+ return session;
71
+ }
72
+ const now = (/* @__PURE__ */ new Date()).toISOString();
73
+ return stopSessionInStore(store, sessionId, now);
74
+ }
75
+ function stopSessionInStore(store, sessionId, stoppedAt) {
76
+ const session = store.sessions.get(sessionId);
77
+ if (!session) {
78
+ return null;
79
+ }
80
+ if (session.status === "stopped") {
81
+ return session;
82
+ }
83
+ const stopped = {
84
+ ...session,
85
+ status: "stopped",
86
+ updatedAt: stoppedAt,
87
+ stoppedAt
88
+ };
89
+ store.sessions.set(sessionId, stopped);
90
+ if (store.localSessionId === sessionId) {
91
+ store.localSessionId = null;
92
+ }
93
+ return stopped;
94
+ }
95
+ function recordScreenshareFrame(sessionId) {
96
+ const session = getStore().sessions.get(sessionId);
97
+ if (!session) {
98
+ return null;
99
+ }
100
+ const now = (/* @__PURE__ */ new Date()).toISOString();
101
+ const updated = {
102
+ ...session,
103
+ frameCount: session.frameCount + 1,
104
+ lastFrameAt: now,
105
+ updatedAt: now
106
+ };
107
+ getStore().sessions.set(sessionId, updated);
108
+ return updated;
109
+ }
110
+ function recordScreenshareInput(sessionId) {
111
+ const session = getStore().sessions.get(sessionId);
112
+ if (!session) {
113
+ return null;
114
+ }
115
+ const now = (/* @__PURE__ */ new Date()).toISOString();
116
+ const updated = {
117
+ ...session,
118
+ inputCount: session.inputCount + 1,
119
+ lastInputAt: now,
120
+ updatedAt: now
121
+ };
122
+ getStore().sessions.set(sessionId, updated);
123
+ return updated;
124
+ }
125
+ function canAccessScreenshareSession(session, token) {
126
+ return session.token === token;
127
+ }
128
+ function toPublicSession(session) {
129
+ return {
130
+ id: session.id,
131
+ label: session.label,
132
+ status: session.status,
133
+ createdAt: session.createdAt,
134
+ updatedAt: session.updatedAt,
135
+ stoppedAt: session.stoppedAt,
136
+ platform: session.platform,
137
+ frameCount: session.frameCount,
138
+ inputCount: session.inputCount,
139
+ lastFrameAt: session.lastFrameAt,
140
+ lastInputAt: session.lastInputAt
141
+ };
142
+ }
143
+ function describeScreenshareReadiness(session, unavailable) {
144
+ if (session.status !== "active") {
145
+ return "Desktop stream is stopped.";
146
+ }
147
+ if (unavailable.length > 0) {
148
+ return `Desktop stream needs attention: ${unavailable.join(", ")}`;
149
+ }
150
+ return "Desktop stream is unavailable.";
151
+ }
152
+ function getScreenshareCapabilities() {
153
+ return detectDesktopControlCapabilities();
154
+ }
155
+ function buildScreenshareAppSession(session) {
156
+ const capabilities = getScreenshareCapabilities();
157
+ const ready = session.status === "active" && capabilities.headfulGui.available && capabilities.screenshot.available && capabilities.computerUse.available;
158
+ const unavailable = Object.entries(capabilities).filter(([, capability]) => !capability.available).map(([name]) => name);
159
+ return {
160
+ sessionId: session.id,
161
+ appName: SCREENSHARE_APP_NAME,
162
+ mode: "spectate-and-steer",
163
+ status: ready ? "streaming" : "degraded",
164
+ displayName: SCREENSHARE_DISPLAY_NAME,
165
+ canSendCommands: ready,
166
+ controls: [],
167
+ summary: ready ? "Desktop stream is ready for remote control." : describeScreenshareReadiness(session, unavailable),
168
+ suggestedPrompts: [
169
+ "Start a fresh screen share session",
170
+ "List visible desktop windows",
171
+ "Stop the current screen share"
172
+ ],
173
+ telemetry: {
174
+ platform: session.platform,
175
+ frameCount: session.frameCount,
176
+ inputCount: session.inputCount,
177
+ lastFrameAt: session.lastFrameAt,
178
+ lastInputAt: session.lastInputAt,
179
+ screenshot: capabilities.screenshot.available,
180
+ computerUse: capabilities.computerUse.available,
181
+ headfulGui: capabilities.headfulGui.available
182
+ }
183
+ };
184
+ }
185
+ export {
186
+ SCREENSHARE_APP_NAME,
187
+ SCREENSHARE_DISPLAY_NAME,
188
+ buildScreenshareAppSession,
189
+ canAccessScreenshareSession,
190
+ createScreenshareSession,
191
+ getOrCreateLocalScreenshareSession,
192
+ getScreenshareCapabilities,
193
+ getScreenshareSession,
194
+ listScreenshareSessions,
195
+ recordScreenshareFrame,
196
+ recordScreenshareInput,
197
+ stopScreenshareSession,
198
+ toPublicSession
199
+ };
200
+ //# sourceMappingURL=session-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/session-store.ts"],"sourcesContent":["import { randomBytes, randomUUID } from \"node:crypto\";\nimport {\n type DesktopControlCapabilities,\n detectDesktopControlCapabilities,\n getDesktopPlatformName,\n} from \"@elizaos/plugin-computeruse\";\nimport type { AppSessionState } from \"@elizaos/shared\";\n\nexport const SCREENSHARE_APP_NAME = \"@elizaos/plugin-screenshare\";\nexport const SCREENSHARE_DISPLAY_NAME = \"Screen Share\";\n\nexport type ScreenshareSessionStatus = \"active\" | \"stopped\";\n\nexport interface ScreenshareSession {\n id: string;\n token: string;\n label: string;\n status: ScreenshareSessionStatus;\n createdAt: string;\n updatedAt: string;\n stoppedAt: string | null;\n platform: NodeJS.Platform;\n frameCount: number;\n inputCount: number;\n lastFrameAt: string | null;\n lastInputAt: string | null;\n}\n\nexport interface ScreensharePublicSession {\n id: string;\n label: string;\n status: ScreenshareSessionStatus;\n createdAt: string;\n updatedAt: string;\n stoppedAt: string | null;\n platform: NodeJS.Platform;\n frameCount: number;\n inputCount: number;\n lastFrameAt: string | null;\n lastInputAt: string | null;\n}\n\ninterface ScreenshareSessionStore {\n sessions: Map<string, ScreenshareSession>;\n localSessionId: string | null;\n}\n\nconst STORE_KEY = Symbol.for(\"elizaos.app-screenshare.session-store\");\n\nfunction getStore(): ScreenshareSessionStore {\n const globalObject = globalThis as Record<PropertyKey, unknown>;\n const existing = globalObject[STORE_KEY] as\n | ScreenshareSessionStore\n | undefined;\n if (existing) {\n return existing;\n }\n\n const created: ScreenshareSessionStore = {\n sessions: new Map<string, ScreenshareSession>(),\n localSessionId: null,\n };\n globalObject[STORE_KEY] = created;\n return created;\n}\n\nexport function createScreenshareSession(\n label = \"This machine\",\n): ScreenshareSession {\n const now = new Date().toISOString();\n const store = getStore();\n if (store.localSessionId) {\n stopSessionInStore(store, store.localSessionId, now);\n }\n const session: ScreenshareSession = {\n id: randomUUID(),\n token: randomBytes(24).toString(\"base64url\"),\n label,\n status: \"active\",\n createdAt: now,\n updatedAt: now,\n stoppedAt: null,\n platform: getDesktopPlatformName(),\n frameCount: 0,\n inputCount: 0,\n lastFrameAt: null,\n lastInputAt: null,\n };\n\n store.sessions.set(session.id, session);\n store.localSessionId = session.id;\n return session;\n}\n\nexport function getOrCreateLocalScreenshareSession(): ScreenshareSession {\n const store = getStore();\n const localId = store.localSessionId;\n if (localId) {\n const existing = store.sessions.get(localId);\n if (existing?.status === \"active\") {\n return existing;\n }\n }\n return createScreenshareSession();\n}\n\nexport function getScreenshareSession(\n sessionId: string,\n): ScreenshareSession | null {\n return getStore().sessions.get(sessionId) ?? null;\n}\n\nexport function listScreenshareSessions(): ScreensharePublicSession[] {\n return Array.from(getStore().sessions.values())\n .sort((left, right) => right.updatedAt.localeCompare(left.updatedAt))\n .map(toPublicSession);\n}\n\nexport function stopScreenshareSession(\n sessionId: string,\n): ScreenshareSession | null {\n const store = getStore();\n const session = store.sessions.get(sessionId);\n if (!session) {\n return null;\n }\n if (session.status === \"stopped\") {\n return session;\n }\n const now = new Date().toISOString();\n return stopSessionInStore(store, sessionId, now);\n}\n\nfunction stopSessionInStore(\n store: ScreenshareSessionStore,\n sessionId: string,\n stoppedAt: string,\n): ScreenshareSession | null {\n const session = store.sessions.get(sessionId);\n if (!session) {\n return null;\n }\n if (session.status === \"stopped\") {\n return session;\n }\n const stopped: ScreenshareSession = {\n ...session,\n status: \"stopped\",\n updatedAt: stoppedAt,\n stoppedAt,\n };\n store.sessions.set(sessionId, stopped);\n if (store.localSessionId === sessionId) {\n store.localSessionId = null;\n }\n return stopped;\n}\n\nexport function recordScreenshareFrame(\n sessionId: string,\n): ScreenshareSession | null {\n const session = getStore().sessions.get(sessionId);\n if (!session) {\n return null;\n }\n const now = new Date().toISOString();\n const updated: ScreenshareSession = {\n ...session,\n frameCount: session.frameCount + 1,\n lastFrameAt: now,\n updatedAt: now,\n };\n getStore().sessions.set(sessionId, updated);\n return updated;\n}\n\nexport function recordScreenshareInput(\n sessionId: string,\n): ScreenshareSession | null {\n const session = getStore().sessions.get(sessionId);\n if (!session) {\n return null;\n }\n const now = new Date().toISOString();\n const updated: ScreenshareSession = {\n ...session,\n inputCount: session.inputCount + 1,\n lastInputAt: now,\n updatedAt: now,\n };\n getStore().sessions.set(sessionId, updated);\n return updated;\n}\n\nexport function canAccessScreenshareSession(\n session: ScreenshareSession,\n token: string | null | undefined,\n): boolean {\n return session.token === token;\n}\n\nexport function toPublicSession(\n session: ScreenshareSession,\n): ScreensharePublicSession {\n return {\n id: session.id,\n label: session.label,\n status: session.status,\n createdAt: session.createdAt,\n updatedAt: session.updatedAt,\n stoppedAt: session.stoppedAt,\n platform: session.platform,\n frameCount: session.frameCount,\n inputCount: session.inputCount,\n lastFrameAt: session.lastFrameAt,\n lastInputAt: session.lastInputAt,\n };\n}\n\nfunction describeScreenshareReadiness(\n session: ScreenshareSession,\n unavailable: string[],\n): string {\n if (session.status !== \"active\") {\n return \"Desktop stream is stopped.\";\n }\n if (unavailable.length > 0) {\n return `Desktop stream needs attention: ${unavailable.join(\", \")}`;\n }\n return \"Desktop stream is unavailable.\";\n}\n\nexport function getScreenshareCapabilities(): DesktopControlCapabilities {\n return detectDesktopControlCapabilities();\n}\n\nexport function buildScreenshareAppSession(\n session: ScreenshareSession,\n): AppSessionState {\n const capabilities = getScreenshareCapabilities();\n const ready =\n session.status === \"active\" &&\n capabilities.headfulGui.available &&\n capabilities.screenshot.available &&\n capabilities.computerUse.available;\n const unavailable = Object.entries(capabilities)\n .filter(([, capability]) => !capability.available)\n .map(([name]) => name);\n\n return {\n sessionId: session.id,\n appName: SCREENSHARE_APP_NAME,\n mode: \"spectate-and-steer\",\n status: ready ? \"streaming\" : \"degraded\",\n displayName: SCREENSHARE_DISPLAY_NAME,\n canSendCommands: ready,\n controls: [],\n summary: ready\n ? \"Desktop stream is ready for remote control.\"\n : describeScreenshareReadiness(session, unavailable),\n suggestedPrompts: [\n \"Start a fresh screen share session\",\n \"List visible desktop windows\",\n \"Stop the current screen share\",\n ],\n telemetry: {\n platform: session.platform,\n frameCount: session.frameCount,\n inputCount: session.inputCount,\n lastFrameAt: session.lastFrameAt,\n lastInputAt: session.lastInputAt,\n screenshot: capabilities.screenshot.available,\n computerUse: capabilities.computerUse.available,\n headfulGui: capabilities.headfulGui.available,\n },\n };\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,5 @@
1
+ import { type AppOperatorSurfaceProps } from "@elizaos/ui";
2
+ export declare function ScreenshareOperatorSurface({ appName, focus, }: AppOperatorSurfaceProps): import("react/jsx-runtime").JSX.Element;
3
+ export declare function ScreenshareTuiView(): import("react/jsx-runtime").JSX.Element;
4
+ export { ScreenshareView } from "../components/ScreenshareView";
5
+ //# sourceMappingURL=ScreenshareOperatorSurface.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScreenshareOperatorSurface.d.ts","sourceRoot":"","sources":["../../src/ui/ScreenshareOperatorSurface.tsx"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,uBAAuB,EAU7B,MAAM,aAAa,CAAC;AA6IrB,wBAAgB,0BAA0B,CAAC,EACzC,OAAO,EACP,KAAa,GACd,EAAE,uBAAuB,2CA4YzB;AAED,wBAAgB,kBAAkB,4CAsLjC;AAMD,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC"}
@@ -0,0 +1,40 @@
1
+ export interface Capability {
2
+ available: boolean;
3
+ tool: string;
4
+ }
5
+ export interface CapabilitiesResponse {
6
+ platform: string;
7
+ capabilities: Record<string, Capability>;
8
+ }
9
+ export interface PublicSession {
10
+ id: string;
11
+ label: string;
12
+ status: "active" | "stopped";
13
+ createdAt: string;
14
+ updatedAt: string;
15
+ stoppedAt: string | null;
16
+ platform: string;
17
+ frameCount: number;
18
+ inputCount: number;
19
+ lastFrameAt: string | null;
20
+ lastInputAt: string | null;
21
+ }
22
+ export interface StartSessionResponse {
23
+ session: PublicSession;
24
+ token: string;
25
+ viewerUrl: string;
26
+ }
27
+ export interface SessionsResponse {
28
+ sessions: PublicSession[];
29
+ }
30
+ export declare function fetchJson<T>(path: string, init?: RequestInit): Promise<T>;
31
+ export declare function buildViewerUrl(args: {
32
+ baseUrl?: string;
33
+ sessionId: string;
34
+ token: string;
35
+ }): string;
36
+ export declare function loadScreenshareTuiState(): Promise<{
37
+ capabilities: CapabilitiesResponse;
38
+ sessions: SessionsResponse;
39
+ }>;
40
+ //# sourceMappingURL=ScreenshareOperatorSurface.helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScreenshareOperatorSurface.helpers.d.ts","sourceRoot":"","sources":["../../src/ui/ScreenshareOperatorSurface.helpers.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,OAAO,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;CAC1C;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,aAAa,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,aAAa,EAAE,CAAC;CAC3B;AAOD,wBAAsB,SAAS,CAAC,CAAC,EAC/B,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,WAAW,GACjB,OAAO,CAAC,CAAC,CAAC,CAsBZ;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE;IACnC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf,GAAG,MAAM,CAWT;AAED,wBAAsB,uBAAuB,IAAI,OAAO,CAAC;IACvD,YAAY,EAAE,oBAAoB,CAAC;IACnC,QAAQ,EAAE,gBAAgB,CAAC;CAC5B,CAAC,CAMD"}
@@ -0,0 +1,47 @@
1
+ import { client } from "@elizaos/ui";
2
+ function apiUrl(path) {
3
+ const base = client.getBaseUrl();
4
+ return base ? `${base}${path}` : path;
5
+ }
6
+ async function fetchJson(path, init) {
7
+ const token = client.getRestAuthToken();
8
+ const response = await fetch(apiUrl(path), {
9
+ ...init,
10
+ headers: {
11
+ ...init?.body ? { "Content-Type": "application/json" } : {},
12
+ ...token ? { Authorization: `Bearer ${token}` } : {},
13
+ ...init?.headers
14
+ }
15
+ });
16
+ const body = await response.json().catch(() => null);
17
+ if (!response.ok) {
18
+ const message = body && typeof body === "object" && "error" in body && typeof body.error === "string" ? body.error : `Request failed (${response.status})`;
19
+ throw new Error(message);
20
+ }
21
+ return body;
22
+ }
23
+ function buildViewerUrl(args) {
24
+ const params = new URLSearchParams({
25
+ sessionId: args.sessionId,
26
+ token: args.token
27
+ });
28
+ const base = args.baseUrl?.trim().replace(/\/+$/, "") ?? "";
29
+ if (base) {
30
+ params.set("remoteBase", base);
31
+ return `${base}/api/apps/screenshare/viewer?${params.toString()}`;
32
+ }
33
+ return apiUrl(`/api/apps/screenshare/viewer?${params.toString()}`);
34
+ }
35
+ async function loadScreenshareTuiState() {
36
+ const [capabilities, sessions] = await Promise.all([
37
+ fetchJson("/api/apps/screenshare/capabilities"),
38
+ fetchJson("/api/apps/screenshare/sessions")
39
+ ]);
40
+ return { capabilities, sessions };
41
+ }
42
+ export {
43
+ buildViewerUrl,
44
+ fetchJson,
45
+ loadScreenshareTuiState
46
+ };
47
+ //# sourceMappingURL=ScreenshareOperatorSurface.helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/ui/ScreenshareOperatorSurface.helpers.ts"],"sourcesContent":["// Shared data contracts + fetch helpers for the Screenshare view, used by both\n// ScreenshareOperatorSurface.tsx (host + connect + TUI components) and the\n// `interact` capability handler (ScreenshareOperatorSurface.interact.ts). Kept\n// out of the .tsx so that file exports only React components and stays\n// Fast-Refresh-compatible in dev.\nimport { client } from \"@elizaos/ui\";\n\nexport interface Capability {\n available: boolean;\n tool: string;\n}\n\nexport interface CapabilitiesResponse {\n platform: string;\n capabilities: Record<string, Capability>;\n}\n\nexport interface PublicSession {\n id: string;\n label: string;\n status: \"active\" | \"stopped\";\n createdAt: string;\n updatedAt: string;\n stoppedAt: string | null;\n platform: string;\n frameCount: number;\n inputCount: number;\n lastFrameAt: string | null;\n lastInputAt: string | null;\n}\n\nexport interface StartSessionResponse {\n session: PublicSession;\n token: string;\n viewerUrl: string;\n}\n\nexport interface SessionsResponse {\n sessions: PublicSession[];\n}\n\nfunction apiUrl(path: string): string {\n const base = client.getBaseUrl();\n return base ? `${base}${path}` : path;\n}\n\nexport async function fetchJson<T>(\n path: string,\n init?: RequestInit,\n): Promise<T> {\n const token = client.getRestAuthToken();\n const response = await fetch(apiUrl(path), {\n ...init,\n headers: {\n ...(init?.body ? { \"Content-Type\": \"application/json\" } : {}),\n ...(token ? { Authorization: `Bearer ${token}` } : {}),\n ...init?.headers,\n },\n });\n const body = (await response.json().catch(() => null)) as unknown;\n if (!response.ok) {\n const message =\n body &&\n typeof body === \"object\" &&\n \"error\" in body &&\n typeof body.error === \"string\"\n ? body.error\n : `Request failed (${response.status})`;\n throw new Error(message);\n }\n return body as T;\n}\n\nexport function buildViewerUrl(args: {\n baseUrl?: string;\n sessionId: string;\n token: string;\n}): string {\n const params = new URLSearchParams({\n sessionId: args.sessionId,\n token: args.token,\n });\n const base = args.baseUrl?.trim().replace(/\\/+$/, \"\") ?? \"\";\n if (base) {\n params.set(\"remoteBase\", base);\n return `${base}/api/apps/screenshare/viewer?${params.toString()}`;\n }\n return apiUrl(`/api/apps/screenshare/viewer?${params.toString()}`);\n}\n\nexport async function loadScreenshareTuiState(): Promise<{\n capabilities: CapabilitiesResponse;\n sessions: SessionsResponse;\n}> {\n const [capabilities, sessions] = await Promise.all([\n fetchJson<CapabilitiesResponse>(\"/api/apps/screenshare/capabilities\"),\n fetchJson<SessionsResponse>(\"/api/apps/screenshare/sessions\"),\n ]);\n return { capabilities, sessions };\n}\n"],"mappings":"AAKA,SAAS,cAAc;AAoCvB,SAAS,OAAO,MAAsB;AACpC,QAAM,OAAO,OAAO,WAAW;AAC/B,SAAO,OAAO,GAAG,IAAI,GAAG,IAAI,KAAK;AACnC;AAEA,eAAsB,UACpB,MACA,MACY;AACZ,QAAM,QAAQ,OAAO,iBAAiB;AACtC,QAAM,WAAW,MAAM,MAAM,OAAO,IAAI,GAAG;AAAA,IACzC,GAAG;AAAA,IACH,SAAS;AAAA,MACP,GAAI,MAAM,OAAO,EAAE,gBAAgB,mBAAmB,IAAI,CAAC;AAAA,MAC3D,GAAI,QAAQ,EAAE,eAAe,UAAU,KAAK,GAAG,IAAI,CAAC;AAAA,MACpD,GAAG,MAAM;AAAA,IACX;AAAA,EACF,CAAC;AACD,QAAM,OAAQ,MAAM,SAAS,KAAK,EAAE,MAAM,MAAM,IAAI;AACpD,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,UACJ,QACA,OAAO,SAAS,YAChB,WAAW,QACX,OAAO,KAAK,UAAU,WAClB,KAAK,QACL,mBAAmB,SAAS,MAAM;AACxC,UAAM,IAAI,MAAM,OAAO;AAAA,EACzB;AACA,SAAO;AACT;AAEO,SAAS,eAAe,MAIpB;AACT,QAAM,SAAS,IAAI,gBAAgB;AAAA,IACjC,WAAW,KAAK;AAAA,IAChB,OAAO,KAAK;AAAA,EACd,CAAC;AACD,QAAM,OAAO,KAAK,SAAS,KAAK,EAAE,QAAQ,QAAQ,EAAE,KAAK;AACzD,MAAI,MAAM;AACR,WAAO,IAAI,cAAc,IAAI;AAC7B,WAAO,GAAG,IAAI,gCAAgC,OAAO,SAAS,CAAC;AAAA,EACjE;AACA,SAAO,OAAO,gCAAgC,OAAO,SAAS,CAAC,EAAE;AACnE;AAEA,eAAsB,0BAGnB;AACD,QAAM,CAAC,cAAc,QAAQ,IAAI,MAAM,QAAQ,IAAI;AAAA,IACjD,UAAgC,oCAAoC;AAAA,IACpE,UAA4B,gCAAgC;AAAA,EAC9D,CAAC;AACD,SAAO,EAAE,cAAc,SAAS;AAClC;","names":[]}
@@ -0,0 +1,2 @@
1
+ export declare function interact(capability: string, params?: Record<string, unknown>): Promise<unknown>;
2
+ //# sourceMappingURL=ScreenshareOperatorSurface.interact.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScreenshareOperatorSurface.interact.d.ts","sourceRoot":"","sources":["../../src/ui/ScreenshareOperatorSurface.interact.ts"],"names":[],"mappings":"AAaA,wBAAsB,QAAQ,CAC5B,UAAU,EAAE,MAAM,EAClB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,OAAO,CAAC,OAAO,CAAC,CAuGlB"}
@@ -0,0 +1,100 @@
1
+ import {
2
+ buildViewerUrl,
3
+ fetchJson,
4
+ loadScreenshareTuiState
5
+ } from "./ScreenshareOperatorSurface.helpers";
6
+ async function interact(capability, params) {
7
+ if (capability === "terminal-screenshare-state") {
8
+ return { viewType: "tui", ...await loadScreenshareTuiState() };
9
+ }
10
+ if (capability === "terminal-screenshare-start") {
11
+ return {
12
+ viewType: "tui",
13
+ ...await fetchJson(
14
+ "/api/apps/screenshare/session",
15
+ {
16
+ method: "POST",
17
+ body: JSON.stringify({
18
+ label: typeof params?.label === "string" ? params.label : "Terminal"
19
+ })
20
+ }
21
+ )
22
+ };
23
+ }
24
+ if (capability === "terminal-screenshare-session") {
25
+ const sessionId = typeof params?.sessionId === "string" ? params.sessionId.trim() : "";
26
+ const token = typeof params?.token === "string" ? params.token.trim() : "";
27
+ if (!sessionId) throw new Error("sessionId is required");
28
+ if (!token) throw new Error("token is required");
29
+ return {
30
+ viewType: "tui",
31
+ ...await fetchJson(
32
+ `/api/apps/screenshare/session/${encodeURIComponent(
33
+ sessionId
34
+ )}?token=${encodeURIComponent(token)}`
35
+ )
36
+ };
37
+ }
38
+ if (capability === "terminal-screenshare-stop") {
39
+ const sessionId = typeof params?.sessionId === "string" ? params.sessionId.trim() : "";
40
+ const token = typeof params?.token === "string" ? params.token.trim() : "";
41
+ if (!sessionId) throw new Error("sessionId is required");
42
+ if (!token) throw new Error("token is required");
43
+ return {
44
+ viewType: "tui",
45
+ ...await fetchJson(
46
+ `/api/apps/screenshare/session/${encodeURIComponent(sessionId)}/stop`,
47
+ {
48
+ method: "POST",
49
+ body: JSON.stringify({ token }),
50
+ headers: { "X-Screenshare-Token": token }
51
+ }
52
+ )
53
+ };
54
+ }
55
+ if (capability === "terminal-screenshare-input") {
56
+ const sessionId = typeof params?.sessionId === "string" ? params.sessionId.trim() : "";
57
+ const token = typeof params?.token === "string" ? params.token.trim() : "";
58
+ if (!sessionId) throw new Error("sessionId is required");
59
+ if (!token) throw new Error("token is required");
60
+ return {
61
+ viewType: "tui",
62
+ ...await fetchJson(
63
+ `/api/apps/screenshare/session/${encodeURIComponent(sessionId)}/input`,
64
+ {
65
+ method: "POST",
66
+ body: JSON.stringify({
67
+ token,
68
+ type: typeof params?.type === "string" ? params.type : "keypress",
69
+ keys: typeof params?.keys === "string" ? params.keys : void 0,
70
+ text: typeof params?.text === "string" ? params.text : void 0,
71
+ x: typeof params?.x === "number" ? params.x : void 0,
72
+ y: typeof params?.y === "number" ? params.y : void 0,
73
+ button: typeof params?.button === "string" ? params.button : void 0,
74
+ deltaY: typeof params?.deltaY === "number" ? params.deltaY : void 0
75
+ }),
76
+ headers: { "X-Screenshare-Token": token }
77
+ }
78
+ )
79
+ };
80
+ }
81
+ if (capability === "terminal-screenshare-viewer-url") {
82
+ const sessionId = typeof params?.sessionId === "string" ? params.sessionId.trim() : "";
83
+ const token = typeof params?.token === "string" ? params.token.trim() : "";
84
+ if (!sessionId) throw new Error("sessionId is required");
85
+ if (!token) throw new Error("token is required");
86
+ return {
87
+ viewType: "tui",
88
+ viewerUrl: buildViewerUrl({
89
+ baseUrl: typeof params?.baseUrl === "string" ? params.baseUrl : "",
90
+ sessionId,
91
+ token
92
+ })
93
+ };
94
+ }
95
+ throw new Error(`Unsupported capability "${capability}"`);
96
+ }
97
+ export {
98
+ interact
99
+ };
100
+ //# sourceMappingURL=ScreenshareOperatorSurface.interact.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/ui/ScreenshareOperatorSurface.interact.ts"],"sourcesContent":["// View-bundle `interact` capability handler, split out of\n// ScreenshareOperatorSurface.tsx so that file exports only React components and\n// stays Fast-Refresh-compatible (Vite would full-reload a component file that\n// also exports a plain function). The view bundle re-exports `interact` via\n// ./screenshare-view-bundle.ts.\nimport {\n buildViewerUrl,\n fetchJson,\n loadScreenshareTuiState,\n type PublicSession,\n type StartSessionResponse,\n} from \"./ScreenshareOperatorSurface.helpers\";\n\nexport async function interact(\n capability: string,\n params?: Record<string, unknown>,\n): Promise<unknown> {\n if (capability === \"terminal-screenshare-state\") {\n return { viewType: \"tui\", ...(await loadScreenshareTuiState()) };\n }\n\n if (capability === \"terminal-screenshare-start\") {\n return {\n viewType: \"tui\",\n ...(await fetchJson<StartSessionResponse>(\n \"/api/apps/screenshare/session\",\n {\n method: \"POST\",\n body: JSON.stringify({\n label:\n typeof params?.label === \"string\" ? params.label : \"Terminal\",\n }),\n },\n )),\n };\n }\n\n if (capability === \"terminal-screenshare-session\") {\n const sessionId =\n typeof params?.sessionId === \"string\" ? params.sessionId.trim() : \"\";\n const token = typeof params?.token === \"string\" ? params.token.trim() : \"\";\n if (!sessionId) throw new Error(\"sessionId is required\");\n if (!token) throw new Error(\"token is required\");\n return {\n viewType: \"tui\",\n ...(await fetchJson<{ session: PublicSession }>(\n `/api/apps/screenshare/session/${encodeURIComponent(\n sessionId,\n )}?token=${encodeURIComponent(token)}`,\n )),\n };\n }\n\n if (capability === \"terminal-screenshare-stop\") {\n const sessionId =\n typeof params?.sessionId === \"string\" ? params.sessionId.trim() : \"\";\n const token = typeof params?.token === \"string\" ? params.token.trim() : \"\";\n if (!sessionId) throw new Error(\"sessionId is required\");\n if (!token) throw new Error(\"token is required\");\n return {\n viewType: \"tui\",\n ...(await fetchJson<{ session: PublicSession }>(\n `/api/apps/screenshare/session/${encodeURIComponent(sessionId)}/stop`,\n {\n method: \"POST\",\n body: JSON.stringify({ token }),\n headers: { \"X-Screenshare-Token\": token },\n },\n )),\n };\n }\n\n if (capability === \"terminal-screenshare-input\") {\n const sessionId =\n typeof params?.sessionId === \"string\" ? params.sessionId.trim() : \"\";\n const token = typeof params?.token === \"string\" ? params.token.trim() : \"\";\n if (!sessionId) throw new Error(\"sessionId is required\");\n if (!token) throw new Error(\"token is required\");\n return {\n viewType: \"tui\",\n ...(await fetchJson<Record<string, unknown>>(\n `/api/apps/screenshare/session/${encodeURIComponent(sessionId)}/input`,\n {\n method: \"POST\",\n body: JSON.stringify({\n token,\n type: typeof params?.type === \"string\" ? params.type : \"keypress\",\n keys: typeof params?.keys === \"string\" ? params.keys : undefined,\n text: typeof params?.text === \"string\" ? params.text : undefined,\n x: typeof params?.x === \"number\" ? params.x : undefined,\n y: typeof params?.y === \"number\" ? params.y : undefined,\n button:\n typeof params?.button === \"string\" ? params.button : undefined,\n deltaY:\n typeof params?.deltaY === \"number\" ? params.deltaY : undefined,\n }),\n headers: { \"X-Screenshare-Token\": token },\n },\n )),\n };\n }\n\n if (capability === \"terminal-screenshare-viewer-url\") {\n const sessionId =\n typeof params?.sessionId === \"string\" ? params.sessionId.trim() : \"\";\n const token = typeof params?.token === \"string\" ? params.token.trim() : \"\";\n if (!sessionId) throw new Error(\"sessionId is required\");\n if (!token) throw new Error(\"token is required\");\n return {\n viewType: \"tui\",\n viewerUrl: buildViewerUrl({\n baseUrl: typeof params?.baseUrl === \"string\" ? params.baseUrl : \"\",\n sessionId,\n token,\n }),\n };\n }\n\n throw new Error(`Unsupported capability \"${capability}\"`);\n}\n"],"mappings":"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","names":[]}