@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.
Files changed (62) 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/snapshot/list.js +11 -2
  15. package/dist/commands/snapshot/prune.js +265 -0
  16. package/dist/components/BenchmarkMenu.js +23 -3
  17. package/dist/components/DetailedInfoView.js +20 -0
  18. package/dist/components/DevboxActionsMenu.js +9 -61
  19. package/dist/components/DevboxCreatePage.js +531 -14
  20. package/dist/components/DevboxDetailPage.js +27 -22
  21. package/dist/components/GatewayConfigCreatePage.js +265 -0
  22. package/dist/components/LogsViewer.js +6 -40
  23. package/dist/components/ResourceDetailPage.js +143 -160
  24. package/dist/components/ResourceListView.js +3 -33
  25. package/dist/components/ResourcePicker.js +220 -0
  26. package/dist/components/SecretCreatePage.js +2 -4
  27. package/dist/components/SettingsMenu.js +12 -2
  28. package/dist/components/StateHistory.js +1 -20
  29. package/dist/components/StatusBadge.js +9 -2
  30. package/dist/components/StreamingLogsViewer.js +8 -42
  31. package/dist/components/form/FormTextInput.js +4 -2
  32. package/dist/components/resourceDetailTypes.js +18 -0
  33. package/dist/hooks/useInputHandler.js +103 -0
  34. package/dist/router/Router.js +79 -2
  35. package/dist/screens/BenchmarkDetailScreen.js +163 -0
  36. package/dist/screens/BenchmarkJobCreateScreen.js +524 -0
  37. package/dist/screens/BenchmarkJobDetailScreen.js +614 -0
  38. package/dist/screens/BenchmarkJobListScreen.js +479 -0
  39. package/dist/screens/BenchmarkListScreen.js +266 -0
  40. package/dist/screens/BenchmarkMenuScreen.js +6 -0
  41. package/dist/screens/BenchmarkRunDetailScreen.js +258 -22
  42. package/dist/screens/BenchmarkRunListScreen.js +21 -1
  43. package/dist/screens/BlueprintDetailScreen.js +5 -1
  44. package/dist/screens/DevboxCreateScreen.js +2 -2
  45. package/dist/screens/GatewayConfigDetailScreen.js +236 -0
  46. package/dist/screens/GatewayConfigListScreen.js +7 -0
  47. package/dist/screens/ScenarioRunDetailScreen.js +6 -0
  48. package/dist/screens/SettingsMenuScreen.js +3 -0
  49. package/dist/screens/SnapshotDetailScreen.js +6 -0
  50. package/dist/services/agentService.js +42 -0
  51. package/dist/services/benchmarkJobService.js +122 -0
  52. package/dist/services/benchmarkService.js +47 -0
  53. package/dist/services/gatewayConfigService.js +114 -0
  54. package/dist/services/scenarioService.js +34 -0
  55. package/dist/store/benchmarkJobStore.js +66 -0
  56. package/dist/store/benchmarkStore.js +63 -0
  57. package/dist/store/gatewayConfigStore.js +83 -0
  58. package/dist/utils/browser.js +22 -0
  59. package/dist/utils/clipboard.js +41 -0
  60. package/dist/utils/commands.js +80 -0
  61. package/dist/utils/time.js +121 -0
  62. package/package.json +42 -43
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Shared time formatting utilities using the Temporal API.
3
+ */
4
+ import { Temporal } from "@js-temporal/polyfill";
5
+ /**
6
+ * Get elapsed seconds since the given epoch millisecond timestamp.
7
+ */
8
+ function getElapsedSeconds(timestampMs) {
9
+ const now = Temporal.Now.instant();
10
+ const then = Temporal.Instant.fromEpochMilliseconds(timestampMs);
11
+ return Math.floor(now.since(then).total("second"));
12
+ }
13
+ /**
14
+ * Format a relative timestamp in concise form.
15
+ * Examples: "5s ago", "3m ago", "2h ago", "14d ago", "3mo ago", "1y ago"
16
+ *
17
+ * Used for UI detail components.
18
+ */
19
+ export function formatTimeAgo(timestampMs) {
20
+ const seconds = getElapsedSeconds(timestampMs);
21
+ if (seconds < 60)
22
+ return `${seconds}s ago`;
23
+ const minutes = Math.floor(seconds / 60);
24
+ if (minutes < 60)
25
+ return `${minutes}m ago`;
26
+ const hours = Math.floor(minutes / 60);
27
+ if (hours < 24)
28
+ return `${hours}h ago`;
29
+ const days = Math.floor(hours / 24);
30
+ if (days < 30)
31
+ return `${days}d ago`;
32
+ const months = Math.floor(days / 30);
33
+ if (months < 12)
34
+ return `${months}mo ago`;
35
+ const years = Math.floor(months / 12);
36
+ return `${years}y ago`;
37
+ }
38
+ /**
39
+ * Format a relative timestamp in verbose form.
40
+ * Examples: "5 minutes ago", "3 hours ago", "14 days ago"
41
+ *
42
+ * Used for CLI text output (e.g. prune commands).
43
+ */
44
+ export function formatRelativeTime(timestampMs) {
45
+ if (!timestampMs)
46
+ return "unknown time";
47
+ const seconds = getElapsedSeconds(timestampMs);
48
+ const minutes = Math.floor(seconds / 60);
49
+ const hours = Math.floor(seconds / 3600);
50
+ const days = Math.floor(seconds / 86400);
51
+ if (minutes < 60) {
52
+ return `${minutes} minute${minutes !== 1 ? "s" : ""} ago`;
53
+ }
54
+ else if (hours < 24) {
55
+ return `${hours} hour${hours !== 1 ? "s" : ""} ago`;
56
+ }
57
+ else {
58
+ return `${days} day${days !== 1 ? "s" : ""} ago`;
59
+ }
60
+ }
61
+ /**
62
+ * Format a timestamp with HH:MM:SS time and a relative indicator.
63
+ * Examples: "14:30:05 (3m ago)", "01-15 09:00:00 (2d)"
64
+ *
65
+ * Used for resource list views.
66
+ */
67
+ export function formatTimeAgoRich(timestampMs) {
68
+ const instant = Temporal.Instant.fromEpochMilliseconds(timestampMs);
69
+ const zdt = instant.toZonedDateTimeISO(Temporal.Now.timeZoneId());
70
+ const time = `${String(zdt.hour).padStart(2, "0")}:${String(zdt.minute).padStart(2, "0")}:${String(zdt.second).padStart(2, "0")}`;
71
+ const seconds = getElapsedSeconds(timestampMs);
72
+ // Less than 1 minute
73
+ if (seconds < 60)
74
+ return `${time} (${seconds}s ago)`;
75
+ const minutes = Math.floor(seconds / 60);
76
+ // Less than 1 hour
77
+ if (minutes < 60)
78
+ return `${time} (${minutes}m ago)`;
79
+ const hours = Math.floor(minutes / 60);
80
+ // Less than 24 hours
81
+ if (hours < 24)
82
+ return `${time} (${hours}hr ago)`;
83
+ const days = Math.floor(hours / 24);
84
+ const month = String(zdt.month).padStart(2, "0");
85
+ const day = String(zdt.day).padStart(2, "0");
86
+ const dateStr = `${month}-${day}`;
87
+ // 1-7 days - show date + time + relative
88
+ if (days <= 7) {
89
+ return `${dateStr} ${time} (${days}d)`;
90
+ }
91
+ // More than 7 days - just date + time
92
+ return `${dateStr} ${time}`;
93
+ }
94
+ /**
95
+ * Format a timestamp as "locale string (relative ago)".
96
+ * Example: "1/15/2025, 3:30:00 PM (2h ago)"
97
+ *
98
+ * Returns undefined if timestamp is falsy.
99
+ */
100
+ export function formatTimestamp(timestamp) {
101
+ if (!timestamp)
102
+ return undefined;
103
+ const formatted = new Date(timestamp).toLocaleString();
104
+ const ago = formatTimeAgo(timestamp);
105
+ return `${formatted} (${ago})`;
106
+ }
107
+ /**
108
+ * Format a time range as "start → end" or "start (relative ago)" if no end.
109
+ *
110
+ * Returns undefined if createTime is falsy.
111
+ */
112
+ export function formatTimeRange(createTime, endTime) {
113
+ if (!createTime)
114
+ return undefined;
115
+ const start = new Date(createTime).toLocaleString();
116
+ if (endTime) {
117
+ const end = new Date(endTime).toLocaleString();
118
+ return `${start} → ${end}`;
119
+ }
120
+ return `${start} (${formatTimeAgo(createTime)})`;
121
+ }
package/package.json CHANGED
@@ -1,32 +1,11 @@
1
1
  {
2
2
  "name": "@runloop/rl-cli",
3
- "version": "1.8.0",
3
+ "version": "1.9.0",
4
4
  "description": "Beautiful CLI for the Runloop platform",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "rli": "./dist/cli.js"
8
8
  },
