Skip to content

Commit 8c3f698

Browse files
committed
cp dines
1 parent d3ac5e1 commit 8c3f698

46 files changed

Lines changed: 2664 additions & 1859 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.prettierrc

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,2 @@
11
{
2-
"semi": true,
3-
"singleQuote": true,
4-
"tabWidth": 2,
5-
"trailingComma": "es5",
6-
"printWidth": 100,
7-
"arrowParens": "avoid"
82
}

src/cli.ts

Lines changed: 147 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,180 +1,237 @@
11
#!/usr/bin/env node
22

3-
import { Command } from 'commander';
4-
import { createDevbox } from './commands/devbox/create.js';
5-
import { listDevboxes } from './commands/devbox/list.js';
6-
import { deleteDevbox } from './commands/devbox/delete.js';
7-
import { execCommand } from './commands/devbox/exec.js';
8-
import { uploadFile } from './commands/devbox/upload.js';
9-
import { getConfig } from './utils/config.js';
10-
import { readFileSync } from 'fs';
11-
import { fileURLToPath } from 'url';
12-
import { dirname, join } from 'path';
3+
import { Command } from "commander";
4+
import { createDevbox } from "./commands/devbox/create.js";
5+
import { listDevboxes } from "./commands/devbox/list.js";
6+
import { deleteDevbox } from "./commands/devbox/delete.js";
7+
import { execCommand } from "./commands/devbox/exec.js";
8+
import { uploadFile } from "./commands/devbox/upload.js";
9+
import { getConfig } from "./utils/config.js";
10+
import { readFileSync } from "fs";
11+
import { fileURLToPath } from "url";
12+
import { dirname, join } from "path";
1313

1414
// Get version from package.json
1515
const __filename = fileURLToPath(import.meta.url);
1616
const __dirname = dirname(__filename);
17-
const packageJson = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf8'));
17+
const packageJson = JSON.parse(
18+
readFileSync(join(__dirname, "../package.json"), "utf8"),
19+
);
1820
export const VERSION = packageJson.version;
1921

