diff --git a/console/src/api.ts b/console/src/api.ts
index 2996c7c..0124db3 100644
--- a/console/src/api.ts
+++ b/console/src/api.ts
@@ -4,11 +4,19 @@ const API_BASE_STORAGE_KEY = "wasmeld-api-base";
export type BackendServiceStatus = "running" | "stopped" | "faulted";
+export type BackendCapability = {
+ interface: string;
+ package: string;
+ name: string;
+ version: string;
+};
+
export type BackendService = {
id: string;
revision: string;
artifact: string;
world: string;
+ capabilities: BackendCapability[];
status: BackendServiceStatus;
updated_at_ms: number;
limits: {
diff --git a/console/src/routes/index.tsx b/console/src/routes/index.tsx
index b090ba5..8d11143 100644
--- a/console/src/routes/index.tsx
+++ b/console/src/routes/index.tsx
@@ -31,6 +31,7 @@ import {
} from "lucide-react";
import { FormEvent, ReactNode, useCallback, useEffect, useMemo, useState } from "react";
import {
+ BackendCapability,
BackendEvent,
BackendDeployment,
BackendRuntime,
@@ -66,6 +67,7 @@ type Service = {
deadlineMs: number;
mailbox: number;
maxInputKb: number;
+ capabilities: BackendCapability[];
calls: number;
errors: number;
latencyMs: number | null;
@@ -120,6 +122,7 @@ function toService(service: BackendService, activeRevision?: string): Service {
deadlineMs: service.limits.deadline_ms,
mailbox: service.limits.mailbox_capacity,
maxInputKb: Math.round(service.limits.max_input_bytes / 1024),
+ capabilities: service.capabilities,
calls: service.calls,
errors: service.errors,
latencyMs: service.last_latency_ms,
@@ -977,6 +980,26 @@ function StatusBadge({ status }: { status: ServiceStatus }) {
);
}
+function CapabilitySummary({ capabilities }: { capabilities: BackendCapability[] }) {
+ if (capabilities.length === 0) {
+ return 无;
+ }
+
+ const [first, ...remaining] = capabilities;
+ return (
+ capability.interface).join("\n")}
+ >
+
| 服务 | 状态 | {!compact &&制品 | } + {!compact &&Host 能力 | }调用 | 延迟 |
@@ -1059,6 +1083,11 @@ function ServiceTable({
| {service.artifact} | } + {!compact && ( +
+ |
+ )}
{service.calls.toLocaleString("zh-CN")} |
{service.latencyMs ? `${service.latencyMs} ms` : "—"}
diff --git a/console/src/styles/app.css b/console/src/styles/app.css
index 8f3c764..a0961a7 100644
--- a/console/src/styles/app.css
+++ b/console/src/styles/app.css
@@ -768,6 +768,31 @@ button:disabled {
table-layout: fixed;
}
+.service-table-full {
+ min-width: 980px;
+}
+
+.data-table.service-table-full th:first-child {
+ width: 24%;
+}
+
+.service-table-full th:nth-child(2) {
+ width: 90px;
+}
+
+.service-table-full th:nth-child(3) {
+ width: 150px;
+}
+
+.service-table-full th:nth-child(4) {
+ width: 220px;
+}
+
+.service-table-full th:nth-child(5),
+.service-table-full th:nth-child(6) {
+ width: 72px;
+}
+
.data-table th {
height: 34px;
padding: 0 12px;
@@ -891,6 +916,38 @@ button:disabled {
white-space: nowrap;
}
+.capability-summary {
+ max-width: 100%;
+ height: 23px;
+ padding: 0 7px;
+ display: inline-flex;
+ align-items: center;
+ gap: 5px;
+ color: #315f52;
+ background: #edf5f2;
+ border: 1px solid #d2e4de;
+ border-radius: 4px;
+ font-family: var(--font-geist-mono), monospace;
+ font-size: 8px;
+}
+
+.capability-summary > span {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.capability-summary > small {
+ flex: 0 0 auto;
+ color: var(--ink-faint);
+ font-size: 7px;
+}
+
+.capability-empty {
+ color: var(--ink-faint);
+ font-size: 9px;
+}
+
.numeric-cell {
color: var(--ink-soft);
font-family: var(--font-geist-mono), monospace;
diff --git a/console/tests/rendered-html.test.mjs b/console/tests/rendered-html.test.mjs
index 2614f89..df9cd08 100644
--- a/console/tests/rendered-html.test.mjs
+++ b/console/tests/rendered-html.test.mjs
@@ -97,7 +97,10 @@ test("uses TanStack Start routing and produces Node artifacts", async () => {
assert.match(rootRoute, / |
|---|