9
- "scripts": {
10
- "build": "tsc",
11
- "build:mcp": "npm run build && node scripts/build-mcp.js",
12
- "dev": "tsc --watch",
13
- "start": "node dist/cli.js",
14
- "prepublishOnly": "npm run build",
15
- "version:patch": "npm version patch",
16
- "version:minor": "npm version minor",
17
- "version:major": "npm version major",
18
- "release": "npm run build && npm publish",
19
- "format": "prettier --write \"src/**/*.{ts,tsx,js,jsx,json}\"",
20
- "format:check": "prettier --check \"src/**/*.{ts,tsx,js,jsx,json}\"",
21
- "lint": "eslint src --ext .ts,.tsx",
22
- "lint:fix": "eslint src --ext .ts,.tsx --fix",
23
- "test": "NODE_OPTIONS='--experimental-vm-modules' jest",
24
- "test:watch": "NODE_OPTIONS='--experimental-vm-modules' jest --watch",
25
- "test:coverage": "NODE_OPTIONS='--experimental-vm-modules' jest --coverage",
26
- "test:components": "NODE_OPTIONS='--experimental-vm-modules' jest --config jest.components.config.js --coverage --forceExit",
27
- "docs:commands": "npm run build && node scripts/generate-command-docs.js",
28
- "prepare": "husky"
29
- },
30
9
  "keywords": [
31
10
  "runloop",
32
11
  "cli",
@@ -66,44 +45,64 @@
66
45
  "access": "public"
67
46
  },
