@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.
- package/LICENSE +21 -0
- package/README.md +190 -0
- package/dist/cli.js +110 -0
- package/dist/commands/auth.js +27 -0
- package/dist/commands/blueprint/list.js +373 -0
- package/dist/commands/create.js +42 -0
- package/dist/commands/delete.js +34 -0
- package/dist/commands/devbox/create.js +45 -0
- package/dist/commands/devbox/delete.js +37 -0
- package/dist/commands/devbox/exec.js +35 -0
- package/dist/commands/devbox/list.js +302 -0
- package/dist/commands/devbox/upload.js +40 -0
- package/dist/commands/exec.js +35 -0
- package/dist/commands/list.js +59 -0
- package/dist/commands/snapshot/create.js +40 -0
- package/dist/commands/snapshot/delete.js +37 -0
- package/dist/commands/snapshot/list.js +122 -0
- package/dist/commands/upload.js +40 -0
- package/dist/components/Banner.js +11 -0
- package/dist/components/Breadcrumb.js +9 -0
- package/dist/components/DetailView.js +29 -0
- package/dist/components/DevboxCard.js +24 -0
- package/dist/components/DevboxCreatePage.js +370 -0
- package/dist/components/DevboxDetailPage.js +621 -0
- package/dist/components/ErrorMessage.js +6 -0
- package/dist/components/Header.js +6 -0
- package/dist/components/MetadataDisplay.js +24 -0
- package/dist/components/OperationsMenu.js +19 -0
- package/dist/components/Spinner.js +6 -0
- package/dist/components/StatusBadge.js +41 -0
- package/dist/components/SuccessMessage.js +6 -0
- package/dist/components/Table.example.js +55 -0
- package/dist/components/Table.js +54 -0
- package/dist/utils/CommandExecutor.js +115 -0
- package/dist/utils/client.js +13 -0
- package/dist/utils/config.js +17 -0
- package/dist/utils/output.js +115 -0
- 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
|
+
}
|