/** * Workspace Services view — the workspace-scope Service reflection as a * standalone rail view. Same Postman-style three-pane layout * (`ScopePanelsScrollspy`) as App Services, plus a left sidebar holding the * workspace picker: a server-side directory browser (`WorkspaceDirBrowser`, * over the App-scope `IHostFolderBrowser`) that marks already-registered * workspaces and registers a picked folder on demand. The proxies resolve on * the `/workspace/:id` route, so a workspace must be selected before any * Service is callable. Picking one materializes its handler on demand * server-side (`IWorkspaceLifecycleService.handlerFor` is create-or-get), no * manual join needed. */ import { IWorkspaceService } from '@moonshot-ai/agent-core-v2/app/workspace/workspace'; import { useQuery } from '@tanstack/react-query'; import { useEffect, useState, type ReactNode } from 'react'; import { useConnection } from '../connection'; import { fetchWorkspaceSnapshot } from '../snapshots/api'; import { Badge, ErrorLine } from '../ui'; import { WorkspaceDirBrowser } from './WorkspaceDirBrowser'; export function WorkspaceServicesView() { const { klient, baseUrl } = useConnection(); const [workspaceId, setWorkspaceId] = useState(null); const workspaces = useQuery({ queryKey: ['workspaces', klient.baseUrl], queryFn: () => klient.core(IWorkspaceService).list(), }); const snapshot = useQuery({ queryKey: ['workspace-snapshot', klient.baseUrl, workspaceId], queryFn: () => fetchWorkspaceSnapshot(klient, workspaceId as string), enabled: workspaceId !== null, refetchInterval: 1_000, }); useEffect(() => { setWorkspaceId(null); }, [baseUrl]); const selected = (workspaces.data ?? []).find((ws) => ws.id === workspaceId); const data = snapshot.data; return (
{workspaceId === null ? (
select a workspace to inspect
) : snapshot.isError ? ( ) : data === undefined ? (
loading workspace snapshot…
) : (
{data.lifecycle}} /> {data.program.status}} /> {data.runtimes.runtimes.length === 0 ? (
no current generations
) : data.runtimes.runtimes.map((runtime) => (
{runtime.runtimeId} {runtime.status}
))}
`${source.source}:${source.count}`).join(', ') || 'none'} /> `${source.sourceId}:${source.profiles.join('|')}`).join(', ') || 'none'} />
)}
); } function SnapshotPanel({ title, children }: { readonly title: string; readonly children: ReactNode }) { return (

{title}

{children}
); } function SnapshotRow({ label, value }: { readonly label: string; readonly value: ReactNode }) { return (
{label} {value}
); }