import { useCallback, useEffect, useState } from "react"; import { getUserSessions, type Session, checkInstanceHealth, } from "../api/sessions"; import { getUserConfig } from "../api/settings"; import { ErrorState, LoadingState } from "../components/data-states"; import { SessionList } from "../components/session-list"; import { SessionCard } from "../components/session-card"; import { useInstanceActions } from "../hooks/use-instance-actions"; import type { InstanceHealth } from "../api/sessions"; type SessionsStatus = "loading" | "ready" | "error"; export const SessionsPage = () => { const [status, setStatus] = useState("loading"); const [sessions, setSessions] = useState([]); const [lastSessionId, setLastSessionId] = useState(null); const [tunnelHealth, setTunnelHealth] = useState< Record >({}); const loadSessions = useCallback(async () => { setStatus("loading"); try { const [sessionsData, config] = await Promise.all([ getUserSessions(), getUserConfig(), ]); setSessions(sessionsData); setLastSessionId(config.last_session_id ?? null); setStatus("ready"); } catch { setStatus("error"); } }, []); useEffect(() => { void loadSessions(); }, [loadSessions]); const { loadingSessionId, dirtyDeleteSession, dirtyDeleteFiles, handleOpen, handleStart, handleStop, handleDelete, handleForceDelete, handleRecreateTunnel, clearDirtyDelete, } = useInstanceActions({ onRefresh: loadSessions }); // Poll health every 30 seconds for active web-enabled instances useEffect(() => { const checkHealth = async () => { const activeSessions = sessions.filter( (s) => ["running", "starting", "unhealthy", "probing"].includes(s.status) && s.tool_type_interfaces?.includes("web"), ); for (const session of activeSessions) { try { const health = await checkInstanceHealth( session.project_id, session.repository_id, session.id, ); setTunnelHealth((prev) => ({ ...prev, [session.id]: health, })); } catch { setTunnelHealth((prev) => ({ ...prev, [session.id]: { healthy: false, container_status: "unknown", container_health: null, tunnel_status: "unreachable", tunnel_status_code: null, probe_status: "unknown", last_probe_output: null, error: "check failed", } as InstanceHealth, })); } } }; void checkHealth(); const interval = setInterval(() => void checkHealth(), 30000); return () => clearInterval(interval); }, [sessions]); const lastSession = sessions.find((s) => s.id === lastSessionId) ?? null; return (

Sessions

{status === "loading" && } {status === "error" && ( void loadSessions()} /> )} {status === "ready" && ( <> {/* Last Session */} {lastSession && (

Last Session

)} {/* Session List */}
{/* Create Session — use floating button */}

Start New Tool

Use the floating button (bottom-right) to start a tool in any workspace.

{/* Dirty Delete Confirmation Modal */} {dirtyDeleteSession && (
e.stopPropagation()} >

Uncommitted Changes

The repository{" "} {dirtyDeleteSession.repository_name} has uncommitted changes. Deleting this session will permanently lose these changes.

Changed files:

    {dirtyDeleteFiles.map((file, idx) => (
  • {file}
  • ))}
)} )}
); };