import { Component, Show, For, createSignal, createEffect, onCleanup } from "solid-js" import Switch from "@suid/material/Switch" import type { Instance, RawMcpStatus } from "../types/instance" import { fetchLspStatus, updateInstance } from "../stores/instances" import { getLogger } from "../lib/logger" const log = getLogger("session") interface InstanceInfoProps { instance: Instance compact?: boolean } type ParsedMcpStatus = { name: string status: "running" | "stopped" | "error" error?: string } function parseMcpStatus(status: RawMcpStatus): ParsedMcpStatus[] { if (!status || typeof status !== "object") return [] const result: ParsedMcpStatus[] = [] for (const [name, value] of Object.entries(status)) { if (!value || typeof value !== "object") continue const rawStatus = (value as { status?: string }).status if (!rawStatus) continue let mappedStatus: ParsedMcpStatus["status"] if (rawStatus === "connected") { mappedStatus = "running" } else if (rawStatus === "failed") { mappedStatus = "error" } else { mappedStatus = "stopped" } result.push({ name, status: mappedStatus, error: typeof (value as { error?: unknown }).error === "string" ? (value as { error?: string }).error : undefined, }) } return result } const pendingMetadataRequests = new Set() const InstanceInfo: Component = (props) => { const [isLoadingMetadata, setIsLoadingMetadata] = createSignal(true) const [pendingMcpActions, setPendingMcpActions] = createSignal>({}) const metadata = () => props.instance.metadata const binaryVersion = () => props.instance.binaryVersion || metadata()?.version const mcpServers = () => { const status = metadata()?.mcpStatus return status ? parseMcpStatus(status) : [] } const lspServers = () => metadata()?.lspStatus ?? [] const setPendingMcpAction = (name: string, action?: "connect" | "disconnect") => { setPendingMcpActions((prev) => { const next = { ...prev } if (action) { next[name] = action } else { delete next[name] } return next }) } const refreshMcpStatus = async () => { const client = props.instance.client if (!client?.mcp?.status) { return } try { const result = await client.mcp.status() const status = result.data as RawMcpStatus | undefined if (!status) return updateInstance(props.instance.id, { metadata: { ...(props.instance.metadata ?? {}), mcpStatus: status, }, }) } catch (error) { log.error("Failed to refresh MCP status", error) } } const toggleMcpServer = async (serverName: string, shouldEnable: boolean) => { const client = props.instance.client if (!client?.mcp) { return } const action: "connect" | "disconnect" = shouldEnable ? "connect" : "disconnect" setPendingMcpAction(serverName, action) try { if (shouldEnable) { await client.mcp.connect({ path: { name: serverName } }) } else { await client.mcp.disconnect({ path: { name: serverName } }) } await refreshMcpStatus() } catch (error) { log.error("Failed to toggle MCP server", { serverName, action, error }) } finally { setPendingMcpAction(serverName) } } createEffect(() => { const instance = props.instance const instanceId = instance.id const client = instance.client const hasMetadata = Boolean(instance.metadata) if (!client) { setIsLoadingMetadata(false) pendingMetadataRequests.delete(instanceId) return } if (hasMetadata) { setIsLoadingMetadata(false) pendingMetadataRequests.delete(instanceId) return } if (pendingMetadataRequests.has(instanceId)) { setIsLoadingMetadata(true) return } let cancelled = false pendingMetadataRequests.add(instanceId) setIsLoadingMetadata(true) void (async () => { try { const [projectResult, mcpResult, lspResult] = await Promise.allSettled([ client.project.current(), client.mcp.status(), fetchLspStatus(instanceId), ]) if (cancelled) { return } const project = projectResult.status === "fulfilled" ? projectResult.value.data : undefined const mcpStatus = mcpResult.status === "fulfilled" ? (mcpResult.value.data as RawMcpStatus) : undefined const lspStatus = lspResult.status === "fulfilled" ? lspResult.value ?? [] : undefined const nextMetadata = { ...(instance.metadata ?? {}), ...(project ? { project } : {}), ...(mcpStatus ? { mcpStatus } : {}), ...(lspStatus ? { lspStatus } : {}), } if (!nextMetadata.version && instance.binaryVersion) { nextMetadata.version = instance.binaryVersion } updateInstance(instanceId, { metadata: nextMetadata }) } catch (error) { if (!cancelled) { log.error("Failed to load instance metadata", error) } } finally { pendingMetadataRequests.delete(instanceId) if (!cancelled) { setIsLoadingMetadata(false) } } })() onCleanup(() => { cancelled = true }) }) return (

Instance Information

Folder
{props.instance.folder}
{(project) => ( <>
Project
{project().id}
Version Control
{project().vcs}
)}
OpenCode Version
v{binaryVersion()}
Binary Path
{props.instance.binaryPath}
0}>
Environment Variables ({Object.keys(props.instance.environmentVariables!).length})
{([key, value]) => (
{key} {value}
)}
0}>
LSP Servers
{(server) => (
{server.name ?? server.id} {server.root}
{server.status === "connected" ? "Connected" : "Error"}
)}
0}>
MCP Servers
{(server) => { const pendingAction = pendingMcpActions()[server.name] const isPending = Boolean(pendingAction) const isRunning = server.status === "running" const switchDisabled = isPending || !props.instance.client const statusDotClass = () => { if (isPending) { return "status-dot animate-pulse" } if (server.status === "running") { return "status-dot ready animate-pulse" } if (server.status === "error") { return "status-dot error" } return "status-dot stopped" } const statusDotStyle = () => (isPending ? { background: "var(--status-warning)" } : undefined) return (
{server.name}
{ if (switchDisabled) return void toggleMcpServer(server.name, Boolean(checked)) }} />
{(error) => (
{error()}
)}
) }}
Loading...
Server
Port: {props.instance.port}
PID: {props.instance.pid}
Status:
{props.instance.status}
) } export default InstanceInfo