@runloop/rl-cli 1.8.0 → 1.10.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.
- package/README.md +21 -7
- package/dist/cli.js +0 -0
- package/dist/commands/blueprint/delete.js +21 -0
- package/dist/commands/blueprint/list.js +226 -174
- package/dist/commands/blueprint/prune.js +13 -28
- package/dist/commands/devbox/create.js +41 -0
- package/dist/commands/devbox/list.js +142 -110
- package/dist/commands/devbox/rsync.js +69 -41
- package/dist/commands/devbox/scp.js +180 -39
- package/dist/commands/devbox/tunnel.js +4 -19
- package/dist/commands/gateway-config/create.js +53 -0
- package/dist/commands/gateway-config/delete.js +21 -0
- package/dist/commands/gateway-config/get.js +18 -0
- package/dist/commands/gateway-config/list.js +493 -0
- package/dist/commands/gateway-config/update.js +70 -0
- package/dist/commands/snapshot/list.js +11 -2
- package/dist/commands/snapshot/prune.js +265 -0
- package/dist/components/BenchmarkMenu.js +23 -3
- package/dist/components/DetailedInfoView.js +20 -0
- package/dist/components/DevboxActionsMenu.js +26 -62
- package/dist/components/DevboxCreatePage.js +763 -15
- package/dist/components/DevboxDetailPage.js +73 -24
- package/dist/components/GatewayConfigCreatePage.js +272 -0
- package/dist/components/LogsViewer.js +6 -40
- package/dist/components/ResourceDetailPage.js +143 -160
- package/dist/components/ResourceListView.js +3 -33
- package/dist/components/ResourcePicker.js +234 -0
- package/dist/components/SecretCreatePage.js +71 -27
- package/dist/components/SettingsMenu.js +12 -2
- package/dist/components/StateHistory.js +1 -20
- package/dist/components/StatusBadge.js +9 -2
- package/dist/components/StreamingLogsViewer.js +8 -42
- package/dist/components/form/FormTextInput.js +4 -2
- package/dist/components/resourceDetailTypes.js +18 -0
- package/dist/hooks/useInputHandler.js +103 -0
- package/dist/router/Router.js +79 -2
- package/dist/screens/BenchmarkDetailScreen.js +163 -0
- package/dist/screens/BenchmarkJobCreateScreen.js +524 -0
- package/dist/screens/BenchmarkJobDetailScreen.js +614 -0
- package/dist/screens/BenchmarkJobListScreen.js +479 -0
- package/dist/screens/BenchmarkListScreen.js +266 -0
- package/dist/screens/BenchmarkMenuScreen.js +6 -0
- package/dist/screens/BenchmarkRunDetailScreen.js +258 -22
- package/dist/screens/BenchmarkRunListScreen.js +21 -1
- package/dist/screens/BlueprintDetailScreen.js +5 -1
- package/dist/screens/DevboxCreateScreen.js +2 -2
- package/dist/screens/GatewayConfigDetailScreen.js +236 -0
- package/dist/screens/GatewayConfigListScreen.js +7 -0
- package/dist/screens/ScenarioRunDetailScreen.js +6 -0
- package/dist/screens/SecretDetailScreen.js +26 -2
- package/dist/screens/SettingsMenuScreen.js +3 -0
- package/dist/screens/SnapshotDetailScreen.js +6 -0
- package/dist/services/agentService.js +42 -0
- package/dist/services/benchmarkJobService.js +122 -0
- package/dist/services/benchmarkService.js +47 -0
- package/dist/services/gatewayConfigService.js +153 -0
- package/dist/services/scenarioService.js +34 -0
- package/dist/store/benchmarkJobStore.js +66 -0
- package/dist/store/benchmarkStore.js +63 -0
- package/dist/store/gatewayConfigStore.js +83 -0
- package/dist/utils/browser.js +22 -0
- package/dist/utils/clipboard.js +41 -0
- package/dist/utils/commands.js +105 -9
- package/dist/utils/gatewayConfigValidation.js +58 -0
- package/dist/utils/time.js +121 -0
- package/package.json +43 -43
|
@@ -14,10 +14,11 @@ import { NavigationTips } from "./NavigationTips.js";
|
|
|
14
14
|
import { FormTextInput, FormActionButton } from "./form/index.js";
|
|
15
15
|
import { colors } from "../utils/theme.js";
|
|
16
16
|
import { useExitOnCtrlC } from "../hooks/useExitOnCtrlC.js";
|
|
17
|
-
export const SecretCreatePage = ({ onBack, onCreate, }) => {
|
|
17
|
+
export const SecretCreatePage = ({ onBack, onCreate, initialSecret, }) => {
|
|
18
|
+
const isUpdating = !!initialSecret;
|
|
18
19
|
const [currentField, setCurrentField] = React.useState("submit");
|
|
19
20
|
const [formData, setFormData] = React.useState({
|
|
20
|
-
name: "",
|
|
21
|
+
name: initialSecret?.name || "",
|
|
21
22
|
value: "",
|
|
22
23
|
});
|
|
23
24
|
const [submitting, setSubmitting] = React.useState(false);
|
|
@@ -25,9 +26,17 @@ export const SecretCreatePage = ({ onBack, onCreate, }) => {
|
|
|
25
26
|
const [error, setError] = React.useState(null);
|
|
26
27
|
const [validationError, setValidationError] = React.useState(null);
|
|
27
28
|
const fields = [
|
|
28
|
-
{
|
|
29
|
+
{
|
|
30
|
+
key: "submit",
|
|
31
|
+
label: isUpdating ? "Update Secret" : "Create Secret",
|
|
32
|
+
type: "action",
|
|
33
|
+
},
|
|
29
34
|
{ key: "name", label: "Name (required)", type: "text" },
|
|
30
|
-
{
|
|
35
|
+
{
|
|
36
|
+
key: "value",
|
|
37
|
+
label: isUpdating ? "New Value (required)" : "Value (required)",
|
|
38
|
+
type: "password",
|
|
39
|
+
},
|
|
31
40
|
];
|
|
32
41
|
const currentFieldIndex = fields.findIndex((f) => f.key === currentField);
|
|
33
42
|
// Handle Ctrl+C to exit
|
|
@@ -78,13 +87,30 @@ export const SecretCreatePage = ({ onBack, onCreate, }) => {
|
|
|
78
87
|
return;
|
|
79
88
|
}
|
|
80
89
|
// Navigation between fields (up/down arrows and tab/shift+tab)
|
|
81
|
-
|
|
82
|
-
|
|
90
|
+
// In update mode, skip the name field (it's read-only)
|
|
91
|
+
const getNextFieldIndex = (direction) => {
|
|
92
|
+
let nextIdx = direction === "up" ? currentFieldIndex - 1 : currentFieldIndex + 1;
|
|
93
|
+
while (nextIdx >= 0 && nextIdx < fields.length) {
|
|
94
|
+
if (isUpdating && fields[nextIdx].key === "name") {
|
|
95
|
+
nextIdx = direction === "up" ? nextIdx - 1 : nextIdx + 1;
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
return nextIdx;
|
|
99
|
+
}
|
|
100
|
+
return null;
|
|
101
|
+
};
|
|
102
|
+
if (key.upArrow || (key.tab && key.shift)) {
|
|
103
|
+
const nextIdx = getNextFieldIndex("up");
|
|
104
|
+
if (nextIdx !== null) {
|
|
105
|
+
setCurrentField(fields[nextIdx].key);
|
|
106
|
+
}
|
|
83
107
|
return;
|
|
84
108
|
}
|
|
85
|
-
if (
|
|
86
|
-
|
|
87
|
-
|
|
109
|
+
if (key.downArrow || (key.tab && !key.shift)) {
|
|
110
|
+
const nextIdx = getNextFieldIndex("down");
|
|
111
|
+
if (nextIdx !== null) {
|
|
112
|
+
setCurrentField(fields[nextIdx].key);
|
|
113
|
+
}
|
|
88
114
|
return;
|
|
89
115
|
}
|
|
90
116
|
});
|
|
@@ -105,10 +131,19 @@ export const SecretCreatePage = ({ onBack, onCreate, }) => {
|
|
|
105
131
|
setValidationError(null);
|
|
106
132
|
try {
|
|
107
133
|
const client = getClient();
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
134
|
+
let secret;
|
|
135
|
+
if (isUpdating) {
|
|
136
|
+
// Update existing secret by name
|
|
137
|
+
secret = (await client.secrets.update(formData.name.trim(), {
|
|
138
|
+
value: formData.value,
|
|
139
|
+
}));
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
secret = (await client.secrets.create({
|
|
143
|
+
name: formData.name.trim(),
|
|
144
|
+
value: formData.value,
|
|
145
|
+
}));
|
|
146
|
+
}
|
|
112
147
|
setResult(secret);
|
|
113
148
|
}
|
|
114
149
|
catch (err) {
|
|
@@ -123,16 +158,21 @@ export const SecretCreatePage = ({ onBack, onCreate, }) => {
|
|
|
123
158
|
return (_jsxs(_Fragment, { children: [_jsx(Breadcrumb, { items: [
|
|
124
159
|
{ label: "Settings" },
|
|
125
160
|
{ label: "Secrets" },
|
|
126
|
-
{ label: "Create", active: true },
|
|
127
|
-
] }), _jsx(SuccessMessage, { message:
|
|
161
|
+
{ label: isUpdating ? "Update" : "Create", active: true },
|
|
162
|
+
] }), _jsx(SuccessMessage, { message: `Secret ${isUpdating ? "updated" : "created"} successfully!` }), _jsxs(Box, { marginLeft: 2, flexDirection: "column", marginTop: 1, children: [_jsxs(Box, { children: [_jsxs(Text, { color: colors.textDim, dimColor: true, children: ["ID:", " "] }), _jsx(Text, { color: colors.idColor, children: result.id })] }), _jsx(Box, { children: _jsxs(Text, { color: colors.textDim, dimColor: true, children: ["Name: ", result.name] }) })] }), _jsx(Box, { marginTop: 1, marginLeft: 2, children: _jsxs(Text, { color: colors.info, children: [figures.info, " The secret value has been securely stored and cannot be retrieved."] }) }), _jsx(NavigationTips, { tips: [
|
|
163
|
+
{
|
|
164
|
+
key: "Enter/q/esc",
|
|
165
|
+
label: isUpdating ? "Return to details" : "View secret details",
|
|
166
|
+
},
|
|
167
|
+
] })] }));
|
|
128
168
|
}
|
|
129
169
|
// Error screen
|
|
130
170
|
if (error) {
|
|
131
171
|
return (_jsxs(_Fragment, { children: [_jsx(Breadcrumb, { items: [
|
|
132
172
|
{ label: "Settings" },
|
|
133
173
|
{ label: "Secrets" },
|
|
134
|
-
{ label: "Create", active: true },
|
|
135
|
-
] }), _jsx(ErrorMessage, { message:
|
|
174
|
+
{ label: isUpdating ? "Update" : "Create", active: true },
|
|
175
|
+
] }), _jsx(ErrorMessage, { message: `Failed to ${isUpdating ? "update" : "create"} secret`, error: error }), _jsx(NavigationTips, { tips: [
|
|
136
176
|
{ key: "Enter/r", label: "Retry" },
|
|
137
177
|
{ key: "q/esc", label: "Cancel" },
|
|
138
178
|
] })] }));
|
|
@@ -142,20 +182,26 @@ export const SecretCreatePage = ({ onBack, onCreate, }) => {
|
|
|
142
182
|
return (_jsxs(_Fragment, { children: [_jsx(Breadcrumb, { items: [
|
|
143
183
|
{ label: "Settings" },
|
|
144
184
|
{ label: "Secrets" },
|
|
145
|
-
{ label: "Create", active: true },
|
|
146
|
-
] }), _jsx(SpinnerComponent, { message: "Creating secret
|
|
185
|
+
{ label: isUpdating ? "Update" : "Create", active: true },
|
|
186
|
+
] }), _jsx(SpinnerComponent, { message: `${isUpdating ? "Updating" : "Creating"} secret...` })] }));
|
|
147
187
|
}
|
|
148
188
|
// Form screen
|
|
149
189
|
return (_jsxs(_Fragment, { children: [_jsx(Breadcrumb, { items: [
|
|
150
190
|
{ label: "Settings" },
|
|
151
191
|
{ label: "Secrets" },
|
|
152
|
-
{ label: "Create", active: true },
|
|
153
|
-
] }), _jsx(Box, { borderStyle: "round", borderColor: colors.info, paddingX: 1, paddingY: 0, marginBottom: 1, children: _jsxs(Text, { color: colors.info, children: [figures.info, " ", _jsx(Text, { bold: true, children: "Note:" }), " Secret values are", " ", _jsx(Text, { bold: true, children: "write-only" }), ". Once
|
|
192
|
+
{ label: isUpdating ? "Update" : "Create", active: true },
|
|
193
|
+
] }), _jsx(Box, { borderStyle: "round", borderColor: colors.info, paddingX: 1, paddingY: 0, marginBottom: 1, children: _jsxs(Text, { color: colors.info, children: [figures.info, " ", _jsx(Text, { bold: true, children: "Note:" }), " Secret values are", " ", _jsx(Text, { bold: true, children: "write-only" }), ". Once stored, the value cannot be retrieved or viewed.", " ", isUpdating
|
|
194
|
+
? "Enter a new value to replace the current one."
|
|
195
|
+
: "You can update a secret's value later."] }) }), _jsx(Box, { flexDirection: "column", marginBottom: 1, children: fields.map((field) => {
|
|
154
196
|
const isActive = currentField === field.key;
|
|
155
197
|
if (field.type === "action") {
|
|
156
|
-
return (_jsx(FormActionButton, { label: field.label, isActive: isActive, hint:
|
|
198
|
+
return (_jsx(FormActionButton, { label: field.label, isActive: isActive, hint: `[Enter to ${isUpdating ? "update" : "create"}]` }, field.key));
|
|
157
199
|
}
|
|
158
200
|
if (field.type === "text") {
|
|
201
|
+
// In update mode, name is read-only
|
|
202
|
+
if (isUpdating && field.key === "name") {
|
|
203
|
+
return (_jsxs(Box, { marginBottom: 0, children: [_jsxs(Text, { color: colors.textDim, children: [" ", field.label, ":", " "] }), _jsx(Text, { color: colors.text, bold: true, children: formData.name })] }, field.key));
|
|
204
|
+
}
|
|
159
205
|
const value = formData[field.key];
|
|
160
206
|
const hasError = field.key === "name" && validationError === "Name is required";
|
|
161
207
|
return (_jsx(FormTextInput, { label: field.label, value: value, onChange: (newValue) => {
|
|
@@ -168,18 +214,16 @@ export const SecretCreatePage = ({ onBack, onCreate, }) => {
|
|
|
168
214
|
if (field.type === "password") {
|
|
169
215
|
const value = formData[field.key];
|
|
170
216
|
const hasError = field.key === "value" && validationError === "Value is required";
|
|
171
|
-
|
|
172
|
-
const maskedValue = "*".repeat(value.length);
|
|
173
|
-
return (_jsx(FormTextInput, { label: field.label, value: isActive ? value : maskedValue, onChange: (newValue) => {
|
|
217
|
+
return (_jsx(FormTextInput, { label: field.label, value: value, onChange: (newValue) => {
|
|
174
218
|
setFormData({ ...formData, [field.key]: newValue });
|
|
175
219
|
if (validationError) {
|
|
176
220
|
setValidationError(null);
|
|
177
221
|
}
|
|
178
|
-
}, onSubmit: handleSubmit, isActive: isActive, placeholder: "Enter secret value", error: hasError ? validationError : undefined }, field.key));
|
|
222
|
+
}, onSubmit: handleSubmit, isActive: isActive, placeholder: "Enter secret value", error: hasError ? validationError : undefined, mask: "*" }, field.key));
|
|
179
223
|
}
|
|
180
224
|
return null;
|
|
181
225
|
}) }), _jsx(NavigationTips, { showArrows: true, tips: [
|
|
182
|
-
{ key: "Enter", label: "Create" },
|
|
226
|
+
{ key: "Enter", label: isUpdating ? "Update" : "Create" },
|
|
183
227
|
{ key: "q", label: "Cancel" },
|
|
184
228
|
] })] }));
|
|
185
229
|
};
|
|
@@ -14,6 +14,13 @@ const settingsMenuItems = [
|
|
|
14
14
|
icon: "◇",
|
|
15
15
|
color: colors.info,
|
|
16
16
|
},
|
|
17
|
+
{
|
|
18
|
+
key: "gateway-configs",
|
|
19
|
+
label: "AI Gateway Configs",
|
|
20
|
+
description: "Configure API credential proxying",
|
|
21
|
+
icon: "⬡",
|
|
22
|
+
color: colors.success,
|
|
23
|
+
},
|
|
17
24
|
{
|
|
18
25
|
key: "secrets",
|
|
19
26
|
label: "Secrets",
|
|
@@ -66,7 +73,10 @@ export const SettingsMenu = ({ onSelect, onBack }) => {
|
|
|
66
73
|
else if (input === "n" || input === "1") {
|
|
67
74
|
onSelect("network-policies");
|
|
68
75
|
}
|
|
69
|
-
else if (input === "
|
|
76
|
+
else if (input === "g" || input === "2") {
|
|
77
|
+
onSelect("gateway-configs");
|
|
78
|
+
}
|
|
79
|
+
else if (input === "s" || input === "3") {
|
|
70
80
|
onSelect("secrets");
|
|
71
81
|
}
|
|
72
82
|
else if (input === "q") {
|
|
@@ -77,7 +87,7 @@ export const SettingsMenu = ({ onSelect, onBack }) => {
|
|
|
77
87
|
const isSelected = index === selectedIndex;
|
|
78
88
|
return (_jsxs(Box, { marginBottom: 0, children: [_jsx(Text, { color: isSelected ? item.color : colors.textDim, children: isSelected ? figures.pointer : " " }), _jsx(Text, { children: " " }), _jsx(Text, { color: item.color, bold: true, children: item.icon }), _jsx(Text, { children: " " }), _jsx(Text, { color: isSelected ? item.color : colors.text, bold: isSelected, children: item.label }), !isNarrow && (_jsxs(Text, { color: colors.textDim, dimColor: true, children: [" ", "- ", item.description] })), _jsxs(Text, { color: colors.textDim, dimColor: true, children: [" ", "[", index + 1, "]"] })] }, item.key));
|
|
79
89
|
}) }), _jsx(NavigationTips, { showArrows: true, paddingX: 2, tips: [
|
|
80
|
-
{ key: "1-
|
|
90
|
+
{ key: "1-3", label: "Quick select" },
|
|
81
91
|
{ key: "Enter", label: "Select" },
|
|
82
92
|
{ key: "Esc", label: "Back" },
|
|
83
93
|
{ key: "q", label: "Quit" },
|
|
@@ -3,6 +3,7 @@ import { Box, Text } from "ink";
|
|
|
3
3
|
import figures from "figures";
|
|
4
4
|
import { colors } from "../utils/theme.js";
|
|
5
5
|
import { getStatusDisplay } from "./StatusBadge.js";
|
|
6
|
+
import { formatTimeAgo } from "../utils/time.js";
|
|
6
7
|
// Format shutdown reason into human-readable text
|
|
7
8
|
const formatShutdownReason = (reason) => {
|
|
8
9
|
switch (reason) {
|
|
@@ -33,26 +34,6 @@ const formatShutdownReason = (reason) => {
|
|
|
33
34
|
return reason.replace(/_/g, " ");
|
|
34
35
|
}
|
|
35
36
|
};
|
|
36
|
-
// Format time ago in a succinct way
|
|
37
|
-
const formatTimeAgo = (timestamp) => {
|
|
38
|
-
const seconds = Math.floor((Date.now() - timestamp) / 1000);
|
|
39
|
-
if (seconds < 60)
|
|
40
|
-
return `${seconds}s ago`;
|
|
41
|
-
const minutes = Math.floor(seconds / 60);
|
|
42
|
-
if (minutes < 60)
|
|
43
|
-
return `${minutes}m ago`;
|
|
44
|
-
const hours = Math.floor(minutes / 60);
|
|
45
|
-
if (hours < 24)
|
|
46
|
-
return `${hours}h ago`;
|
|
47
|
-
const days = Math.floor(hours / 24);
|
|
48
|
-
if (days < 30)
|
|
49
|
-
return `${days}d ago`;
|
|
50
|
-
const months = Math.floor(days / 30);
|
|
51
|
-
if (months < 12)
|
|
52
|
-
return `${months}mo ago`;
|
|
53
|
-
const years = Math.floor(months / 12);
|
|
54
|
-
return `${years}y ago`;
|
|
55
|
-
};
|
|
56
37
|
// Format duration in a succinct way
|
|
57
38
|
const formatDuration = (milliseconds) => {
|
|
58
39
|
const seconds = Math.floor(milliseconds / 1000);
|
|
@@ -82,6 +82,13 @@ export const getStatusDisplay = (status) => {
|
|
|
82
82
|
label: "Failed",
|
|
83
83
|
};
|
|
84
84
|
// === BUILD STATES (for blueprints) ===
|
|
85
|
+
case "queued":
|
|
86
|
+
return {
|
|
87
|
+
icon: figures.ellipsis,
|
|
88
|
+
color: colors.warning,
|
|
89
|
+
text: "QUEUED ",
|
|
90
|
+
label: "Queued",
|
|
91
|
+
};
|
|
85
92
|
case "ready":
|
|
86
93
|
return {
|
|
87
94
|
icon: figures.tick,
|
|
@@ -109,8 +116,8 @@ export const getStatusDisplay = (status) => {
|
|
|
109
116
|
return {
|
|
110
117
|
icon: figures.tick,
|
|
111
118
|
color: colors.success,
|
|
112
|
-
text: "
|
|
113
|
-
label: "
|
|
119
|
+
text: "COMPLETE ",
|
|
120
|
+
label: "Complete",
|
|
114
121
|
};
|
|
115
122
|
case "canceled":
|
|
116
123
|
return {
|
|
@@ -9,6 +9,7 @@ import figures from "figures";
|
|
|
9
9
|
import { Breadcrumb } from "./Breadcrumb.js";
|
|
10
10
|
import { NavigationTips } from "./NavigationTips.js";
|
|
11
11
|
import { colors } from "../utils/theme.js";
|
|
12
|
+
import { copyToClipboard } from "../utils/clipboard.js";
|
|
12
13
|
import { useViewportHeight } from "../hooks/useViewportHeight.js";
|
|
13
14
|
import { useExitOnCtrlC } from "../hooks/useExitOnCtrlC.js";
|
|
14
15
|
import { parseAnyLogEntry } from "../utils/logFormatter.js";
|
|
@@ -167,42 +168,10 @@ export const StreamingLogsViewer = ({ devboxId, breadcrumbItems = [{ label: "Log
|
|
|
167
168
|
return `${parts.timestamp} ${parts.level} [${parts.source}] ${shell}${cmd}${parts.message} ${exitCode}`.trim();
|
|
168
169
|
})
|
|
169
170
|
.join("\n");
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
let args;
|
|
175
|
-
if (platform === "darwin") {
|
|
176
|
-
command = "pbcopy";
|
|
177
|
-
args = [];
|
|
178
|
-
}
|
|
179
|
-
else if (platform === "win32") {
|
|
180
|
-
command = "clip";
|
|
181
|
-
args = [];
|
|
182
|
-
}
|
|
183
|
-
else {
|
|
184
|
-
command = "xclip";
|
|
185
|
-
args = ["-selection", "clipboard"];
|
|
186
|
-
}
|
|
187
|
-
const proc = spawn(command, args);
|
|
188
|
-
proc.stdin.write(text);
|
|
189
|
-
proc.stdin.end();
|
|
190
|
-
proc.on("exit", (code) => {
|
|
191
|
-
if (code === 0) {
|
|
192
|
-
setCopyStatus("Copied!");
|
|
193
|
-
setTimeout(() => setCopyStatus(null), 2000);
|
|
194
|
-
}
|
|
195
|
-
else {
|
|
196
|
-
setCopyStatus("Failed");
|
|
197
|
-
setTimeout(() => setCopyStatus(null), 2000);
|
|
198
|
-
}
|
|
199
|
-
});
|
|
200
|
-
proc.on("error", () => {
|
|
201
|
-
setCopyStatus("Not supported");
|
|
202
|
-
setTimeout(() => setCopyStatus(null), 2000);
|
|
203
|
-
});
|
|
204
|
-
};
|
|
205
|
-
copyToClipboard(logsText);
|
|
171
|
+
copyToClipboard(logsText).then((status) => {
|
|
172
|
+
setCopyStatus(status);
|
|
173
|
+
setTimeout(() => setCopyStatus(null), 2000);
|
|
174
|
+
});
|
|
206
175
|
}
|
|
207
176
|
else if (input === "q" || key.escape || key.return) {
|
|
208
177
|
onBack();
|
|
@@ -214,16 +183,13 @@ export const StreamingLogsViewer = ({ devboxId, breadcrumbItems = [{ label: "Log
|
|
|
214
183
|
const contentWidth = Math.max(40, terminalWidth - boxChrome);
|
|
215
184
|
// Helper to sanitize log message
|
|
216
185
|
const sanitizeMessage = (message) => {
|
|
217
|
-
const strippedAnsi = message.replace(
|
|
218
|
-
|
|
219
|
-
/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "");
|
|
220
|
-
return (strippedAnsi
|
|
186
|
+
const strippedAnsi = message.replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "");
|
|
187
|
+
return strippedAnsi
|
|
221
188
|
.replace(/\r\n/g, " ")
|
|
222
189
|
.replace(/\n/g, " ")
|
|
223
190
|
.replace(/\r/g, " ")
|
|
224
191
|
.replace(/\t/g, " ")
|
|
225
|
-
|
|
226
|
-
.replace(/[\x00-\x1F]/g, ""));
|
|
192
|
+
.replace(/[\x00-\x1F]/g, "");
|
|
227
193
|
};
|
|
228
194
|
// Calculate visible logs
|
|
229
195
|
let visibleLogs;
|
|
@@ -3,6 +3,8 @@ import { Text } from "ink";
|
|
|
3
3
|
import TextInput from "ink-text-input";
|
|
4
4
|
import { FormField } from "./FormField.js";
|
|
5
5
|
import { colors } from "../../utils/theme.js";
|
|
6
|
-
export const FormTextInput = ({ label, value, onChange, isActive, placeholder, error, onSubmit, }) => {
|
|
7
|
-
|
|
6
|
+
export const FormTextInput = ({ label, value, onChange, isActive, placeholder, error, onSubmit, mask, }) => {
|
|
7
|
+
// Display value: use mask character if provided
|
|
8
|
+
const displayValue = mask && value ? mask.repeat(value.length) : value;
|
|
9
|
+
return (_jsx(FormField, { label: label, isActive: isActive, error: error, children: isActive ? (_jsx(TextInput, { value: value, onChange: onChange, placeholder: placeholder, onSubmit: onSubmit, mask: mask })) : (_jsx(Text, { color: error ? colors.error : colors.text, children: displayValue || "(empty)" })) }));
|
|
8
10
|
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Walk all sections and collect fields that have an action defined.
|
|
3
|
+
* Returns a flat list of references preserving section/field indices
|
|
4
|
+
* so the component can map selections back to the right field.
|
|
5
|
+
*/
|
|
6
|
+
export function collectActionableFields(sections) {
|
|
7
|
+
const refs = [];
|
|
8
|
+
sections.forEach((section, sectionIndex) => {
|
|
9
|
+
section.fields
|
|
10
|
+
.filter((field) => field.value !== undefined && field.value !== null)
|
|
11
|
+
.forEach((field, fieldIndex) => {
|
|
12
|
+
if (field.action) {
|
|
13
|
+
refs.push({ sectionIndex, fieldIndex, action: field.action });
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
return refs;
|
|
18
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useInputHandler - Declarative, mode-based input handling for Ink components.
|
|
3
|
+
*
|
|
4
|
+
* Replaces long imperative if/else chains in useInput callbacks with a
|
|
5
|
+
* structured system of ordered modes, each with a key-binding map.
|
|
6
|
+
* The first active mode wins; bindings are looked up by canonical key name.
|
|
7
|
+
*/
|
|
8
|
+
import { useInput } from "ink";
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// Key resolution
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
/**
|
|
13
|
+
* Normalise Ink's (input, key) pair into a single canonical key name.
|
|
14
|
+
*
|
|
15
|
+
* Priority order (first match wins):
|
|
16
|
+
* 1. Special keys (arrows, enter, escape, etc.)
|
|
17
|
+
* 2. Ctrl+<char> combinations
|
|
18
|
+
* 3. The raw `input` string (printable character)
|
|
19
|
+
*/
|
|
20
|
+
export function resolveKeyName(input, key) {
|
|
21
|
+
// Special keys
|
|
22
|
+
if (key.upArrow)
|
|
23
|
+
return "up";
|
|
24
|
+
if (key.downArrow)
|
|
25
|
+
return "down";
|
|
26
|
+
if (key.leftArrow)
|
|
27
|
+
return "left";
|
|
28
|
+
if (key.rightArrow)
|
|
29
|
+
return "right";
|
|
30
|
+
if (key.return)
|
|
31
|
+
return "enter";
|
|
32
|
+
if (key.escape)
|
|
33
|
+
return "escape";
|
|
34
|
+
if (key.tab)
|
|
35
|
+
return "tab";
|
|
36
|
+
if (key.backspace)
|
|
37
|
+
return "backspace";
|
|
38
|
+
if (key.delete)
|
|
39
|
+
return "delete";
|
|
40
|
+
if (key.pageUp)
|
|
41
|
+
return "pageUp";
|
|
42
|
+
if (key.pageDown)
|
|
43
|
+
return "pageDown";
|
|
44
|
+
// Ctrl combinations (e.g. ctrl+c)
|
|
45
|
+
if (key.ctrl && input)
|
|
46
|
+
return `ctrl+${input}`;
|
|
47
|
+
// Printable character
|
|
48
|
+
return input;
|
|
49
|
+
}
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
// Hook
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
/**
|
|
54
|
+
* Declarative input handler.
|
|
55
|
+
*
|
|
56
|
+
* @param modes Ordered array of input modes. The first mode whose `active()`
|
|
57
|
+
* returns `true` gets to handle the key event.
|
|
58
|
+
* @param options Optional settings forwarded to Ink's useInput.
|
|
59
|
+
*/
|
|
60
|
+
export function useInputHandler(modes, options) {
|
|
61
|
+
useInput((input, key) => {
|
|
62
|
+
const keyName = resolveKeyName(input, key);
|
|
63
|
+
for (const mode of modes) {
|
|
64
|
+
if (!mode.active())
|
|
65
|
+
continue;
|
|
66
|
+
// Try an exact binding match
|
|
67
|
+
const handler = mode.bindings[keyName];
|
|
68
|
+
if (handler) {
|
|
69
|
+
handler();
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
// No binding matched — try the dynamic fallback
|
|
73
|
+
if (mode.onUnmatched) {
|
|
74
|
+
mode.onUnmatched(input, key);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
// captureAll: swallow the event silently
|
|
78
|
+
if (mode.captureAll)
|
|
79
|
+
return;
|
|
80
|
+
// Default: first active mode consumes the event even if nothing matched
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
}, { isActive: options?.isActive ?? true });
|
|
84
|
+
}
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
// Preset binding helpers
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
/**
|
|
89
|
+
* Common scroll bindings (j/k, arrows, page up/down).
|
|
90
|
+
* Spread into a mode's `bindings` to get standard scrolling behaviour.
|
|
91
|
+
*/
|
|
92
|
+
export function scrollBindings(getScroll, setScroll) {
|
|
93
|
+
return {
|
|
94
|
+
down: () => setScroll(getScroll() + 1),
|
|
95
|
+
up: () => setScroll(Math.max(0, getScroll() - 1)),
|
|
96
|
+
j: () => setScroll(getScroll() + 1),
|
|
97
|
+
k: () => setScroll(Math.max(0, getScroll() - 1)),
|
|
98
|
+
s: () => setScroll(getScroll() + 1),
|
|
99
|
+
w: () => setScroll(Math.max(0, getScroll() - 1)),
|
|
100
|
+
pageDown: () => setScroll(getScroll() + 10),
|
|
101
|
+
pageUp: () => setScroll(Math.max(0, getScroll() - 10)),
|
|
102
|
+
};
|
|
103
|
+
}
|
package/dist/router/Router.js
CHANGED
|
@@ -1,17 +1,71 @@
|
|
|
1
|
-
import {
|
|
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";
|
|
13
16
|
import { useBenchmarkStore } from "../store/benchmarkStore.js";
|
|
17
|
+
import { useBenchmarkJobStore } from "../store/benchmarkJobStore.js";
|
|
14
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
|
+
}
|
|
15
69
|
// Import screen components
|
|
16
70
|
import { MenuScreen } from "../screens/MenuScreen.js";
|
|
17
71
|
import { DevboxListScreen } from "../screens/DevboxListScreen.js";
|
|
@@ -27,6 +81,8 @@ import { SnapshotDetailScreen } from "../screens/SnapshotDetailScreen.js";
|
|
|
27
81
|
import { NetworkPolicyListScreen } from "../screens/NetworkPolicyListScreen.js";
|
|
28
82
|
import { NetworkPolicyDetailScreen } from "../screens/NetworkPolicyDetailScreen.js";
|
|
29
83
|
import { NetworkPolicyCreateScreen } from "../screens/NetworkPolicyCreateScreen.js";
|
|
84
|
+
import { GatewayConfigListScreen } from "../screens/GatewayConfigListScreen.js";
|
|
85
|
+
import { GatewayConfigDetailScreen } from "../screens/GatewayConfigDetailScreen.js";
|
|
30
86
|
import { SettingsMenuScreen } from "../screens/SettingsMenuScreen.js";
|
|
31
87
|
import { SecretListScreen } from "../screens/SecretListScreen.js";
|
|
32
88
|
import { SecretDetailScreen } from "../screens/SecretDetailScreen.js";
|
|
@@ -35,10 +91,15 @@ import { ObjectListScreen } from "../screens/ObjectListScreen.js";
|
|
|
35
91
|
import { ObjectDetailScreen } from "../screens/ObjectDetailScreen.js";
|
|
36
92
|
import { SSHSessionScreen } from "../screens/SSHSessionScreen.js";
|
|
37
93
|
import { BenchmarkMenuScreen } from "../screens/BenchmarkMenuScreen.js";
|
|
94
|
+
import { BenchmarkListScreen } from "../screens/BenchmarkListScreen.js";
|
|
95
|
+
import { BenchmarkDetailScreen } from "../screens/BenchmarkDetailScreen.js";
|
|
38
96
|
import { BenchmarkRunListScreen } from "../screens/BenchmarkRunListScreen.js";
|
|
39
97
|
import { BenchmarkRunDetailScreen } from "../screens/BenchmarkRunDetailScreen.js";
|
|
40
98
|
import { ScenarioRunListScreen } from "../screens/ScenarioRunListScreen.js";
|
|
41
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";
|
|
42
103
|
/**
|
|
43
104
|
* Router component that renders the current screen
|
|
44
105
|
* Implements memory cleanup on route changes
|
|
@@ -86,6 +147,13 @@ export function Router() {
|
|
|
86
147
|
useNetworkPolicyStore.getState().clearAll();
|
|
87
148
|
}
|
|
88
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;
|
|
89
157
|
case "object-list":
|
|
90
158
|
case "object-detail":
|
|
91
159
|
if (!currentScreen.startsWith("object")) {
|
|
@@ -93,6 +161,8 @@ export function Router() {
|
|
|
93
161
|
}
|
|
94
162
|
break;
|
|
95
163
|
case "benchmark-menu":
|
|
164
|
+
case "benchmark-list":
|
|
165
|
+
case "benchmark-detail":
|
|
96
166
|
case "benchmark-run-list":
|
|
97
167
|
case "benchmark-run-detail":
|
|
98
168
|
case "scenario-run-list":
|
|
@@ -102,6 +172,13 @@ export function Router() {
|
|
|
102
172
|
useBenchmarkStore.getState().clearAll();
|
|
103
173
|
}
|
|
104
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;
|
|
105
182
|
}
|
|
106
183
|
}
|
|
107
184
|
prevScreenRef.current = currentScreen;
|
|
@@ -110,5 +187,5 @@ export function Router() {
|
|
|
110
187
|
// and mount new component, preventing race conditions during screen transitions.
|
|
111
188
|
// The key ensures React treats this as a completely new component tree.
|
|
112
189
|
// Wrap in ErrorBoundary to catch any Yoga WASM errors gracefully.
|
|
113
|
-
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 === "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-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))] }, `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}`));
|
|
114
191
|
}
|