From fc2671d7695408e27ab0ed10651844673a273ebd Mon Sep 17 00:00:00 2001 From: jaylfc Date: Wed, 17 Jun 2026 00:24:25 +0100 Subject: [PATCH 1/3] feat(userspace): re-integrate web app-runtime foundation onto dev (#89 P3a) --- desktop/src/apps/SandboxedAppWindow.tsx | 56 ++++++ .../__tests__/SandboxedAppWindow.test.tsx | 47 +++++ .../src/lib/__tests__/userspace-apps.test.ts | 72 +++++++ desktop/src/lib/userspace-apps.ts | 81 ++++++++ desktop/src/registry/app-registry.ts | 2 +- tests/userspace/__init__.py | 0 tests/userspace/conftest.py | 75 +++++++ tests/userspace/test_broker.py | 133 +++++++++++++ tests/userspace/test_broker_route.py | 64 ++++++ tests/userspace/test_data_store.py | 34 ++++ tests/userspace/test_e2e.py | 32 +++ tests/userspace/test_immutability.py | 39 ++++ tests/userspace/test_install_security.py | 43 ++++ tests/userspace/test_package.py | 54 +++++ tests/userspace/test_routes.py | 44 +++++ tests/userspace/test_sdk_route.py | 9 + tests/userspace/test_store.py | 83 ++++++++ tests/userspace/test_update_consent.py | 25 +++ tests/userspace/test_url_guard.py | 26 +++ tinyagentos/app.py | 14 ++ tinyagentos/routes/__init__.py | 5 +- tinyagentos/routes/userspace_apps.py | 186 ++++++++++++++++++ tinyagentos/userspace/__init__.py | 1 + tinyagentos/userspace/broker.py | 116 +++++++++++ tinyagentos/userspace/data_store.py | 80 ++++++++ tinyagentos/userspace/package.py | 74 +++++++ tinyagentos/userspace/sdk/taos-app-sdk.js | 53 +++++ tinyagentos/userspace/store.py | 97 +++++++++ tinyagentos/userspace/url_guard.py | 47 +++++ 29 files changed, 1590 insertions(+), 2 deletions(-) create mode 100644 desktop/src/apps/SandboxedAppWindow.tsx create mode 100644 desktop/src/apps/__tests__/SandboxedAppWindow.test.tsx create mode 100644 desktop/src/lib/__tests__/userspace-apps.test.ts create mode 100644 desktop/src/lib/userspace-apps.ts create mode 100644 tests/userspace/__init__.py create mode 100644 tests/userspace/conftest.py create mode 100644 tests/userspace/test_broker.py create mode 100644 tests/userspace/test_broker_route.py create mode 100644 tests/userspace/test_data_store.py create mode 100644 tests/userspace/test_e2e.py create mode 100644 tests/userspace/test_immutability.py create mode 100644 tests/userspace/test_install_security.py create mode 100644 tests/userspace/test_package.py create mode 100644 tests/userspace/test_routes.py create mode 100644 tests/userspace/test_sdk_route.py create mode 100644 tests/userspace/test_store.py create mode 100644 tests/userspace/test_update_consent.py create mode 100644 tests/userspace/test_url_guard.py create mode 100644 tinyagentos/routes/userspace_apps.py create mode 100644 tinyagentos/userspace/__init__.py create mode 100644 tinyagentos/userspace/broker.py create mode 100644 tinyagentos/userspace/data_store.py create mode 100644 tinyagentos/userspace/package.py create mode 100644 tinyagentos/userspace/sdk/taos-app-sdk.js create mode 100644 tinyagentos/userspace/store.py create mode 100644 tinyagentos/userspace/url_guard.py diff --git a/desktop/src/apps/SandboxedAppWindow.tsx b/desktop/src/apps/SandboxedAppWindow.tsx new file mode 100644 index 000000000..c0f827827 --- /dev/null +++ b/desktop/src/apps/SandboxedAppWindow.tsx @@ -0,0 +1,56 @@ +// desktop/src/apps/SandboxedAppWindow.tsx +import { useEffect, useRef } from "react"; + +interface Props { + windowId: string; + appId: string; +} + +interface BrokerRequest { + taosApp: string; + id: number; + capability: string; + args?: Record; +} + +export function SandboxedAppWindow({ appId }: Props) { + const iframeRef = useRef(null); + + useEffect(() => { + async function onMessage(e: MessageEvent) { + const iframe = iframeRef.current; + // Only handle messages from THIS app's sandboxed iframe. + if (!iframe || e.source !== iframe.contentWindow) return; + const msg = e.data as BrokerRequest; + if (!msg || msg.taosApp !== appId || typeof msg.id !== "number" || !msg.capability) return; + let result: Record; + try { + const res = await fetch(`/api/userspace-apps/${encodeURIComponent(appId)}/broker`, { + method: "POST", + // Carry the taos_session cookie so the broker authenticates the + // caller -- matches every other SPA API call, and stays correct + // under the Vite dev proxy (SPA :5173 -> API :6969). + credentials: "include", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ capability: msg.capability, args: msg.args || {} }), + }); + result = res.ok ? await res.json() : { error: `broker_${res.status}` }; + } catch { + result = { error: "broker_unreachable" }; + } + iframe.contentWindow?.postMessage({ taosAppReply: msg.id, ...result }, "*"); + } + window.addEventListener("message", onMessage); + return () => window.removeEventListener("message", onMessage); + }, [appId]); + + return ( +