@runloop/rl-cli 0.3.0 ā 0.4.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 +5 -65
- package/dist/cli.js +19 -45
- package/dist/commands/mcp-install.js +1 -1
- package/dist/components/ResourceListView.js +3 -3
- package/dist/hooks/useCursorPagination.js +3 -3
- package/dist/mcp/server.js +1 -1
- package/dist/utils/client.js +1 -1
- package/dist/utils/config.js +2 -1
- package/package.json +1 -2
package/README.md
CHANGED
|
@@ -6,7 +6,6 @@ A beautiful, interactive CLI for managing Runloop devboxes built with Ink and Ty
|
|
|
6
6
|
|
|
7
7
|
- šØ Beautiful terminal UI with colors and gradients
|
|
8
8
|
- ā” Fast and responsive with pagination
|
|
9
|
-
- š Secure API key management
|
|
10
9
|
- š¦ Manage devboxes, snapshots, and blueprints
|
|
11
10
|
- š Execute commands in devboxes
|
|
12
11
|
- š¤ Upload files to devboxes
|
|
@@ -33,90 +32,31 @@ npm link
|
|
|
33
32
|
|
|
34
33
|
## Setup
|
|
35
34
|
|
|
36
|
-
Configure your API key
|
|
37
|
-
|
|
38
|
-
### Option 1: Environment Variable (Recommended for CI/CD)
|
|
35
|
+
Configure your API key:
|
|
39
36
|
|
|
40
37
|
```bash
|
|
41
38
|
export RUNLOOP_API_KEY=your_api_key_here
|
|
42
39
|
```
|
|
43
40
|
|
|
44
|
-
### Option 2: Interactive Setup
|
|
45
|
-
|
|
46
|
-
```bash
|
|
47
|
-
rli auth
|
|
48
|
-
```
|
|
49
|
-
|
|
50
41
|
Get your API key from [https://runloop.ai/settings](https://runloop.ai/settings)
|
|
51
42
|
|
|
52
43
|
## Usage
|
|
53
44
|
|
|
54
|
-
### Authentication
|
|
55
|
-
|
|
56
|
-
```bash
|
|
57
|
-
# Interactive setup (stores API key locally)
|
|
58
|
-
rli auth
|
|
59
|
-
|
|
60
|
-
# Or use environment variable
|
|
61
|
-
export RUNLOOP_API_KEY=your_api_key_here
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
The CLI will automatically use `RUNLOOP_API_KEY` if set, otherwise it will use the stored configuration.
|
|
65
|
-
|
|
66
45
|
### Theme Configuration
|
|
67
46
|
|
|
68
|
-
The CLI supports both light and dark terminal themes
|
|
47
|
+
The CLI supports both light and dark terminal themes. Set the theme via environment variable:
|
|
69
48
|
|
|
70
49
|
```bash
|
|
71
|
-
#
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
# Or set theme directly
|
|
75
|
-
rli config theme auto # Auto-detect terminal background (default)
|
|
76
|
-
rli config theme light # Force light mode (dark text on light background)
|
|
77
|
-
rli config theme dark # Force dark mode (light text on dark background)
|
|
78
|
-
|
|
79
|
-
# Or use environment variable
|
|
80
|
-
export RUNLOOP_THEME=light
|
|
50
|
+
export RUNLOOP_THEME=light # Force light mode (dark text on light background)
|
|
51
|
+
export RUNLOOP_THEME=dark # Force dark mode (light text on dark background)
|
|
81
52
|
```
|
|
82
53
|
|
|
83
|
-
**Interactive Mode:**
|
|
84
|
-
|
|
85
|
-
- When you run `rli config theme` without arguments, you get an interactive selector
|
|
86
|
-
- Use arrow keys to navigate between auto/light/dark options
|
|
87
|
-
- See live preview of colors as you navigate
|
|
88
|
-
- Press Enter to save, Esc to cancel
|
|
89
|
-
|
|
90
54
|
**How it works:**
|
|
91
55
|
|
|
92
|
-
- **auto** (default):
|
|
56
|
+
- **auto** (default): Detects correct theme by default
|
|
93
57
|
- **light**: Optimized for light-themed terminals (uses dark text colors)
|
|
94
58
|
- **dark**: Optimized for dark-themed terminals (uses light text colors)
|
|
95
59
|
|
|
96
|
-
**Terminal Compatibility:**
|
|
97
|
-
|
|
98
|
-
- Works with all modern terminals (iTerm2, Terminal.app, VS Code integrated terminal, tmux)
|
|
99
|
-
- The CLI defaults to dark mode for the best experience
|
|
100
|
-
- You can manually set light or dark mode based on your terminal theme
|
|
101
|
-
|
|
102
|
-
**Note on Auto-Detection:**
|
|
103
|
-
|
|
104
|
-
- Auto theme detection is **disabled by default** to prevent screen flashing
|
|
105
|
-
- To enable it, set `RUNLOOP_ENABLE_THEME_DETECTION=1`
|
|
106
|
-
- If you use a light terminal, we recommend setting: `rli config theme light`
|
|
107
|
-
- The result is cached, so subsequent runs are instant (no flashing!)
|
|
108
|
-
- If you change your terminal theme, you can re-detect by running:
|
|
109
|
-
|
|
110
|
-
```bash
|
|
111
|
-
rli config theme auto
|
|
112
|
-
```
|
|
113
|
-
- To manually set your theme without detection:
|
|
114
|
-
```bash
|
|
115
|
-
export RUNLOOP_THEME=dark # or light
|
|
116
|
-
# Or disable auto-detection entirely:
|
|
117
|
-
export RUNLOOP_DISABLE_THEME_DETECTION=1
|
|
118
|
-
```
|
|
119
|
-
|
|
120
60
|
### Devbox Commands
|
|
121
61
|
|
|
122
62
|
```bash
|
package/dist/cli.js
CHANGED
|
@@ -5,7 +5,6 @@ import { listDevboxes } from "./commands/devbox/list.js";
|
|
|
5
5
|
import { deleteDevbox } from "./commands/devbox/delete.js";
|
|
6
6
|
import { execCommand } from "./commands/devbox/exec.js";
|
|
7
7
|
import { uploadFile } from "./commands/devbox/upload.js";
|
|
8
|
-
import { getConfig } from "./utils/config.js";
|
|
9
8
|
import { VERSION } from "./version.js";
|
|
10
9
|
import { exitAlternateScreenBuffer } from "./utils/screen.js";
|
|
11
10
|
import { processUtils } from "./utils/processUtils.js";
|
|
@@ -21,37 +20,6 @@ program
|
|
|
21
20
|
.name("rli")
|
|
22
21
|
.description("Beautiful CLI for Runloop devbox management")
|
|
23
22
|
.version(VERSION);
|
|
24
|
-
program
|
|
25
|
-
.command("auth")
|
|
26
|
-
.description("Configure API authentication")
|
|
27
|
-
.action(async () => {
|
|
28
|
-
const { default: auth } = await import("./commands/auth.js");
|
|
29
|
-
auth();
|
|
30
|
-
});
|
|
31
|
-
// Config commands
|
|
32
|
-
const config = program
|
|
33
|
-
.command("config")
|
|
34
|
-
.description("Configure CLI settings")
|
|
35
|
-
.action(async () => {
|
|
36
|
-
const { showThemeConfig } = await import("./commands/config.js");
|
|
37
|
-
showThemeConfig();
|
|
38
|
-
});
|
|
39
|
-
config
|
|
40
|
-
.command("theme [mode]")
|
|
41
|
-
.description("Get or set theme mode (auto|light|dark)")
|
|
42
|
-
.action(async (mode) => {
|
|
43
|
-
const { showThemeConfig, setThemeConfig } = await import("./commands/config.js");
|
|
44
|
-
if (!mode) {
|
|
45
|
-
showThemeConfig();
|
|
46
|
-
}
|
|
47
|
-
else if (mode === "auto" || mode === "light" || mode === "dark") {
|
|
48
|
-
setThemeConfig(mode);
|
|
49
|
-
}
|
|
50
|
-
else {
|
|
51
|
-
console.error(`\nā Invalid theme mode: ${mode}\nValid options: auto, light, dark\n`);
|
|
52
|
-
processUtils.exit(1);
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
23
|
// Devbox commands
|
|
56
24
|
const devbox = program
|
|
57
25
|
.command("devbox")
|
|
@@ -446,20 +414,26 @@ program
|
|
|
446
414
|
// Initialize theme system early (before any UI rendering)
|
|
447
415
|
const { initializeTheme } = await import("./utils/theme.js");
|
|
448
416
|
await initializeTheme();
|
|
449
|
-
// Check if API key is configured (except for
|
|
417
|
+
// Check if API key is configured (except for mcp commands)
|
|
450
418
|
const args = process.argv.slice(2);
|
|
451
|
-
if (
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
419
|
+
if (!process.env.RUNLOOP_API_KEY) {
|
|
420
|
+
console.error(`
|
|
421
|
+
ā API key not configured.
|
|
422
|
+
|
|
423
|
+
To get started:
|
|
424
|
+
1. Go to https://platform.runloop.ai/settings and create an API key
|
|
425
|
+
2. Set the environment variable:
|
|
426
|
+
|
|
427
|
+
export RUNLOOP_API_KEY=your_api_key_here
|
|
428
|
+
|
|
429
|
+
To make it permanent, add this line to your shell config:
|
|
430
|
+
⢠For zsh: echo 'export RUNLOOP_API_KEY=your_api_key_here' >> ~/.zshrc
|
|
431
|
+
⢠For bash: echo 'export RUNLOOP_API_KEY=your_api_key_here' >> ~/.bashrc
|
|
432
|
+
|
|
433
|
+
Then restart your terminal or run: source ~/.zshrc (or ~/.bashrc)
|
|
434
|
+
`);
|
|
435
|
+
processUtils.exit(1);
|
|
436
|
+
return; // Ensure execution stops
|
|
463
437
|
}
|
|
464
438
|
// If no command provided, show main menu
|
|
465
439
|
if (args.length === 0) {
|
|
@@ -101,7 +101,7 @@ export async function installMcpConfig() {
|
|
|
101
101
|
console.log("\nš Next steps:");
|
|
102
102
|
console.log("1. Restart Claude Desktop completely (quit and reopen)");
|
|
103
103
|
console.log('2. Ask Claude: "List my devboxes" or "What Runloop tools do you have?"');
|
|
104
|
-
console.log(
|
|
104
|
+
console.log("\nš” Tip: Make sure RUNLOOP_API_KEY environment variable is set!");
|
|
105
105
|
}
|
|
106
106
|
catch (error) {
|
|
107
107
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -87,15 +87,15 @@ export function ResourceListView({ config }) {
|
|
|
87
87
|
React.useEffect(() => {
|
|
88
88
|
fetchData(true);
|
|
89
89
|
}, [fetchData]);
|
|
90
|
-
// Auto-refresh
|
|
90
|
+
// Auto-refresh - STOP refreshing when there's an error to avoid flickering
|
|
91
91
|
React.useEffect(() => {
|
|
92
|
-
if (config.autoRefresh?.enabled) {
|
|
92
|
+
if (config.autoRefresh?.enabled && !error) {
|
|
93
93
|
const interval = setInterval(() => {
|
|
94
94
|
fetchData(false);
|
|
95
95
|
}, config.autoRefresh.interval || 3000);
|
|
96
96
|
return () => clearInterval(interval);
|
|
97
97
|
}
|
|
98
|
-
}, [config.autoRefresh, fetchData]);
|
|
98
|
+
}, [config.autoRefresh, fetchData, error]);
|
|
99
99
|
// Removed refresh icon animation to prevent constant re-renders and flashing
|
|
100
100
|
// Filter resources based on search query
|
|
101
101
|
const filteredResources = React.useMemo(() => {
|
|
@@ -119,9 +119,9 @@ export function useCursorPagination(config) {
|
|
|
119
119
|
// Fetch page 0
|
|
120
120
|
fetchPageData(0, true);
|
|
121
121
|
}, [depsKey, fetchPageData]);
|
|
122
|
-
// Polling effect
|
|
122
|
+
// Polling effect - STOP polling when there's an error to avoid flickering
|
|
123
123
|
React.useEffect(() => {
|
|
124
|
-
if (!pollInterval || pollInterval <= 0 || !pollingEnabled) {
|
|
124
|
+
if (!pollInterval || pollInterval <= 0 || !pollingEnabled || error) {
|
|
125
125
|
return;
|
|
126
126
|
}
|
|
127
127
|
const timer = setInterval(() => {
|
|
@@ -130,7 +130,7 @@ export function useCursorPagination(config) {
|
|
|
130
130
|
}
|
|
131
131
|
}, pollInterval);
|
|
132
132
|
return () => clearInterval(timer);
|
|
133
|
-
}, [pollInterval, pollingEnabled, currentPage, fetchPageData]);
|
|
133
|
+
}, [pollInterval, pollingEnabled, currentPage, fetchPageData, error]);
|
|
134
134
|
// Navigation functions
|
|
135
135
|
const nextPage = React.useCallback(() => {
|
|
136
136
|
if (!loading && !navigating && hasMore) {
|
package/dist/mcp/server.js
CHANGED
|
@@ -45,7 +45,7 @@ function getBaseUrl() {
|
|
|
45
45
|
function getClient() {
|
|
46
46
|
const config = getConfig();
|
|
47
47
|
if (!config.apiKey) {
|
|
48
|
-
throw new Error("API key not configured. Please set RUNLOOP_API_KEY environment variable
|
|
48
|
+
throw new Error("API key not configured. Please set RUNLOOP_API_KEY environment variable.");
|
|
49
49
|
}
|
|
50
50
|
const baseURL = getBaseUrl();
|
|
51
51
|
return new Runloop({
|
package/dist/utils/client.js
CHANGED
|
@@ -19,7 +19,7 @@ function getBaseUrl() {
|
|
|
19
19
|
export function getClient() {
|
|
20
20
|
const config = getConfig();
|
|
21
21
|
if (!config.apiKey) {
|
|
22
|
-
throw new Error("API key not configured.
|
|
22
|
+
throw new Error("API key not configured. Set RUNLOOP_API_KEY environment variable.");
|
|
23
23
|
}
|
|
24
24
|
const baseURL = getBaseUrl();
|
|
25
25
|
return new Runloop({
|
package/dist/utils/config.js
CHANGED
|
@@ -4,6 +4,7 @@ import { join } from "path";
|
|
|
4
4
|
import { existsSync, statSync, mkdirSync, writeFileSync } from "fs";
|
|
5
5
|
const config = new Conf({
|
|
6
6
|
projectName: "runloop-cli",
|
|
7
|
+
cwd: join(homedir(), ".runloop"),
|
|
7
8
|
});
|
|
8
9
|
export function getConfig() {
|
|
9
10
|
// Check environment variable first, then fall back to stored config
|
|
@@ -29,7 +30,7 @@ export function sshUrl() {
|
|
|
29
30
|
: "ssh.runloop.ai:443";
|
|
30
31
|
}
|
|
31
32
|
export function getCacheDir() {
|
|
32
|
-
return join(homedir(), ".
|
|
33
|
+
return join(homedir(), ".runloop", "rl-cli");
|
|
33
34
|
}
|
|
34
35
|
export function shouldCheckForUpdates() {
|
|
35
36
|
const cacheDir = getCacheDir();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@runloop/rl-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Beautiful CLI for the Runloop platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -56,7 +56,6 @@
|
|
|
56
56
|
"dependencies": {
|
|
57
57
|
"@modelcontextprotocol/sdk": "^1.19.1",
|
|
58
58
|
"@runloop/api-client": "^1.0.0",
|
|
59
|
-
"@runloop/rl-cli": "^0.1.2",
|
|
60
59
|
"@types/express": "^5.0.3",
|
|
61
60
|
"chalk": "^5.3.0",
|
|
62
61
|
"commander": "^14.0.1",
|