@runloop/rl-cli 1.8.0 → 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/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 +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/ResourceDetailPage.js +143 -160
- package/dist/components/ResourceListView.js +3 -33
- package/dist/components/ResourcePicker.js +220 -0
- package/dist/components/SecretCreatePage.js +2 -4
- 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/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 +114 -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 +80 -0
- package/dist/utils/time.js +121 -0
- package/package.json +42 -43
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gateway Config Service - Handles all gateway config API calls
|
|
3
|
+
*/
|
|
4
|
+
import { getClient } from "../utils/client.js";
|
|
5
|
+
/**
|
|
6
|
+
* List gateway configs with pagination
|
|
7
|
+
*/
|
|
8
|
+
export async function listGatewayConfigs(options) {
|
|
9
|
+
const client = getClient();
|
|
10
|
+
const queryParams = {
|
|
11
|
+
limit: options.limit,
|
|
12
|
+
};
|
|
13
|
+
if (options.startingAfter) {
|
|
14
|
+
queryParams.starting_after = options.startingAfter;
|
|
15
|
+
}
|
|
16
|
+
if (options.search) {
|
|
17
|
+
queryParams.name = options.search;
|
|
18
|
+
}
|
|
19
|
+
const pagePromise = client.gatewayConfigs.list(queryParams);
|
|
20
|
+
const page = (await pagePromise);
|
|
21
|
+
const gatewayConfigs = [];
|
|
22
|
+
if (page.gateway_configs && Array.isArray(page.gateway_configs)) {
|
|
23
|
+
page.gateway_configs.forEach((g) => {
|
|
24
|
+
// CRITICAL: Truncate all strings to prevent Yoga crashes
|
|
25
|
+
const MAX_ID_LENGTH = 100;
|
|
26
|
+
const MAX_NAME_LENGTH = 200;
|
|
27
|
+
const MAX_DESC_LENGTH = 500;
|
|
28
|
+
const MAX_ENDPOINT_LENGTH = 500;
|
|
29
|
+
gatewayConfigs.push({
|
|
30
|
+
id: String(g.id || "").substring(0, MAX_ID_LENGTH),
|
|
31
|
+
name: String(g.name || "").substring(0, MAX_NAME_LENGTH),
|
|
32
|
+
description: g.description
|
|
33
|
+
? String(g.description).substring(0, MAX_DESC_LENGTH)
|
|
34
|
+
: undefined,
|
|
35
|
+
endpoint: String(g.endpoint || "").substring(0, MAX_ENDPOINT_LENGTH),
|
|
36
|
+
create_time_ms: g.create_time_ms,
|
|
37
|
+
auth_mechanism: {
|
|
38
|
+
type: g.auth_mechanism.type,
|
|
39
|
+
key: g.auth_mechanism.key ?? undefined,
|
|
40
|
+
},
|
|
41
|
+
account_id: g.account_id ?? undefined,
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
const result = {
|
|
46
|
+
gatewayConfigs,
|
|
47
|
+
totalCount: page.total_count || gatewayConfigs.length,
|
|
48
|
+
hasMore: page.has_more || false,
|
|
49
|
+
};
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Get a single gateway config by ID
|
|
54
|
+
*/
|
|
55
|
+
export async function getGatewayConfig(id) {
|
|
56
|
+
const client = getClient();
|
|
57
|
+
const config = await client.gatewayConfigs.retrieve(id);
|
|
58
|
+
return {
|
|
59
|
+
id: config.id,
|
|
60
|
+
name: config.name,
|
|
61
|
+
description: config.description ?? undefined,
|
|
62
|
+
endpoint: config.endpoint,
|
|
63
|
+
create_time_ms: config.create_time_ms,
|
|
64
|
+
auth_mechanism: {
|
|
65
|
+
type: config.auth_mechanism.type,
|
|
66
|
+
key: config.auth_mechanism.key ?? undefined,
|
|
67
|
+
},
|
|
68
|
+
account_id: config.account_id ?? undefined,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Delete a gateway config
|
|
73
|
+
*/
|
|
74
|
+
export async function deleteGatewayConfig(id) {
|
|
75
|
+
const client = getClient();
|
|
76
|
+
await client.gatewayConfigs.delete(id);
|
|
77
|
+
}
|
|
78
|
+
export async function createGatewayConfig(params) {
|
|
79
|
+
const client = getClient();
|
|
80
|
+
const config = await client.gatewayConfigs.create({
|
|
81
|
+
name: params.name,
|
|
82
|
+
endpoint: params.endpoint,
|
|
83
|
+
auth_mechanism: params.auth_mechanism,
|
|
84
|
+
description: params.description,
|
|
85
|
+
});
|
|
86
|
+
return {
|
|
87
|
+
id: config.id,
|
|
88
|
+
name: config.name,
|
|
89
|
+
description: config.description ?? undefined,
|
|
90
|
+
endpoint: config.endpoint,
|
|
91
|
+
create_time_ms: config.create_time_ms,
|
|
92
|
+
auth_mechanism: {
|
|
93
|
+
type: config.auth_mechanism.type,
|
|
94
|
+
key: config.auth_mechanism.key ?? undefined,
|
|
95
|
+
},
|
|
96
|
+
account_id: config.account_id ?? undefined,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
export async function updateGatewayConfig(id, params) {
|
|
100
|
+
const client = getClient();
|
|
101
|
+
const config = await client.gatewayConfigs.update(id, params);
|
|
102
|
+
return {
|
|
103
|
+
id: config.id,
|
|
104
|
+
name: config.name,
|
|
105
|
+
description: config.description ?? undefined,
|
|
106
|
+
endpoint: config.endpoint,
|
|
107
|
+
create_time_ms: config.create_time_ms,
|
|
108
|
+
auth_mechanism: {
|
|
109
|
+
type: config.auth_mechanism.type,
|
|
110
|
+
key: config.auth_mechanism.key ?? undefined,
|
|
111
|
+
},
|
|
112
|
+
account_id: config.account_id ?? undefined,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scenario Service - Handles all scenario-related API calls
|
|
3
|
+
*/
|
|
4
|
+
import { getClient } from "../utils/client.js";
|
|
5
|
+
/**
|
|
6
|
+
* List scenarios with pagination
|
|
7
|
+
*/
|
|
8
|
+
export async function listScenarios(options) {
|
|
9
|
+
const client = getClient();
|
|
10
|
+
const queryParams = {
|
|
11
|
+
limit: options.limit,
|
|
12
|
+
};
|
|
13
|
+
if (options.startingAfter) {
|
|
14
|
+
queryParams.starting_after = options.startingAfter;
|
|
15
|
+
}
|
|
16
|
+
// Use name filter instead of search
|
|
17
|
+
if (options.search) {
|
|
18
|
+
queryParams.name = options.search;
|
|
19
|
+
}
|
|
20
|
+
const page = await client.scenarios.list(queryParams);
|
|
21
|
+
const scenarios = page.scenarios || [];
|
|
22
|
+
return {
|
|
23
|
+
scenarios,
|
|
24
|
+
totalCount: page.total_count || scenarios.length,
|
|
25
|
+
hasMore: page.has_more || false,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Get scenario by ID
|
|
30
|
+
*/
|
|
31
|
+
export async function getScenario(id) {
|
|
32
|
+
const client = getClient();
|
|
33
|
+
return client.scenarios.retrieve(id);
|
|
34
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Benchmark Job Store - Manages benchmark job state
|
|
3
|
+
*/
|
|
4
|
+
import { create } from "zustand";
|
|
5
|
+
const MAX_CACHE_SIZE = 10;
|
|
6
|
+
export const useBenchmarkJobStore = create((set, get) => ({
|
|
7
|
+
// Initial state
|
|
8
|
+
benchmarkJobs: [],
|
|
9
|
+
benchmarkJobsLoading: false,
|
|
10
|
+
benchmarkJobsError: null,
|
|
11
|
+
benchmarkJobsTotalCount: 0,
|
|
12
|
+
benchmarkJobsHasMore: false,
|
|
13
|
+
benchmarkJobsCurrentPage: 0,
|
|
14
|
+
// Selection
|
|
15
|
+
selectedBenchmarkJobIndex: 0,
|
|
16
|
+
// Cache
|
|
17
|
+
benchmarkJobPageCache: new Map(),
|
|
18
|
+
// Actions
|
|
19
|
+
setBenchmarkJobs: (jobs) => set({ benchmarkJobs: jobs }),
|
|
20
|
+
setBenchmarkJobsLoading: (loading) => set({ benchmarkJobsLoading: loading }),
|
|
21
|
+
setBenchmarkJobsError: (error) => set({ benchmarkJobsError: error }),
|
|
22
|
+
setBenchmarkJobsTotalCount: (count) => set({ benchmarkJobsTotalCount: count }),
|
|
23
|
+
setBenchmarkJobsHasMore: (hasMore) => set({ benchmarkJobsHasMore: hasMore }),
|
|
24
|
+
setBenchmarkJobsCurrentPage: (page) => set({ benchmarkJobsCurrentPage: page }),
|
|
25
|
+
setSelectedBenchmarkJobIndex: (index) => set({ selectedBenchmarkJobIndex: index }),
|
|
26
|
+
// Cache management
|
|
27
|
+
cacheBenchmarkJobPage: (page, data) => {
|
|
28
|
+
const state = get();
|
|
29
|
+
const cache = state.benchmarkJobPageCache;
|
|
30
|
+
if (cache.size >= MAX_CACHE_SIZE) {
|
|
31
|
+
const oldestKey = cache.keys().next().value;
|
|
32
|
+
if (oldestKey !== undefined) {
|
|
33
|
+
cache.delete(oldestKey);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const plainData = data.map((d) => JSON.parse(JSON.stringify(d)));
|
|
37
|
+
cache.set(page, plainData);
|
|
38
|
+
set({});
|
|
39
|
+
},
|
|
40
|
+
getCachedBenchmarkJobPage: (page) => {
|
|
41
|
+
return get().benchmarkJobPageCache.get(page);
|
|
42
|
+
},
|
|
43
|
+
clearCache: () => {
|
|
44
|
+
const state = get();
|
|
45
|
+
state.benchmarkJobPageCache.clear();
|
|
46
|
+
set({ benchmarkJobPageCache: new Map() });
|
|
47
|
+
},
|
|
48
|
+
clearAll: () => {
|
|
49
|
+
const state = get();
|
|
50
|
+
state.benchmarkJobPageCache.clear();
|
|
51
|
+
set({
|
|
52
|
+
benchmarkJobs: [],
|
|
53
|
+
benchmarkJobsLoading: false,
|
|
54
|
+
benchmarkJobsError: null,
|
|
55
|
+
benchmarkJobsTotalCount: 0,
|
|
56
|
+
benchmarkJobsHasMore: false,
|
|
57
|
+
benchmarkJobsCurrentPage: 0,
|
|
58
|
+
selectedBenchmarkJobIndex: 0,
|
|
59
|
+
benchmarkJobPageCache: new Map(),
|
|
60
|
+
});
|
|
61
|
+
},
|
|
62
|
+
getSelectedBenchmarkJob: () => {
|
|
63
|
+
const state = get();
|
|
64
|
+
return state.benchmarkJobs[state.selectedBenchmarkJobIndex];
|
|
65
|
+
},
|
|
66
|
+
}));
|
|
@@ -4,6 +4,15 @@
|
|
|
4
4
|
import { create } from "zustand";
|
|
5
5
|
const MAX_CACHE_SIZE = 10;
|
|
6
6
|
export const useBenchmarkStore = create((set, get) => ({
|
|
7
|
+
// Initial benchmark (definition) state
|
|
8
|
+
benchmarks: [],
|
|
9
|
+
benchmarksLoading: false,
|
|
10
|
+
benchmarksError: null,
|
|
11
|
+
benchmarksTotalCount: 0,
|
|
12
|
+
benchmarksHasMore: false,
|
|
13
|
+
benchmarksCurrentPage: 0,
|
|
14
|
+
selectedBenchmarkIndex: 0,
|
|
15
|
+
selectedBenchmarkIds: new Set(),
|
|
7
16
|
// Initial benchmark run state
|
|
8
17
|
benchmarkRuns: [],
|
|
9
18
|
benchmarkRunsLoading: false,
|
|
@@ -24,8 +33,30 @@ export const useBenchmarkStore = create((set, get) => ({
|
|
|
24
33
|
selectedBenchmarkRunIndex: 0,
|
|
25
34
|
selectedScenarioRunIndex: 0,
|
|
26
35
|
// Caches
|
|
36
|
+
benchmarkPageCache: new Map(),
|
|
27
37
|
benchmarkRunPageCache: new Map(),
|
|
28
38
|
scenarioRunPageCache: new Map(),
|
|
39
|
+
// Benchmark (definition) Actions
|
|
40
|
+
setBenchmarks: (benchmarks) => set({ benchmarks }),
|
|
41
|
+
setBenchmarksLoading: (loading) => set({ benchmarksLoading: loading }),
|
|
42
|
+
setBenchmarksError: (error) => set({ benchmarksError: error }),
|
|
43
|
+
setBenchmarksTotalCount: (count) => set({ benchmarksTotalCount: count }),
|
|
44
|
+
setBenchmarksHasMore: (hasMore) => set({ benchmarksHasMore: hasMore }),
|
|
45
|
+
setBenchmarksCurrentPage: (page) => set({ benchmarksCurrentPage: page }),
|
|
46
|
+
setSelectedBenchmarkIndex: (index) => set({ selectedBenchmarkIndex: index }),
|
|
47
|
+
setSelectedBenchmarkIds: (ids) => set({ selectedBenchmarkIds: ids }),
|
|
48
|
+
toggleBenchmarkSelection: (id) => {
|
|
49
|
+
const state = get();
|
|
50
|
+
const next = new Set(state.selectedBenchmarkIds);
|
|
51
|
+
if (next.has(id)) {
|
|
52
|
+
next.delete(id);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
next.add(id);
|
|
56
|
+
}
|
|
57
|
+
set({ selectedBenchmarkIds: next });
|
|
58
|
+
},
|
|
59
|
+
clearBenchmarkSelection: () => set({ selectedBenchmarkIds: new Set() }),
|
|
29
60
|
// Benchmark Run Actions
|
|
30
61
|
setBenchmarkRuns: (runs) => set({ benchmarkRuns: runs }),
|
|
31
62
|
setBenchmarkRunsLoading: (loading) => set({ benchmarkRunsLoading: loading }),
|
|
@@ -44,6 +75,22 @@ export const useBenchmarkStore = create((set, get) => ({
|
|
|
44
75
|
setSelectedScenarioRunIndex: (index) => set({ selectedScenarioRunIndex: index }),
|
|
45
76
|
setBenchmarkRunIdFilter: (id) => set({ benchmarkRunIdFilter: id }),
|
|
46
77
|
// Cache management
|
|
78
|
+
cacheBenchmarkPage: (page, data) => {
|
|
79
|
+
const state = get();
|
|
80
|
+
const cache = state.benchmarkPageCache;
|
|
81
|
+
if (cache.size >= MAX_CACHE_SIZE) {
|
|
82
|
+
const oldestKey = cache.keys().next().value;
|
|
83
|
+
if (oldestKey !== undefined) {
|
|
84
|
+
cache.delete(oldestKey);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const plainData = data.map((d) => JSON.parse(JSON.stringify(d)));
|
|
88
|
+
cache.set(page, plainData);
|
|
89
|
+
set({});
|
|
90
|
+
},
|
|
91
|
+
getCachedBenchmarkPage: (page) => {
|
|
92
|
+
return get().benchmarkPageCache.get(page);
|
|
93
|
+
},
|
|
47
94
|
cacheBenchmarkRunPage: (page, data) => {
|
|
48
95
|
const state = get();
|
|
49
96
|
const cache = state.benchmarkRunPageCache;
|
|
@@ -78,18 +125,29 @@ export const useBenchmarkStore = create((set, get) => ({
|
|
|
78
125
|
},
|
|
79
126
|
clearCache: () => {
|
|
80
127
|
const state = get();
|
|
128
|
+
state.benchmarkPageCache.clear();
|
|
81
129
|
state.benchmarkRunPageCache.clear();
|
|
82
130
|
state.scenarioRunPageCache.clear();
|
|
83
131
|
set({
|
|
132
|
+
benchmarkPageCache: new Map(),
|
|
84
133
|
benchmarkRunPageCache: new Map(),
|
|
85
134
|
scenarioRunPageCache: new Map(),
|
|
86
135
|
});
|
|
87
136
|
},
|
|
88
137
|
clearAll: () => {
|
|
89
138
|
const state = get();
|
|
139
|
+
state.benchmarkPageCache.clear();
|
|
90
140
|
state.benchmarkRunPageCache.clear();
|
|
91
141
|
state.scenarioRunPageCache.clear();
|
|
92
142
|
set({
|
|
143
|
+
benchmarks: [],
|
|
144
|
+
benchmarksLoading: false,
|
|
145
|
+
benchmarksError: null,
|
|
146
|
+
benchmarksTotalCount: 0,
|
|
147
|
+
benchmarksHasMore: false,
|
|
148
|
+
benchmarksCurrentPage: 0,
|
|
149
|
+
selectedBenchmarkIndex: 0,
|
|
150
|
+
selectedBenchmarkIds: new Set(),
|
|
93
151
|
benchmarkRuns: [],
|
|
94
152
|
benchmarkRunsLoading: false,
|
|
95
153
|
benchmarkRunsError: null,
|
|
@@ -105,10 +163,15 @@ export const useBenchmarkStore = create((set, get) => ({
|
|
|
105
163
|
benchmarkRunIdFilter: undefined,
|
|
106
164
|
selectedBenchmarkRunIndex: 0,
|
|
107
165
|
selectedScenarioRunIndex: 0,
|
|
166
|
+
benchmarkPageCache: new Map(),
|
|
108
167
|
benchmarkRunPageCache: new Map(),
|
|
109
168
|
scenarioRunPageCache: new Map(),
|
|
110
169
|
});
|
|
111
170
|
},
|
|
171
|
+
getSelectedBenchmark: () => {
|
|
172
|
+
const state = get();
|
|
173
|
+
return state.benchmarks[state.selectedBenchmarkIndex];
|
|
174
|
+
},
|
|
112
175
|
getSelectedBenchmarkRun: () => {
|
|
113
176
|
const state = get();
|
|
114
177
|
return state.benchmarkRuns[state.selectedBenchmarkRunIndex];
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gateway Config Store - Manages gateway configuration state, pagination, and caching
|
|
3
|
+
*/
|
|
4
|
+
import { create } from "zustand";
|
|
5
|
+
const MAX_CACHE_SIZE = 10;
|
|
6
|
+
export const useGatewayConfigStore = create((set, get) => ({
|
|
7
|
+
gatewayConfigs: [],
|
|
8
|
+
loading: false,
|
|
9
|
+
initialLoading: true,
|
|
10
|
+
error: null,
|
|
11
|
+
currentPage: 0,
|
|
12
|
+
pageSize: 10,
|
|
13
|
+
totalCount: 0,
|
|
14
|
+
hasMore: false,
|
|
15
|
+
pageCache: new Map(),
|
|
16
|
+
lastIdCache: new Map(),
|
|
17
|
+
searchQuery: "",
|
|
18
|
+
selectedIndex: 0,
|
|
19
|
+
setGatewayConfigs: (configs) => set({ gatewayConfigs: configs }),
|
|
20
|
+
setLoading: (loading) => set({ loading }),
|
|
21
|
+
setInitialLoading: (loading) => set({ initialLoading: loading }),
|
|
22
|
+
setError: (error) => set({ error }),
|
|
23
|
+
setCurrentPage: (page) => set({ currentPage: page }),
|
|
24
|
+
setPageSize: (size) => set({ pageSize: size }),
|
|
25
|
+
setTotalCount: (count) => set({ totalCount: count }),
|
|
26
|
+
setHasMore: (hasMore) => set({ hasMore }),
|
|
27
|
+
setSearchQuery: (query) => set({ searchQuery: query }),
|
|
28
|
+
setSelectedIndex: (index) => set({ selectedIndex: index }),
|
|
29
|
+
cachePageData: (page, data, lastId) => {
|
|
30
|
+
const state = get();
|
|
31
|
+
const pageCache = state.pageCache;
|
|
32
|
+
const lastIdCache = state.lastIdCache;
|
|
33
|
+
// Aggressive LRU eviction
|
|
34
|
+
if (pageCache.size >= MAX_CACHE_SIZE) {
|
|
35
|
+
const oldestKey = pageCache.keys().next().value;
|
|
36
|
+
if (oldestKey !== undefined) {
|
|
37
|
+
pageCache.delete(oldestKey);
|
|
38
|
+
lastIdCache.delete(oldestKey);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
// Deep copy all fields to avoid SDK references
|
|
42
|
+
const plainData = data.map((d) => {
|
|
43
|
+
return JSON.parse(JSON.stringify(d));
|
|
44
|
+
});
|
|
45
|
+
pageCache.set(page, plainData);
|
|
46
|
+
lastIdCache.set(page, lastId);
|
|
47
|
+
set({});
|
|
48
|
+
},
|
|
49
|
+
getCachedPage: (page) => {
|
|
50
|
+
return get().pageCache.get(page);
|
|
51
|
+
},
|
|
52
|
+
clearCache: () => {
|
|
53
|
+
const state = get();
|
|
54
|
+
state.pageCache.clear();
|
|
55
|
+
state.lastIdCache.clear();
|
|
56
|
+
set({
|
|
57
|
+
pageCache: new Map(),
|
|
58
|
+
lastIdCache: new Map(),
|
|
59
|
+
});
|
|
60
|
+
},
|
|
61
|
+
clearAll: () => {
|
|
62
|
+
const state = get();
|
|
63
|
+
state.pageCache.clear();
|
|
64
|
+
state.lastIdCache.clear();
|
|
65
|
+
set({
|
|
66
|
+
gatewayConfigs: [],
|
|
67
|
+
loading: false,
|
|
68
|
+
initialLoading: true,
|
|
69
|
+
error: null,
|
|
70
|
+
currentPage: 0,
|
|
71
|
+
totalCount: 0,
|
|
72
|
+
hasMore: false,
|
|
73
|
+
pageCache: new Map(),
|
|
74
|
+
lastIdCache: new Map(),
|
|
75
|
+
searchQuery: "",
|
|
76
|
+
selectedIndex: 0,
|
|
77
|
+
});
|
|
78
|
+
},
|
|
79
|
+
getSelectedGatewayConfig: () => {
|
|
80
|
+
const state = get();
|
|
81
|
+
return state.gatewayConfigs[state.selectedIndex];
|
|
82
|
+
},
|
|
83
|
+
}));
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-platform browser-opening utility.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Open a URL in the system's default browser.
|
|
6
|
+
* Works on macOS (open), Windows (start), and Linux (xdg-open).
|
|
7
|
+
*/
|
|
8
|
+
export async function openInBrowser(url) {
|
|
9
|
+
const { exec } = await import("child_process");
|
|
10
|
+
const platform = process.platform;
|
|
11
|
+
let openCommand;
|
|
12
|
+
if (platform === "darwin") {
|
|
13
|
+
openCommand = `open "${url}"`;
|
|
14
|
+
}
|
|
15
|
+
else if (platform === "win32") {
|
|
16
|
+
openCommand = `start "${url}"`;
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
openCommand = `xdg-open "${url}"`;
|
|
20
|
+
}
|
|
21
|
+
exec(openCommand);
|
|
22
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-platform clipboard utility.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Copy text to the system clipboard.
|
|
6
|
+
* Returns a promise that resolves with a status message.
|
|
7
|
+
*/
|
|
8
|
+
export async function copyToClipboard(text) {
|
|
9
|
+
const { spawn } = await import("child_process");
|
|
10
|
+
const platform = process.platform;
|
|
11
|
+
let command;
|
|
12
|
+
let args;
|
|
13
|
+
if (platform === "darwin") {
|
|
14
|
+
command = "pbcopy";
|
|
15
|
+
args = [];
|
|
16
|
+
}
|
|
17
|
+
else if (platform === "win32") {
|
|
18
|
+
command = "clip";
|
|
19
|
+
args = [];
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
command = "xclip";
|
|
23
|
+
args = ["-selection", "clipboard"];
|
|
24
|
+
}
|
|
25
|
+
return new Promise((resolve) => {
|
|
26
|
+
const proc = spawn(command, args);
|
|
27
|
+
proc.stdin.write(text);
|
|
28
|
+
proc.stdin.end();
|
|
29
|
+
proc.on("exit", (code) => {
|
|
30
|
+
if (code === 0) {
|
|
31
|
+
resolve("Copied to clipboard!");
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
resolve("Failed to copy");
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
proc.on("error", () => {
|
|
38
|
+
resolve("Copy not supported");
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
}
|
package/dist/utils/commands.js
CHANGED
|
@@ -40,6 +40,8 @@ export function createProgram() {
|
|
|
40
40
|
.option("--root", "Run as root")
|
|
41
41
|
.option("--user <user:uid>", "Run as this user (format: username:uid)")
|
|
42
42
|
.option("--network-policy <id>", "Network policy ID to apply")
|
|
43
|
+
.option("--tunnel <mode>", "Tunnel authentication mode (open, authenticated)")
|
|
44
|
+
.option("--gateways <gateways...>", "Gateway configurations (format: ENV_PREFIX=gateway_id_or_name,secret_id_or_name)")
|
|
43
45
|
.option("-o, --output [format]", "Output format: text|json|yaml (default: text)")
|
|
44
46
|
.action(createDevbox);
|
|
45
47
|
devbox
|
|
@@ -248,6 +250,17 @@ export function createProgram() {
|
|
|
248
250
|
const { getSnapshot } = await import("../commands/snapshot/get.js");
|
|
249
251
|
await getSnapshot({ id, ...options });
|
|
250
252
|
});
|
|
253
|
+
snapshot
|
|
254
|
+
.command("prune <devbox-id>")
|
|
255
|
+
.description("Delete old snapshots for a devbox, keeping only recent ready ones")
|
|
256
|
+
.option("--dry-run", "Show what would be deleted without actually deleting")
|
|
257
|
+
.option("-y, --yes", "Skip confirmation prompt")
|
|
258
|
+
.option("--keep <n>", "Number of ready snapshots to keep", "1")
|
|
259
|
+
.option("-o, --output [format]", "Output format: text|json|yaml (default: text)")
|
|
260
|
+
.action(async (devboxId, options) => {
|
|
261
|
+
const { pruneSnapshots } = await import("../commands/snapshot/prune.js");
|
|
262
|
+
await pruneSnapshots(devboxId, options);
|
|
263
|
+
});
|
|
251
264
|
snapshot
|
|
252
265
|
.command("status <snapshot-id>")
|
|
253
266
|
.description("Get snapshot operation status")
|
|
@@ -303,6 +316,15 @@ export function createProgram() {
|
|
|
303
316
|
const { getBlueprintLogs } = await import("../commands/blueprint/logs.js");
|
|
304
317
|
await getBlueprintLogs({ id, ...options });
|
|
305
318
|
});
|
|
319
|
+
blueprint
|
|
320
|
+
.command("delete <id>")
|
|
321
|
+
.description("Delete a blueprint by ID")
|
|
322
|
+
.alias("rm")
|
|
323
|
+
.option("-o, --output [format]", "Output format: text|json|yaml (default: text)")
|
|
324
|
+
.action(async (id, options) => {
|
|
325
|
+
const { deleteBlueprint } = await import("../commands/blueprint/delete.js");
|
|
326
|
+
await deleteBlueprint(id, options);
|
|
327
|
+
});
|
|
306
328
|
blueprint
|
|
307
329
|
.command("prune <name>")
|
|
308
330
|
.description("Delete old blueprint builds, keeping only recent successful ones")
|
|
@@ -488,6 +510,64 @@ export function createProgram() {
|
|
|
488
510
|
const { deleteSecret } = await import("../commands/secret/delete.js");
|
|
489
511
|
await deleteSecret(name, options);
|
|
490
512
|
});
|
|
513
|
+
// Gateway config commands
|
|
514
|
+
const gatewayConfig = program
|
|
515
|
+
.command("gateway-config")
|
|
516
|
+
.description("Manage gateway configurations")
|
|
517
|
+
.alias("gwc");
|
|
518
|
+
gatewayConfig
|
|
519
|
+
.command("list")
|
|
520
|
+
.description("List gateway configurations")
|
|
521
|
+
.option("--name <name>", "Filter by name")
|
|
522
|
+
.option("--limit <n>", "Max results", "20")
|
|
523
|
+
.option("-o, --output [format]", "Output format: text|json|yaml (default: json)")
|
|
524
|
+
.action(async (options) => {
|
|
525
|
+
const { listGatewayConfigs } = await import("../commands/gateway-config/list.js");
|
|
526
|
+
await listGatewayConfigs(options);
|
|
527
|
+
});
|
|
528
|
+
gatewayConfig
|
|
529
|
+
.command("create")
|
|
530
|
+
.description("Create a new gateway configuration")
|
|
531
|
+
.requiredOption("--name <name>", "Gateway config name (required)")
|
|
532
|
+
.requiredOption("--endpoint <url>", "Target endpoint URL (required)")
|
|
533
|
+
.requiredOption("--auth-type <type>", "Authentication type: bearer or header (required)")
|
|
534
|
+
.option("--auth-key <key>", "Header key name (required for header auth type)")
|
|
535
|
+
.option("--description <description>", "Description")
|
|
536
|
+
.option("-o, --output [format]", "Output format: text|json|yaml (default: text)")
|
|
537
|
+
.action(async (options) => {
|
|
538
|
+
const { createGatewayConfig } = await import("../commands/gateway-config/create.js");
|
|
539
|
+
await createGatewayConfig(options);
|
|
540
|
+
});
|
|
541
|
+
gatewayConfig
|
|
542
|
+
.command("get <id>")
|
|
543
|
+
.description("Get gateway configuration details")
|
|
544
|
+
.option("-o, --output [format]", "Output format: text|json|yaml (default: json)")
|
|
545
|
+
.action(async (id, options) => {
|
|
546
|
+
const { getGatewayConfig } = await import("../commands/gateway-config/get.js");
|
|
547
|
+
await getGatewayConfig({ id, ...options });
|
|
548
|
+
});
|
|
549
|
+
gatewayConfig
|
|
550
|
+
.command("update <id>")
|
|
551
|
+
.description("Update a gateway configuration")
|
|
552
|
+
.option("--name <name>", "New name")
|
|
553
|
+
.option("--endpoint <url>", "New endpoint URL")
|
|
554
|
+
.option("--auth-type <type>", "New authentication type: bearer or header")
|
|
555
|
+
.option("--auth-key <key>", "New header key name (required for header auth type)")
|
|
556
|
+
.option("--description <description>", "New description")
|
|
557
|
+
.option("-o, --output [format]", "Output format: text|json|yaml (default: text)")
|
|
558
|
+
.action(async (id, options) => {
|
|
559
|
+
const { updateGatewayConfig } = await import("../commands/gateway-config/update.js");
|
|
560
|
+
await updateGatewayConfig({ id, ...options });
|
|
561
|
+
});
|
|
562
|
+
gatewayConfig
|
|
563
|
+
.command("delete <id>")
|
|
564
|
+
.description("Delete a gateway configuration")
|
|
565
|
+
.alias("rm")
|
|
566
|
+
.option("-o, --output [format]", "Output format: text|json|yaml (default: text)")
|
|
567
|
+
.action(async (id, options) => {
|
|
568
|
+
const { deleteGatewayConfig } = await import("../commands/gateway-config/delete.js");
|
|
569
|
+
await deleteGatewayConfig(id, options);
|
|
570
|
+
});
|
|
491
571
|
// MCP server commands
|
|
492
572
|
const mcp = program
|
|
493
573
|
.command("mcp")
|