@runloop/rl-cli 1.7.1 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/README.md +19 -5
  2. package/dist/cli.js +0 -0
  3. package/dist/commands/blueprint/delete.js +21 -0
  4. package/dist/commands/blueprint/list.js +226 -174
  5. package/dist/commands/blueprint/prune.js +13 -28
  6. package/dist/commands/devbox/create.js +41 -0
  7. package/dist/commands/devbox/list.js +125 -109
  8. package/dist/commands/devbox/tunnel.js +4 -19
  9. package/dist/commands/gateway-config/create.js +44 -0
  10. package/dist/commands/gateway-config/delete.js +21 -0
  11. package/dist/commands/gateway-config/get.js +15 -0
  12. package/dist/commands/gateway-config/list.js +493 -0
  13. package/dist/commands/gateway-config/update.js +60 -0
  14. package/dist/commands/menu.js +2 -1
  15. package/dist/commands/secret/list.js +379 -4
  16. package/dist/commands/snapshot/list.js +11 -2
  17. package/dist/commands/snapshot/prune.js +265 -0
  18. package/dist/components/BenchmarkMenu.js +108 -0
  19. package/dist/components/DetailedInfoView.js +20 -0
  20. package/dist/components/DevboxActionsMenu.js +9 -61
  21. package/dist/components/DevboxCreatePage.js +531 -14
  22. package/dist/components/DevboxDetailPage.js +27 -22
  23. package/dist/components/GatewayConfigCreatePage.js +265 -0
  24. package/dist/components/LogsViewer.js +6 -40
  25. package/dist/components/MainMenu.js +63 -22
  26. package/dist/components/ResourceDetailPage.js +143 -160
  27. package/dist/components/ResourceListView.js +3 -33
  28. package/dist/components/ResourcePicker.js +220 -0
  29. package/dist/components/SecretCreatePage.js +183 -0
  30. package/dist/components/SettingsMenu.js +95 -0
  31. package/dist/components/StateHistory.js +1 -20
  32. package/dist/components/StatusBadge.js +80 -0
  33. package/dist/components/StreamingLogsViewer.js +8 -42
  34. package/dist/components/form/FormTextInput.js +4 -2
  35. package/dist/components/resourceDetailTypes.js +18 -0
  36. package/dist/hooks/useInputHandler.js +103 -0
  37. package/dist/router/Router.js +99 -2
  38. package/dist/screens/BenchmarkDetailScreen.js +163 -0
  39. package/dist/screens/BenchmarkJobCreateScreen.js +524 -0
  40. package/dist/screens/BenchmarkJobDetailScreen.js +614 -0
  41. package/dist/screens/BenchmarkJobListScreen.js +479 -0
  42. package/dist/screens/BenchmarkListScreen.js +266 -0
  43. package/dist/screens/BenchmarkMenuScreen.js +29 -0
  44. package/dist/screens/BenchmarkRunDetailScreen.js +425 -0
  45. package/dist/screens/BenchmarkRunListScreen.js +275 -0
  46. package/dist/screens/BlueprintDetailScreen.js +5 -1
  47. package/dist/screens/DevboxCreateScreen.js +2 -2
  48. package/dist/screens/GatewayConfigDetailScreen.js +236 -0
  49. package/dist/screens/GatewayConfigListScreen.js +7 -0
  50. package/dist/screens/MenuScreen.js +5 -2
  51. package/dist/screens/ScenarioRunDetailScreen.js +226 -0
  52. package/dist/screens/ScenarioRunListScreen.js +245 -0
  53. package/dist/screens/SecretCreateScreen.js +7 -0
  54. package/dist/screens/SecretDetailScreen.js +198 -0
  55. package/dist/screens/SecretListScreen.js +7 -0
  56. package/dist/screens/SettingsMenuScreen.js +26 -0
  57. package/dist/screens/SnapshotDetailScreen.js +6 -0
  58. package/dist/services/agentService.js +42 -0
  59. package/dist/services/benchmarkJobService.js +122 -0
  60. package/dist/services/benchmarkService.js +120 -0
  61. package/dist/services/gatewayConfigService.js +114 -0
  62. package/dist/services/scenarioService.js +34 -0
  63. package/dist/store/benchmarkJobStore.js +66 -0
  64. package/dist/store/benchmarkStore.js +183 -0
  65. package/dist/store/betaFeatureStore.js +47 -0
  66. package/dist/store/gatewayConfigStore.js +83 -0
  67. package/dist/store/index.js +1 -0
  68. package/dist/utils/browser.js +22 -0
  69. package/dist/utils/clipboard.js +41 -0
  70. package/dist/utils/commands.js +80 -0
  71. package/dist/utils/config.js +8 -0
  72. package/dist/utils/time.js +121 -0
  73. package/package.json +42 -43
