diff --git a/skyvern-frontend/src/components/BrowserStream.tsx b/skyvern-frontend/src/components/BrowserStream.tsx index a62dafdd..fd4945d0 100644 --- a/skyvern-frontend/src/components/BrowserStream.tsx +++ b/skyvern-frontend/src/components/BrowserStream.tsx @@ -19,6 +19,7 @@ import type { import "./browser-stream.css"; const wssBaseUrl = import.meta.env.VITE_WSS_BASE_URL; +const newWssBaseUrl = wssBaseUrl.replace("/api", ""); interface CommandTakeControl { kind: "take-control"; @@ -31,6 +32,7 @@ interface CommandCedeControl { type Command = CommandTakeControl | CommandCedeControl; type Props = { + browserSessionId?: string; task?: { run: TaskApiResponse; }; @@ -42,6 +44,7 @@ type Props = { }; function BrowserStream({ + browserSessionId = undefined, task = undefined, workflow = undefined, // -- @@ -49,9 +52,13 @@ function BrowserStream({ }: Props) { let showStream: boolean = false; let runId: string; - let entity: "task" | "workflow"; + let entity: "browserSession" | "task" | "workflow"; - if (task) { + if (browserSessionId) { + runId = browserSessionId; + entity = "browserSession"; + showStream = true; + } else if (task) { runId = task.run.task_id; showStream = statusIsNotFinalized(task.run); entity = "task"; @@ -60,7 +67,7 @@ function BrowserStream({ showStream = statusIsNotFinalized(workflow.run); entity = "workflow"; } else { - throw new Error("No task or workflow provided"); + throw new Error("No browser session, task or workflow provided"); } const [commandSocket, setCommandSocket] = useState(null); @@ -141,11 +148,13 @@ function BrowserStream({ const wsParams = await getWebSocketParams(); const vncUrl = - entity === "task" - ? `${wssBaseUrl}/stream/vnc/task/${runId}?${wsParams}` - : entity === "workflow" - ? `${wssBaseUrl}/stream/vnc/workflow_run/${runId}?${wsParams}` - : null; + entity === "browserSession" + ? `${newWssBaseUrl}/stream/vnc/browser_session/${runId}?${wsParams}` + : entity === "task" + ? `${wssBaseUrl}/stream/vnc/task/${runId}?${wsParams}` + : entity === "workflow" + ? `${wssBaseUrl}/stream/vnc/workflow_run/${runId}?${wsParams}` + : null; if (!vncUrl) { throw new Error("No vnc url"); @@ -209,11 +218,13 @@ function BrowserStream({ const wsParams = await getWebSocketParams(); const commandUrl = - entity === "task" - ? `${wssBaseUrl}/stream/commands/task/${runId}?${wsParams}` - : entity === "workflow" - ? `${wssBaseUrl}/stream/commands/workflow_run/${runId}?${wsParams}` - : null; + entity === "browserSession" + ? `${newWssBaseUrl}/stream/commands/browser_session/${runId}?${wsParams}` + : entity === "task" + ? `${wssBaseUrl}/stream/commands/task/${runId}?${wsParams}` + : entity === "workflow" + ? `${wssBaseUrl}/stream/commands/workflow_run/${runId}?${wsParams}` + : null; if (!commandUrl) { throw new Error("No command url"); diff --git a/skyvern-frontend/src/components/browser-stream.css b/skyvern-frontend/src/components/browser-stream.css index f1d47dc6..edefc92d 100644 --- a/skyvern-frontend/src/components/browser-stream.css +++ b/skyvern-frontend/src/components/browser-stream.css @@ -86,6 +86,10 @@ animation: skyvern-anim-fadeIn 1s ease-in forwards; } +.browser-stream > div { + background: transparent !important; +} + @keyframes skyvern-anim-fadeIn { from { opacity: 0; diff --git a/skyvern-frontend/src/router.tsx b/skyvern-frontend/src/router.tsx index 0b5725dc..72edd503 100644 --- a/skyvern-frontend/src/router.tsx +++ b/skyvern-frontend/src/router.tsx @@ -1,4 +1,5 @@ import { Navigate, Outlet, createBrowserRouter } from "react-router-dom"; +import { BrowserSession } from "@/routes/browserSession/BrowserSession"; import { PageLayout } from "./components/PageLayout"; import { DiscoverPage } from "./routes/discover/DiscoverPage"; import { HistoryPage } from "./routes/history/HistoryPage"; @@ -25,6 +26,10 @@ import { WorkflowRunRecording } from "./routes/workflows/workflowRun/WorkflowRun import { DebugStoreProvider } from "@/store/DebugStoreContext"; const router = createBrowserRouter([ + { + path: "browser-session/:browserSessionId", + element: , + }, { path: "/", element: ( diff --git a/skyvern-frontend/src/routes/browserSession/BrowserSession.tsx b/skyvern-frontend/src/routes/browserSession/BrowserSession.tsx new file mode 100644 index 00000000..8641ff14 --- /dev/null +++ b/skyvern-frontend/src/routes/browserSession/BrowserSession.tsx @@ -0,0 +1,60 @@ +import { useState } from "react"; +import { useParams } from "react-router-dom"; +import { BrowserStream } from "@/components/BrowserStream"; +import { getClient } from "@/api/AxiosClient"; + +import { useQuery } from "@tanstack/react-query"; +import { useCredentialGetter } from "@/hooks/useCredentialGetter"; + +function BrowserSession() { + const { browserSessionId } = useParams(); + const [hasBrowserSession, setHasBrowserSession] = useState(false); + + const credentialGetter = useCredentialGetter(); + + const query = useQuery({ + queryKey: ["browserSession", browserSessionId], + queryFn: async () => { + const client = await getClient(credentialGetter, "sans-api-v1"); + + try { + await client.get(`/browser_sessions/${browserSessionId}`); + setHasBrowserSession(true); + } catch (error) { + setHasBrowserSession(false); + } + }, + }); + + if (query.isLoading) { + return ( +
+
+ {/* we need nice artwork here */} + Loading... +
+
+ ); + } + + if (!hasBrowserSession) { + return ( +
+
+ {/* we need nice artwork here */} + No browser session found. +
+
+ ); + } + + return ( +
+
+ +
+
+ ); +} + +export { BrowserSession };