2022
// Global Ctrl+C handler to ensure it always exits
21-
process.on('SIGINT', () => {
23+
process.on("SIGINT", () => {
2224
// Force exit immediately, clearing alternate screen buffer
23-
process.stdout.write('\x1b[?1049l');
24-
process.stdout.write('\n');
25+
process.stdout.write("\x1b[?1049l");
26+
process.stdout.write("\n");
2527
process.exit(130); // Standard exit code for SIGINT
2628
});
2729

2830
const program = new Command();
2931

30-
program.name('rln').description('Beautiful CLI for Runloop devbox management').version(VERSION);
32+
program
33+
.name("rln")
34+
.description("Beautiful CLI for Runloop devbox management")
35+
.version(VERSION);
3136

3237
program
33-
.command('auth')
34-
.description('Configure API authentication')
38+
.command("auth")
39+
.description("Configure API authentication")
3540
.action(async () => {
36-
const { default: auth } = await import('./commands/auth.js');
41+
const { default: auth } = await import("./commands/auth.js");
3742
auth();
3843
});
3944

4045
// Devbox commands
41-
const devbox = program.command('devbox').description('Manage devboxes').alias('d');
46+
const devbox = program
47+
.command("devbox")
48+
.description("Manage devboxes")
49+
.alias("d");
4250

4351
devbox
44-
.command('create')
45-
.description('Create a new devbox')
46-
.option('-n, --name <name>', 'Devbox name')
47-
.option('-t, --template <template>', 'Template to use')
48-
.option('-o, --output [format]', 'Output format: text|json|yaml (default: interactive)')
52+
.command("create")
53+
.description("Create a new devbox")
54+
.option("-n, --name <name>", "Devbox name")
55+
.option("-t, --template <template>", "Template to use")
56+
.option(
57+
"-o, --output [format]",
58+
"Output format: text|json|yaml (default: interactive)",
59+
)
4960
.action(createDevbox);
5061

5162
devbox
52-
.command('list')
53-
.description('List all devboxes')
54-
.option('-s, --status <status>', 'Filter by status')
55-
.option('-o, --output [format]', 'Output format: text|json|yaml (default: interactive)')
56-
.action(async options => {
63+
.command("list")
64+
.description("List all devboxes")
65+
.option("-s, --status <status>", "Filter by status")
66+
.option(
67+
"-o, --output [format]",
68+
"Output format: text|json|yaml (default: interactive)",
69+
)
70+
.action(async (options) => {
5771
// Only use alternate screen for interactive mode
5872
if (!options.output) {
59-
const { runInteractiveCommand } = await import('./utils/interactiveCommand.js');
73+
const { runInteractiveCommand } = await import(
74+
"./utils/interactiveCommand.js"
75+
);
6076
await runInteractiveCommand(() => listDevboxes(options));
6177
} else {
6278
await listDevboxes(options);
6379
}
6480
});
6581

6682
devbox
67-
.command('delete <id>')
68-
.description('Shutdown a devbox')
69-
.alias('rm')
70-
.option('-o, --output [format]', 'Output format: text|json|yaml (default: interactive)')
83+
.command("delete <id>")
84+
.description("Shutdown a devbox")
85+
.alias("rm")
86+
.option(
87+
"-o, --output [format]",
88+
"Output format: text|json|yaml (default: interactive)",
89+
)
7190
.action(deleteDevbox);
7291

7392
devbox
74-
.command('exec <id> <command...>')
75-
.description('Execute a command in a devbox')
76-
.option('-o, --output [format]', 'Output format: text|json|yaml (default: interactive)')
93+
.command("exec <id> <command...>")
94+
.description("Execute a command in a devbox")
95+
.option(
96+
"-o, --output [format]",
97+
"Output format: text|json|yaml (default: interactive)",
98+
)
7799
.action(execCommand);
78100

79101
devbox
80-
.command('upload <id> <file>')
81-
.description('Upload a file to a devbox')
82-
.option('-p, --path <path>', 'Target path in devbox')
83-
.option('-o, --output [format]', 'Output format: text|json|yaml (default: interactive)')
102+
.command("upload <id> <file>")
103+
.description("Upload a file to a devbox")
104+
.option("-p, --path <path>", "Target path in devbox")
105+
.option(
106+
"-o, --output [format]",
107+
"Output format: text|json|yaml (default: interactive)",
108+
)
84109
.action(uploadFile);
85110

86111
// Snapshot commands
87-
const snapshot = program.command('snapshot').description('Manage devbox snapshots').alias('snap');
112+
const snapshot = program
113+
.command("snapshot")
114+
.description("Manage devbox snapshots")
115+
.alias("snap");
88116

89117
snapshot
90-
.command('list')
91-
.description('List all snapshots')
92-
.option('-d, --devbox <id>', 'Filter by devbox ID')
93-
.option('-o, --output [format]', 'Output format: text|json|yaml (default: interactive)')
94-
.action(async options => {
95-
const { listSnapshots } = await import('./commands/snapshot/list.js');
118+
.command("list")
119+
.description("List all snapshots")
120+
.option("-d, --devbox <id>", "Filter by devbox ID")
121+
.option(
122+
"-o, --output [format]",
123+
"Output format: text|json|yaml (default: interactive)",
124+
)
125+
.action(async (options) => {
126+
const { listSnapshots } = await import("./commands/snapshot/list.js");
96127
if (!options.output) {
97-
const { runInteractiveCommand } = await import('./utils/interactiveCommand.js');
128+
const { runInteractiveCommand } = await import(
129+
"./utils/interactiveCommand.js"
130+
);
98131
await runInteractiveCommand(() => listSnapshots(options));
99132
} else {
100133
await listSnapshots(options);
101134
}
102135
});
103136

104137
snapshot
105-
.command('create <devbox-id>')
106-
.description('Create a snapshot of a devbox')
107-
.option('-n, --name <name>', 'Snapshot name')
108-
.option('-o, --output [format]', 'Output format: text|json|yaml (default: interactive)')
138+
.command("create <devbox-id>")
139+
.description("Create a snapshot of a devbox")
140+
.option("-n, --name <name>", "Snapshot name")
141+
.option(
142+
"-o, --output [format]",
143+
"Output format: text|json|yaml (default: interactive)",
144+
)
109145
.action(async (devboxId, options) => {
110-
const { createSnapshot } = await import('./commands/snapshot/create.js');
146+
const { createSnapshot } = await import("./commands/snapshot/create.js");
111147
createSnapshot(devboxId, options);
112148
});
113149

114150
snapshot
115-
.command('delete <id>')
116-
.description('Delete a snapshot')
117-
.alias('rm')
118-
.option('-o, --output [format]', 'Output format: text|json|yaml (default: interactive)')
151+
.command("delete <id>")
152+
.description("Delete a snapshot")
153+
.alias("rm")
154+
.option(
155+
"-o, --output [format]",
156+
"Output format: text|json|yaml (default: interactive)",
157+
)
119158
.action(async (id, options) => {
120-
const { deleteSnapshot } = await import('./commands/snapshot/delete.js');
159+
const { deleteSnapshot } = await import("./commands/snapshot/delete.js");
121160
deleteSnapshot(id, options);
122161
});
123162

124163
// Blueprint commands
125-
const blueprint = program.command('blueprint').description('Manage blueprints').alias('bp');
164+
const blueprint = program
165+
.command("blueprint")
166+
.description("Manage blueprints")
167+
.alias("bp");
126168

127169
blueprint
128-
.command('list')
129-
.description('List all blueprints')
130-
.option('-o, --output [format]', 'Output format: text|json|yaml (default: interactive)')
131-
.action(async options => {
132-
const { listBlueprints } = await import('./commands/blueprint/list.js');
170+
.command("list")
171+
.description("List all blueprints")
172+
.option(
173+
"-o, --output [format]",
174+
"Output format: text|json|yaml (default: interactive)",
175+
)
176+
.action(async (options) => {
177+
const { listBlueprints } = await import("./commands/blueprint/list.js");
133178
if (!options.output) {
134-
const { runInteractiveCommand } = await import('./utils/interactiveCommand.js');
179+
const { runInteractiveCommand } = await import(
180+
"./utils/interactiveCommand.js"
181+
);
135182
await runInteractiveCommand(() => listBlueprints(options));
136183
} else {
137184
await listBlueprints(options);
138185
}
139186
});
140187

141188
// MCP server commands
142-
const mcp = program.command('mcp').description('Model Context Protocol (MCP) server commands');
189+
const mcp = program
190+
.command("mcp")
191+
.description("Model Context Protocol (MCP) server commands");
143192

144193
mcp
145-
.command('start')
146-
.description('Start the MCP server')
147-
.option('--http', 'Use HTTP/SSE transport instead of stdio')
148-
.option('-p, --port <port>', 'Port to listen on for HTTP mode (default: 3000)', parseInt)
149-
.action(async options => {
194+
.command("start")
195+
.description("Start the MCP server")
196+
.option("--http", "Use HTTP/SSE transport instead of stdio")
197+
.option(
198+
"-p, --port <port>",
199+
"Port to listen on for HTTP mode (default: 3000)",
200+
parseInt,
201+
)
202+
.action(async (options) => {
150203
if (options.http) {
151-
const { startMcpHttpServer } = await import('./commands/mcp-http.js');
204+
const { startMcpHttpServer } = await import("./commands/mcp-http.js");
152205
await startMcpHttpServer(options.port);
153206
} else {
154-
const { startMcpServer } = await import('./commands/mcp.js');
207+
const { startMcpServer } = await import("./commands/mcp.js");
155208
await startMcpServer();
156209
}
157210
});
158211

159212
mcp
160-
.command('install')
161-
.description('Install Runloop MCP server configuration in Claude Desktop')
213+
.command("install")
214+
.description("Install Runloop MCP server configuration in Claude Desktop")
162215
.action(async () => {
163-
const { installMcpConfig } = await import('./commands/mcp-install.js');
216+
const { installMcpConfig } = await import("./commands/mcp-install.js");
164217
await installMcpConfig();
165218
});
166219

167220
// Hidden command: 'rln mcp' without subcommand starts the server (for Claude Desktop config compatibility)
168221
program
169-
.command('mcp-server', { hidden: true })
170-
.option('--http', 'Use HTTP/SSE transport instead of stdio')
171-
.option('-p, --port <port>', 'Port to listen on for HTTP mode (default: 3000)', parseInt)
172-
.action(async options => {
222+
.command("mcp-server", { hidden: true })
223+
.option("--http", "Use HTTP/SSE transport instead of stdio")
224+
.option(
225+
"-p, --port <port>",
226+
"Port to listen on for HTTP mode (default: 3000)",
227+
parseInt,
228+
)
229+
.action(async (options) => {
173230
if (options.http) {
174-
const { startMcpHttpServer } = await import('./commands/mcp-http.js');
231+
const { startMcpHttpServer } = await import("./commands/mcp-http.js");
175232
await startMcpHttpServer(options.port);
176233
} else {
177-
const { startMcpServer } = await import('./commands/mcp.js');
234+
const { startMcpServer } = await import("./commands/mcp.js");
178235
await startMcpServer();
179236
}
180237
});
@@ -184,23 +241,23 @@ program
184241
// Check if API key is configured (except for auth and mcp commands)
185242
const args = process.argv.slice(2);
186243
if (
187-
args[0] !== 'auth' &&
188-
args[0] !== 'mcp' &&
189-
args[0] !== 'mcp-server' &&
190-
args[0] !== '--help' &&
191-
args[0] !== '-h' &&
244+
args[0] !== "auth" &&
245+
args[0] !== "mcp" &&
246+
args[0] !== "mcp-server" &&
247+
args[0] !== "--help" &&
248+
args[0] !== "-h" &&
192249
args.length > 0
193250
) {
194251
const config = getConfig();
195252
if (!config.apiKey) {
196-
console.error('\n❌ API key not configured. Run: rln auth\n');
253+
console.error("\n❌ API key not configured. Run: rln auth\n");
197254
process.exit(1);
198255
}
199256
}
200257

201258
// If no command provided, show main menu
202259
if (args.length === 0) {
203-
const { runMainMenu } = await import('./commands/menu.js');
260+
const { runMainMenu } = await import("./commands/menu.js");
204261
runMainMenu();
205262
} else {
206263
program.parse();

0 commit comments

Comments
 (0)