@@ -1,16 +1,71 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
2
  /**
3
3
  * Router - Manages screen navigation with clean mount/unmount lifecycle
4
4
  * Replaces conditional rendering pattern from menu.tsx
5
5
  */
6
6
  import React from "react";
7
+ import { Box, Text, useInput } from "ink";
8
+ import figures from "figures";
7
9
  import { useNavigation } from "../store/navigationStore.js";
8
10
  import { useDevboxStore } from "../store/devboxStore.js";
9
11
  import { useBlueprintStore } from "../store/blueprintStore.js";
10
12
  import { useSnapshotStore } from "../store/snapshotStore.js";
11
13
  import { useNetworkPolicyStore } from "../store/networkPolicyStore.js";
14
+ import { useGatewayConfigStore } from "../store/gatewayConfigStore.js";
12
15
  import { useObjectStore } from "../store/objectStore.js";
16
+ import { useBenchmarkStore } from "../store/benchmarkStore.js";
17
+ import { useBenchmarkJobStore } from "../store/benchmarkJobStore.js";
13
18
  import { ErrorBoundary } from "../components/ErrorBoundary.js";
19
+ import { colors } from "../utils/theme.js";
20
+ // List of all known screens
21
+ const KNOWN_SCREENS = new Set([
22
+ "menu",
23
+ "settings-menu",
24
+ "devbox-list",
25
+ "devbox-detail",
26
+ "devbox-actions",
27
+ "devbox-exec",
28
+ "devbox-create",
29
+ "blueprint-list",
30
+ "blueprint-detail",
31
+ "blueprint-logs",
32
+ "snapshot-list",
33
+ "snapshot-detail",
34
+ "network-policy-list",
35
+ "network-policy-detail",
36
+ "network-policy-create",
37
+ "gateway-config-list",
38
+ "gateway-config-detail",
39
+ "gateway-config-create",
40
+ "secret-list",
41
+ "secret-detail",
42
+ "secret-create",
43
+ "object-list",
44
+ "object-detail",
45
+ "ssh-session",
46
+ "benchmark-menu",
47
+ "benchmark-list",
48
+ "benchmark-detail",
49
+ "benchmark-run-list",
50
+ "benchmark-run-detail",
51
+ "scenario-run-list",
52
+ "scenario-run-detail",
53
+ "benchmark-job-list",
54
+ "benchmark-job-detail",
55
+ "benchmark-job-create",
56
+ ]);
57
+ /**
58
+ * Fallback screen for unknown routes
59
+ */
60
+ function UnknownScreen({ screenName }) {
61
+ const { navigate } = useNavigation();
62
+ useInput((input, key) => {
63
+ if (key.return) {
64
+ navigate("menu");
65
+ }
66
+ });
67
+ return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Box, { marginBottom: 1, children: _jsxs(Text, { color: colors.warning, bold: true, children: [figures.warning, " Unknown Page"] }) }), _jsx(Box, { marginBottom: 1, children: _jsxs(Text, { color: colors.textDim, children: ["You've navigated to an unknown page: ", _jsx(Text, { color: colors.error, children: `"${screenName}"` })] }) }), _jsx(Box, { children: _jsxs(Text, { color: colors.textDim, children: ["Press", " ", _jsx(Text, { color: colors.primary, bold: true, children: "Enter" }), " ", "to return to the main menu"] }) })] }));
68
+ }
14
69
  // Import screen components
15
70
  import { MenuScreen } from "../screens/MenuScreen.js";
16
71
  import { DevboxListScreen } from "../screens/DevboxListScreen.js";
@@ -26,9 +81,25 @@ import { SnapshotDetailScreen } from "../screens/SnapshotDetailScreen.js";
26
81
  import { NetworkPolicyListScreen } from "../screens/NetworkPolicyListScreen.js";
27
82
  import { NetworkPolicyDetailScreen } from "../screens/NetworkPolicyDetailScreen.js";
28
83
  import { NetworkPolicyCreateScreen } from "../screens/NetworkPolicyCreateScreen.js";
