@runloop/rl-cli 0.0.3 → 0.1.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 (73) hide show
  1. package/README.md +64 -29
  2. package/dist/cli.js +401 -92
  3. package/dist/commands/auth.js +12 -11
  4. package/dist/commands/blueprint/create.js +108 -0
  5. package/dist/commands/blueprint/get.js +37 -0
  6. package/dist/commands/blueprint/list.js +293 -225
  7. package/dist/commands/blueprint/logs.js +40 -0
  8. package/dist/commands/blueprint/preview.js +45 -0
  9. package/dist/commands/devbox/create.js +10 -9
  10. package/dist/commands/devbox/delete.js +8 -8
  11. package/dist/commands/devbox/download.js +49 -0
  12. package/dist/commands/devbox/exec.js +23 -13
  13. package/dist/commands/devbox/execAsync.js +43 -0
  14. package/dist/commands/devbox/get.js +37 -0
  15. package/dist/commands/devbox/getAsync.js +37 -0
  16. package/dist/commands/devbox/list.js +328 -190
  17. package/dist/commands/devbox/logs.js +40 -0
  18. package/dist/commands/devbox/read.js +49 -0
  19. package/dist/commands/devbox/resume.js +37 -0
  20. package/dist/commands/devbox/rsync.js +118 -0
  21. package/dist/commands/devbox/scp.js +122 -0
  22. package/dist/commands/devbox/shutdown.js +37 -0
  23. package/dist/commands/devbox/ssh.js +104 -0
  24. package/dist/commands/devbox/suspend.js +37 -0
  25. package/dist/commands/devbox/tunnel.js +120 -0
  26. package/dist/commands/devbox/upload.js +10 -10
  27. package/dist/commands/devbox/write.js +51 -0
  28. package/dist/commands/mcp-http.js +37 -0
  29. package/dist/commands/mcp-install.js +120 -0
  30. package/dist/commands/mcp.js +30 -0
  31. package/dist/commands/menu.js +20 -20
  32. package/dist/commands/object/delete.js +37 -0
  33. package/dist/commands/object/download.js +88 -0
  34. package/dist/commands/object/get.js +37 -0
  35. package/dist/commands/object/list.js +112 -0
  36. package/dist/commands/object/upload.js +130 -0
  37. package/dist/commands/snapshot/create.js +12 -11
  38. package/dist/commands/snapshot/delete.js +8 -8
  39. package/dist/commands/snapshot/list.js +56 -97
  40. package/dist/commands/snapshot/status.js +37 -0
  41. package/dist/components/ActionsPopup.js +16 -13
  42. package/dist/components/Banner.js +4 -4
  43. package/dist/components/Breadcrumb.js +55 -5
  44. package/dist/components/DetailView.js +7 -4
  45. package/dist/components/DevboxActionsMenu.js +315 -178
  46. package/dist/components/DevboxCard.js +15 -14
  47. package/dist/components/DevboxCreatePage.js +147 -113
  48. package/dist/components/DevboxDetailPage.js +180 -102
  49. package/dist/components/ErrorMessage.js +5 -4
  50. package/dist/components/Header.js +4 -3
  51. package/dist/components/MainMenu.js +34 -33
  52. package/dist/components/MetadataDisplay.js +17 -9
  53. package/dist/components/OperationsMenu.js +6 -5
  54. package/dist/components/ResourceActionsMenu.js +117 -0
  55. package/dist/components/ResourceListView.js +213 -0
  56. package/dist/components/Spinner.js +5 -4
  57. package/dist/components/StatusBadge.js +81 -31
  58. package/dist/components/SuccessMessage.js +4 -3
  59. package/dist/components/Table.example.js +53 -23
  60. package/dist/components/Table.js +19 -11
  61. package/dist/hooks/useCursorPagination.js +125 -0
  62. package/dist/mcp/server-http.js +416 -0
  63. package/dist/mcp/server.js +397 -0
  64. package/dist/utils/CommandExecutor.js +16 -12
  65. package/dist/utils/client.js +7 -7
  66. package/dist/utils/config.js +130 -4
  67. package/dist/utils/interactiveCommand.js +2 -2
  68. package/dist/utils/output.js +17 -17
  69. package/dist/utils/ssh.js +160 -0
  70. package/dist/utils/sshSession.js +16 -12
  71. package/dist/utils/theme.js +22 -0
  72. package/dist/utils/url.js +4 -4
  73. package/package.json +29 -4
