diff --git a/frontend/src/app/dialogs/show-runner-metadata-frame.tsx b/frontend/src/app/dialogs/show-runner-metadata-frame.tsx
new file mode 100644
index 0000000000..ee67590fa7
--- /dev/null
+++ b/frontend/src/app/dialogs/show-runner-metadata-frame.tsx
@@ -0,0 +1,50 @@
+import { useSuspenseQuery } from "@tanstack/react-query";
+import { type DialogContentProps, Frame } from "@/components";
+import { useEngineCompatDataProvider } from "@/components/actors";
+
+interface ShowRunnerMetadataFrameContentProps extends DialogContentProps {
+ runnerId: string;
+}
+
+export default function ShowRunnerMetadataFrameContent({
+ runnerId,
+}: ShowRunnerMetadataFrameContentProps) {
+ const provider = useEngineCompatDataProvider();
+
+ const { data: runners } = useSuspenseQuery({
+ ...provider.runnersQueryOptions(),
+ });
+
+ const runner = runners?.find((r) => r.runnerId === runnerId);
+
+ if (!runner) {
+ return (
+ <>
+
+ Runner Metadata
+
+ Runner not found.
+ >
+ );
+ }
+
+ const metadataJson = runner.metadata
+ ? JSON.stringify(runner.metadata, null, 2)
+ : "{}";
+
+ return (
+ <>
+
+ Runner Metadata
+
+ Metadata for runner: {runner.name}
+
+
+
+
+ {metadataJson}
+
+
+ >
+ );
+}
diff --git a/frontend/src/app/runners-table.tsx b/frontend/src/app/runners-table.tsx
index aca5ec735b..d31f5d465c 100644
--- a/frontend/src/app/runners-table.tsx
+++ b/frontend/src/app/runners-table.tsx
@@ -1,4 +1,5 @@
import {
+ faEllipsisH,
faPlus,
faSignalAlt,
faSignalAlt2,
@@ -8,11 +9,15 @@ import {
} from "@rivet-gg/icons";
import type { Rivet } from "@rivetkit/engine-api-full";
import { useInfiniteQuery } from "@tanstack/react-query";
-import { Link } from "@tanstack/react-router";
+import { Link, useNavigate } from "@tanstack/react-router";
import { formatDistance } from "date-fns";
import {
Button,
DiscreteCopyButton,
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
Skeleton,
Table,
TableBody,
@@ -50,6 +55,7 @@ export function RunnersTable({
Datacenter
Slots
Created
+
@@ -126,6 +132,8 @@ function RowSkeleton() {
}
function Row(runner: Rivet.Runner) {
+ const navigate = useNavigate();
+
return (
@@ -155,6 +163,33 @@ function Row(runner: Rivet.Runner) {
+
+
+
+
+
+
+
+ {
+ navigate({
+ to: ".",
+ search: (prev) => ({
+ ...prev,
+ modal: "show-runner-metadata",
+ runnerId: runner.runnerId,
+ }),
+ });
+ }}
+ >
+ Show metadata
+
+
+
+
);
}
diff --git a/frontend/src/app/use-dialog.tsx b/frontend/src/app/use-dialog.tsx
index 14f84484b7..07b894f14b 100644
--- a/frontend/src/app/use-dialog.tsx
+++ b/frontend/src/app/use-dialog.tsx
@@ -38,6 +38,9 @@ export const useDialog = {
DeleteConfig: createDialogHook(
() => import("@/app/dialogs/confirm-delete-config-frame"),
),
+ ShowRunnerMetadata: createDialogHook(
+ () => import("@/app/dialogs/show-runner-metadata-frame"),
+ ),
Billing: createDialogHook(() => import("@/app/dialogs/billing-frame")),
ProvideEngineCredentials: createDialogHook(
() => import("@/app/dialogs/provide-engine-credentials-frame"),
diff --git a/frontend/src/routes/_context/_cloud.tsx b/frontend/src/routes/_context/_cloud.tsx
index d9779d4d27..d54839cb6b 100644
--- a/frontend/src/routes/_context/_cloud.tsx
+++ b/frontend/src/routes/_context/_cloud.tsx
@@ -47,6 +47,7 @@ function CloudModals() {
const ConnectHetznerDialog = useDialog.ConnectHetzner.Dialog;
const EditProviderConfigDialog = useDialog.EditProviderConfig.Dialog;
const DeleteConfigDialog = useDialog.DeleteConfig.Dialog;
+ const ShowRunnerMetadataDialog = useDialog.ShowRunnerMetadata.Dialog;
return (
<>
@@ -284,6 +285,25 @@ function CloudModals() {
},
}}
/>
+ {
+ if (!value) {
+ navigate({
+ to: ".",
+ search: (old) => ({
+ ...old,
+ modal: undefined,
+ runnerId: undefined,
+ }),
+ });
+ }
+ },
+ }}
+ />
>
);
}
diff --git a/frontend/src/routes/_context/_engine.tsx b/frontend/src/routes/_context/_engine.tsx
index 085a73dd1b..8785102207 100644
--- a/frontend/src/routes/_context/_engine.tsx
+++ b/frontend/src/routes/_context/_engine.tsx
@@ -44,6 +44,7 @@ function EngineModals() {
const ConnectHetznerDialog = useDialog.ConnectHetzner.Dialog;
const EditProviderConfigDialog = useDialog.EditProviderConfig.Dialog;
const DeleteConfigDialog = useDialog.DeleteConfig.Dialog;
+ const ShowRunnerMetadataDialog = useDialog.ShowRunnerMetadata.Dialog;
return (
<>
@@ -265,6 +266,25 @@ function EngineModals() {
},
}}
/>
+ {
+ if (!value) {
+ navigate({
+ to: ".",
+ search: (old) => ({
+ ...old,
+ modal: undefined,
+ runnerId: undefined,
+ }),
+ });
+ }
+ },
+ }}
+ />
>
);
}