84
+ import { GatewayConfigListScreen } from "../screens/GatewayConfigListScreen.js";
85
+ import { GatewayConfigDetailScreen } from "../screens/GatewayConfigDetailScreen.js";
86
+ import { SettingsMenuScreen } from "../screens/SettingsMenuScreen.js";
87
+ import { SecretListScreen } from "../screens/SecretListScreen.js";
88
+ import { SecretDetailScreen } from "../screens/SecretDetailScreen.js";
89
+ import { SecretCreateScreen } from "../screens/SecretCreateScreen.js";
29
90
  import { ObjectListScreen } from "../screens/ObjectListScreen.js";
30
91
  import { ObjectDetailScreen } from "../screens/ObjectDetailScreen.js";
31
92
  import { SSHSessionScreen } from "../screens/SSHSessionScreen.js";
93
+ import { BenchmarkMenuScreen } from "../screens/BenchmarkMenuScreen.js";
94
+ import { BenchmarkListScreen } from "../screens/BenchmarkListScreen.js";
95
+ import { BenchmarkDetailScreen } from "../screens/BenchmarkDetailScreen.js";
96
+ import { BenchmarkRunListScreen } from "../screens/BenchmarkRunListScreen.js";
97
+ import { BenchmarkRunDetailScreen } from "../screens/BenchmarkRunDetailScreen.js";
98
+ import { ScenarioRunListScreen } from "../screens/ScenarioRunListScreen.js";
99
+ import { ScenarioRunDetailScreen } from "../screens/ScenarioRunDetailScreen.js";
100
+ import { BenchmarkJobListScreen } from "../screens/BenchmarkJobListScreen.js";
101
+ import { BenchmarkJobDetailScreen } from "../screens/BenchmarkJobDetailScreen.js";
102
+ import { BenchmarkJobCreateScreen } from "../screens/BenchmarkJobCreateScreen.js";
32
103
  /**
33
104
  * Router component that renders the current screen
34
105
  * Implements memory cleanup on route changes
@@ -76,12 +147,38 @@ export function Router() {
76
147
  useNetworkPolicyStore.getState().clearAll();
77
148
  }
78
149
  break;
150
+ case "gateway-config-list":
151
+ case "gateway-config-detail":
152
+ case "gateway-config-create":
153
+ if (!currentScreen.startsWith("gateway-config")) {
154
+ useGatewayConfigStore.getState().clearAll();
155
+ }
156
+ break;
79
157
  case "object-list":
80
158
  case "object-detail":
81
159
  if (!currentScreen.startsWith("object")) {
82
160
  useObjectStore.getState().clearAll();
83
161
  }
84
162
  break;
163
+ case "benchmark-menu":
164
+ case "benchmark-list":
165
+ case "benchmark-detail":
166
+ case "benchmark-run-list":
167
+ case "benchmark-run-detail":
168
+ case "scenario-run-list":
169
+ case "scenario-run-detail":
170
+ if (!currentScreen.startsWith("benchmark") &&
171
+ !currentScreen.startsWith("scenario")) {
172
+ useBenchmarkStore.getState().clearAll();
173
+ }
174
+ break;
175
+ case "benchmark-job-list":
176
+ case "benchmark-job-detail":
177
+ case "benchmark-job-create":
178
+ if (!currentScreen.startsWith("benchmark-job")) {
179
+ useBenchmarkJobStore.getState().clearAll();
180
+ }
181
+ break;
85
182
  }
86
183
  }
87
184
  prevScreenRef.current = currentScreen;
@@ -90,5 +187,5 @@ export function Router() {
90
187
  // and mount new component, preventing race conditions during screen transitions.
91
188
  // The key ensures React treats this as a completely new component tree.
92
189
  // Wrap in ErrorBoundary to catch any Yoga WASM errors gracefully.
93
- return (_jsxs(ErrorBoundary, { children: [currentScreen === "menu" && (_jsx(MenuScreen, { ...params }, currentScreen)), currentScreen === "devbox-list" && (_jsx(DevboxListScreen, { ...params }, currentScreen)), currentScreen === "devbox-detail" && (_jsx(DevboxDetailScreen, { ...params }, currentScreen)), currentScreen === "devbox-actions" && (_jsx(DevboxActionsScreen, { ...params }, currentScreen)), currentScreen === "devbox-exec" && (_jsx(DevboxExecScreen, { ...params }, currentScreen)), currentScreen === "devbox-create" && (_jsx(DevboxCreateScreen, { ...params }, currentScreen)), currentScreen === "blueprint-list" && (_jsx(BlueprintListScreen, { ...params }, currentScreen)), currentScreen === "blueprint-detail" && (_jsx(BlueprintDetailScreen, { ...params }, currentScreen)), currentScreen === "blueprint-logs" && (_jsx(BlueprintLogsScreen, { ...params }, currentScreen)), currentScreen === "snapshot-list" && (_jsx(SnapshotListScreen, { ...params }, currentScreen)), currentScreen === "snapshot-detail" && (_jsx(SnapshotDetailScreen, { ...params }, currentScreen)), currentScreen === "network-policy-list" && (_jsx(NetworkPolicyListScreen, { ...params }, currentScreen)), currentScreen === "network-policy-detail" && (_jsx(NetworkPolicyDetailScreen, { ...params }, currentScreen)), currentScreen === "network-policy-create" && (_jsx(NetworkPolicyCreateScreen, { ...params }, currentScreen)), currentScreen === "object-list" && (_jsx(ObjectListScreen, { ...params }, currentScreen)), currentScreen === "object-detail" && (_jsx(ObjectDetailScreen, { ...params }, currentScreen)), currentScreen === "ssh-session" && (_jsx(SSHSessionScreen, { ...params }, currentScreen))] }, `boundary-${currentScreen}`));
190
+ return (_jsxs(ErrorBoundary, { children: [currentScreen === "menu" && (_jsx(MenuScreen, { ...params }, currentScreen)), currentScreen === "settings-menu" && (_jsx(SettingsMenuScreen, { ...params }, currentScreen)), currentScreen === "devbox-list" && (_jsx(DevboxListScreen, { ...params }, currentScreen)), currentScreen === "devbox-detail" && (_jsx(DevboxDetailScreen, { ...params }, currentScreen)), currentScreen === "devbox-actions" && (_jsx(DevboxActionsScreen, { ...params }, currentScreen)), currentScreen === "devbox-exec" && (_jsx(DevboxExecScreen, { ...params }, currentScreen)), currentScreen === "devbox-create" && (_jsx(DevboxCreateScreen, { ...params }, currentScreen)), currentScreen === "blueprint-list" && (_jsx(BlueprintListScreen, { ...params }, currentScreen)), currentScreen === "blueprint-detail" && (_jsx(BlueprintDetailScreen, { ...params }, currentScreen)), currentScreen === "blueprint-logs" && (_jsx(BlueprintLogsScreen, { ...params }, currentScreen)), currentScreen === "snapshot-list" && (_jsx(SnapshotListScreen, { ...params }, currentScreen)), currentScreen === "snapshot-detail" && (_jsx(SnapshotDetailScreen, { ...params }, currentScreen)), currentScreen === "network-policy-list" && (_jsx(NetworkPolicyListScreen, { ...params }, currentScreen)), currentScreen === "network-policy-detail" && (_jsx(NetworkPolicyDetailScreen, { ...params }, currentScreen)), currentScreen === "network-policy-create" && (_jsx(NetworkPolicyCreateScreen, { ...params }, currentScreen)), currentScreen === "gateway-config-list" && (_jsx(GatewayConfigListScreen, { ...params }, currentScreen)), currentScreen === "gateway-config-detail" && (_jsx(GatewayConfigDetailScreen, { ...params }, currentScreen)), currentScreen === "secret-list" && (_jsx(SecretListScreen, { ...params }, currentScreen)), currentScreen === "secret-detail" && (_jsx(SecretDetailScreen, { ...params }, currentScreen)), currentScreen === "secret-create" && (_jsx(SecretCreateScreen, { ...params }, currentScreen)), currentScreen === "object-list" && (_jsx(ObjectListScreen, { ...params }, currentScreen)), currentScreen === "object-detail" && (_jsx(ObjectDetailScreen, { ...params }, currentScreen)), currentScreen === "ssh-session" && (_jsx(SSHSessionScreen, { ...params }, currentScreen)), currentScreen === "benchmark-menu" && (_jsx(BenchmarkMenuScreen, { ...params }, currentScreen)), currentScreen === "benchmark-list" && (_jsx(BenchmarkListScreen, { ...params }, currentScreen)), currentScreen === "benchmark-detail" && (_jsx(BenchmarkDetailScreen, { ...params }, currentScreen)), currentScreen === "benchmark-run-list" && (_jsx(BenchmarkRunListScreen, { ...params }, currentScreen)), currentScreen === "benchmark-run-detail" && (_jsx(BenchmarkRunDetailScreen, { ...params }, currentScreen)), currentScreen === "scenario-run-list" && (_jsx(ScenarioRunListScreen, { ...params }, currentScreen)), currentScreen === "scenario-run-detail" && (_jsx(ScenarioRunDetailScreen, { ...params }, currentScreen)), currentScreen === "benchmark-job-list" && (_jsx(BenchmarkJobListScreen, { ...params }, currentScreen)), currentScreen === "benchmark-job-detail" && (_jsx(BenchmarkJobDetailScreen, { ...params }, currentScreen)), currentScreen === "benchmark-job-create" && (_jsx(BenchmarkJobCreateScreen, { ...params }, currentScreen)), !KNOWN_SCREENS.has(currentScreen) && (_jsx(UnknownScreen, { screenName: currentScreen }, currentScreen))] }, `boundary-${currentScreen}`));
94
191
  }
@@ -0,0 +1,163 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * BenchmarkDetailScreen - Detail page for benchmark definitions
4
+ * Uses the generic ResourceDetailPage component
5
+ */
6
+ import React from "react";
7
+ import { Text } from "ink";
8
+ import figures from "figures";
9
+ import { useNavigation } from "../store/navigationStore.js";
10
+ import { useBenchmarkStore } from "../store/benchmarkStore.js";
11
+ import { ResourceDetailPage, formatTimestamp, } from "../components/ResourceDetailPage.js";
12
+ import { getBenchmark } from "../services/benchmarkService.js";
13
+ import { SpinnerComponent } from "../components/Spinner.js";
14
+ import { ErrorMessage } from "../components/ErrorMessage.js";
15
+ import { Breadcrumb } from "../components/Breadcrumb.js";
16
+ import { colors } from "../utils/theme.js";
17
+ export function BenchmarkDetailScreen({ benchmarkId, }) {
18
+ const { goBack, navigate } = useNavigation();
19
+ const benchmarks = useBenchmarkStore((state) => state.benchmarks);
20
+ const [loading, setLoading] = React.useState(false);
21
+ const [error, setError] = React.useState(null);
22
+ const [fetchedBenchmark, setFetchedBenchmark] = React.useState(null);
23
+ // Find benchmark in store first
24
+ const benchmarkFromStore = benchmarks.find((b) => b.id === benchmarkId);
25
+ // Polling function
26
+ const pollBenchmark = React.useCallback(async () => {
27
+ if (!benchmarkId)
28
+ return null;
29
+ return getBenchmark(benchmarkId);
30
+ }, [benchmarkId]);
31
+ // Fetch benchmark from API if not in store
32
+ React.useEffect(() => {
33
+ if (benchmarkId && !loading && !fetchedBenchmark && !benchmarkFromStore) {
34
+ setLoading(true);
35
+ setError(null);
36
+ getBenchmark(benchmarkId)
37
+ .then((benchmark) => {
38
+ setFetchedBenchmark(benchmark);
39
+ setLoading(false);
40
+ })
41
+ .catch((err) => {
42
+ setError(err);
43
+ setLoading(false);
44
+ });
45
+ }
46
+ }, [benchmarkId, loading, fetchedBenchmark, benchmarkFromStore]);
47
+ // Use fetched benchmark for full details, fall back to store for basic display
48
+ const benchmark = fetchedBenchmark || benchmarkFromStore;
49
+ // Show loading state
50
+ if (!benchmark && benchmarkId && !error) {
51
+ return (_jsxs(_Fragment, { children: [_jsx(Breadcrumb, { items: [
52
+ { label: "Home" },
53
+ { label: "Benchmarks" },
54
+ { label: "Benchmark Definitions" },
55
+ { label: "Loading...", active: true },
56
+ ] }), _jsx(SpinnerComponent, { message: "Loading benchmark details..." })] }));
57
+ }
58
+ // Show error state
59
+ if (error && !benchmark) {
60
+ return (_jsxs(_Fragment, { children: [_jsx(Breadcrumb, { items: [
61
+ { label: "Home" },
62
+ { label: "Benchmarks" },
63
+ { label: "Benchmark Definitions" },
64
+ { label: "Error", active: true },
65
+ ] }), _jsx(ErrorMessage, { message: "Failed to load benchmark details", error: error })] }));
66
+ }
67
+ // Show not found error
68
+ if (!benchmark) {
69
+ return (_jsxs(_Fragment, { children: [_jsx(Breadcrumb, { items: [
70
+ { label: "Home" },
71
+ { label: "Benchmarks" },
72
+ { label: "Benchmark Definitions" },
73
+ { label: "Not Found", active: true },
74
+ ] }), _jsx(ErrorMessage, { message: `Benchmark ${benchmarkId || "unknown"} not found`, error: new Error("Benchmark not found") })] }));
75
+ }
76
+ // Build detail sections
77
+ const detailSections = [];
78
+ // Basic details section
79
+ const basicFields = [];
80
+ if (benchmark.created_at) {
81
+ basicFields.push({
82
+ label: "Created",
83
+ value: formatTimestamp(benchmark.created_at),
84
+ });
85
+ }
86
+ if (benchmark.description) {
87
+ basicFields.push({
88
+ label: "Description",
89
+ value: benchmark.description,
90
+ });
91
+ }
92
+ if (basicFields.length > 0) {
93
+ detailSections.push({
94
+ title: "Details",
95
+ icon: figures.squareSmallFilled,
96
+ color: colors.warning,
97
+ fields: basicFields,
98
+ });
99
+ }
100
+ // Metadata section
101
+ if (benchmark.metadata &&
102
+ Object.keys(benchmark.metadata).length > 0) {
103
+ const metadataFields = Object.entries(benchmark.metadata).map(([key, value]) => ({
104
+ label: key,
105
+ value: value,
106
+ }));
107
+ detailSections.push({
108
+ title: "Metadata",
109
+ icon: figures.identical,
110
+ color: colors.secondary,
111
+ fields: metadataFields,
112
+ });
113
+ }
114
+ // Operations available for benchmarks
115
+ const operations = [
116
+ {
117
+ key: "create-job",
118
+ label: "Create Benchmark Job",
119
+ color: colors.success,
120
+ icon: figures.play,
121
+ shortcut: "c",
122
+ },
123
+ ];
124
+ // Handle operation selection
125
+ const handleOperation = async (operation, resource) => {
126
+ switch (operation) {
127
+ case "create-job":
128
+ navigate("benchmark-job-create", { initialBenchmarkIds: resource.id });
129
+ break;
130
+ }
131
+ };
132
+ // Build detailed info lines for full details view
133
+ const buildDetailLines = (b) => {
134
+ const lines = [];
135
+ // Core Information
136
+ lines.push(_jsx(Text, { color: colors.warning, bold: true, children: "Benchmark Details" }, "core-title"));
137
+ lines.push(_jsxs(Text, { color: colors.idColor, children: [" ", "ID: ", b.id] }, "core-id"));
138
+ lines.push(_jsxs(Text, { dimColor: true, children: [" ", "Name: ", b.name || "(none)"] }, "core-name"));
139
+ if (b.description) {
140
+ lines.push(_jsxs(Text, { dimColor: true, children: [" ", "Description: ", b.description] }, "core-desc"));
141
+ }
142
+ if (b.created_at) {
143
+ lines.push(_jsxs(Text, { dimColor: true, children: [" ", "Created: ", new Date(b.created_at).toLocaleString()] }, "core-created"));
144
+ }
145
+ lines.push(_jsx(Text, { children: " " }, "core-space"));
146
+ // Metadata
147
+ if (b.metadata && Object.keys(b.metadata).length > 0) {
148
+ lines.push(_jsx(Text, { color: colors.warning, bold: true, children: "Metadata" }, "meta-title"));
149
+ Object.entries(b.metadata).forEach(([key, value], idx) => {
150
+ lines.push(_jsxs(Text, { dimColor: true, children: [" ", key, ": ", value] }, `meta-${idx}`));
151
+ });
152
+ lines.push(_jsx(Text, { children: " " }, "meta-space"));
153
+ }
154
+ // Raw JSON
155
+ lines.push(_jsx(Text, { color: colors.warning, bold: true, children: "Raw JSON" }, "json-title"));
156
+ const jsonLines = JSON.stringify(b, null, 2).split("\n");
157
+ jsonLines.forEach((line, idx) => {
158
+ lines.push(_jsxs(Text, { dimColor: true, children: [" ", line] }, `json-${idx}`));
159
+ });
160
+ return lines;
161
+ };
162
+ return (_jsx(ResourceDetailPage, { resource: benchmark, resourceType: "Benchmark Definitions", getDisplayName: (b) => b.name || b.id, getId: (b) => b.id, getStatus: (b) => b.status, detailSections: detailSections, operations: operations, onOperation: handleOperation, onBack: goBack, buildDetailLines: buildDetailLines, breadcrumbPrefix: [{ label: "Home" }, { label: "Benchmarks" }] }));
163
+ }