@@ -0,0 +1,416 @@
1
+ #!/usr/bin/env node
2
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
+ import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
4
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
5
+ import { getClient } from "../utils/client.js";
6
+ import express from "express";
7
+ // Define available tools for the MCP server
8
+ const TOOLS = [
9
+ {
10
+ name: "list_devboxes",
11
+ description: "List all devboxes with optional filtering by status",
12
+ inputSchema: {
13
+ type: "object",
14
+ properties: {
15
+ status: {
16
+ type: "string",
17
+ description: "Filter by status (running, provisioning, suspended, etc.)",
18
+ enum: [
19
+ "running",
20
+ "provisioning",
21
+ "initializing",
22
+ "suspended",
23
+ "shutdown",
24
+ "failure",
25
+ ],
26
+ },
27
+ limit: {
28
+ type: "number",
29
+ description: "Maximum number of devboxes to return",
30
+ },
31
+ },
32
+ },
33
+ },
34
+ {
35
+ name: "get_devbox",
36
+ description: "Get detailed information about a specific devbox by ID",
37
+ inputSchema: {
38
+ type: "object",
39
+ properties: {
40
+ id: {
41
+ type: "string",
42
+ description: "The devbox ID",
43
+ },
44
+ },
45
+ required: ["id"],
46
+ },
47
+ },
48
+ {
49
+ name: "create_devbox",
50
+ description: "Create a new devbox with specified configuration",
51
+ inputSchema: {
52
+ type: "object",
53
+ properties: {
54
+ name: {
55
+ type: "string",
56
+ description: "Name for the devbox",
57
+ },
58
+ blueprint_id: {
59
+ type: "string",
60
+ description: "Blueprint ID to use as template",
61
+ },
62
+ snapshot_id: {
63
+ type: "string",
64
+ description: "Snapshot ID to restore from",
65
+ },
66
+ entrypoint: {
67
+ type: "string",
68
+ description: "Entrypoint script to run on startup",
69
+ },
70
+ environment_variables: {
71
+ type: "object",
72
+ description: "Environment variables as key-value pairs",
73
+ },
74
+ resource_size: {
75
+ type: "string",
76
+ description: "Resource size (SMALL, MEDIUM, LARGE, XLARGE)",
77
+ enum: ["SMALL", "MEDIUM", "LARGE", "XLARGE"],
78
+ },
79
+ keep_alive_seconds: {
80
+ type: "number",
81
+ description: "Keep alive time in seconds",
82
+ },
83
+ },
84
+ },
85
+ },
86
+ {
87
+ name: "execute_command",
88
+ description: "Execute a command on a devbox and get the result",
89
+ inputSchema: {
90
+ type: "object",
91
+ properties: {
92
+ devbox_id: {
93
+ type: "string",
94
+ description: "The devbox ID to execute the command on",
95
+ },
96
+ command: {
97
+ type: "string",
98
+ description: "The command to execute",
99
+ },
100
+ },
101
+ required: ["devbox_id", "command"],
102
+ },
103
+ },
104
+ {
105
+ name: "shutdown_devbox",
106
+ description: "Shutdown a devbox by ID",
107
+ inputSchema: {
108
+ type: "object",
109
+ properties: {
110
+ id: {
111
+ type: "string",
112
+ description: "The devbox ID to shutdown",
113
+ },
114
+ },
115
+ required: ["id"],
116
+ },
117
+ },
118
+ {
119
+ name: "suspend_devbox",
120
+ description: "Suspend a devbox by ID",
121
+ inputSchema: {
122
+ type: "object",
123
+ properties: {
124
+ id: {
125
+ type: "string",
126
+ description: "The devbox ID to suspend",
127
+ },
128
+ },
129
+ required: ["id"],
130
+ },
131
+ },
132
+ {
133
+ name: "resume_devbox",
134
+ description: "Resume a suspended devbox by ID",
135
+ inputSchema: {
136
+ type: "object",
137
+ properties: {
138
+ id: {
139
+ type: "string",
140
+ description: "The devbox ID to resume",
141
+ },
142
+ },
143
+ required: ["id"],
144
+ },
145
+ },
146
+ {
147
+ name: "list_blueprints",
148
+ description: "List all available blueprints",
149
+ inputSchema: {
150
+ type: "object",
151
+ properties: {
152
+ limit: {
153
+ type: "number",
154
+ description: "Maximum number of blueprints to return",
155
+ },
156
+ },
157
+ },
158
+ },
159
+ {
160
+ name: "list_snapshots",
161
+ description: "List all snapshots",
162
+ inputSchema: {
163
+ type: "object",
164
+ properties: {
165
+ devbox_id: {
166
+ type: "string",
167
+ description: "Filter snapshots by devbox ID",
168
+ },
169
+ limit: {
170
+ type: "number",
171
+ description: "Maximum number of snapshots to return",
172
+ },
173
+ },
174
+ },
175
+ },
176
+ {
177
+ name: "create_snapshot",
178
+ description: "Create a snapshot of a devbox",
179
+ inputSchema: {
180
+ type: "object",
181
+ properties: {
182
+ devbox_id: {
183
+ type: "string",
184
+ description: "The devbox ID to snapshot",
185
+ },
186
+ name: {
187
+ type: "string",
188
+ description: "Name for the snapshot",
189
+ },
190
+ },
191
+ required: ["devbox_id"],
192
+ },
193
+ },
194
+ ];
195
+ // Create the MCP server
196
+ const server = new Server({
197
+ name: "runloop-mcp-server",
198
+ version: "1.0.0",
199
+ }, {
200
+ capabilities: {
201
+ tools: {},
202
+ },
203
+ });
204
+ // Handle tool listing
205
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
206
+ return {
207
+ tools: TOOLS,
208
+ };
209
+ });
210
+ // Handle tool execution
211
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
212
+ const { name, arguments: args } = request.params;
213
+ try {
214
+ const client = getClient();
215
+ if (!args) {
216
+ throw new Error("Missing arguments");
217
+ }
218
+ switch (name) {
219
+ case "list_devboxes": {
220
+ const result = await client.devboxes.list({
221
+ status: args.status,
222
+ limit: args.limit,
223
+ });
224
+ return {
225
+ content: [
226
+ {
227
+ type: "text",
228
+ text: JSON.stringify(result, null, 2),
229
+ },
230
+ ],
231
+ };
232
+ }
233
+ case "get_devbox": {
234
+ const result = await client.devboxes.retrieve(args.id);
235
+ return {
236
+ content: [
237
+ {
238
+ type: "text",
239
+ text: JSON.stringify(result, null, 2),
240
+ },
241
+ ],
242
+ };
243
+ }
244
+ case "create_devbox": {
245
+ const createParams = {};
246
+ if (args.name)
247
+ createParams.name = args.name;
248
+ if (args.blueprint_id)
249
+ createParams.blueprint_id = args.blueprint_id;
250
+ if (args.snapshot_id)
251
+ createParams.snapshot_id = args.snapshot_id;
252
+ if (args.entrypoint)
253
+ createParams.entrypoint = args.entrypoint;
254
+ if (args.environment_variables)
255
+ createParams.environment_variables = args.environment_variables;
256
+ if (args.resource_size) {
257
+ createParams.launch_parameters = {
258
+ resource_size_request: args.resource_size,
259
+ };
260
+ }
261
+ if (args.keep_alive_seconds) {
262
+ if (!createParams.launch_parameters)
263
+ createParams.launch_parameters = {};
264
+ createParams.launch_parameters.keep_alive_time_seconds =
265
+ args.keep_alive_seconds;
266
+ }
267
+ const result = await client.devboxes.create(createParams);
268
+ return {
269
+ content: [
270
+ {
271
+ type: "text",
272
+ text: JSON.stringify(result, null, 2),
273
+ },
274
+ ],
275
+ };
276
+ }
277
+ case "execute_command": {
278
+ const result = await client.devboxes.executeSync(args.devbox_id, {
279
+ command: args.command,
280
+ });
281
+ return {
282
+ content: [
283
+ {
284
+ type: "text",
285
+ text: JSON.stringify(result, null, 2),
286
+ },
287
+ ],
288
+ };
289
+ }
290
+ case "shutdown_devbox": {
291
+ const result = await client.devboxes.shutdown(args.id);
292
+ return {
293
+ content: [
294
+ {
295
+ type: "text",
296
+ text: JSON.stringify(result, null, 2),
297
+ },
298
+ ],
299
+ };
300
+ }
301
+ case "suspend_devbox": {
302
+ const result = await client.devboxes.suspend(args.id);
303
+ return {
304
+ content: [
305
+ {
306
+ type: "text",
307
+ text: JSON.stringify(result, null, 2),
308
+ },
309
+ ],
310
+ };
311
+ }
312
+ case "resume_devbox": {
313
+ const result = await client.devboxes.resume(args.id);
314
+ return {
315
+ content: [
316
+ {
317
+ type: "text",
318
+ text: JSON.stringify(result, null, 2),
319
+ },
320
+ ],
321
+ };
322
+ }
323
+ case "list_blueprints": {
324
+ const result = await client.blueprints.list({
325
+ limit: args.limit,
326
+ });
327
+ return {
328
+ content: [
329
+ {
330
+ type: "text",
331
+ text: JSON.stringify(result, null, 2),
332
+ },
333
+ ],
334
+ };
335
+ }
336
+ case "list_snapshots": {
337
+ const params = {};
338
+ if (args.devbox_id)
339
+ params.devbox_id = args.devbox_id;
340
+ const allSnapshots = [];
341
+ let count = 0;
342
+ const limit = args.limit || 100;
343
+ for await (const snapshot of client.devboxes.listDiskSnapshots(params)) {
344
+ allSnapshots.push(snapshot);
345
+ count++;
346
+ if (count >= limit)
347
+ break;
348
+ }
349
+ return {
350
+ content: [
351
+ {
352
+ type: "text",
353
+ text: JSON.stringify(allSnapshots, null, 2),
354
+ },
355
+ ],
356
+ };
357
+ }
358
+ case "create_snapshot": {
359
+ const params = {};
360
+ if (args.name)
361
+ params.name = args.name;
362
+ const result = await client.devboxes.snapshotDisk(args.devbox_id, params);
363
+ return {
364
+ content: [
365
+ {
366
+ type: "text",
367
+ text: JSON.stringify(result, null, 2),
368
+ },
369
+ ],
370
+ };
371
+ }
372
+ default:
373
+ throw new Error(`Unknown tool: ${name}`);
374
+ }
375
+ }
376
+ catch (error) {
377
+ return {
378
+ content: [
379
+ {
380
+ type: "text",
381
+ text: `Error: ${error.message}`,
382
+ },
383
+ ],
384
+ isError: true,
385
+ };
386
+ }
387
+ });
388
+ // Start the HTTP/SSE server
389
+ async function main() {
390
+ const app = express();
391
+ const port = parseInt(process.env.PORT || "3000");
392
+ // Handle SSE endpoint
393
+ app.get("/sse", async (req, res) => {
394
+ console.log("New SSE connection established");
395
+ const transport = new SSEServerTransport("/message", res);
396
+ await server.connect(transport);
397
+ // Keep connection alive
398
+ req.on("close", () => {
399
+ console.log("SSE connection closed");
400
+ });
401
+ });
402
+ // Handle message endpoint for client requests
403
+ app.post("/message", express.json(), async (req, res) => {
404
+ // The SSE transport handles this automatically
405
+ res.status(200).end();
406
+ });
407
+ app.listen(port, () => {
408
+ console.log(`Runloop MCP HTTP server running on http://localhost:${port}`);
409
+ console.log(`SSE endpoint: http://localhost:${port}/sse`);
410
+ console.log(`Message endpoint: http://localhost:${port}/message`);
411
+ });
412
+ }
413
+ main().catch((error) => {
414
+ console.error("Fatal error in main():", error);
415
+ process.exit(1);
416
+ });