@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.
- package/README.md +19 -5
- 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 +125 -109
- package/dist/commands/devbox/tunnel.js +4 -19
- package/dist/commands/gateway-config/create.js +44 -0
- package/dist/commands/gateway-config/delete.js +21 -0
- package/dist/commands/gateway-config/get.js +15 -0
- package/dist/commands/gateway-config/list.js +493 -0
- package/dist/commands/gateway-config/update.js +60 -0
- package/dist/commands/menu.js +2 -1
- package/dist/commands/secret/list.js +379 -4
- package/dist/commands/snapshot/list.js +11 -2
- package/dist/commands/snapshot/prune.js +265 -0
- package/dist/components/BenchmarkMenu.js +108 -0
- package/dist/components/DetailedInfoView.js +20 -0
- package/dist/components/DevboxActionsMenu.js +9 -61
- package/dist/components/DevboxCreatePage.js +531 -14
- package/dist/components/DevboxDetailPage.js +27 -22
- package/dist/components/GatewayConfigCreatePage.js +265 -0
- package/dist/components/LogsViewer.js +6 -40
- package/dist/components/MainMenu.js +63 -22
- package/dist/components/ResourceDetailPage.js +143 -160
- package/dist/components/ResourceListView.js +3 -33
- package/dist/components/ResourcePicker.js +220 -0
- package/dist/components/SecretCreatePage.js +183 -0
- package/dist/components/SettingsMenu.js +95 -0
- package/dist/components/StateHistory.js +1 -20
- package/dist/components/StatusBadge.js +80 -0
- 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 +99 -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 +29 -0
- package/dist/screens/BenchmarkRunDetailScreen.js +425 -0
- package/dist/screens/BenchmarkRunListScreen.js +275 -0
- 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/MenuScreen.js +5 -2
- package/dist/screens/ScenarioRunDetailScreen.js +226 -0
- package/dist/screens/ScenarioRunListScreen.js +245 -0
- package/dist/screens/SecretCreateScreen.js +7 -0
- package/dist/screens/SecretDetailScreen.js +198 -0
- package/dist/screens/SecretListScreen.js +7 -0
- package/dist/screens/SettingsMenuScreen.js +26 -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 +120 -0
- package/dist/services/gatewayConfigService.js +114 -0
- package/dist/services/scenarioService.js +34 -0
- package/dist/store/benchmarkJobStore.js +66 -0
- package/dist/store/benchmarkStore.js +183 -0
- package/dist/store/betaFeatureStore.js +47 -0
- package/dist/store/gatewayConfigStore.js +83 -0
- package/dist/store/index.js +1 -0
- package/dist/utils/browser.js +22 -0
- package/dist/utils/clipboard.js +41 -0
- package/dist/utils/commands.js +80 -0
- package/dist/utils/config.js +8 -0
- package/dist/utils/time.js +121 -0
- package/package.json +42 -43
|
@@ -0,0 +1,493 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { Box, Text, useInput, useApp } from "ink";
|
|
4
|
+
import figures from "figures";
|
|
5
|
+
import { getClient } from "../../utils/client.js";
|
|
6
|
+
import { Header } from "../../components/Header.js";
|
|
7
|
+
import { SpinnerComponent } from "../../components/Spinner.js";
|
|
8
|
+
import { ErrorMessage } from "../../components/ErrorMessage.js";
|
|
9
|
+
import { SuccessMessage } from "../../components/SuccessMessage.js";
|
|
10
|
+
import { Breadcrumb } from "../../components/Breadcrumb.js";
|
|
11
|
+
import { NavigationTips } from "../../components/NavigationTips.js";
|
|
12
|
+
import { Table, createTextColumn } from "../../components/Table.js";
|
|
13
|
+
import { ActionsPopup } from "../../components/ActionsPopup.js";
|
|
14
|
+
import { formatTimeAgo } from "../../components/ResourceListView.js";
|
|
15
|
+
import { SearchBar } from "../../components/SearchBar.js";
|
|
16
|
+
import { output, outputError } from "../../utils/output.js";
|
|
17
|
+
import { colors } from "../../utils/theme.js";
|
|
18
|
+
import { useViewportHeight } from "../../hooks/useViewportHeight.js";
|
|
19
|
+
import { useExitOnCtrlC } from "../../hooks/useExitOnCtrlC.js";
|
|
20
|
+
import { useCursorPagination } from "../../hooks/useCursorPagination.js";
|
|
21
|
+
import { useListSearch } from "../../hooks/useListSearch.js";
|
|
22
|
+
import { useNavigation } from "../../store/navigationStore.js";
|
|
23
|
+
import { GatewayConfigCreatePage } from "../../components/GatewayConfigCreatePage.js";
|
|
24
|
+
import { ConfirmationPrompt } from "../../components/ConfirmationPrompt.js";
|
|
25
|
+
const DEFAULT_PAGE_SIZE = 10;
|
|
26
|
+
/**
|
|
27
|
+
* Get a display label for the auth mechanism type
|
|
28
|
+
*/
|
|
29
|
+
function getAuthTypeLabel(authMechanism) {
|
|
30
|
+
if (authMechanism.type === "bearer") {
|
|
31
|
+
return "Bearer";
|
|
32
|
+
}
|
|
33
|
+
if (authMechanism.type === "header") {
|
|
34
|
+
return authMechanism.key ? `Header: ${authMechanism.key}` : "Header";
|
|
35
|
+
}
|
|
36
|
+
return authMechanism.type;
|
|
37
|
+
}
|
|
38
|
+
const ListGatewayConfigsUI = ({ onBack, onExit, }) => {
|
|
39
|
+
const { exit: inkExit } = useApp();
|
|
40
|
+
const { navigate } = useNavigation();
|
|
41
|
+
const [selectedIndex, setSelectedIndex] = React.useState(0);
|
|
42
|
+
const [showPopup, setShowPopup] = React.useState(false);
|
|
43
|
+
const [selectedOperation, setSelectedOperation] = React.useState(0);
|
|
44
|
+
const [selectedConfig, setSelectedConfig] = React.useState(null);
|
|
45
|
+
const [executingOperation, setExecutingOperation] = React.useState(null);
|
|
46
|
+
const [operationResult, setOperationResult] = React.useState(null);
|
|
47
|
+
const [operationError, setOperationError] = React.useState(null);
|
|
48
|
+
const [operationLoading, setOperationLoading] = React.useState(false);
|
|
49
|
+
const [showCreateConfig, setShowCreateConfig] = React.useState(false);
|
|
50
|
+
const [showEditConfig, setShowEditConfig] = React.useState(false);
|
|
51
|
+
const [editingConfig, setEditingConfig] = React.useState(null);
|
|
52
|
+
const [showDeleteConfirm, setShowDeleteConfirm] = React.useState(false);
|
|
53
|
+
// Search state
|
|
54
|
+
const search = useListSearch({
|
|
55
|
+
onSearchSubmit: () => setSelectedIndex(0),
|
|
56
|
+
onSearchClear: () => setSelectedIndex(0),
|
|
57
|
+
});
|
|
58
|
+
// Calculate overhead for viewport height
|
|
59
|
+
const overhead = 13 + search.getSearchOverhead();
|
|
60
|
+
const { viewportHeight, terminalWidth } = useViewportHeight({
|
|
61
|
+
overhead,
|
|
62
|
+
minHeight: 5,
|
|
63
|
+
});
|
|
64
|
+
const PAGE_SIZE = viewportHeight;
|
|
65
|
+
// All width constants
|
|
66
|
+
const fixedWidth = 6; // border + padding
|
|
67
|
+
const idWidth = 25;
|
|
68
|
+
const authWidth = 15;
|
|
69
|
+
const timeWidth = 20;
|
|
70
|
+
const showEndpoint = terminalWidth >= 100;
|
|
71
|
+
const endpointWidth = Math.max(20, terminalWidth >= 140 ? 40 : 25);
|
|
72
|
+
// Name width uses remaining space after fixed columns
|
|
73
|
+
const baseWidth = fixedWidth + idWidth + authWidth + timeWidth;
|
|
74
|
+
const optionalWidth = showEndpoint ? endpointWidth : 0;
|
|
75
|
+
const remainingWidth = terminalWidth - baseWidth - optionalWidth;
|
|
76
|
+
const nameWidth = Math.min(80, Math.max(15, remainingWidth));
|
|
77
|
+
// Fetch function for pagination hook
|
|
78
|
+
const fetchPage = React.useCallback(async (params) => {
|
|
79
|
+
const client = getClient();
|
|
80
|
+
const pageConfigs = [];
|
|
81
|
+
// Build query params
|
|
82
|
+
const queryParams = {
|
|
83
|
+
limit: params.limit,
|
|
84
|
+
};
|
|
85
|
+
if (params.startingAt) {
|
|
86
|
+
queryParams.starting_after = params.startingAt;
|
|
87
|
+
}
|
|
88
|
+
if (search.submittedSearchQuery) {
|
|
89
|
+
queryParams.name = search.submittedSearchQuery;
|
|
90
|
+
}
|
|
91
|
+
// Fetch ONE page only
|
|
92
|
+
const page = (await client.gatewayConfigs.list(queryParams));
|
|
93
|
+
// Extract data and create defensive copies
|
|
94
|
+
if (page.gateway_configs && Array.isArray(page.gateway_configs)) {
|
|
95
|
+
page.gateway_configs.forEach((g) => {
|
|
96
|
+
pageConfigs.push({
|
|
97
|
+
id: g.id,
|
|
98
|
+
name: g.name,
|
|
99
|
+
description: g.description,
|
|
100
|
+
endpoint: g.endpoint,
|
|
101
|
+
create_time_ms: g.create_time_ms,
|
|
102
|
+
auth_mechanism: {
|
|
103
|
+
type: g.auth_mechanism.type,
|
|
104
|
+
key: g.auth_mechanism.key,
|
|
105
|
+
},
|
|
106
|
+
account_id: g.account_id,
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
const result = {
|
|
111
|
+
items: pageConfigs,
|
|
112
|
+
hasMore: page.has_more || false,
|
|
113
|
+
totalCount: page.total_count || pageConfigs.length,
|
|
114
|
+
};
|
|
115
|
+
return result;
|
|
116
|
+
}, [search.submittedSearchQuery]);
|
|
117
|
+
// Use the shared pagination hook
|
|
118
|
+
const { items: configs, loading, navigating, error, currentPage, hasMore, hasPrev, totalCount, nextPage, prevPage, refresh, } = useCursorPagination({
|
|
119
|
+
fetchPage,
|
|
120
|
+
pageSize: PAGE_SIZE,
|
|
121
|
+
getItemId: (config) => config.id,
|
|
122
|
+
pollInterval: 5000,
|
|
123
|
+
pollingEnabled: !showPopup &&
|
|
124
|
+
!executingOperation &&
|
|
125
|
+
!showCreateConfig &&
|
|
126
|
+
!showEditConfig &&
|
|
127
|
+
!showDeleteConfirm &&
|
|
128
|
+
!search.searchMode,
|
|
129
|
+
deps: [PAGE_SIZE, search.submittedSearchQuery],
|
|
130
|
+
});
|
|
131
|
+
// Operations for a specific gateway config (shown in popup)
|
|
132
|
+
const operations = React.useMemo(() => [
|
|
133
|
+
{
|
|
134
|
+
key: "view_details",
|
|
135
|
+
label: "View Details",
|
|
136
|
+
color: colors.primary,
|
|
137
|
+
icon: figures.pointer,
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
key: "edit",
|
|
141
|
+
label: "Edit Gateway Config",
|
|
142
|
+
color: colors.warning,
|
|
143
|
+
icon: figures.pointer,
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
key: "delete",
|
|
147
|
+
label: "Delete Gateway Config",
|
|
148
|
+
color: colors.error,
|
|
149
|
+
icon: figures.cross,
|
|
150
|
+
},
|
|
151
|
+
], []);
|
|
152
|
+
// Build columns
|
|
153
|
+
const columns = React.useMemo(() => [
|
|
154
|
+
createTextColumn("id", "ID", (config) => config.id, {
|
|
155
|
+
width: idWidth + 1,
|
|
156
|
+
color: colors.idColor,
|
|
157
|
+
dimColor: false,
|
|
158
|
+
bold: false,
|
|
159
|
+
}),
|
|
160
|
+
createTextColumn("name", "Name", (config) => config.name || "", {
|
|
161
|
+
width: nameWidth,
|
|
162
|
+
}),
|
|
163
|
+
...(showEndpoint
|
|
164
|
+
? [
|
|
165
|
+
createTextColumn("endpoint", "Endpoint", (config) => config.endpoint || "", {
|
|
166
|
+
width: endpointWidth,
|
|
167
|
+
color: colors.textDim,
|
|
168
|
+
dimColor: false,
|
|
169
|
+
bold: false,
|
|
170
|
+
}),
|
|
171
|
+
]
|
|
172
|
+
: []),
|
|
173
|
+
createTextColumn("auth", "Auth", (config) => getAuthTypeLabel(config.auth_mechanism), {
|
|
174
|
+
width: authWidth,
|
|
175
|
+
color: colors.info,
|
|
176
|
+
dimColor: false,
|
|
177
|
+
bold: false,
|
|
178
|
+
}),
|
|
179
|
+
createTextColumn("created", "Created", (config) => config.create_time_ms ? formatTimeAgo(config.create_time_ms) : "", {
|
|
180
|
+
width: timeWidth,
|
|
181
|
+
color: colors.textDim,
|
|
182
|
+
dimColor: false,
|
|
183
|
+
bold: false,
|
|
184
|
+
}),
|
|
185
|
+
], [idWidth, nameWidth, endpointWidth, authWidth, timeWidth, showEndpoint]);
|
|
186
|
+
// Handle Ctrl+C to exit
|
|
187
|
+
useExitOnCtrlC();
|
|
188
|
+
// Ensure selected index is within bounds
|
|
189
|
+
React.useEffect(() => {
|
|
190
|
+
if (configs.length > 0 && selectedIndex >= configs.length) {
|
|
191
|
+
setSelectedIndex(Math.max(0, configs.length - 1));
|
|
192
|
+
}
|
|
193
|
+
}, [configs.length, selectedIndex]);
|
|
194
|
+
const selectedConfigItem = configs[selectedIndex];
|
|
195
|
+
// Calculate pagination info for display
|
|
196
|
+
const totalPages = Math.max(1, Math.ceil(totalCount / PAGE_SIZE));
|
|
197
|
+
const startIndex = currentPage * PAGE_SIZE;
|
|
198
|
+
const endIndex = startIndex + configs.length;
|
|
199
|
+
const executeOperation = async (config, operationKey) => {
|
|
200
|
+
const client = getClient();
|
|
201
|
+
if (!config)
|
|
202
|
+
return;
|
|
203
|
+
try {
|
|
204
|
+
setOperationLoading(true);
|
|
205
|
+
switch (operationKey) {
|
|
206
|
+
case "delete":
|
|
207
|
+
await client.gatewayConfigs.delete(config.id);
|
|
208
|
+
setOperationResult(`Gateway config "${config.name}" deleted successfully`);
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
catch (err) {
|
|
213
|
+
setOperationError(err);
|
|
214
|
+
}
|
|
215
|
+
finally {
|
|
216
|
+
setOperationLoading(false);
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
useInput((input, key) => {
|
|
220
|
+
// Handle search mode input
|
|
221
|
+
if (search.searchMode) {
|
|
222
|
+
if (key.escape) {
|
|
223
|
+
search.cancelSearch();
|
|
224
|
+
}
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
// Handle operation result display
|
|
228
|
+
if (operationResult || operationError) {
|
|
229
|
+
if (input === "q" || key.escape || key.return) {
|
|
230
|
+
const wasDelete = executingOperation === "delete";
|
|
231
|
+
const hadError = operationError !== null;
|
|
232
|
+
setOperationResult(null);
|
|
233
|
+
setOperationError(null);
|
|
234
|
+
setExecutingOperation(null);
|
|
235
|
+
setSelectedConfig(null);
|
|
236
|
+
// Refresh the list after delete to show updated data
|
|
237
|
+
if (wasDelete && !hadError) {
|
|
238
|
+
setTimeout(() => refresh(), 0);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
// Handle create config screen
|
|
244
|
+
if (showCreateConfig) {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
// Handle edit config screen
|
|
248
|
+
if (showEditConfig) {
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
// Handle popup navigation
|
|
252
|
+
if (showPopup) {
|
|
253
|
+
if (key.upArrow && selectedOperation > 0) {
|
|
254
|
+
setSelectedOperation(selectedOperation - 1);
|
|
255
|
+
}
|
|
256
|
+
else if (key.downArrow && selectedOperation < operations.length - 1) {
|
|
257
|
+
setSelectedOperation(selectedOperation + 1);
|
|
258
|
+
}
|
|
259
|
+
else if (key.return) {
|
|
260
|
+
setShowPopup(false);
|
|
261
|
+
const operationKey = operations[selectedOperation].key;
|
|
262
|
+
if (operationKey === "create") {
|
|
263
|
+
setShowCreateConfig(true);
|
|
264
|
+
}
|
|
265
|
+
else if (operationKey === "view_details") {
|
|
266
|
+
navigate("gateway-config-detail", {
|
|
267
|
+
gatewayConfigId: selectedConfigItem.id,
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
else if (operationKey === "edit") {
|
|
271
|
+
// Show edit form
|
|
272
|
+
setEditingConfig(selectedConfigItem);
|
|
273
|
+
setShowEditConfig(true);
|
|
274
|
+
}
|
|
275
|
+
else if (operationKey === "delete") {
|
|
276
|
+
// Show delete confirmation
|
|
277
|
+
setSelectedConfig(selectedConfigItem);
|
|
278
|
+
setShowDeleteConfirm(true);
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
setSelectedConfig(selectedConfigItem);
|
|
282
|
+
setExecutingOperation(operationKey);
|
|
283
|
+
// Execute immediately with values passed directly
|
|
284
|
+
executeOperation(selectedConfigItem, operationKey);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
else if (input === "c") {
|
|
288
|
+
// Create hotkey
|
|
289
|
+
setShowPopup(false);
|
|
290
|
+
setShowCreateConfig(true);
|
|
291
|
+
}
|
|
292
|
+
else if (input === "v" && selectedConfigItem) {
|
|
293
|
+
// View details hotkey
|
|
294
|
+
setShowPopup(false);
|
|
295
|
+
navigate("gateway-config-detail", {
|
|
296
|
+
gatewayConfigId: selectedConfigItem.id,
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
else if (input === "e" && selectedConfigItem) {
|
|
300
|
+
// Edit hotkey
|
|
301
|
+
setShowPopup(false);
|
|
302
|
+
setEditingConfig(selectedConfigItem);
|
|
303
|
+
setShowEditConfig(true);
|
|
304
|
+
}
|
|
305
|
+
else if (key.escape || input === "q") {
|
|
306
|
+
setShowPopup(false);
|
|
307
|
+
setSelectedOperation(0);
|
|
308
|
+
}
|
|
309
|
+
else if (input === "d") {
|
|
310
|
+
// Delete hotkey - show confirmation
|
|
311
|
+
setShowPopup(false);
|
|
312
|
+
setSelectedConfig(selectedConfigItem);
|
|
313
|
+
setShowDeleteConfirm(true);
|
|
314
|
+
}
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
const pageConfigs = configs.length;
|
|
318
|
+
// Handle list view navigation
|
|
319
|
+
if (key.upArrow && selectedIndex > 0) {
|
|
320
|
+
setSelectedIndex(selectedIndex - 1);
|
|
321
|
+
}
|
|
322
|
+
else if (key.downArrow && selectedIndex < pageConfigs - 1) {
|
|
323
|
+
setSelectedIndex(selectedIndex + 1);
|
|
324
|
+
}
|
|
325
|
+
else if ((input === "n" || key.rightArrow) &&
|
|
326
|
+
!loading &&
|
|
327
|
+
!navigating &&
|
|
328
|
+
hasMore) {
|
|
329
|
+
nextPage();
|
|
330
|
+
setSelectedIndex(0);
|
|
331
|
+
}
|
|
332
|
+
else if ((input === "p" || key.leftArrow) &&
|
|
333
|
+
!loading &&
|
|
334
|
+
!navigating &&
|
|
335
|
+
hasPrev) {
|
|
336
|
+
prevPage();
|
|
337
|
+
setSelectedIndex(0);
|
|
338
|
+
}
|
|
339
|
+
else if (key.return && selectedConfigItem) {
|
|
340
|
+
// Enter key navigates to detail view
|
|
341
|
+
navigate("gateway-config-detail", {
|
|
342
|
+
gatewayConfigId: selectedConfigItem.id,
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
else if (input === "a") {
|
|
346
|
+
setShowPopup(true);
|
|
347
|
+
setSelectedOperation(0);
|
|
348
|
+
}
|
|
349
|
+
else if (input === "c") {
|
|
350
|
+
// Create shortcut
|
|
351
|
+
setShowCreateConfig(true);
|
|
352
|
+
}
|
|
353
|
+
else if (input === "e" && selectedConfigItem) {
|
|
354
|
+
// Edit shortcut
|
|
355
|
+
setEditingConfig(selectedConfigItem);
|
|
356
|
+
setShowEditConfig(true);
|
|
357
|
+
}
|
|
358
|
+
else if (input === "/") {
|
|
359
|
+
search.enterSearchMode();
|
|
360
|
+
}
|
|
361
|
+
else if (key.escape) {
|
|
362
|
+
if (search.handleEscape()) {
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
if (onBack) {
|
|
366
|
+
onBack();
|
|
367
|
+
}
|
|
368
|
+
else if (onExit) {
|
|
369
|
+
onExit();
|
|
370
|
+
}
|
|
371
|
+
else {
|
|
372
|
+
inkExit();
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
});
|
|
376
|
+
// Delete confirmation
|
|
377
|
+
if (showDeleteConfirm && selectedConfig) {
|
|
378
|
+
return (_jsx(ConfirmationPrompt, { title: "Delete Gateway Config", message: `Are you sure you want to delete "${selectedConfig.name}"?`, details: "This action cannot be undone. Any devboxes using this gateway config will no longer have access to it.", breadcrumbItems: [
|
|
379
|
+
{ label: "Gateway Configs" },
|
|
380
|
+
{ label: selectedConfig.name || selectedConfig.id },
|
|
381
|
+
{ label: "Delete", active: true },
|
|
382
|
+
], onConfirm: () => {
|
|
383
|
+
setShowDeleteConfirm(false);
|
|
384
|
+
setExecutingOperation("delete");
|
|
385
|
+
executeOperation(selectedConfig, "delete");
|
|
386
|
+
}, onCancel: () => {
|
|
387
|
+
setShowDeleteConfirm(false);
|
|
388
|
+
setSelectedConfig(null);
|
|
389
|
+
} }));
|
|
390
|
+
}
|
|
391
|
+
// Operation result display
|
|
392
|
+
if (operationResult || operationError) {
|
|
393
|
+
const operationLabel = operations.find((o) => o.key === executingOperation)?.label ||
|
|
394
|
+
"Operation";
|
|
395
|
+
return (_jsxs(_Fragment, { children: [_jsx(Breadcrumb, { items: [
|
|
396
|
+
{ label: "Gateway Configs" },
|
|
397
|
+
{
|
|
398
|
+
label: selectedConfig?.name || selectedConfig?.id || "Config",
|
|
399
|
+
},
|
|
400
|
+
{ label: operationLabel, active: true },
|
|
401
|
+
] }), _jsx(Header, { title: "Operation Result" }), operationResult && _jsx(SuccessMessage, { message: operationResult }), operationError && (_jsx(ErrorMessage, { message: "Operation failed", error: operationError })), _jsx(NavigationTips, { tips: [{ key: "Enter/q/esc", label: "Continue" }] })] }));
|
|
402
|
+
}
|
|
403
|
+
// Operation loading state
|
|
404
|
+
if (operationLoading && selectedConfig) {
|
|
405
|
+
const operationLabel = operations.find((o) => o.key === executingOperation)?.label ||
|
|
406
|
+
"Operation";
|
|
407
|
+
const messages = {
|
|
408
|
+
delete: "Deleting gateway config...",
|
|
409
|
+
};
|
|
410
|
+
return (_jsxs(_Fragment, { children: [_jsx(Breadcrumb, { items: [
|
|
411
|
+
{ label: "Gateway Configs" },
|
|
412
|
+
{ label: selectedConfig.name || selectedConfig.id },
|
|
413
|
+
{ label: operationLabel, active: true },
|
|
414
|
+
] }), _jsx(Header, { title: "Executing Operation" }), _jsx(SpinnerComponent, { message: messages[executingOperation] || "Please wait..." })] }));
|
|
415
|
+
}
|
|
416
|
+
// Create config screen
|
|
417
|
+
if (showCreateConfig) {
|
|
418
|
+
return (_jsx(GatewayConfigCreatePage, { onBack: () => setShowCreateConfig(false), onCreate: (config) => {
|
|
419
|
+
setShowCreateConfig(false);
|
|
420
|
+
navigate("gateway-config-detail", { gatewayConfigId: config.id });
|
|
421
|
+
} }));
|
|
422
|
+
}
|
|
423
|
+
// Edit config screen
|
|
424
|
+
if (showEditConfig && editingConfig) {
|
|
425
|
+
return (_jsx(GatewayConfigCreatePage, { onBack: () => {
|
|
426
|
+
setShowEditConfig(false);
|
|
427
|
+
setEditingConfig(null);
|
|
428
|
+
}, onCreate: () => {
|
|
429
|
+
setShowEditConfig(false);
|
|
430
|
+
setEditingConfig(null);
|
|
431
|
+
// Refresh the list to show updated data
|
|
432
|
+
setTimeout(() => refresh(), 0);
|
|
433
|
+
}, initialConfig: editingConfig }));
|
|
434
|
+
}
|
|
435
|
+
// Loading state
|
|
436
|
+
if (loading && configs.length === 0) {
|
|
437
|
+
return (_jsxs(_Fragment, { children: [_jsx(Breadcrumb, { items: [{ label: "Gateway Configs", active: true }] }), _jsx(SpinnerComponent, { message: "Loading gateway configs..." })] }));
|
|
438
|
+
}
|
|
439
|
+
// Error state
|
|
440
|
+
if (error) {
|
|
441
|
+
return (_jsxs(_Fragment, { children: [_jsx(Breadcrumb, { items: [{ label: "Gateway Configs", active: true }] }), _jsx(ErrorMessage, { message: "Failed to list gateway configs", error: error })] }));
|
|
442
|
+
}
|
|
443
|
+
// Main list view
|
|
444
|
+
return (_jsxs(_Fragment, { children: [_jsx(Breadcrumb, { items: [{ label: "Gateway Configs", active: true }] }), _jsx(SearchBar, { searchMode: search.searchMode, searchQuery: search.searchQuery, submittedSearchQuery: search.submittedSearchQuery, resultCount: totalCount, onSearchChange: search.setSearchQuery, onSearchSubmit: search.submitSearch, placeholder: "Search gateway configs..." }), !showPopup && (_jsx(Table, { data: configs, keyExtractor: (config) => config.id, selectedIndex: selectedIndex, title: `gateway_configs[${totalCount}]`, columns: columns, emptyState: _jsxs(Text, { color: colors.textDim, children: [figures.info, " No gateway configs found. Press [c] to create one."] }) })), !showPopup && (_jsxs(Box, { marginTop: 1, paddingX: 1, children: [_jsxs(Text, { color: colors.primary, bold: true, children: [figures.hamburger, " ", totalCount] }), _jsxs(Text, { color: colors.textDim, dimColor: true, children: [" ", "total"] }), totalPages > 1 && (_jsxs(_Fragment, { children: [_jsxs(Text, { color: colors.textDim, dimColor: true, children: [" ", "\u2022", " "] }), navigating ? (_jsxs(Text, { color: colors.warning, children: [figures.pointer, " Loading page ", currentPage + 1, "..."] })) : (_jsxs(Text, { color: colors.textDim, dimColor: true, children: ["Page ", currentPage + 1, " of ", totalPages] }))] })), _jsxs(Text, { color: colors.textDim, dimColor: true, children: [" ", "\u2022", " "] }), _jsxs(Text, { color: colors.textDim, dimColor: true, children: ["Showing ", startIndex + 1, "-", endIndex, " of ", totalCount] }), search.submittedSearchQuery && (_jsxs(_Fragment, { children: [_jsxs(Text, { color: colors.textDim, dimColor: true, children: [" ", "\u2022", " "] }), _jsxs(Text, { color: colors.warning, children: ["Filtered: \"", search.submittedSearchQuery, "\""] })] }))] })), showPopup && selectedConfigItem && (_jsx(Box, { marginTop: 2, justifyContent: "center", children: _jsx(ActionsPopup, { devbox: selectedConfigItem, operations: operations.map((op) => ({
|
|
445
|
+
key: op.key,
|
|
446
|
+
label: op.label,
|
|
447
|
+
color: op.color,
|
|
448
|
+
icon: op.icon,
|
|
449
|
+
shortcut: op.key === "create"
|
|
450
|
+
? "c"
|
|
451
|
+
: op.key === "view_details"
|
|
452
|
+
? "v"
|
|
453
|
+
: op.key === "edit"
|
|
454
|
+
? "e"
|
|
455
|
+
: op.key === "delete"
|
|
456
|
+
? "d"
|
|
457
|
+
: "",
|
|
458
|
+
})), selectedOperation: selectedOperation, onClose: () => setShowPopup(false) }) })), _jsx(NavigationTips, { showArrows: true, tips: [
|
|
459
|
+
{
|
|
460
|
+
icon: `${figures.arrowLeft}${figures.arrowRight}`,
|
|
461
|
+
label: "Page",
|
|
462
|
+
condition: hasMore || hasPrev,
|
|
463
|
+
},
|
|
464
|
+
{ key: "Enter", label: "Details" },
|
|
465
|
+
{ key: "c", label: "Create" },
|
|
466
|
+
{ key: "e", label: "Edit" },
|
|
467
|
+
{ key: "a", label: "Actions" },
|
|
468
|
+
{ key: "/", label: "Search" },
|
|
469
|
+
{ key: "Esc", label: "Back" },
|
|
470
|
+
] })] }));
|
|
471
|
+
};
|
|
472
|
+
// Export the UI component for use in the main menu
|
|
473
|
+
export { ListGatewayConfigsUI };
|
|
474
|
+
export async function listGatewayConfigs(options = {}) {
|
|
475
|
+
try {
|
|
476
|
+
const client = getClient();
|
|
477
|
+
// Build query params
|
|
478
|
+
const queryParams = {
|
|
479
|
+
limit: DEFAULT_PAGE_SIZE,
|
|
480
|
+
};
|
|
481
|
+
if (options.name) {
|
|
482
|
+
queryParams.name = options.name;
|
|
483
|
+
}
|
|
484
|
+
// Fetch gateway configs
|
|
485
|
+
const page = (await client.gatewayConfigs.list(queryParams));
|
|
486
|
+
// Extract gateway configs array
|
|
487
|
+
const gatewayConfigs = page.gateway_configs || [];
|
|
488
|
+
output(gatewayConfigs, { format: options.output, defaultFormat: "json" });
|
|
489
|
+
}
|
|
490
|
+
catch (error) {
|
|
491
|
+
outputError("Failed to list gateway configs", error);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Update gateway config command
|
|
3
|
+
*/
|
|
4
|
+
import { getClient } from "../../utils/client.js";
|
|
5
|
+
import { output, outputError } from "../../utils/output.js";
|
|
6
|
+
export async function updateGatewayConfig(options) {
|
|
7
|
+
try {
|
|
8
|
+
const client = getClient();
|
|
9
|
+
// Build update params - only include fields that are provided
|
|
10
|
+
const updateParams = {};
|
|
11
|
+
if (options.name) {
|
|
12
|
+
updateParams.name = options.name;
|
|
13
|
+
}
|
|
14
|
+
if (options.endpoint) {
|
|
15
|
+
updateParams.endpoint = options.endpoint;
|
|
16
|
+
}
|
|
17
|
+
if (options.description !== undefined) {
|
|
18
|
+
updateParams.description = options.description;
|
|
19
|
+
}
|
|
20
|
+
// Handle auth mechanism update
|
|
21
|
+
if (options.authType) {
|
|
22
|
+
const authType = options.authType.toLowerCase();
|
|
23
|
+
if (authType !== "bearer" && authType !== "header") {
|
|
24
|
+
outputError("Invalid auth type. Must be 'bearer' or 'header'");
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const authMechanism = {
|
|
28
|
+
type: authType,
|
|
29
|
+
};
|
|
30
|
+
if (authType === "header") {
|
|
31
|
+
if (!options.authKey) {
|
|
32
|
+
outputError("--auth-key is required when auth-type is 'header'");
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
authMechanism.key = options.authKey;
|
|
36
|
+
}
|
|
37
|
+
updateParams.auth_mechanism = authMechanism;
|
|
38
|
+
}
|
|
39
|
+
else if (options.authKey) {
|
|
40
|
+
// If only auth key is provided without auth type, we need the type
|
|
41
|
+
outputError("--auth-type is required when updating --auth-key");
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (Object.keys(updateParams).length === 0) {
|
|
45
|
+
outputError("No update options provided. Use --name, --endpoint, --auth-type, --auth-key, or --description");
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const config = await client.gatewayConfigs.update(options.id, updateParams);
|
|
49
|
+
// Default: just output the ID for easy scripting
|
|
50
|
+
if (!options.output || options.output === "text") {
|
|
51
|
+
console.log(config.id);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
output(config, { format: options.output, defaultFormat: "json" });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
outputError("Failed to update gateway config", error);
|
|
59
|
+
}
|
|
60
|
+
}
|
package/dist/commands/menu.js
CHANGED
|
@@ -4,13 +4,14 @@ import { enterAlternateScreenBuffer, exitAlternateScreenBuffer, clearScreen, } f
|
|
|
4
4
|
import { processUtils } from "../utils/processUtils.js";
|
|
5
5
|
import { Router } from "../router/Router.js";
|
|
6
6
|
import { NavigationProvider } from "../store/navigationStore.js";
|
|
7
|
+
import { BetaFeatureProvider } from "../store/betaFeatureStore.js";
|
|
7
8
|
function AppInner() {
|
|
8
9
|
// NavigationProvider already handles initialScreen and initialParams
|
|
9
10
|
// No need for useEffect here - provider sets state on mount
|
|
10
11
|
return _jsx(Router, {});
|
|
11
12
|
}
|
|
12
13
|
function App({ initialScreen = "menu", focusDevboxId, }) {
|
|
13
|
-
return (_jsx(NavigationProvider, { initialScreen: initialScreen, initialParams: focusDevboxId ? { focusDevboxId } : {}, children: _jsx(AppInner, {}) }));
|
|
14
|
+
return (_jsx(BetaFeatureProvider, { children: _jsx(NavigationProvider, { initialScreen: initialScreen, initialParams: focusDevboxId ? { focusDevboxId } : {}, children: _jsx(AppInner, {}) }) }));
|
|
14
15
|
}
|
|
15
16
|
export async function runMainMenu(initialScreen = "menu", focusDevboxId) {
|
|
16
17
|
enterAlternateScreenBuffer();
|