@runloop/rl-cli 1.7.1 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/README.md +19 -5
  2. package/dist/cli.js +0 -0
  3. package/dist/commands/blueprint/delete.js +21 -0
  4. package/dist/commands/blueprint/list.js +226 -174
  5. package/dist/commands/blueprint/prune.js +13 -28
  6. package/dist/commands/devbox/create.js +41 -0
  7. package/dist/commands/devbox/list.js +125 -109
  8. package/dist/commands/devbox/tunnel.js +4 -19
  9. package/dist/commands/gateway-config/create.js +44 -0
  10. package/dist/commands/gateway-config/delete.js +21 -0
  11. package/dist/commands/gateway-config/get.js +15 -0
  12. package/dist/commands/gateway-config/list.js +493 -0
  13. package/dist/commands/gateway-config/update.js +60 -0
  14. package/dist/commands/menu.js +2 -1
  15. package/dist/commands/secret/list.js +379 -4
  16. package/dist/commands/snapshot/list.js +11 -2
  17. package/dist/commands/snapshot/prune.js +265 -0
  18. package/dist/components/BenchmarkMenu.js +108 -0
  19. package/dist/components/DetailedInfoView.js +20 -0
  20. package/dist/components/DevboxActionsMenu.js +9 -61
  21. package/dist/components/DevboxCreatePage.js +531 -14
  22. package/dist/components/DevboxDetailPage.js +27 -22
  23. package/dist/components/GatewayConfigCreatePage.js +265 -0
  24. package/dist/components/LogsViewer.js +6 -40
  25. package/dist/components/MainMenu.js +63 -22
  26. package/dist/components/ResourceDetailPage.js +143 -160
  27. package/dist/components/ResourceListView.js +3 -33
  28. package/dist/components/ResourcePicker.js +220 -0
  29. package/dist/components/SecretCreatePage.js +183 -0
  30. package/dist/components/SettingsMenu.js +95 -0
  31. package/dist/components/StateHistory.js +1 -20
  32. package/dist/components/StatusBadge.js +80 -0
  33. package/dist/components/StreamingLogsViewer.js +8 -42
  34. package/dist/components/form/FormTextInput.js +4 -2
  35. package/dist/components/resourceDetailTypes.js +18 -0
  36. package/dist/hooks/useInputHandler.js +103 -0
  37. package/dist/router/Router.js +99 -2
  38. package/dist/screens/BenchmarkDetailScreen.js +163 -0
  39. package/dist/screens/BenchmarkJobCreateScreen.js +524 -0
  40. package/dist/screens/BenchmarkJobDetailScreen.js +614 -0
  41. package/dist/screens/BenchmarkJobListScreen.js +479 -0
  42. package/dist/screens/BenchmarkListScreen.js +266 -0
  43. package/dist/screens/BenchmarkMenuScreen.js +29 -0
  44. package/dist/screens/BenchmarkRunDetailScreen.js +425 -0
  45. package/dist/screens/BenchmarkRunListScreen.js +275 -0
  46. package/dist/screens/BlueprintDetailScreen.js +5 -1
  47. package/dist/screens/DevboxCreateScreen.js +2 -2
  48. package/dist/screens/GatewayConfigDetailScreen.js +236 -0
  49. package/dist/screens/GatewayConfigListScreen.js +7 -0
  50. package/dist/screens/MenuScreen.js +5 -2
  51. package/dist/screens/ScenarioRunDetailScreen.js +226 -0
  52. package/dist/screens/ScenarioRunListScreen.js +245 -0
  53. package/dist/screens/SecretCreateScreen.js +7 -0
  54. package/dist/screens/SecretDetailScreen.js +198 -0
  55. package/dist/screens/SecretListScreen.js +7 -0
  56. package/dist/screens/SettingsMenuScreen.js +26 -0
  57. package/dist/screens/SnapshotDetailScreen.js +6 -0
  58. package/dist/services/agentService.js +42 -0
  59. package/dist/services/benchmarkJobService.js +122 -0
  60. package/dist/services/benchmarkService.js +120 -0
  61. package/dist/services/gatewayConfigService.js +114 -0
  62. package/dist/services/scenarioService.js +34 -0
  63. package/dist/store/benchmarkJobStore.js +66 -0
  64. package/dist/store/benchmarkStore.js +183 -0
  65. package/dist/store/betaFeatureStore.js +47 -0
  66. package/dist/store/gatewayConfigStore.js +83 -0
  67. package/dist/store/index.js +1 -0
  68. package/dist/utils/browser.js +22 -0
  69. package/dist/utils/clipboard.js +41 -0
  70. package/dist/utils/commands.js +80 -0
  71. package/dist/utils/config.js +8 -0
  72. package/dist/utils/time.js +121 -0
  73. package/package.json +42 -43
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useNavigation } from "../store/navigationStore.js";
3
+ import { ListSecretsUI } from "../commands/secret/list.js";
4
+ export function SecretListScreen() {
5
+ const { goBack } = useNavigation();
6
+ return _jsx(ListSecretsUI, { onBack: goBack });
7
+ }
@@ -0,0 +1,26 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useNavigation } from "../store/navigationStore.js";
3
+ import { SettingsMenu } from "../components/SettingsMenu.js";
4
+ export function SettingsMenuScreen() {
5
+ const { navigate, goBack } = useNavigation();
6
+ const handleSelect = (key) => {
7
+ switch (key) {
8
+ case "network-policies":
9
+ navigate("network-policy-list");
10
+ break;
11
+ case "gateway-configs":
12
+ navigate("gateway-config-list");
13
+ break;
14
+ case "secrets":
15
+ navigate("secret-list");
16
+ break;
17
+ default:
18
+ // Fallback for any other screen names
19
+ navigate(key);
20
+ }
21
+ };
22
+ const handleBack = () => {
23
+ goBack();
24
+ };
25
+ return _jsx(SettingsMenu, { onSelect: handleSelect, onBack: handleBack });
26
+ }
@@ -79,6 +79,12 @@ export function SnapshotDetailScreen({ snapshotId, }) {
79
79
  basicFields.push({
80
80
  label: "Source Devbox",
81
81
  value: _jsx(Text, { color: colors.idColor, children: snapshot.devbox_id }),
82
+ action: {
83
+ type: "navigate",
84
+ screen: "devbox-detail",
85
+ params: { devboxId: snapshot.devbox_id },
86
+ hint: "View Devbox",
87
+ },
82
88
  });
83
89
  }
