import { createFileRoute } from "@tanstack/react-router"; import { Activity, AlertTriangle, Archive, ArrowRight, Box, Braces, Check, CheckCircle2, CircleStop, Clock3, CloudUpload, Cpu, Download, FileCode2, History, LayoutDashboard, Package, Play, RefreshCw, RotateCcw, Search, Server, Settings, ShieldCheck, SquareTerminal, X, Zap, } from "lucide-react"; import { FormEvent, ReactNode, useCallback, useEffect, useMemo, useState } from "react"; import { BackendEvent, BackendRuntime, BackendService, BackendWitPackage, InvokeResult, RegisterComponentInput, changeRuntimeState, changeServiceState, fetchSnapshot, getApiBase, invokeComponent, publishWitPackage, registerComponent, saveApiBase, witPackageDownloadUrl, } from "../api"; type View = "overview" | "services" | "instances" | "wit-packages" | "activity" | "settings"; type ServiceStatus = "running" | "stopped" | "faulted"; type EventTone = "success" | "warning" | "danger" | "neutral"; type Service = { id: string; revision: string; description: string; artifact: string; status: ServiceStatus; updatedAt: string; memoryMb: number; fuel: string; deadlineMs: number; mailbox: number; maxInputKb: number; calls: number; errors: number; latencyMs: number | null; }; type RuntimeEvent = { id: number; time: string; title: string; detail: string; tone: EventTone; }; type ConnectionState = "connecting" | "online" | "offline"; const NAV_ITEMS: Array<{ id: View; label: string; icon: typeof LayoutDashboard; }> = [ { id: "overview", label: "运行概览", icon: LayoutDashboard }, { id: "services", label: "服务版本", icon: Package }, { id: "instances", label: "运行实例", icon: Server }, { id: "wit-packages", label: "WIT 包", icon: FileCode2 }, { id: "activity", label: "调用记录", icon: History }, { id: "settings", label: "运行设置", icon: Settings }, ]; const STATUS_META: Record = { running: { label: "运行中", className: "status-running" }, stopped: { label: "已停止", className: "status-stopped" }, faulted: { label: "故障", className: "status-faulted" }, }; function serviceKey(service: Pick) { return `${service.id}@${service.revision}`; } function toService(service: BackendService): Service { return { id: service.id, revision: service.revision, description: "Wasm Component", artifact: service.artifact.split(/[\\/]/).pop() ?? service.artifact, status: service.status, updatedAt: new Date(service.updated_at_ms).toLocaleString("zh-CN", { hour12: false, }), memoryMb: Math.round(service.limits.memory_bytes / 1024 / 1024), fuel: compactNumber(service.limits.fuel_per_call), deadlineMs: service.limits.deadline_ms, mailbox: service.limits.mailbox_capacity, maxInputKb: Math.round(service.limits.max_input_bytes / 1024), calls: service.calls, errors: service.errors, latencyMs: service.last_latency_ms, }; } function toEvent(event: BackendEvent): RuntimeEvent { const identity = event.service_id && event.revision ? `${event.service_id}@${event.revision}` : "Wasmeld"; const meta: Record = { registered: { label: "已注册", tone: "success" }, started: { label: "已启动", tone: "success" }, stopped: { label: "已停止", tone: "neutral" }, invoked: { label: "调用完成", tone: "success" }, failed: { label: "操作失败", tone: "danger" }, }; return { id: event.id, time: new Date(event.timestamp_ms).toLocaleTimeString("zh-CN", { hour12: false, }), title: `${identity} ${meta[event.kind].label}`, detail: event.message, tone: meta[event.kind].tone, }; } function compactNumber(value: number) { if (value >= 1_000_000) return `${value / 1_000_000}M`; if (value >= 1_000) return `${value / 1_000}K`; return value.toString(); } function formatDuration(milliseconds: number) { const totalSeconds = Math.floor(milliseconds / 1000); const hours = Math.floor(totalSeconds / 3600); const minutes = Math.floor((totalSeconds % 3600) / 60); const seconds = totalSeconds % 60; return hours > 0 ? `${hours}h ${minutes}m` : `${minutes}m ${seconds}s`; } function formatApiHost(value: string) { try { return new URL(value).host; } catch { return value; } } function parseInvocationInput(value: string, format: "utf8" | "hex") { if (format === "utf8") return new TextEncoder().encode(value); const compact = value.replace(/\s+/g, ""); if (compact.length % 2 !== 0 || /[^0-9a-f]/i.test(compact)) { throw new Error("HEX 输入必须由成对的十六进制字符组成"); } return Uint8Array.from(compact.match(/.{2}/g)?.map((byte) => Number.parseInt(byte, 16)) ?? []); } function invocationInputSize(value: string, format: "utf8" | "hex") { try { return parseInvocationInput(value, format).byteLength; } catch { return 0; } } function formatInvocationOutput(service: Service, output: Uint8Array, format: "utf8" | "hex") { if (service.id === "counter" && output.byteLength === 8) { return new DataView(output.buffer, output.byteOffset, output.byteLength) .getBigUint64(0, true) .toString(); } if (format === "hex") { return Array.from(output, (byte) => byte.toString(16).padStart(2, "0")).join(" "); } return new TextDecoder().decode(output) || "(empty)"; } export const Route = createFileRoute("/")({ component: Home, }); function Home() { const [view, setView] = useState("overview"); const [services, setServices] = useState([]); const [events, setEvents] = useState([]); const [witPackages, setWitPackages] = useState([]); const [runtime, setRuntime] = useState(null); const [runtimeAction, setRuntimeAction] = useState<"start" | "stop" | "restart" | null>(null); const [connection, setConnection] = useState("connecting"); const [apiBase, setApiBase] = useState(getApiBase); const [query, setQuery] = useState(""); const [statusFilter, setStatusFilter] = useState<"all" | ServiceStatus>("all"); const [selectedKey, setSelectedKey] = useState(""); const [deployOpen, setDeployOpen] = useState(false); const [witPublishOpen, setWitPublishOpen] = useState(false); const [invokeKey, setInvokeKey] = useState(null); const [toast, setToast] = useState(null); const selectedService = services.find((service) => serviceKey(service) === selectedKey) ?? services[0] ?? null; const invokeService = services.find((service) => serviceKey(service) === invokeKey) ?? null; const filteredServices = useMemo(() => { const normalized = query.trim().toLowerCase(); return services.filter((service) => { const matchesText = !normalized || service.id.toLowerCase().includes(normalized) || service.description.toLowerCase().includes(normalized) || service.artifact.toLowerCase().includes(normalized); const matchesStatus = statusFilter === "all" || service.status === statusFilter; return matchesText && matchesStatus; }); }, [query, services, statusFilter]); const filteredWitPackages = useMemo(() => { const normalized = query.trim().toLowerCase(); return witPackages.filter( (packageMetadata) => !normalized || packageMetadata.name.toLowerCase().includes(normalized) || packageMetadata.version.toLowerCase().includes(normalized) || packageMetadata.sha256.toLowerCase().includes(normalized), ); }, [query, witPackages]); const running = services.filter((service) => service.status === "running").length; const faulted = services.filter((service) => service.status === "faulted").length; const totalCalls = services.reduce((sum, service) => sum + service.calls, 0); const refreshSnapshot = useCallback(async () => { try { const snapshot = await fetchSnapshot(apiBase); const nextServices = snapshot.services.map(toService); setServices(nextServices); setEvents(snapshot.events.map(toEvent)); setRuntime(snapshot.runtime); setWitPackages(snapshot.witPackages); setSelectedKey((current) => nextServices.some((service) => serviceKey(service) === current) ? current : nextServices[0] ? serviceKey(nextServices[0]) : "", ); setConnection("online"); } catch { setServices([]); setEvents([]); setRuntime(null); setWitPackages([]); setConnection("offline"); } }, [apiBase]); useEffect(() => { void refreshSnapshot(); const interval = window.setInterval(() => void refreshSnapshot(), 5000); return () => window.clearInterval(interval); }, [refreshSnapshot]); function notify(message: string) { setToast(message); window.setTimeout(() => setToast(null), 2600); } async function setServiceStatus(service: Service, status: ServiceStatus) { const action = status === "stopped" ? "stop" : service.status === "running" ? "restart" : "start"; try { await changeServiceState(apiBase, service, action); await refreshSnapshot(); notify( `${service.id} ${ action === "stop" ? "已停止" : action === "restart" ? "已重启" : "已启动" }`, ); } catch (error) { notify(error instanceof Error ? error.message : "运行时操作失败"); } } async function setRuntimeStatus(action: "start" | "stop" | "restart") { setRuntimeAction(action); try { await changeRuntimeState(apiBase, action); await refreshSnapshot(); notify( action === "start" ? "Runtime 已启动" : action === "stop" ? "Runtime 已停止" : "Runtime 已重启", ); } catch (error) { notify(error instanceof Error ? error.message : "Runtime 操作失败"); } finally { setRuntimeAction(null); } } async function runInvocation(service: Service, input: Uint8Array) { const result = await invokeComponent(apiBase, service, input); await refreshSnapshot(); return result; } async function registerService(input: RegisterComponentInput) { const registered = await registerComponent(apiBase, input); await refreshSnapshot(); setSelectedKey(serviceKey(registered)); setView("services"); setDeployOpen(false); notify("组件注册成功"); } async function publishWit(file: File) { const packageMetadata = await publishWitPackage(apiBase, file); await refreshSnapshot(); setWitPublishOpen(false); notify(`${packageMetadata.name}@${packageMetadata.version} 已发布`); } function updateApiBase(value: string) { try { const nextBase = saveApiBase(value); setConnection("connecting"); setApiBase(nextBase); notify("API 地址已保存"); } catch (error) { notify(error instanceof Error ? error.message : "API 地址无效"); } } const title = NAV_ITEMS.find((item) => item.id === view)?.label ?? "运行概览"; return (
运行平台 {title}
{connection === "online" ? "后端在线" : connection === "connecting" ? "连接中" : "后端离线"}
{view === "overview" && ( setSelectedKey(serviceKey(service))} onDeploy={() => setDeployOpen(true)} onInvoke={(service) => setInvokeKey(serviceKey(service))} onStatus={setServiceStatus} onViewAll={() => setView("services")} onRefresh={() => void refreshSnapshot()} onRuntimeAction={(action) => void setRuntimeStatus(action)} runtimeAction={runtimeAction} /> )} {view === "services" && ( setDeployOpen(true)} onInvoke={(service) => setInvokeKey(serviceKey(service))} onSelect={(service) => setSelectedKey(serviceKey(service))} onStatus={setServiceStatus} /> )} {view === "instances" && ( setInvokeKey(serviceKey(service))} onStatus={setServiceStatus} onRefresh={() => void refreshSnapshot()} /> )} {view === "activity" && } {view === "wit-packages" && ( setWitPublishOpen(true)} /> )} {view === "settings" && ( )}
{connection !== "online" ? "等待 Wasmeld" : runtime?.status === "running" ? "Wasmtime sandbox 正常" : "Wasmeld Runtime 已停止"} Component Model · WASI P2 Runtime 0.1.0
{deployOpen && ( setDeployOpen(false)} onRegister={registerService} /> )} {witPublishOpen && ( setWitPublishOpen(false)} onPublish={publishWit} /> )} {invokeService && ( setInvokeKey(null)} onInvoke={runInvocation} /> )} {toast && ( {toast} )}
); } function Sidebar({ view, onChange, running, connection, apiBase, uptimeMs, runtimeStatus, }: { view: View; onChange: (view: View) => void; running: number; connection: ConnectionState; apiBase: string; uptimeMs: number | null; runtimeStatus: BackendRuntime["status"] | null; }) { const runtimeLabel = connection !== "online" ? "离线" : runtimeStatus === "running" ? "运行中" : runtimeStatus === "stopped" ? "已停止" : "连接中"; const runtimeHealthClass = connection !== "online" ? "health-pulse health-offline" : runtimeStatus === "running" ? "health-pulse" : "health-pulse health-stopped"; return ( ); } function PageHeading({ eyebrow, title, description, actions, }: { eyebrow: string; title: string; description: string; actions?: ReactNode; }) { return (
{eyebrow}

{title}

{description}

{actions &&
{actions}
}
); } function Overview({ services, filteredServices, events, running, faulted, totalCalls, runtime, selectedService, onSelect, onDeploy, onInvoke, onStatus, onViewAll, onRefresh, onRuntimeAction, runtimeAction, }: { services: Service[]; filteredServices: Service[]; events: RuntimeEvent[]; running: number; faulted: number; totalCalls: number; runtime: BackendRuntime | null; selectedService: Service | null; onSelect: (service: Service) => void; onDeploy: () => void; onInvoke: (service: Service) => void; onStatus: (service: Service, status: ServiceStatus) => void; onViewAll: () => void; onRefresh: () => void; onRuntimeAction: (action: "start" | "stop" | "restart") => void; runtimeAction: "start" | "stop" | "restart" | null; }) { const errorCount = services.reduce((sum, service) => sum + service.errors, 0); const latencies = services .map((service) => service.latencyMs) .filter((latency): latency is number => latency !== null); const latestLatency = latencies.length ? Math.max(...latencies) : null; return ( <> } />
} label="服务版本" value={services.length.toString()} note="来自 Wasmeld" tone="cyan" /> } label="运行实例" value={running.toString()} note={`${services.length - running} 个未运行`} tone="green" /> } label="累计调用" value={totalCalls.toLocaleString("zh-CN")} note="持久化统计" tone="amber" /> } label="异常" value={(faulted + errorCount).toString()} note={faulted + errorCount ? "需要检查" : "无异常"} tone="coral" />
Wasmeld Runtime {runtime?.status === "running" ? "Engine 已启用 fuel 与 epoch interruption" : "Engine 与所有 Actor 已释放"}
{runtime?.status === "stopped" ? ( ) : ( <> )}