68
47
  "dependencies": {
69
- "@modelcontextprotocol/sdk": "^1.19.1",
70
- "@runloop/api-client": "1.3.0",
71
- "@types/express": "^5.0.3",
72
- "chalk": "^5.3.0",
73
- "commander": "^14.0.1",
48
+ "@js-temporal/polyfill": "^0.5.1",
49
+ "@modelcontextprotocol/sdk": "^1.26.0",
50
+ "@runloop/api-client": "1.6.0",
51
+ "@types/express": "^5.0.6",
52
+ "chalk": "^5.6.2",
53
+ "commander": "^14.0.2",
74
54
  "conf": "^15.0.2",
75
55
  "dotenv": "^17.2.3",
76
- "express": "^5.1.0",
56
+ "express": "^5.2.1",
77
57
  "figures": "^6.1.0",
78
58
  "gradient-string": "^3.0.0",
79
- "ink": "^6.3.1",
59
+ "ink": "^6.6.0",
80
60
  "ink-big-text": "^2.0.0",
81
61
  "ink-gradient": "^3.0.0",
82
62
  "ink-link": "^5.0.0",
83
63
  "ink-spinner": "^5.0.0",
84
64
  "ink-text-input": "^6.0.0",
85
65
  "react": "19.2.0",
86
- "yaml": "^2.8.1",
87
- "zustand": "^5.0.2"
66
+ "yaml": "^2.8.2",
67
+ "zustand": "^5.0.10"
88
68
  },
89
69
  "devDependencies": {
90
70
  "@anthropic-ai/mcpb": "^2.1.2",
91
- "@types/jest": "^29.5.0",
92
- "@types/node": "^22.7.9",
93
- "@types/react": "^19.2.2",
94
- "@typescript-eslint/eslint-plugin": "^8.46.0",
95
- "@typescript-eslint/parser": "^8.46.0",
71
+ "@types/jest": "^29.5.14",
72
+ "@types/node": "^22.19.7",
73
+ "@types/react": "^19.2.10",
74
+ "@typescript-eslint/eslint-plugin": "^8.54.0",
75
+ "@typescript-eslint/parser": "^8.54.0",
96
76
  "esbuild": "^0.27.2",
97
- "eslint": "^9.37.0",
77
+ "eslint": "^9.39.2",
98
78
  "eslint-plugin-react": "^7.37.5",
99
79
  "eslint-plugin-react-hooks": "^6.1.1",
100
- "globals": "^16.4.0",
80
+ "globals": "^16.5.0",
101
81
  "husky": "^9.1.7",
102
82
  "ink-testing-library": "^4.0.0",
103
83
  "jest": "^29.7.0",
104
- "prettier": "^3.6.2",
105
- "ts-jest": "^29.1.0",
106
- "ts-node": "^10.9.0",
107
- "typescript": "^5.6.3"
84
+ "prettier": "^3.8.1",
85
+ "ts-jest": "^29.4.6",
86
+ "ts-node": "^10.9.2",
87
+ "typescript": "^5.9.3"
88
+ },
89
+ "scripts": {
90
+ "build": "tsc",
91
+ "build:mcp": "pnpm run build && node scripts/build-mcp.js",
92
+ "dev": "tsc --watch",
93
+ "start": "node dist/cli.js",
94
+ "version:patch": "pnpm version patch",
95
+ "version:minor": "pnpm version minor",
96
+ "version:major": "pnpm version major",
97
+ "release": "pnpm run build && pnpm publish",
98
+ "format": "prettier --write \"src/**/*.{ts,tsx,js,jsx,json}\"",
99
+ "format:check": "prettier --check \"src/**/*.{ts,tsx,js,jsx,json}\"",
100
+ "lint": "eslint src --ext .ts,.tsx",
101
+ "lint:fix": "eslint src --ext .ts,.tsx --fix",
102
+ "test": "NODE_OPTIONS='--experimental-vm-modules' jest",
103
+ "test:watch": "NODE_OPTIONS='--experimental-vm-modules' jest --watch",
104
+ "test:coverage": "NODE_OPTIONS='--experimental-vm-modules' jest --coverage",
105
+ "test:components": "NODE_OPTIONS='--experimental-vm-modules' jest --config jest.components.config.js --coverage --forceExit",
106
+ "docs:commands": "pnpm run build && node scripts/generate-command-docs.js"
108
107
  }
109
- }
108
+ }