84
90
  if (snapshot.disk_size_bytes) {
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Agent Service - Handles API calls for agents
3
+ */
4
+ import { getClient } from "../utils/client.js";
5
+ /**
6
+ * List agents with pagination
7
+ * Can filter to only return public agents for benchmark jobs
8
+ */
9
+ export async function listAgents(options) {
10
+ const client = getClient();
11
+ const queryParams = {
12
+ limit: options.limit || 50,
13
+ };
14
+ if (options.startingAfter) {
15
+ queryParams.starting_after = options.startingAfter;
16
+ }
17
+ // Use API filter for public agents
18
+ if (options.publicOnly) {
19
+ queryParams.is_public = true;
20
+ }
21
+ const page = await client.agents.list(queryParams);
22
+ const agents = [];
23
+ // Collect agents from the cursor page
24
+ for await (const agent of page) {
25
+ agents.push(agent);
26
+ if (options.limit && agents.length >= options.limit) {
27
+ break;
28
+ }
29
+ }
30
+ return {
31
+ agents,
32
+ totalCount: agents.length,
33
+ hasMore: false, // Cursor pagination doesn't give us this directly
34
+ };
35
+ }
36
+ /**
37
+ * Get agent by ID
38
+ */
39
+ export async function getAgent(id) {
40
+ const client = getClient();
41
+ return client.agents.retrieve(id);
42
+ }
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Benchmark Job Service - Handles API calls for benchmark jobs
3
+ */
4
+ import { getClient } from "../utils/client.js";
5
+ /**
6
+ * List benchmark jobs with pagination
7
+ */
8
+ export async function listBenchmarkJobs(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.name) {
17
+ queryParams.name = options.name;
18
+ }
19
+ const page = await client.benchmarkJobs.list(queryParams);
20
+ const jobs = page.jobs || [];
21
+ return {
22
+ jobs,
23
+ totalCount: page.total_count || jobs.length,
24
+ hasMore: page.has_more || false,
25
+ };
26
+ }
27
+ /**
28
+ * Get benchmark job by ID
29
+ */
30
+ export async function getBenchmarkJob(id) {
31
+ const client = getClient();
32
+ return client.benchmarkJobs.retrieve(id);
33
+ }
34
+ /**
35
+ * Create a benchmark job with either benchmark definition spec or scenario definition spec
36
+ */
37
+ export async function createBenchmarkJob(options) {
38
+ const client = getClient();
39
+ // Validate that either benchmarkId or scenarioIds is provided
40
+ if (!options.benchmarkId && !options.scenarioIds) {
41
+ throw new Error("Either benchmarkId or scenarioIds must be provided");
42
+ }
43
+ if (options.benchmarkId && options.scenarioIds) {
44
+ throw new Error("Cannot specify both benchmarkId and scenarioIds");
45
+ }
46
+ // Build agent configs in API format
47
+ // Use the same agent config type for both spec types
48
+ const agentConfigs = options.agentConfigs.map((agent) => {
49
+ const config = {
50
+ name: agent.name,
51
+ type: "job_agent",
52
+ };
53
+ if (agent.agentId) {
54
+ config.agent_id = agent.agentId;
55
+ }
56
+ if (agent.modelName) {
57
+ config.model_name = agent.modelName;
58
+ }
59
+ if (agent.timeoutSeconds) {
60
+ config.timeout_seconds = agent.timeoutSeconds;
61
+ }
62
+ if (agent.kwargs && Object.keys(agent.kwargs).length > 0) {
63
+ config.kwargs = agent.kwargs;
64
+ }
65
+ if ((agent.environmentVariables &&
66
+ Object.keys(agent.environmentVariables).length > 0) ||
67
+ (agent.secrets && Object.keys(agent.secrets).length > 0)) {
68
+ config.agent_environment = {};
69
+ if (agent.environmentVariables &&
70
+ Object.keys(agent.environmentVariables).length > 0) {
71
+ config.agent_environment.environment_variables =
72
+ agent.environmentVariables;
73
+ }
74
+ if (agent.secrets && Object.keys(agent.secrets).length > 0) {
75
+ config.agent_environment.secrets = agent.secrets;
76
+ }
77
+ }
78
+ return config;
79
+ });
80
+ // Build orchestrator config if provided
81
+ let orchestratorConfig;
82
+ if (options.orchestratorConfig) {
83
+ orchestratorConfig = {};
84
+ if (options.orchestratorConfig.nAttempts !== undefined) {
85
+ orchestratorConfig.n_attempts = options.orchestratorConfig.nAttempts;
86
+ }
87
+ if (options.orchestratorConfig.nConcurrentTrials !== undefined) {
88
+ orchestratorConfig.n_concurrent_trials =
89
+ options.orchestratorConfig.nConcurrentTrials;
90
+ }
91
+ if (options.orchestratorConfig.quiet !== undefined) {
92
+ orchestratorConfig.quiet = options.orchestratorConfig.quiet;
93
+ }
94
+ if (options.orchestratorConfig.timeoutMultiplier !== undefined) {
95
+ orchestratorConfig.timeout_multiplier =
96
+ options.orchestratorConfig.timeoutMultiplier;
97
+ }
98
+ }
99
+ // Build the appropriate spec based on what's provided
100
+ let spec;
101
+ if (options.benchmarkId) {
102
+ spec = {
103
+ type: "benchmark",
104
+ benchmark_id: options.benchmarkId,
105
+ agent_configs: agentConfigs,
106
+ orchestrator_config: orchestratorConfig,
107
+ };
108
+ }
109
+ else if (options.scenarioIds) {
110
+ spec = {
111
+ type: "scenarios",
112
+ scenario_ids: options.scenarioIds,
113
+ agent_configs: agentConfigs,
114
+ orchestrator_config: orchestratorConfig,
115
+ };
116
+ }
117
+ const createParams = {
118
+ name: options.name,
119
+ spec,
120
+ };
121
+ return client.benchmarkJobs.create(createParams);
122
+ }
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Benchmark Service - Handles all benchmark-related API calls
3
+ */
4
+ import { getClient } from "../utils/client.js";
5
+ /**
6
+ * List benchmark runs with pagination
7
+ */
8
+ export async function listBenchmarkRuns(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
+ const page = await client.benchmarkRuns.list(queryParams);
17
+ const benchmarkRuns = page.runs || [];
18
+ return {
19
+ benchmarkRuns,
20
+ totalCount: page.total_count || benchmarkRuns.length,
21
+ hasMore: page.has_more || false,
22
+ };
23
+ }
24
+ /**
25
+ * Get benchmark run by ID
26
+ */
27
+ export async function getBenchmarkRun(id) {
28
+ const client = getClient();
29
+ return client.benchmarkRuns.retrieve(id);
30
+ }
31
+ /**
32
+ * List scenario runs with pagination
33
+ */
34
+ export async function listScenarioRuns(options) {
35
+ const client = getClient();
36
+ // If we have a benchmark run ID, use the dedicated endpoint
37
+ if (options.benchmarkRunId) {
38
+ const queryParams = {
39
+ limit: options.limit,
40
+ };
41
+ if (options.startingAfter) {
42
+ queryParams.starting_after = options.startingAfter;
43
+ }
44
+ const page = await client.benchmarkRuns.listScenarioRuns(options.benchmarkRunId, queryParams);
45
+ const scenarioRuns = page.runs || [];
46
+ return {
47
+ scenarioRuns,
48
+ totalCount: page.total_count || scenarioRuns.length,
49
+ hasMore: page.has_more || false,
50
+ };
51
+ }
52
+ // Otherwise, list all scenario runs
53
+ const queryParams = {
54
+ limit: options.limit,
55
+ };
56
+ if (options.startingAfter) {
57
+ queryParams.starting_after = options.startingAfter;
58
+ }
59
+ const page = await client.scenarios.runs.list(queryParams);
60
+ const scenarioRuns = page.runs || [];
61
+ return {
62
+ scenarioRuns,
63
+ totalCount: page.total_count || scenarioRuns.length,
64
+ hasMore: page.has_more || false,
65
+ };
66
+ }
67
+ /**
68
+ * Get scenario run by ID
69
+ */
70
+ export async function getScenarioRun(id) {
71
+ const client = getClient();
72
+ return client.scenarios.runs.retrieve(id);
73
+ }
74
+ /**
75
+ * List benchmark definitions with pagination
76
+ */
77
+ export async function listBenchmarks(options) {
78
+ const client = getClient();
79
+ const queryParams = {
80
+ limit: options.limit,
81
+ };
82
+ if (options.startingAfter) {
83
+ queryParams.starting_after = options.startingAfter;
84
+ }
85
+ if (options.search) {
86
+ queryParams.search = options.search;
87
+ }
88
+ const page = await client.benchmarks.list(queryParams);
89
+ const benchmarks = page.benchmarks || [];
90
+ return {
91
+ benchmarks,
92
+ totalCount: page.total_count || benchmarks.length,
93
+ hasMore: page.has_more || false,
94
+ };
95
+ }
96
+ /**
97
+ * Get benchmark definition by ID
98
+ */
99
+ export async function getBenchmark(id) {
100
+ const client = getClient();
101
+ return client.benchmarks.retrieve(id);
102
+ }
103
+ /**
104
+ * Create/start a benchmark run with selected benchmarks
105
+ */
106
+ export async function createBenchmarkRun(benchmarkIds, options) {
107
+ const client = getClient();
108
+ const createParams = {
109
+ benchmark_ids: benchmarkIds,
110
+ };
111
+ if (options?.name) {
112
+ createParams.name = options.name;
113
+ }
114
+ if (options?.metadata) {
115
+ createParams.metadata = options.metadata;
116
+ }
117
+ // Use type assertion since the API client types may not be fully defined
118
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
119
+ return client.benchmarkRuns.create(createParams);
120
+ }
@@ -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
+ }));