@runloop/rl-cli 0.0.1

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 (38) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +190 -0
  3. package/dist/cli.js +110 -0
  4. package/dist/commands/auth.js +27 -0
  5. package/dist/commands/blueprint/list.js +373 -0
  6. package/dist/commands/create.js +42 -0
  7. package/dist/commands/delete.js +34 -0
  8. package/dist/commands/devbox/create.js +45 -0
  9. package/dist/commands/devbox/delete.js +37 -0
  10. package/dist/commands/devbox/exec.js +35 -0
  11. package/dist/commands/devbox/list.js +302 -0
  12. package/dist/commands/devbox/upload.js +40 -0
  13. package/dist/commands/exec.js +35 -0
  14. package/dist/commands/list.js +59 -0
  15. package/dist/commands/snapshot/create.js +40 -0
  16. package/dist/commands/snapshot/delete.js +37 -0
  17. package/dist/commands/snapshot/list.js +122 -0
  18. package/dist/commands/upload.js +40 -0
  19. package/dist/components/Banner.js +11 -0
  20. package/dist/components/Breadcrumb.js +9 -0
  21. package/dist/components/DetailView.js +29 -0
  22. package/dist/components/DevboxCard.js +24 -0
  23. package/dist/components/DevboxCreatePage.js +370 -0
  24. package/dist/components/DevboxDetailPage.js +621 -0
  25. package/dist/components/ErrorMessage.js +6 -0
  26. package/dist/components/Header.js +6 -0
  27. package/dist/components/MetadataDisplay.js +24 -0
  28. package/dist/components/OperationsMenu.js +19 -0
  29. package/dist/components/Spinner.js +6 -0
  30. package/dist/components/StatusBadge.js +41 -0
  31. package/dist/components/SuccessMessage.js +6 -0
  32. package/dist/components/Table.example.js +55 -0
  33. package/dist/components/Table.js +54 -0
  34. package/dist/utils/CommandExecutor.js +115 -0
  35. package/dist/utils/client.js +13 -0
  36. package/dist/utils/config.js +17 -0
  37. package/dist/utils/output.js +115 -0
  38. package/package.json +69 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Runloop
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,190 @@
1
+ # Runloop CLI
2
+
3
+ A beautiful, interactive CLI for managing Runloop devboxes built with Ink and TypeScript.
4
+
5
+ ## Features
6
+
7
+ - šŸŽØ Beautiful terminal UI with colors and gradients
8
+ - ⚔ Fast and responsive with pagination
9
+ - šŸ” Secure API key management
10
+ - šŸ“¦ Manage devboxes, snapshots, and blueprints
11
+ - šŸš€ Execute commands in devboxes
12
+ - šŸ“¤ Upload files to devboxes
13
+ - šŸŽÆ Organized command structure with aliases
14
+
15
+ ## Installation
16
+
17
+ Install globally via npm:
18
+
19
+ ```bash
20
+ npm install -g @runloop/rl-cli
21
+ ```
22
+
23
+ Or install from source:
24
+
25
+ ```bash
26
+ git clone https://github.com/runloop/rl-cli-node.git
27
+ cd rl-cli-node
28
+ npm install
29
+ npm run build
30
+ npm link
31
+ ```
32
+
33
+ ## Setup
34
+
35
+ Configure your API key using either method:
36
+
37
+ ### Option 1: Environment Variable (Recommended for CI/CD)
38
+
39
+ ```bash
40
+ export RUNLOOP_API_KEY=your_api_key_here
41
+ ```
42
+
43
+ ### Option 2: Interactive Setup
44
+
45
+ ```bash
46
+ rln auth
47
+ ```
48
+
49
+ Get your API key from [https://runloop.ai/settings](https://runloop.ai/settings)
50
+
51
+ ## Usage
52
+
53
+ ### Authentication
54
+
55
+ ```bash
56
+ # Interactive setup (stores API key locally)
57
+ rln auth
58
+
59
+ # Or use environment variable
60
+ export RUNLOOP_API_KEY=your_api_key_here
61
+ ```
62
+
63
+ The CLI will automatically use `RUNLOOP_API_KEY` if set, otherwise it will use the stored configuration.
64
+
65
+ ### Devbox Commands
66
+
67
+ ```bash
68
+ # Create devboxes
69
+ rln devbox create # Create with auto-generated name
70
+ rln devbox create --name my-devbox # Create with custom name
71
+ rln devbox create --template nodejs # Create from template
72
+ rln d create -n my-devbox # Short alias
73
+
74
+ # List devboxes (paginated)
75
+ rln devbox list # List all devboxes
76
+ rln devbox list --status running # Filter by status
77
+ rln d list # Short alias
78
+
79
+ # Execute commands
80
+ rln devbox exec <devbox-id> echo "Hello" # Run a command
81
+ rln devbox exec <devbox-id> ls -la # List files
82
+ rln d exec <id> <command> # Short alias
83
+
84
+ # Upload files
85
+ rln devbox upload <devbox-id> ./file.txt # Upload to home
86
+ rln devbox upload <id> ./file.txt -p /path # Upload to specific path
87
+ rln d upload <id> <file> # Short alias
88
+
89
+ # Delete devboxes
90
+ rln devbox delete <devbox-id> # Shutdown a devbox
91
+ rln devbox rm <devbox-id> # Alias
92
+ rln d delete <id> # Short alias
93
+ ```
94
+
95
+ ### Snapshot Commands
96
+
97
+ ```bash
98
+ # Create snapshots
99
+ rln snapshot create <devbox-id> # Create snapshot
100
+ rln snapshot create <id> --name backup-1 # Create with name
101
+ rln snap create <id> # Short alias
102
+
103
+ # List snapshots (paginated)
104
+ rln snapshot list # List all snapshots
105
+ rln snapshot list --devbox <id> # Filter by devbox
106
+ rln snap list # Short alias
107
+
108
+ # Delete snapshots
109
+ rln snapshot delete <snapshot-id> # Delete snapshot
110
+ rln snapshot rm <snapshot-id> # Alias
111
+ rln snap delete <id> # Short alias
112
+ ```
113
+
114
+ ### Blueprint Commands
115
+
116
+ ```bash
117
+ # List blueprints
118
+ rln blueprint list # List blueprints (coming soon)
119
+ rln bp list # Short alias
120
+ ```
121
+
122
+ ## Command Structure
123
+
124
+ The CLI is organized into command buckets:
125
+
126
+ - **`devbox` (alias: `d`)** - Manage devboxes
127
+ - `create` - Create new devboxes
128
+ - `list` - List devboxes with pagination
129
+ - `exec` - Execute commands
130
+ - `upload` - Upload files
131
+ - `delete` (alias: `rm`) - Shutdown devboxes
132
+
133
+ - **`snapshot` (alias: `snap`)** - Manage snapshots
134
+ - `create` - Create snapshots
135
+ - `list` - List snapshots with pagination
136
+ - `delete` (alias: `rm`) - Delete snapshots
137
+
138
+ - **`blueprint` (alias: `bp`)** - Manage blueprints
139
+ - `list` - List blueprints (coming soon)
140
+
141
+ ## Interactive Features
142
+
143
+ - **Pagination** - Lists show 10 items per page with keyboard navigation
144
+ - `n` - Next page
145
+ - `p` - Previous page
146
+ - `q` - Quit
147
+ - **Beautiful UI** - Gradient text, colored borders, Unicode icons
148
+ - **Real-time Status** - Spinners and progress indicators
149
+ - **Summary Stats** - Count running, stopped, and total resources
150
+
151
+ ## Development
152
+
153
+ ```bash
154
+ # Install dependencies
155
+ npm install
156
+
157
+ # Build
158
+ npm run build
159
+
160
+ # Watch mode
161
+ npm run dev
162
+
163
+ # Run CLI
164
+ npm start -- <command>
165
+ ```
166
+
167
+ ## Tech Stack
168
+
169
+ - [Ink](https://github.com/vadimdemedes/ink) - React for CLIs
170
+ - [Ink Gradient](https://github.com/sindresorhus/ink-gradient) - Gradient text
171
+ - [Ink Big Text](https://github.com/sindresorhus/ink-big-text) - ASCII art
172
+ - [Commander.js](https://github.com/tj/commander.js) - CLI framework
173
+ - [@runloop/api-client](https://github.com/runloopai/api-client-ts) - Runloop API client
174
+ - TypeScript - Type safety
175
+ - [Figures](https://github.com/sindresorhus/figures) - Unicode symbols
176
+
177
+ ## Publishing
178
+
179
+ To publish a new version to npm:
180
+
181
+ ```bash
182
+ npm run build
183
+ npm publish
184
+ ```
185
+
186
+ **Note:** Make sure you're logged in to npm with access to the `@runloop` organization.
187
+
188
+ ## License
189
+
190
+ MIT
package/dist/cli.js ADDED
@@ -0,0 +1,110 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { createDevbox } from './commands/devbox/create.js';
4
+ import { listDevboxes } from './commands/devbox/list.js';
5
+ import { deleteDevbox } from './commands/devbox/delete.js';
6
+ import { execCommand } from './commands/devbox/exec.js';
7
+ import { uploadFile } from './commands/devbox/upload.js';
8
+ import { getConfig } from './utils/config.js';
9
+ const program = new Command();
10
+ program
11
+ .name('rln')
12
+ .description('Beautiful CLI for Runloop devbox management')
13
+ .version('1.0.0');
14
+ program
15
+ .command('auth')
16
+ .description('Configure API authentication')
17
+ .action(async () => {
18
+ const { default: auth } = await import('./commands/auth.js');
19
+ auth();
20
+ });
21
+ // Devbox commands
22
+ const devbox = program
23
+ .command('devbox')
24
+ .description('Manage devboxes')
25
+ .alias('d');
26
+ devbox
27
+ .command('create')
28
+ .description('Create a new devbox')
29
+ .option('-n, --name <name>', 'Devbox name')
30
+ .option('-t, --template <template>', 'Template to use')
31
+ .option('-o, --output [format]', 'Output format: text|json|yaml (default: interactive)')
32
+ .action(createDevbox);
33
+ devbox
34
+ .command('list')
35
+ .description('List all devboxes')
36
+ .option('-s, --status <status>', 'Filter by status')
37
+ .option('-o, --output [format]', 'Output format: text|json|yaml (default: interactive)')
38
+ .action(listDevboxes);
39
+ devbox
40
+ .command('delete <id>')
41
+ .description('Shutdown a devbox')
42
+ .alias('rm')
43
+ .option('-o, --output [format]', 'Output format: text|json|yaml (default: interactive)')
44
+ .action(deleteDevbox);
45
+ devbox
46
+ .command('exec <id> <command...>')
47
+ .description('Execute a command in a devbox')
48
+ .option('-o, --output [format]', 'Output format: text|json|yaml (default: interactive)')
49
+ .action(execCommand);
50
+ devbox
51
+ .command('upload <id> <file>')
52
+ .description('Upload a file to a devbox')
53
+ .option('-p, --path <path>', 'Target path in devbox')
54
+ .option('-o, --output [format]', 'Output format: text|json|yaml (default: interactive)')
55
+ .action(uploadFile);
56
+ // Snapshot commands
57
+ const snapshot = program
58
+ .command('snapshot')
59
+ .description('Manage devbox snapshots')
60
+ .alias('snap');
61
+ snapshot
62
+ .command('list')
63
+ .description('List all snapshots')
64
+ .option('-d, --devbox <id>', 'Filter by devbox ID')
65
+ .option('-o, --output [format]', 'Output format: text|json|yaml (default: interactive)')
66
+ .action(async (options) => {
67
+ const { listSnapshots } = await import('./commands/snapshot/list.js');
68
+ listSnapshots(options);
69
+ });
70
+ snapshot
71
+ .command('create <devbox-id>')
72
+ .description('Create a snapshot of a devbox')
73
+ .option('-n, --name <name>', 'Snapshot name')
74
+ .option('-o, --output [format]', 'Output format: text|json|yaml (default: interactive)')
75
+ .action(async (devboxId, options) => {
76
+ const { createSnapshot } = await import('./commands/snapshot/create.js');
77
+ createSnapshot(devboxId, options);
78
+ });
79
+ snapshot
80
+ .command('delete <id>')
81
+ .description('Delete a snapshot')
82
+ .alias('rm')
83
+ .option('-o, --output [format]', 'Output format: text|json|yaml (default: interactive)')
84
+ .action(async (id, options) => {
85
+ const { deleteSnapshot } = await import('./commands/snapshot/delete.js');
86
+ deleteSnapshot(id, options);
87
+ });
88
+ // Blueprint commands
89
+ const blueprint = program
90
+ .command('blueprint')
91
+ .description('Manage blueprints')
92
+ .alias('bp');
93
+ blueprint
94
+ .command('list')
95
+ .description('List all blueprints')
96
+ .option('-o, --output [format]', 'Output format: text|json|yaml (default: interactive)')
97
+ .action(async (options) => {
98
+ const { listBlueprints } = await import('./commands/blueprint/list.js');
99
+ listBlueprints(options);
100
+ });
101
+ // Check if API key is configured (except for auth command)
102
+ const args = process.argv.slice(2);
103
+ if (args[0] !== 'auth' && args[0] !== '--help' && args[0] !== '-h' && args.length > 0) {
104
+ const config = getConfig();
105
+ if (!config.apiKey) {
106
+ console.error('\nāŒ API key not configured. Run: rln auth\n');
107
+ process.exit(1);
108
+ }
109
+ }
110
+ program.parse();
@@ -0,0 +1,27 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React from 'react';
3
+ import { render, Box, Text, useInput } from 'ink';
4
+ import TextInput from 'ink-text-input';
5
+ import { setApiKey } from '../utils/config.js';
6
+ import { Header } from '../components/Header.js';
7
+ import { Banner } from '../components/Banner.js';
8
+ import { SuccessMessage } from '../components/SuccessMessage.js';
9
+ const AuthUI = () => {
10
+ const [apiKey, setApiKeyInput] = React.useState('');
11
+ const [saved, setSaved] = React.useState(false);
12
+ useInput((input, key) => {
13
+ if (key.return && apiKey.trim()) {
14
+ setApiKey(apiKey.trim());
15
+ setSaved(true);
16
+ setTimeout(() => process.exit(0), 1000);
17
+ }
18
+ });
19
+ if (saved) {
20
+ return (_jsxs(_Fragment, { children: [_jsx(Banner, {}), _jsx(Header, { title: "Authentication" }), _jsx(SuccessMessage, { message: "API key saved!", details: "Try: rln devbox list" })] }));
21
+ }
22
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Banner, {}), _jsx(Header, { title: "Authentication" }), _jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { color: "gray", children: "Get your key: " }), _jsx(Text, { color: "cyan", children: "https://runloop.ai/settings" })] }), _jsxs(Box, { children: [_jsx(Text, { color: "cyan", children: "API Key: " }), _jsx(TextInput, { value: apiKey, onChange: setApiKeyInput, placeholder: "rl_...", mask: "*" })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "gray", dimColor: true, children: "Press Enter to save" }) })] }));
23
+ };
24
+ export default function auth() {
25
+ console.clear();
26
+ render(_jsx(AuthUI, {}));
27
+ }