服务状态

已注册版本及当前 Actor 状态

{selectedService && (
当前选中 {selectedService.id} @{selectedService.revision}
)} ); } function Metric({ icon, label, value, note, tone, }: { icon: ReactNode; label: string; value: string; note: string; tone: string; }) { return (
{icon}
{label} {value}
{note}
); } function RuntimeStat({ label, value, detail }: { label: string; value: string; detail: string }) { return (
{label} {value} {detail}
); } function DetailValue({ label, value }: { label: string; value: string }) { return (
{label} {value}
); } function StatusBadge({ status }: { status: ServiceStatus }) { const meta = STATUS_META[status]; return ( {meta.label} ); } function ServiceTable({ services, selectedKey, onSelect, onInvoke, onStatus, compact = false, }: { services: Service[]; selectedKey?: string; onSelect: (service: Service) => void; onInvoke: (service: Service) => void; onStatus: (service: Service, status: ServiceStatus) => void; compact?: boolean; }) { if (services.length === 0) { return (
没有匹配的服务 调整搜索关键词或状态筛选。
); } return (
{!compact && } {services.map((service) => ( {!compact && } ))}
服务 状态制品调用 延迟 操作
{service.artifact}{service.calls.toLocaleString("zh-CN")} {service.latencyMs ? `${service.latencyMs} ms` : "—"}
{service.status === "running" ? ( <> ) : ( )}
); } function EventList({ events }: { events: RuntimeEvent[] }) { const icons: Record = { success: , warning: , danger: , neutral: , }; return (
{events.map((event) => (
{icons[event.tone]}
{event.title} {event.detail}
))}
); } function ServicesView({ services, statusFilter, onFilter, onDeploy, onInvoke, onSelect, onStatus, }: { services: Service[]; statusFilter: "all" | ServiceStatus; onFilter: (status: "all" | ServiceStatus) => void; onDeploy: () => void; onInvoke: (service: Service) => void; onSelect: (service: Service) => void; onStatus: (service: Service, status: ServiceStatus) => void; }) { return ( <> 注册组件 } />
{[ ["all", "全部"], ["running", "运行中"], ["stopped", "已停止"], ["faulted", "故障"], ].map(([value, label]) => ( ))}
{services.length} 个版本
); } function WitPackagesView({ apiBase, packages, onPublish, }: { apiBase: string; packages: BackendWitPackage[]; onPublish: () => void; }) { function downloadPackage(packageMetadata: BackendWitPackage) { const anchor = document.createElement("a"); anchor.href = witPackageDownloadUrl(apiBase, packageMetadata); anchor.download = `${packageMetadata.name.replace(":", "-")}-${packageMetadata.version}.wasm`; document.body.append(anchor); anchor.click(); anchor.remove(); } return ( <> 发布 WIT 包 } />
发布后不可覆盖;包名、版本与依赖由制品自动解析。 {packages.length} 个包版本
{packages.length === 0 ? (
没有匹配的 WIT 包 发布二进制 WIT Package,或调整搜索关键词。
) : (
{packages.map((packageMetadata) => { const dependencies = packageMetadata.dependencies .map((dependency) => `${dependency.name}@${dependency.version}`) .join(", "); return ( ); })}
Package 依赖 SHA-256 操作
{packageMetadata.name} {packageMetadata.version}
{dependencies || "无直接依赖"} {packageMetadata.sha256}
)}
); } function InstancesView({ services, onInvoke, onStatus, onRefresh, }: { services: Service[]; onInvoke: (service: Service) => void; onStatus: (service: Service, status: ServiceStatus) => void; onRefresh: () => void; }) { const instances = services.filter( (service) => service.status === "running" || service.status === "faulted", ); return ( <> } />
{instances.length === 0 ? (
暂无运行实例 0 ACTIVE ACTORS
) : ( instances.map((service) => (
{service.id}@{service.revision} actor/{service.id}-01
{service.updatedAt}
)) )}
); } function ActivityView({ events, services }: { events: RuntimeEvent[]; services: Service[] }) { const calls = services.reduce((sum, service) => sum + service.calls, 0); const errors = services.reduce((sum, service) => sum + service.errors, 0); const latencies = services .map((service) => service.latencyMs) .filter((latency): latency is number => latency !== null); const averageLatency = latencies.length ? latencies.reduce((sum, latency) => sum + latency, 0) / latencies.length : null; function exportEvents() { const blob = new Blob([JSON.stringify(events, null, 2)], { type: "application/json", }); const url = URL.createObjectURL(blob); const anchor = document.createElement("a"); anchor.href = url; anchor.download = "wasmeld-events.json"; anchor.click(); URL.revokeObjectURL(url); } return ( <> 导出记录 } />
时间范围 当前进程
成功率 {calls ? `${(((calls - errors) / calls) * 100).toFixed(2)}%` : "—"}
平均延迟 {averageLatency === null ? "—" : `${averageLatency.toFixed(1)} ms`}
记录数 {events.length}
); } function SettingsView({ endpoint, onSaveEndpoint, }: { endpoint: string; onSaveEndpoint: (value: string) => void; }) { const [value, setValue] = useState(endpoint); return ( <>
{ event.preventDefault(); onSaveEndpoint(value); }} >

控制端连接

管理 API 的本地访问地址。

setValue(event.target.value)} /> 连接状态将在顶部工具栏显示。
); } function DialogFrame({ title, description, icon, onClose, children, }: { title: string; description: string; icon: ReactNode; onClose: () => void; children: ReactNode; }) { return (
{ if (event.key === "Escape") onClose(); }} >
{icon}

{title}

{description}

{children}
); } function DeployDialog({ onClose, onRegister, }: { onClose: () => void; onRegister: (input: RegisterComponentInput) => Promise; }) { const [file, setFile] = useState(null); const [submitting, setSubmitting] = useState(false); const [error, setError] = useState(""); const fileName = file?.name ?? ""; async function submit(event: FormEvent) { event.preventDefault(); if (!file) return; if (!file.name.toLowerCase().endsWith(".wasmpkg")) { setError("请选择 .wasmpkg 组件包"); return; } setSubmitting(true); setError(""); try { await onRegister({ file }); } catch (submitError) { setError(submitError instanceof Error ? submitError.message : "组件注册失败"); setSubmitting(false); } } return ( } onClose={onClose} >
{error &&
{error}
}
); } function WitPackageDialog({ onClose, onPublish, }: { onClose: () => void; onPublish: (file: File) => Promise; }) { const [file, setFile] = useState(null); const [submitting, setSubmitting] = useState(false); const [error, setError] = useState(""); const fileName = file?.name ?? ""; async function submit(event: FormEvent) { event.preventDefault(); if (!file) return; if (!file.name.toLowerCase().endsWith(".wasm")) { setError("请选择由 wasmeld wit build 生成的 .wasm 文件"); return; } if (file.size > 4 * 1024 * 1024) { setError("WIT 包不能超过 4 MB"); return; } setSubmitting(true); setError(""); try { await onPublish(file); } catch (submitError) { setError(submitError instanceof Error ? submitError.message : "WIT 包发布失败"); setSubmitting(false); } } return ( } onClose={onClose} >
{error &&
{error}
}
); } function InvokeDialog({ service, onClose, onInvoke, }: { service: Service; onClose: () => void; onInvoke: (service: Service, input: Uint8Array) => Promise; }) { const [input, setInput] = useState(service.id === "echo" ? "hello wasm" : ""); const [format, setFormat] = useState<"utf8" | "hex">("utf8"); const [output, setOutput] = useState(""); const [running, setRunning] = useState(false); const [failed, setFailed] = useState(false); const [latency, setLatency] = useState(null); async function invoke() { setRunning(true); setOutput(""); setFailed(false); setLatency(null); try { const result = await onInvoke(service, parseInvocationInput(input, format)); setOutput(formatInvocationOutput(service, result.output, format)); setLatency(result.latencyMs); } catch (invokeError) { setFailed(true); setOutput(invokeError instanceof Error ? invokeError.message : "组件调用失败"); } finally { setRunning(false); } } return ( } onClose={onClose} >
{invocationInputSize(input, format)} B