@runloop/rl-cli 0.5.0 → 0.10.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.
@@ -0,0 +1,408 @@
1
+ import { Command } from "commander";
2
+ import { VERSION } from "../version.js";
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
+ /**
9
+ * Creates and configures the Commander program with all commands.
10
+ * This is shared between the CLI and documentation generation.
11
+ */
12
+ export function createProgram() {
13
+ const program = new Command();
14
+ program
15
+ .name("rli")
16
+ .description("Beautiful CLI for Runloop devbox management")
17
+ .version(VERSION);
18
+ // Devbox commands
19
+ const devbox = program
20
+ .command("devbox")
21
+ .description("Manage devboxes")
22
+ .alias("d");
23
+ devbox
24
+ .command("create")
25
+ .description("Create a new devbox")
26
+ .option("-n, --name <name>", "Devbox name")
27
+ .option("-t, --template <template>", "Snapshot ID to use (alias: --snapshot)")
28
+ .option("-s, --snapshot <snapshot>", "Snapshot ID to use")
29
+ .option("--blueprint <blueprint>", "Blueprint name or ID to use")
30
+ .option("--resources <size>", "Resource size (X_SMALL, SMALL, MEDIUM, LARGE, X_LARGE, XX_LARGE)")
31
+ .option("--architecture <arch>", "Architecture (arm64, x86_64)")
32
+ .option("--entrypoint <command>", "Entrypoint command to run")
33
+ .option("--launch-commands <commands...>", "Initialization commands to run on startup")
34
+ .option("--env-vars <vars...>", "Environment variables (format: KEY=value)")
35
+ .option("--code-mounts <mounts...>", "Code mount configurations (JSON format)")
36
+ .option("--idle-time <seconds>", "Idle time in seconds before idle action")
37
+ .option("--idle-action <action>", "Action on idle (shutdown, suspend)")
38
+ .option("--available-ports <ports...>", "Available ports")
39
+ .option("--root", "Run as root")
40
+ .option("--user <user:uid>", "Run as this user (format: username:uid)")
41
+ .option("-o, --output [format]", "Output format: text|json|yaml (default: text)")
42
+ .action(createDevbox);
43
+ devbox
44
+ .command("list")
45
+ .description("List all devboxes")
46
+ .option("-s, --status <status>", "Filter by status (initializing, running, suspending, suspended, resuming, failure, shutdown)")
47
+ .option("-l, --limit <n>", "Max results", "20")
48
+ .option("-o, --output [format]", "Output format: text|json|yaml (default: json)")
49
+ .action(async (options) => {
50
+ await listDevboxes(options);
51
+ });
52
+ devbox
53
+ .command("delete <id>")
54
+ .description("Shutdown a devbox")
55
+ .alias("rm")
56
+ .option("-o, --output [format]", "Output format: text|json|yaml (default: text)")
57
+ .action(deleteDevbox);
58
+ devbox
59
+ .command("exec <id> <command...>")
60
+ .description("Execute a command in a devbox")
61
+ .option("--shell-name <name>", "Shell name to use (optional)")
62
+ .option("-o, --output [format]", "Output format: text|json|yaml (default: text)")
63
+ .action(async (id, command, options) => {
64
+ await execCommand(id, command, options);
65
+ });
66
+ devbox
67
+ .command("upload <id> <file>")
68
+ .description("Upload a file to a devbox")
69
+ .option("-p, --path <path>", "Target path in devbox")
70
+ .option("-o, --output [format]", "Output format: text|json|yaml (default: text)")
71
+ .action(uploadFile);
72
+ // Additional devbox commands
73
+ devbox
74
+ .command("get <id>")
75
+ .description("Get devbox details")
76
+ .option("-o, --output [format]", "Output format: text|json|yaml (default: json)")
77
+ .action(async (id, options) => {
78
+ const { getDevbox } = await import("../commands/devbox/get.js");
79
+ await getDevbox(id, options);
80
+ });
81
+ devbox
82
+ .command("suspend <id>")
83
+ .description("Suspend a devbox")
84
+ .option("-o, --output [format]", "Output format: text|json|yaml (default: text)")
85
+ .action(async (id, options) => {
86
+ const { suspendDevbox } = await import("../commands/devbox/suspend.js");
87
+ await suspendDevbox(id, options);
88
+ });
89
+ devbox
90
+ .command("resume <id>")
91
+ .description("Resume a suspended devbox")
92
+ .option("-o, --output [format]", "Output format: text|json|yaml (default: text)")
93
+ .action(async (id, options) => {
94
+ const { resumeDevbox } = await import("../commands/devbox/resume.js");
95
+ await resumeDevbox(id, options);
96
+ });
97
+ devbox
98
+ .command("shutdown <id>")
99
+ .description("Shutdown a devbox")
100
+ .option("-o, --output [format]", "Output format: text|json|yaml (default: text)")
101
+ .action(async (id, options) => {
102
+ const { shutdownDevbox } = await import("../commands/devbox/shutdown.js");
103
+ await shutdownDevbox(id, options);
104
+ });
105
+ devbox
106
+ .command("ssh <id>")
107
+ .description("SSH into a devbox")
108
+ .option("--config-only", "Print SSH config only")
109
+ .option("--no-wait", "Do not wait for devbox to be ready")
110
+ .option("--timeout <seconds>", "Timeout in seconds to wait for readiness", "180")
111
+ .option("--poll-interval <seconds>", "Polling interval in seconds while waiting", "3")
112
+ .option("-o, --output [format]", "Output format: text|json|yaml (default: text)")
113
+ .action(async (id, options) => {
114
+ const { sshDevbox } = await import("../commands/devbox/ssh.js");
115
+ await sshDevbox(id, options);
116
+ });
117
+ devbox
118
+ .command("scp <id> <src> <dst>")
119
+ .description("Copy files to/from a devbox using scp")
120
+ .option("--scp-options <options>", "Additional scp options (quoted)")
121
+ .option("-o, --output [format]", "Output format: text|json|yaml (default: text)")
122
+ .action(async (id, src, dst, options) => {
123
+ const { scpFiles } = await import("../commands/devbox/scp.js");
124
+ await scpFiles(id, { src, dst, ...options });
125
+ });
126
+ devbox
127
+ .command("rsync <id> <src> <dst>")
128
+ .description("Sync files to/from a devbox using rsync")
129
+ .option("--rsync-options <options>", "Additional rsync options (quoted)")
130
+ .option("-o, --output [format]", "Output format: text|json|yaml (default: text)")
131
+ .action(async (id, src, dst, options) => {
132
+ const { rsyncFiles } = await import("../commands/devbox/rsync.js");
133
+ await rsyncFiles(id, { src, dst, ...options });
134
+ });
135
+ devbox
136
+ .command("tunnel <id> <ports>")
137
+ .description("Create a port-forwarding tunnel to a devbox")
138
+ .option("-o, --output [format]", "Output format: text|json|yaml (default: text)")
139
+ .action(async (id, ports, options) => {
140
+ const { createTunnel } = await import("../commands/devbox/tunnel.js");
141
+ await createTunnel(id, { ports, ...options });
142
+ });
143
+ devbox
144
+ .command("read <id>")
145
+ .description("Read a file from a devbox using the API")
146
+ .option("--remote <path>", "Remote file path to read from the devbox")
147
+ .option("--output-path <path>", "Local file path to write the contents to")
148
+ .option("-o, --output [format]", "Output format: text|json|yaml (default: text)")
149
+ .action(async (id, options) => {
150
+ const { readFile } = await import("../commands/devbox/read.js");
151
+ await readFile(id, options);
152
+ });
153
+ devbox
154
+ .command("write <id>")
155
+ .description("Write a file to a devbox using the API")
156
+ .option("--input <path>", "Local file path to read contents from")
157
+ .option("--remote <path>", "Remote file path to write to on the devbox")
158
+ .option("-o, --output [format]", "Output format: text|json|yaml (default: text)")
159
+ .action(async (id, options) => {
160
+ const { writeFile } = await import("../commands/devbox/write.js");
161
+ await writeFile(id, options);
162
+ });
163
+ devbox
164
+ .command("download <id>")
165
+ .description("Download a file from a devbox")
166
+ .option("--file-path <path>", "Path to the file in the devbox")
167
+ .option("--output-path <path>", "Local path where to save the downloaded file")
168
+ .option("-o, --output-format [format]", "Output format: text|json|yaml (default: interactive)")
169
+ .action(async (id, options) => {
170
+ const { downloadFile } = await import("../commands/devbox/download.js");
171
+ await downloadFile(id, options);
172
+ });
173
+ devbox
174
+ .command("exec-async <id> <command...>")
175
+ .description("Execute a command asynchronously on a devbox")
176
+ .option("--shell-name <name>", "Shell name to use (optional)")
177
+ .option("-o, --output [format]", "Output format: text|json|yaml (default: text)")
178
+ .action(async (id, command, options) => {
179
+ const { execAsync } = await import("../commands/devbox/execAsync.js");
180
+ await execAsync(id, { command: command.join(" "), ...options });
181
+ });
182
+ devbox
183
+ .command("get-async <id> <execution-id>")
184
+ .description("Get status of an async execution")
185
+ .option("-o, --output [format]", "Output format: text|json|yaml (default: text)")
186
+ .action(async (id, executionId, options) => {
187
+ const { getAsync } = await import("../commands/devbox/getAsync.js");
188
+ await getAsync(id, { executionId, ...options });
189
+ });
190
+ devbox
191
+ .command("send-stdin <id> <execution-id>")
192
+ .description("Send stdin to a running async execution")
193
+ .option("--text <text>", "Text content to send to stdin")
194
+ .option("--signal <signal>", "Signal to send (EOF, INTERRUPT)")
195
+ .option("-o, --output [format]", "Output format: text|json|yaml (default: text)")
196
+ .action(async (id, executionId, options) => {
197
+ const { sendStdin } = await import("../commands/devbox/sendStdin.js");
198
+ await sendStdin(id, executionId, options);
199
+ });
200
+ devbox
201
+ .command("logs <id>")
202
+ .description("View devbox logs")
203
+ .option("-o, --output [format]", "Output format: text|json|yaml (default: text)")
204
+ .action(async (id, options) => {
205
+ const { getLogs } = await import("../commands/devbox/logs.js");
206
+ await getLogs(id, options);
207
+ });
208
+ // Snapshot commands
209
+ const snapshot = program
210
+ .command("snapshot")
211
+ .description("Manage devbox snapshots")
212
+ .alias("snap");
213
+ snapshot
214
+ .command("list")
215
+ .description("List all snapshots")
216
+ .option("-d, --devbox <id>", "Filter by devbox ID")
217
+ .option("-o, --output [format]", "Output format: text|json|yaml (default: json)")
218
+ .action(async (options) => {
219
+ const { listSnapshots } = await import("../commands/snapshot/list.js");
220
+ await listSnapshots(options);
221
+ });
222
+ snapshot
223
+ .command("create <devbox-id>")
224
+ .description("Create a snapshot of a devbox")
225
+ .option("-n, --name <name>", "Snapshot name")
226
+ .option("-o, --output [format]", "Output format: text|json|yaml (default: text)")
227
+ .action(async (devboxId, options) => {
228
+ const { createSnapshot } = await import("../commands/snapshot/create.js");
229
+ createSnapshot(devboxId, options);
230
+ });
231
+ snapshot
232
+ .command("delete <id>")
233
+ .description("Delete a snapshot")
234
+ .alias("rm")
235
+ .option("-o, --output [format]", "Output format: text|json|yaml (default: text)")
236
+ .action(async (id, options) => {
237
+ const { deleteSnapshot } = await import("../commands/snapshot/delete.js");
238
+ deleteSnapshot(id, options);
239
+ });
240
+ snapshot
241
+ .command("get <id>")
242
+ .description("Get snapshot details")
243
+ .option("-o, --output [format]", "Output format: text|json|yaml (default: json)")
244
+ .action(async (id, options) => {
245
+ const { getSnapshot } = await import("../commands/snapshot/get.js");
246
+ await getSnapshot({ id, ...options });
247
+ });
248
+ snapshot
249
+ .command("status <snapshot-id>")
250
+ .description("Get snapshot operation status")
251
+ .option("-o, --output [format]", "Output format: text|json|yaml (default: text)")
252
+ .action(async (snapshotId, options) => {
253
+ const { getSnapshotStatus } = await import("../commands/snapshot/status.js");
254
+ await getSnapshotStatus({ snapshotId, ...options });
255
+ });
256
+ // Blueprint commands
257
+ const blueprint = program
258
+ .command("blueprint")
259
+ .description("Manage blueprints")
260
+ .alias("bp");
261
+ blueprint
262
+ .command("list")
263
+ .description("List all blueprints")
264
+ .option("-n, --name <name>", "Filter by blueprint name")
265
+ .option("-o, --output [format]", "Output format: text|json|yaml (default: json)")
266
+ .action(async (options) => {
267
+ const { listBlueprints } = await import("../commands/blueprint/list.js");
268
+ await listBlueprints(options);
269
+ });
270
+ blueprint
271
+ .command("create")
272
+ .description("Create a new blueprint")
273
+ .requiredOption("--name <name>", "Blueprint name (required)")
274
+ .option("--dockerfile <content>", "Dockerfile contents")
275
+ .option("--dockerfile-path <path>", "Dockerfile path")
276
+ .option("--system-setup-commands <commands...>", "System setup commands")
277
+ .option("--resources <size>", "Resource size (X_SMALL, SMALL, MEDIUM, LARGE, X_LARGE, XX_LARGE)")
278
+ .option("--architecture <arch>", "Architecture (arm64, x86_64)")
279
+ .option("--available-ports <ports...>", "Available ports")
280
+ .option("--root", "Run as root")
281
+ .option("--user <user:uid>", "Run as this user (format: username:uid)")
282
+ .option("-o, --output [format]", "Output format: text|json|yaml (default: json)")
283
+ .action(async (options) => {
284
+ const { createBlueprint } = await import("../commands/blueprint/create.js");
285
+ await createBlueprint(options);
286
+ });
287
+ blueprint
288
+ .command("get <name-or-id>")
289
+ .description("Get blueprint details by name or ID (IDs start with bpt_)")
290
+ .option("-o, --output [format]", "Output format: text|json|yaml (default: json)")
291
+ .action(async (id, options) => {
292
+ const { getBlueprint } = await import("../commands/blueprint/get.js");
293
+ await getBlueprint({ id, ...options });
294
+ });
295
+ blueprint
296
+ .command("logs <name-or-id>")
297
+ .description("Get blueprint build logs by name or ID (IDs start with bpt_)")
298
+ .option("-o, --output [format]", "Output format: text|json|yaml (default: text)")
299
+ .action(async (id, options) => {
300
+ const { getBlueprintLogs } = await import("../commands/blueprint/logs.js");
301
+ await getBlueprintLogs({ id, ...options });
302
+ });
303
+ // Object storage commands
304
+ const object = program
305
+ .command("object")
306
+ .description("Manage object storage")
307
+ .alias("obj");
308
+ object
309
+ .command("list")
310
+ .description("List objects")
311
+ .option("--limit <n>", "Max results", "20")
312
+ .option("--starting-after <id>", "Starting point for pagination")
313
+ .option("--name <name>", "Filter by name (partial match supported)")
314
+ .option("--content-type <type>", "Filter by content type")
315
+ .option("--state <state>", "Filter by state (UPLOADING, READ_ONLY, DELETED)")
316
+ .option("--search <query>", "Search by object ID or name")
317
+ .option("--public", "List public objects only")
318
+ .option("-o, --output [format]", "Output format: text|json|yaml (default: json)")
319
+ .action(async (options) => {
320
+ const { listObjects } = await import("../commands/object/list.js");
321
+ await listObjects(options);
322
+ });
323
+ object
324
+ .command("get <id>")
325
+ .description("Get object details")
326
+ .option("-o, --output [format]", "Output format: text|json|yaml (default: text)")
327
+ .action(async (id, options) => {
328
+ const { getObject } = await import("../commands/object/get.js");
329
+ await getObject({ id, ...options });
330
+ });
331
+ object
332
+ .command("download <id> <path>")
333
+ .description("Download object to local file")
334
+ .option("--extract", "Extract downloaded archive after download")
335
+ .option("--duration-seconds <seconds>", "Duration in seconds for the presigned URL validity", "3600")
336
+ .option("-o, --output [format]", "Output format: text|json|yaml (default: text)")
337
+ .action(async (id, path, options) => {
338
+ const { downloadObject } = await import("../commands/object/download.js");
339
+ await downloadObject({ id, path, ...options });
340
+ });
341
+ object
342
+ .command("upload <path>")
343
+ .description("Upload a file as an object")
344
+ .option("--name <name>", "Object name (required)")
345
+ .option("--content-type <type>", "Content type: unspecified|text|binary|gzip|tar|tgz")
346
+ .option("--public", "Make object publicly accessible")
347
+ .option("-o, --output [format]", "Output format: text|json|yaml (default: text)")
348
+ .action(async (path, options) => {
349
+ const { uploadObject } = await import("../commands/object/upload.js");
350
+ if (!options.output) {
351
+ const { runInteractiveCommand } = await import("../utils/interactiveCommand.js");
352
+ await runInteractiveCommand(() => uploadObject({ path, ...options }));
353
+ }
354
+ else {
355
+ await uploadObject({ path, ...options });
356
+ }
357
+ });
358
+ object
359
+ .command("delete <id>")
360
+ .description("Delete an object (irreversible)")
361
+ .option("-o, --output [format]", "Output format: text|json|yaml (default: text)")
362
+ .action(async (id, options) => {
363
+ const { deleteObject } = await import("../commands/object/delete.js");
364
+ await deleteObject({ id, ...options });
365
+ });
366
+ // MCP server commands
367
+ const mcp = program
368
+ .command("mcp")
369
+ .description("Model Context Protocol (MCP) server commands");
370
+ mcp
371
+ .command("start")
372
+ .description("Start the MCP server")
373
+ .option("--http", "Use HTTP/SSE transport instead of stdio")
374
+ .option("-p, --port <port>", "Port to listen on for HTTP mode (default: 3000)", parseInt)
375
+ .action(async (options) => {
376
+ if (options.http) {
377
+ const { startMcpHttpServer } = await import("../commands/mcp-http.js");
378
+ await startMcpHttpServer(options.port);
379
+ }
380
+ else {
381
+ const { startMcpServer } = await import("../commands/mcp.js");
382
+ await startMcpServer();
383
+ }
384
+ });
385
+ mcp
386
+ .command("install")
387
+ .description("Install Runloop MCP server configuration in Claude Desktop")
388
+ .action(async () => {
389
+ const { installMcpConfig } = await import("../commands/mcp-install.js");
390
+ await installMcpConfig();
391
+ });
392
+ // Hidden command: 'rli mcp' without subcommand starts the server (for Claude Desktop config compatibility)
393
+ program
394
+ .command("mcp-server", { hidden: true })
395
+ .option("--http", "Use HTTP/SSE transport instead of stdio")
396
+ .option("-p, --port <port>", "Port to listen on for HTTP mode (default: 3000)", parseInt)
397
+ .action(async (options) => {
398
+ if (options.http) {
399
+ const { startMcpHttpServer } = await import("../commands/mcp-http.js");
400
+ await startMcpHttpServer(options.port);
401
+ }
402
+ else {
403
+ const { startMcpServer } = await import("../commands/mcp.js");
404
+ await startMcpServer();
405
+ }
406
+ });
407
+ return program;
408
+ }
@@ -72,3 +72,24 @@ export function setDetectedTheme(theme) {
72
72
  export function clearDetectedTheme() {
73
73
  config.delete("detectedTheme");
74
74
  }
75
+ /**
76
+ * Returns the detailed error message for when the API key is not configured.
77
+ * This message provides instructions on how to set up the API key.
78
+ */
79
+ export function getApiKeyErrorMessage() {
80
+ return `
81
+ ❌ API key not configured.
82
+
83
+ To get started:
84
+ 1. Go to https://platform.runloop.ai/settings and create an API key
85
+ 2. Set the environment variable:
86
+
87
+ export RUNLOOP_API_KEY=your_api_key_here
88
+
89
+ To make it permanent, add this line to your shell config:
90
+ • For zsh: echo 'export RUNLOOP_API_KEY=your_api_key_here' >> ~/.zshrc
91
+ • For bash: echo 'export RUNLOOP_API_KEY=your_api_key_here' >> ~/.bashrc
92
+
93
+ Then restart your terminal or run: source ~/.zshrc (or ~/.bashrc)
94
+ `;
95
+ }
@@ -17,7 +17,7 @@ const darkColors = {
17
17
  info: "#3B82F6", // Blue
18
18
  // UI colors
19
19
  text: "#FFFFFF", // White
20
- textDim: "#9CA3AF", // Gray
20
+ textDim: "#B8C2D0", // Brighter gray for better visibility in dark mode
21
21
  border: "#6B7280", // Medium gray
22
22
  background: "#000000", // Black
23
23
  // Accent colors for menu items and highlights
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@runloop/rl-cli",
3
- "version": "0.5.0",
3
+ "version": "0.10.0",
4
4
  "description": "Beautiful CLI for the Runloop platform",
5
5
  "type": "module",
6
6
  "bin": {
@@ -20,10 +20,12 @@
20
20
  "format:check": "prettier --check \"src/**/*.{ts,tsx,js,jsx,json}\"",
21
21
  "lint": "eslint src --ext .ts,.tsx",
22
22
  "lint:fix": "eslint src --ext .ts,.tsx --fix",
23
- "test": "jest",
24
- "test:watch": "jest --watch",
25
- "test:coverage": "jest --coverage",
26
- "test:components": "NODE_OPTIONS='--experimental-vm-modules' jest --config jest.components.config.js --coverage --forceExit"
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"
27
29
  },
28
30
  "keywords": [
29
31
  "runloop",
@@ -34,14 +36,24 @@
34
36
  ],
35
37
  "author": "Runloop",
36
38
  "license": "MIT",
39
+ "badges": [
40
+ {
41
+ "url": "https://img.shields.io/npm/v/@runloop/rl-cli",
42
+ "description": "NPM version"
43
+ },
44
+ {
45
+ "url": "https://img.shields.io/npm/dw/@runloop/rl-cli",
46
+ "description": "NPM downloads"
47
+ }
48
+ ],
37
49
  "repository": {
38
50
  "type": "git",
39
- "url": "https://github.com/runloop/rl-cli-node.git"
51
+ "url": "https://github.com/runloopai/rl-cli.git"
40
52
  },
41
53
  "bugs": {
42
- "url": "https://github.com/runloop/rl-cli-node/issues"
54
+ "url": "https://github.com/runloopai/rl-cli/issues"
43
55
  },
44
- "homepage": "https://github.com/runloop/rl-cli-node#readme",
56
+ "homepage": "https://github.com/runloopai/rl-cli#readme",
45
57
  "engines": {
46
58
  "node": ">=18.0.0"
47
59
  },
@@ -86,6 +98,7 @@
86
98
  "eslint-plugin-react": "^7.37.5",
87
99
  "eslint-plugin-react-hooks": "^6.1.1",
88
100
  "globals": "^16.4.0",
101
+ "husky": "^9.1.7",
89
102
  "ink-testing-library": "^4.0.0",
90
103
  "jest": "^29.7.0",
91
104
  "prettier": "^3.6.2",
@@ -1,29 +0,0 @@
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
- import { getSettingsUrl } from "../utils/url.js";
10
- import { colors } from "../utils/theme.js";
11
- import { processUtils } from "../utils/processUtils.js";
12
- const AuthUI = () => {
13
- const [apiKey, setApiKeyInput] = React.useState("");
14
- const [saved, setSaved] = React.useState(false);
15
- useInput((input, key) => {
16
- if (key.return && apiKey.trim()) {
17
- setApiKey(apiKey.trim());
18
- setSaved(true);
19
- setTimeout(() => processUtils.exit(0), 1000);
20
- }
21
- });
22
- if (saved) {
23
- return (_jsxs(_Fragment, { children: [_jsx(Banner, {}), _jsx(Header, { title: "Authentication" }), _jsx(SuccessMessage, { message: "API key saved!", details: "Try: rli devbox list" })] }));
24
- }
25
- return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Banner, {}), _jsx(Header, { title: "Authentication" }), _jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { color: colors.textDim, children: "Get your key: " }), _jsx(Text, { color: colors.primary, children: getSettingsUrl() })] }), _jsxs(Box, { children: [_jsx(Text, { color: colors.primary, children: "API Key: " }), _jsx(TextInput, { value: apiKey, onChange: setApiKeyInput, placeholder: "ak_...", mask: "*" })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.textDim, dimColor: true, children: "Press Enter to save" }) })] }));
26
- };
27
- export default function auth() {
28
- render(_jsx(AuthUI, {}));
29
- }
@@ -1,45 +0,0 @@
1
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
- import React from "react";
3
- import { getClient } from "../../utils/client.js";
4
- import { Banner } from "../../components/Banner.js";
5
- import { SpinnerComponent } from "../../components/Spinner.js";
6
- import { SuccessMessage } from "../../components/SuccessMessage.js";
7
- import { ErrorMessage } from "../../components/ErrorMessage.js";
8
- import { createExecutor } from "../../utils/CommandExecutor.js";
9
- const PreviewBlueprintUI = ({ name, dockerfile, systemSetupCommands }) => {
10
- const [loading, setLoading] = React.useState(true);
11
- const [result, setResult] = React.useState(null);
12
- const [error, setError] = React.useState(null);
13
- React.useEffect(() => {
14
- const previewBlueprint = async () => {
15
- try {
16
- const client = getClient();
17
- const blueprint = await client.blueprints.preview({
18
- name,
19
- dockerfile,
20
- system_setup_commands: systemSetupCommands,
21
- });
22
- setResult(blueprint);
23
- }
24
- catch (err) {
25
- setError(err);
26
- }
27
- finally {
28
- setLoading(false);
29
- }
30
- };
31
- previewBlueprint();
32
- }, [name, dockerfile, systemSetupCommands]);
33
- return (_jsxs(_Fragment, { children: [_jsx(Banner, {}), loading && _jsx(SpinnerComponent, { message: "Previewing blueprint..." }), result && (_jsx(SuccessMessage, { message: "Blueprint preview generated", details: `Name: ${result.name}\nDockerfile: ${result.dockerfile ? "Present" : "Not provided"}\nSetup Commands: ${result.systemSetupCommands?.length || 0}` })), error && (_jsx(ErrorMessage, { message: "Failed to preview blueprint", error: error }))] }));
34
- };
35
- export async function previewBlueprint(options) {
36
- const executor = createExecutor({ output: options.output });
37
- await executor.executeAction(async () => {
38
- const client = executor.getClient();
39
- return client.blueprints.preview({
40
- name: options.name,
41
- dockerfile: options.dockerfile,
42
- system_setup_commands: options.systemSetupCommands,
43
- });
44
- }, () => (_jsx(PreviewBlueprintUI, { name: options.name, dockerfile: options.dockerfile, systemSetupCommands: options.systemSetupCommands })));
45
- }