@runloop/rl-cli 1.2.0 → 1.3.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 +28 -8
- package/dist/commands/blueprint/list.js +97 -28
- package/dist/commands/blueprint/prune.js +7 -19
- package/dist/commands/devbox/create.js +3 -0
- package/dist/commands/devbox/list.js +44 -65
- package/dist/commands/menu.js +2 -1
- package/dist/commands/network-policy/create.js +27 -0
- package/dist/commands/network-policy/delete.js +21 -0
- package/dist/commands/network-policy/get.js +15 -0
- package/dist/commands/network-policy/list.js +494 -0
- package/dist/commands/object/list.js +516 -24
- package/dist/commands/snapshot/list.js +90 -29
- package/dist/components/Banner.js +109 -8
- package/dist/components/ConfirmationPrompt.js +45 -0
- package/dist/components/DevboxActionsMenu.js +42 -6
- package/dist/components/DevboxCard.js +1 -1
- package/dist/components/DevboxCreatePage.js +95 -81
- package/dist/components/DevboxDetailPage.js +218 -272
- package/dist/components/LogsViewer.js +8 -1
- package/dist/components/MainMenu.js +35 -4
- package/dist/components/NavigationTips.js +24 -0
- package/dist/components/NetworkPolicyCreatePage.js +264 -0
- package/dist/components/OperationsMenu.js +9 -1
- package/dist/components/ResourceActionsMenu.js +5 -1
- package/dist/components/ResourceDetailPage.js +204 -0
- package/dist/components/ResourceListView.js +19 -2
- package/dist/components/StatusBadge.js +2 -2
- package/dist/components/Table.js +6 -8
- package/dist/components/form/FormActionButton.js +7 -0
- package/dist/components/form/FormField.js +7 -0
- package/dist/components/form/FormListManager.js +112 -0
- package/dist/components/form/FormSelect.js +34 -0
- package/dist/components/form/FormTextInput.js +8 -0
- package/dist/components/form/index.js +8 -0
- package/dist/hooks/useViewportHeight.js +38 -20
- package/dist/router/Router.js +23 -1
- package/dist/screens/BlueprintDetailScreen.js +337 -0
- package/dist/screens/MenuScreen.js +6 -0
- package/dist/screens/NetworkPolicyCreateScreen.js +7 -0
- package/dist/screens/NetworkPolicyDetailScreen.js +247 -0
- package/dist/screens/NetworkPolicyListScreen.js +7 -0
- package/dist/screens/ObjectDetailScreen.js +377 -0
- package/dist/screens/ObjectListScreen.js +7 -0
- package/dist/screens/SnapshotDetailScreen.js +208 -0
- package/dist/services/blueprintService.js +30 -11
- package/dist/services/networkPolicyService.js +108 -0
- package/dist/services/objectService.js +101 -0
- package/dist/services/snapshotService.js +39 -3
- package/dist/store/blueprintStore.js +4 -10
- package/dist/store/index.js +1 -0
- package/dist/store/networkPolicyStore.js +83 -0
- package/dist/store/objectStore.js +92 -0
- package/dist/store/snapshotStore.js +4 -8
- package/dist/utils/commands.js +47 -0
- package/package.json +2 -2
|
@@ -8,9 +8,21 @@ import { SpinnerComponent } from "./Spinner.js";
|
|
|
8
8
|
import { ErrorMessage } from "./ErrorMessage.js";
|
|
9
9
|
import { SuccessMessage } from "./SuccessMessage.js";
|
|
10
10
|
import { Breadcrumb } from "./Breadcrumb.js";
|
|
11
|
+
import { NavigationTips } from "./NavigationTips.js";
|
|
11
12
|
import { MetadataDisplay } from "./MetadataDisplay.js";
|
|
13
|
+
import { FormTextInput, FormSelect, FormActionButton, useFormSelectNavigation, } from "./form/index.js";
|
|
12
14
|
import { colors } from "../utils/theme.js";
|
|
13
15
|
import { useExitOnCtrlC } from "../hooks/useExitOnCtrlC.js";
|
|
16
|
+
const architectures = ["arm64", "x86_64"];
|
|
17
|
+
const resourceSizes = [
|
|
18
|
+
"X_SMALL",
|
|
19
|
+
"SMALL",
|
|
20
|
+
"MEDIUM",
|
|
21
|
+
"LARGE",
|
|
22
|
+
"X_LARGE",
|
|
23
|
+
"XX_LARGE",
|
|
24
|
+
"CUSTOM_SIZE",
|
|
25
|
+
];
|
|
14
26
|
export const DevboxCreatePage = ({ onBack, onCreate, initialBlueprintId, initialSnapshotId, }) => {
|
|
15
27
|
const [currentField, setCurrentField] = React.useState("create");
|
|
16
28
|
const [formData, setFormData] = React.useState({
|
|
@@ -24,53 +36,79 @@ export const DevboxCreatePage = ({ onBack, onCreate, initialBlueprintId, initial
|
|
|
24
36
|
metadata: {},
|
|
25
37
|
blueprint_id: initialBlueprintId || "",
|
|
26
38
|
snapshot_id: initialSnapshotId || "",
|
|
39
|
+
network_policy_id: "",
|
|
27
40
|
});
|
|
28
41
|
const [metadataKey, setMetadataKey] = React.useState("");
|
|
29
42
|
const [metadataValue, setMetadataValue] = React.useState("");
|
|
30
43
|
const [inMetadataSection, setInMetadataSection] = React.useState(false);
|
|
31
44
|
const [metadataInputMode, setMetadataInputMode] = React.useState(null);
|
|
32
|
-
const [selectedMetadataIndex, setSelectedMetadataIndex] = React.useState(
|
|
45
|
+
const [selectedMetadataIndex, setSelectedMetadataIndex] = React.useState(0);
|
|
33
46
|
const [creating, setCreating] = React.useState(false);
|
|
34
47
|
const [result, setResult] = React.useState(null);
|
|
35
48
|
const [error, setError] = React.useState(null);
|
|
36
49
|
const baseFields = [
|
|
37
50
|
{ key: "create", label: "Devbox Create", type: "action" },
|
|
38
|
-
{ key: "name", label: "Name", type: "text" },
|
|
51
|
+
{ key: "name", label: "Name", type: "text", placeholder: "my-devbox" },
|
|
39
52
|
{ key: "architecture", label: "Architecture", type: "select" },
|
|
40
53
|
{ key: "resource_size", label: "Resource Size", type: "select" },
|
|
41
54
|
];
|
|
42
55
|
// Add custom resource fields if CUSTOM_SIZE is selected
|
|
43
56
|
const customFields = formData.resource_size === "CUSTOM_SIZE"
|
|
44
57
|
? [
|
|
45
|
-
{
|
|
58
|
+
{
|
|
59
|
+
key: "custom_cpu",
|
|
60
|
+
label: "CPU Cores (2-16, even)",
|
|
61
|
+
type: "text",
|
|
62
|
+
placeholder: "4",
|
|
63
|
+
},
|
|
46
64
|
{
|
|
47
65
|
key: "custom_memory",
|
|
48
66
|
label: "Memory GB (2-64, even)",
|
|
49
67
|
type: "text",
|
|
68
|
+
placeholder: "8",
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
key: "custom_disk",
|
|
72
|
+
label: "Disk GB (2-64, even)",
|
|
73
|
+
type: "text",
|
|
74
|
+
placeholder: "16",
|
|
50
75
|
},
|
|
51
|
-
{ key: "custom_disk", label: "Disk GB (2-64, even)", type: "text" },
|
|
52
76
|
]
|
|
53
77
|
: [];
|
|
54
78
|
const remainingFields = [
|
|
55
|
-
{
|
|
56
|
-
|
|
57
|
-
|
|
79
|
+
{
|
|
80
|
+
key: "keep_alive",
|
|
81
|
+
label: "Keep Alive (seconds)",
|
|
82
|
+
type: "text",
|
|
83
|
+
placeholder: "3600",
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
key: "blueprint_id",
|
|
87
|
+
label: "Blueprint ID (optional)",
|
|
88
|
+
type: "text",
|
|
89
|
+
placeholder: "bpt_xxx",
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
key: "snapshot_id",
|
|
93
|
+
label: "Snapshot ID (optional)",
|
|
94
|
+
type: "text",
|
|
95
|
+
placeholder: "snp_xxx",
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
key: "network_policy_id",
|
|
99
|
+
label: "Network Policy ID (optional)",
|
|
100
|
+
type: "text",
|
|
101
|
+
placeholder: "np_xxx",
|
|
102
|
+
},
|
|
58
103
|
{ key: "metadata", label: "Metadata (optional)", type: "metadata" },
|
|
59
104
|
];
|
|
60
105
|
const fields = [...baseFields, ...customFields, ...remainingFields];
|
|
61
|
-
const architectures = ["arm64", "x86_64"];
|
|
62
|
-
const resourceSizes = [
|
|
63
|
-
"X_SMALL",
|
|
64
|
-
"SMALL",
|
|
65
|
-
"MEDIUM",
|
|
66
|
-
"LARGE",
|
|
67
|
-
"X_LARGE",
|
|
68
|
-
"XX_LARGE",
|
|
69
|
-
"CUSTOM_SIZE",
|
|
70
|
-
];
|
|
71
106
|
const currentFieldIndex = fields.findIndex((f) => f.key === currentField);
|
|
72
107
|
// Handle Ctrl+C to exit
|
|
73
108
|
useExitOnCtrlC();
|
|
109
|
+
// Select navigation handlers using shared hook
|
|
110
|
+
const handleArchitectureNav = useFormSelectNavigation(formData.architecture, architectures, (value) => setFormData({ ...formData, architecture: value }), currentField === "architecture");
|
|
111
|
+
const handleResourceSizeNav = useFormSelectNavigation(formData.resource_size || "SMALL", resourceSizes, (value) => setFormData({ ...formData, resource_size: value }), currentField === "resource_size");
|
|
74
112
|
useInput((input, key) => {
|
|
75
113
|
// Handle result screen
|
|
76
114
|
if (result) {
|
|
@@ -98,22 +136,7 @@ export const DevboxCreatePage = ({ onBack, onCreate, initialBlueprintId, initial
|
|
|
98
136
|
if (creating) {
|
|
99
137
|
return;
|
|
100
138
|
}
|
|
101
|
-
//
|
|
102
|
-
if (input === "q" || key.escape) {
|
|
103
|
-
onBack();
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
// Submit form
|
|
107
|
-
if (input === "s" && key.ctrl) {
|
|
108
|
-
handleCreate();
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
// Handle Enter on create field
|
|
112
|
-
if (currentField === "create" && key.return) {
|
|
113
|
-
handleCreate();
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
// Handle metadata section
|
|
139
|
+
// Handle metadata section FIRST (before general escape handler)
|
|
117
140
|
if (inMetadataSection) {
|
|
118
141
|
const metadataKeys = Object.keys(formData.metadata);
|
|
119
142
|
// Selection model: 0 = "Add new", 1..n = Existing items, n+1 = "Done"
|
|
@@ -213,14 +236,33 @@ export const DevboxCreatePage = ({ onBack, onCreate, initialBlueprintId, initial
|
|
|
213
236
|
}
|
|
214
237
|
return;
|
|
215
238
|
}
|
|
216
|
-
//
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
239
|
+
// Back to list (only when not in metadata section)
|
|
240
|
+
if (input === "q" || key.escape) {
|
|
241
|
+
onBack();
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
// Submit form
|
|
245
|
+
if (input === "s" && key.ctrl) {
|
|
246
|
+
handleCreate();
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
// Handle Enter on create field
|
|
250
|
+
if (currentField === "create" && key.return) {
|
|
251
|
+
handleCreate();
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
// Handle select field navigation using shared hooks
|
|
255
|
+
if (handleArchitectureNav(input, key))
|
|
256
|
+
return;
|
|
257
|
+
if (handleResourceSizeNav(input, key))
|
|
258
|
+
return;
|
|
259
|
+
// Navigation (up/down arrows and tab/shift+tab)
|
|
260
|
+
if ((key.upArrow || (key.tab && key.shift)) && currentFieldIndex > 0) {
|
|
220
261
|
setCurrentField(fields[currentFieldIndex - 1].key);
|
|
221
262
|
return;
|
|
222
263
|
}
|
|
223
|
-
if (key.downArrow
|
|
264
|
+
if ((key.downArrow || (key.tab && !key.shift)) &&
|
|
265
|
+
currentFieldIndex < fields.length - 1) {
|
|
224
266
|
setCurrentField(fields[currentFieldIndex + 1].key);
|
|
225
267
|
return;
|
|
226
268
|
}
|
|
@@ -230,33 +272,6 @@ export const DevboxCreatePage = ({ onBack, onCreate, initialBlueprintId, initial
|
|
|
230
272
|
setSelectedMetadataIndex(0); // Start at "add new" row
|
|
231
273
|
return;
|
|
232
274
|
}
|
|
233
|
-
// Handle select fields
|
|
234
|
-
if (field && field.type === "select" && (key.leftArrow || key.rightArrow)) {
|
|
235
|
-
if (currentField === "architecture") {
|
|
236
|
-
const currentIndex = architectures.indexOf(formData.architecture);
|
|
237
|
-
const newIndex = key.leftArrow
|
|
238
|
-
? Math.max(0, currentIndex - 1)
|
|
239
|
-
: Math.min(architectures.length - 1, currentIndex + 1);
|
|
240
|
-
setFormData({
|
|
241
|
-
...formData,
|
|
242
|
-
architecture: architectures[newIndex],
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
|
-
else if (currentField === "resource_size") {
|
|
246
|
-
// Find current index, defaulting to 0 if not found (e.g., empty string)
|
|
247
|
-
const currentSize = formData.resource_size || "SMALL";
|
|
248
|
-
const currentIndex = resourceSizes.indexOf(currentSize);
|
|
249
|
-
const safeIndex = currentIndex === -1 ? 0 : currentIndex;
|
|
250
|
-
const newIndex = key.leftArrow
|
|
251
|
-
? Math.max(0, safeIndex - 1)
|
|
252
|
-
: Math.min(resourceSizes.length - 1, safeIndex + 1);
|
|
253
|
-
setFormData({
|
|
254
|
-
...formData,
|
|
255
|
-
resource_size: resourceSizes[newIndex],
|
|
256
|
-
});
|
|
257
|
-
}
|
|
258
|
-
return;
|
|
259
|
-
}
|
|
260
275
|
});
|
|
261
276
|
// Validate custom resource configuration
|
|
262
277
|
const validateCustomResources = () => {
|
|
@@ -329,6 +344,9 @@ export const DevboxCreatePage = ({ onBack, onCreate, initialBlueprintId, initial
|
|
|
329
344
|
if (formData.snapshot_id) {
|
|
330
345
|
createParams.snapshot_id = formData.snapshot_id;
|
|
331
346
|
}
|
|
347
|
+
if (formData.network_policy_id) {
|
|
348
|
+
launchParameters.network_policy_id = formData.network_policy_id;
|
|
349
|
+
}
|
|
332
350
|
if (Object.keys(launchParameters).length > 0) {
|
|
333
351
|
createParams.launch_parameters = launchParameters;
|
|
334
352
|
}
|
|
@@ -344,11 +362,14 @@ export const DevboxCreatePage = ({ onBack, onCreate, initialBlueprintId, initial
|
|
|
344
362
|
};
|
|
345
363
|
// Result screen
|
|
346
364
|
if (result) {
|
|
347
|
-
return (_jsxs(_Fragment, { children: [_jsx(Breadcrumb, { items: [{ label: "Devboxes" }, { label: "Create", active: true }] }), _jsx(SuccessMessage, { message: "Devbox 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 || "(none)"] }) }), _jsx(Box, { children: _jsxs(Text, { color: colors.textDim, dimColor: true, children: ["Status: ", result.status] }) })] }), _jsx(
|
|
365
|
+
return (_jsxs(_Fragment, { children: [_jsx(Breadcrumb, { items: [{ label: "Devboxes" }, { label: "Create", active: true }] }), _jsx(SuccessMessage, { message: "Devbox 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 || "(none)"] }) }), _jsx(Box, { children: _jsxs(Text, { color: colors.textDim, dimColor: true, children: ["Status: ", result.status] }) })] }), _jsx(NavigationTips, { tips: [{ key: "Enter/q/esc", label: "Return to list" }] })] }));
|
|
348
366
|
}
|
|
349
367
|
// Error screen
|
|
350
368
|
if (error) {
|
|
351
|
-
return (_jsxs(_Fragment, { children: [_jsx(Breadcrumb, { items: [{ label: "Devboxes" }, { label: "Create", active: true }] }), _jsx(ErrorMessage, { message: "Failed to create devbox", error: error }), _jsx(
|
|
369
|
+
return (_jsxs(_Fragment, { children: [_jsx(Breadcrumb, { items: [{ label: "Devboxes" }, { label: "Create", active: true }] }), _jsx(ErrorMessage, { message: "Failed to create devbox", error: error }), _jsx(NavigationTips, { tips: [
|
|
370
|
+
{ key: "Enter/r", label: "Retry" },
|
|
371
|
+
{ key: "q/esc", label: "Cancel" },
|
|
372
|
+
] })] }));
|
|
352
373
|
}
|
|
353
374
|
// Creating screen
|
|
354
375
|
if (creating) {
|
|
@@ -359,24 +380,14 @@ export const DevboxCreatePage = ({ onBack, onCreate, initialBlueprintId, initial
|
|
|
359
380
|
const isActive = currentField === field.key;
|
|
360
381
|
const fieldData = formData[field.key];
|
|
361
382
|
if (field.type === "action") {
|
|
362
|
-
return (
|
|
383
|
+
return (_jsx(FormActionButton, { label: field.label, isActive: isActive, hint: "[Enter to create]" }, field.key));
|
|
363
384
|
}
|
|
364
385
|
if (field.type === "text") {
|
|
365
|
-
return (
|
|
366
|
-
setFormData({ ...formData, [field.key]: value });
|
|
367
|
-
}, placeholder: field.key === "name"
|
|
368
|
-
? "my-devbox"
|
|
369
|
-
: field.key === "keep_alive"
|
|
370
|
-
? "3600"
|
|
371
|
-
: field.key === "blueprint_id"
|
|
372
|
-
? "bp_xxx"
|
|
373
|
-
: field.key === "snapshot_id"
|
|
374
|
-
? "snap_xxx"
|
|
375
|
-
: "" })) : (_jsx(Text, { color: colors.text, children: String(fieldData || "(empty)") }))] }, field.key));
|
|
386
|
+
return (_jsx(FormTextInput, { label: field.label, value: String(fieldData || ""), onChange: (value) => setFormData({ ...formData, [field.key]: value }), isActive: isActive, placeholder: field.placeholder }, field.key));
|
|
376
387
|
}
|
|
377
388
|
if (field.type === "select") {
|
|
378
389
|
const value = fieldData;
|
|
379
|
-
return (
|
|
390
|
+
return (_jsx(FormSelect, { label: field.label, value: value || "", options: field.key === "architecture" ? architectures : resourceSizes, onChange: (newValue) => setFormData({ ...formData, [field.key]: newValue }), isActive: isActive }, field.key));
|
|
380
391
|
}
|
|
381
392
|
if (field.type === "metadata") {
|
|
382
393
|
if (!inMetadataSection) {
|
|
@@ -413,5 +424,8 @@ export const DevboxCreatePage = ({ onBack, onCreate, initialBlueprintId, initial
|
|
|
413
424
|
}
|
|
414
425
|
return null;
|
|
415
426
|
}) }), formData.resource_size === "CUSTOM_SIZE" &&
|
|
416
|
-
validateCustomResources() && (_jsxs(Box, { borderStyle: "round", borderColor: colors.error, paddingX: 1, paddingY: 0, marginTop: 1, children: [_jsxs(Text, { color: colors.error, bold: true, children: [figures.cross, " Validation Error"] }), _jsx(Text, { color: colors.error, dimColor: true, children: validateCustomResources() })] })), !inMetadataSection && (_jsx(
|
|
427
|
+
validateCustomResources() && (_jsxs(Box, { borderStyle: "round", borderColor: colors.error, paddingX: 1, paddingY: 0, marginTop: 1, children: [_jsxs(Text, { color: colors.error, bold: true, children: [figures.cross, " Validation Error"] }), _jsx(Text, { color: colors.error, dimColor: true, children: validateCustomResources() })] })), !inMetadataSection && (_jsx(NavigationTips, { showArrows: true, tips: [
|
|
428
|
+
{ key: "Enter", label: "Create" },
|
|
429
|
+
{ key: "q", label: "Cancel" },
|
|
430
|
+
] }))] }));
|
|
417
431
|
};
|