@runloop/rl-cli 0.1.2 → 0.3.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.
- package/README.md +54 -10
- package/dist/cli.js +79 -72
- package/dist/commands/auth.js +2 -2
- package/dist/commands/blueprint/create.js +31 -83
- package/dist/commands/blueprint/get.js +29 -34
- package/dist/commands/blueprint/list.js +278 -230
- package/dist/commands/blueprint/logs.js +133 -37
- package/dist/commands/config.js +118 -0
- package/dist/commands/devbox/create.js +120 -40
- package/dist/commands/devbox/delete.js +17 -33
- package/dist/commands/devbox/download.js +29 -43
- package/dist/commands/devbox/exec.js +22 -39
- package/dist/commands/devbox/execAsync.js +20 -37
- package/dist/commands/devbox/get.js +13 -35
- package/dist/commands/devbox/getAsync.js +12 -34
- package/dist/commands/devbox/list.js +241 -402
- package/dist/commands/devbox/logs.js +20 -38
- package/dist/commands/devbox/read.js +29 -43
- package/dist/commands/devbox/resume.js +13 -35
- package/dist/commands/devbox/rsync.js +26 -78
- package/dist/commands/devbox/scp.js +25 -79
- package/dist/commands/devbox/sendStdin.js +41 -0
- package/dist/commands/devbox/shutdown.js +13 -35
- package/dist/commands/devbox/ssh.js +46 -78
- package/dist/commands/devbox/suspend.js +13 -35
- package/dist/commands/devbox/tunnel.js +37 -88
- package/dist/commands/devbox/upload.js +28 -36
- package/dist/commands/devbox/write.js +29 -44
- package/dist/commands/mcp-http.js +6 -5
- package/dist/commands/mcp-install.js +12 -10
- package/dist/commands/mcp.js +5 -4
- package/dist/commands/menu.js +26 -67
- package/dist/commands/object/delete.js +12 -34
- package/dist/commands/object/download.js +26 -74
- package/dist/commands/object/get.js +12 -34
- package/dist/commands/object/list.js +15 -93
- package/dist/commands/object/upload.js +35 -96
- package/dist/commands/snapshot/create.js +23 -39
- package/dist/commands/snapshot/delete.js +17 -33
- package/dist/commands/snapshot/get.js +16 -0
- package/dist/commands/snapshot/list.js +309 -80
- package/dist/commands/snapshot/status.js +12 -34
- package/dist/components/ActionsPopup.js +64 -39
- package/dist/components/Banner.js +7 -1
- package/dist/components/Breadcrumb.js +11 -48
- package/dist/components/DevboxActionsMenu.js +117 -207
- package/dist/components/DevboxCreatePage.js +12 -7
- package/dist/components/DevboxDetailPage.js +76 -28
- package/dist/components/ErrorBoundary.js +29 -0
- package/dist/components/ErrorMessage.js +10 -2
- package/dist/components/Header.js +12 -4
- package/dist/components/InteractiveSpawn.js +104 -0
- package/dist/components/LogsViewer.js +169 -0
- package/dist/components/MainMenu.js +37 -33
- package/dist/components/MetadataDisplay.js +4 -4
- package/dist/components/OperationsMenu.js +1 -1
- package/dist/components/ResourceActionsMenu.js +4 -4
- package/dist/components/ResourceListView.js +46 -34
- package/dist/components/Spinner.js +7 -2
- package/dist/components/StatusBadge.js +1 -1
- package/dist/components/SuccessMessage.js +12 -2
- package/dist/components/Table.js +16 -6
- package/dist/components/UpdateNotification.js +56 -0
- package/dist/hooks/useCursorPagination.js +125 -85
- package/dist/hooks/useExitOnCtrlC.js +15 -0
- package/dist/hooks/useViewportHeight.js +47 -0
- package/dist/mcp/server-http.js +2 -1
- package/dist/mcp/server.js +71 -7
- package/dist/router/Router.js +70 -0
- package/dist/router/types.js +1 -0
- package/dist/screens/BlueprintListScreen.js +7 -0
- package/dist/screens/BlueprintLogsScreen.js +74 -0
- package/dist/screens/DevboxActionsScreen.js +25 -0
- package/dist/screens/DevboxCreateScreen.js +11 -0
- package/dist/screens/DevboxDetailScreen.js +60 -0
- package/dist/screens/DevboxListScreen.js +23 -0
- package/dist/screens/LogsSessionScreen.js +49 -0
- package/dist/screens/MenuScreen.js +23 -0
- package/dist/screens/SSHSessionScreen.js +55 -0
- package/dist/screens/SnapshotListScreen.js +7 -0
- package/dist/services/blueprintService.js +101 -0
- package/dist/services/devboxService.js +215 -0
- package/dist/services/snapshotService.js +81 -0
- package/dist/store/blueprintStore.js +89 -0
- package/dist/store/devboxStore.js +105 -0
- package/dist/store/index.js +7 -0
- package/dist/store/navigationStore.js +101 -0
- package/dist/store/snapshotStore.js +87 -0
- package/dist/utils/client.js +4 -2
- package/dist/utils/config.js +22 -111
- package/dist/utils/interactiveCommand.js +3 -2
- package/dist/utils/logFormatter.js +208 -0
- package/dist/utils/memoryMonitor.js +85 -0
- package/dist/utils/output.js +153 -61
- package/dist/utils/process.js +106 -0
- package/dist/utils/processUtils.js +135 -0
- package/dist/utils/screen.js +61 -0
- package/dist/utils/ssh.js +6 -3
- package/dist/utils/sshSession.js +5 -29
- package/dist/utils/terminalDetection.js +185 -0
- package/dist/utils/terminalSync.js +39 -0
- package/dist/utils/theme.js +162 -13
- package/dist/utils/versionCheck.js +53 -0
- package/dist/version.js +12 -0
- package/package.json +19 -17
|
@@ -2,44 +2,69 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { Box, Text } from "ink";
|
|
3
3
|
import figures from "figures";
|
|
4
4
|
import chalk from "chalk";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
5
|
+
import { getChalkTextColor, getChalkColor } from "../utils/theme.js";
|
|
6
|
+
export const ActionsPopup = ({ devbox: _devbox, operations, selectedOperation, onClose: _onClose, }) => {
|
|
7
|
+
// Calculate max width needed for content (visible characters only)
|
|
8
|
+
// CRITICAL: Ensure all values are valid numbers to prevent Yoga crashes
|
|
9
|
+
const maxContentWidth = Math.max(...operations.map((op) => {
|
|
10
|
+
const lineText = `${figures.pointer} ${op.icon} ${op.label} [${op.shortcut}]`;
|
|
11
|
+
const len = lineText.length;
|
|
12
|
+
return Number.isFinite(len) && len > 0 ? len : 0;
|
|
13
|
+
}), `${figures.play} Quick Actions`.length, `${figures.arrowUp}${figures.arrowDown} Nav • [Enter] • [Esc] Close`.length, 40);
|
|
14
|
+
// Add horizontal padding to width (2 spaces on each side = 4 total)
|
|
15
|
+
// Plus 2 for border characters = 6 total extra
|
|
16
|
+
// CRITICAL: Validate all computed widths are positive integers
|
|
17
|
+
const contentWidth = Math.max(10, maxContentWidth + 4);
|
|
18
|
+
// Get background color chalk function - use theme colors to match the theme mode
|
|
19
|
+
// In light mode, use light background; in dark mode, use dark background
|
|
20
|
+
const popupBgHex = getChalkColor("background");
|
|
21
|
+
const popupTextHex = getChalkColor("text");
|
|
22
|
+
const bgColorFn = chalk.bgHex(popupBgHex);
|
|
23
|
+
const textColorFn = chalk.hex(popupTextHex);
|
|
24
|
+
// Helper to create background lines with proper padding including left/right margins
|
|
25
|
+
const createBgLine = (styledContent, plainContent) => {
|
|
26
|
+
const visibleLength = plainContent.length;
|
|
27
|
+
// CRITICAL: Validate repeat count is non-negative integer
|
|
28
|
+
const repeatCount = Math.max(0, Math.floor(maxContentWidth - visibleLength));
|
|
29
|
+
const rightPadding = " ".repeat(repeatCount);
|
|
30
|
+
// Apply background to left padding + content + right padding
|
|
31
|
+
return bgColorFn(" " + styledContent + rightPadding + " ");
|
|
15
32
|
};
|
|
16
|
-
//
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
33
|
+
// Create empty line with full background
|
|
34
|
+
// CRITICAL: Validate repeat count is positive integer
|
|
35
|
+
const emptyLine = bgColorFn(" ".repeat(Math.max(1, Math.floor(contentWidth))));
|
|
36
|
+
// Create border lines with background and integrated title
|
|
37
|
+
const title = `${figures.play} Quick Actions`;
|
|
38
|
+
// The content between ╭ and ╮ should be exactly contentWidth
|
|
39
|
+
// Format: "─ title ─────"
|
|
40
|
+
const titleWithSpaces = ` ${title} `;
|
|
41
|
+
const titleTotalLength = titleWithSpaces.length + 1; // +1 for leading dash
|
|
42
|
+
// CRITICAL: Validate repeat counts are non-negative integers
|
|
43
|
+
const remainingDashes = Math.max(0, Math.floor(contentWidth - titleTotalLength));
|
|
44
|
+
// Use theme primary color for borders to match theme
|
|
45
|
+
const borderColorFn = getChalkTextColor("primary");
|
|
46
|
+
const borderTop = bgColorFn(borderColorFn("╭─" + titleWithSpaces + "─".repeat(remainingDashes) + "╮"));
|
|
47
|
+
// CRITICAL: Validate contentWidth is a positive integer
|
|
48
|
+
const borderBottom = bgColorFn(borderColorFn("╰" + "─".repeat(Math.max(1, Math.floor(contentWidth))) + "╯"));
|
|
49
|
+
const borderSide = (content) => {
|
|
50
|
+
return bgColorFn(borderColorFn("│") + content + borderColorFn("│"));
|
|
51
|
+
};
|
|
52
|
+
return (_jsx(Box, { flexDirection: "column", alignItems: "center", children: _jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: borderTop }), _jsx(Text, { children: borderSide(emptyLine) }), operations.map((op, index) => {
|
|
53
|
+
const isSelected = index === selectedOperation;
|
|
54
|
+
const pointer = isSelected ? figures.pointer : " ";
|
|
55
|
+
const lineText = `${pointer} ${op.icon} ${op.label} [${op.shortcut}]`;
|
|
56
|
+
let styledLine;
|
|
57
|
+
if (isSelected) {
|
|
58
|
+
// Selected: use operation-specific color for icon and label
|
|
59
|
+
const opColor = op.color;
|
|
60
|
+
const colorFn = chalk[opColor] || textColorFn;
|
|
61
|
+
styledLine = `${textColorFn(pointer)} ${colorFn(op.icon)} ${colorFn.bold(op.label)} ${textColorFn(`[${op.shortcut}]`)}`;
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
// Unselected: use theme's textDim color for dimmed text
|
|
65
|
+
const dimFn = getChalkTextColor("textDim");
|
|
66
|
+
styledLine = `${dimFn(pointer)} ${dimFn(op.icon)} ${dimFn(op.label)} ${dimFn(`[${op.shortcut}]`)}`;
|
|
67
|
+
}
|
|
68
|
+
return (_jsx(Text, { children: borderSide(createBgLine(styledLine, lineText)) }, op.key));
|
|
69
|
+
}), _jsx(Text, { children: borderSide(emptyLine) }), _jsx(Text, { children: borderSide(createBgLine(textColorFn(`${figures.arrowUp}${figures.arrowDown} Nav • [Enter] • [Esc] Close`), `${figures.arrowUp}${figures.arrowDown} Nav • [Enter] • [Esc] Close`)) }), _jsx(Text, { children: borderSide(emptyLine) }), _jsx(Text, { children: borderBottom })] }) }));
|
|
45
70
|
};
|
|
@@ -3,6 +3,12 @@ import React from "react";
|
|
|
3
3
|
import { Box } from "ink";
|
|
4
4
|
import BigText from "ink-big-text";
|
|
5
5
|
import Gradient from "ink-gradient";
|
|
6
|
+
import { isLightMode } from "../utils/theme.js";
|
|
6
7
|
export const Banner = React.memo(() => {
|
|
7
|
-
|
|
8
|
+
// Use theme-aware gradient colors
|
|
9
|
+
// In light mode, use darker/deeper colors for better contrast on light backgrounds
|
|
10
|
+
// "teen" has darker colors (blue/purple) that work well on light backgrounds
|
|
11
|
+
// In dark mode, use the vibrant "vice" gradient (pink/cyan) that works well on dark backgrounds
|
|
12
|
+
const gradientName = isLightMode() ? "teen" : "vice";
|
|
13
|
+
return (_jsx(Box, { flexDirection: "column", alignItems: "flex-start", paddingX: 1, children: _jsx(Gradient, { name: gradientName, children: _jsx(BigText, { text: "RUNLOOP.ai", font: "simple3d" }) }) }));
|
|
8
14
|
});
|
|
@@ -2,53 +2,16 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import React from "react";
|
|
3
3
|
import { Box, Text } from "ink";
|
|
4
4
|
import { colors } from "../utils/theme.js";
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
const VersionCheck = () => {
|
|
8
|
-
const [updateAvailable, setUpdateAvailable] = React.useState(null);
|
|
9
|
-
const [isChecking, setIsChecking] = React.useState(true);
|
|
10
|
-
React.useEffect(() => {
|
|
11
|
-
const checkForUpdates = async () => {
|
|
12
|
-
try {
|
|
13
|
-
// Import the utility functions from config
|
|
14
|
-
const { checkForUpdates: checkForUpdatesUtil } = await import("../utils/config.js");
|
|
15
|
-
// Use the same logic as the non-interactive version
|
|
16
|
-
// We'll call the utility function and capture its output
|
|
17
|
-
const originalConsoleError = console.error;
|
|
18
|
-
let updateMessage = "";
|
|
19
|
-
// Capture the console.error output
|
|
20
|
-
console.error = (...args) => {
|
|
21
|
-
updateMessage = args.join(' ');
|
|
22
|
-
originalConsoleError(...args);
|
|
23
|
-
};
|
|
24
|
-
// Call the update check utility
|
|
25
|
-
await checkForUpdatesUtil(false);
|
|
26
|
-
// Restore original console.error
|
|
27
|
-
console.error = originalConsoleError;
|
|
28
|
-
// Parse the update message to extract the latest version
|
|
29
|
-
if (updateMessage.includes("Update available:")) {
|
|
30
|
-
const match = updateMessage.match(/Update available: .+ → (.+)/);
|
|
31
|
-
if (match && match[1]) {
|
|
32
|
-
setUpdateAvailable(match[1]);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
catch (error) {
|
|
37
|
-
// Silently fail
|
|
38
|
-
}
|
|
39
|
-
finally {
|
|
40
|
-
setIsChecking(false);
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
checkForUpdates();
|
|
44
|
-
}, []);
|
|
45
|
-
if (isChecking || !updateAvailable) {
|
|
46
|
-
return null;
|
|
47
|
-
}
|
|
48
|
-
return (_jsxs(Box, { children: [_jsx(Text, { color: colors.primary, bold: true, children: "\u2728" }), _jsxs(Text, { color: colors.text, bold: true, children: [" ", "Update available:", " "] }), _jsx(Text, { color: colors.textDim, dimColor: true, children: VERSION }), _jsxs(Text, { color: colors.primary, bold: true, children: [" ", "\u2192", " "] }), _jsx(Text, { color: colors.success, bold: true, children: updateAvailable }), _jsxs(Text, { color: colors.textDim, dimColor: true, children: [" ", "\u2022 Run:", " "] }), _jsx(Text, { color: colors.primary, bold: true, children: "npm install -g @runloop/rl-cli@latest" })] }));
|
|
49
|
-
};
|
|
50
|
-
export const Breadcrumb = React.memo(({ items, showVersionCheck = false }) => {
|
|
5
|
+
import { UpdateNotification } from "./UpdateNotification.js";
|
|
6
|
+
export const Breadcrumb = ({ items, showVersionCheck = false, }) => {
|
|
51
7
|
const env = process.env.RUNLOOP_ENV?.toLowerCase();
|
|
52
8
|
const isDevEnvironment = env === "dev";
|
|
53
|
-
return (_jsxs(Box, { marginBottom: 1, paddingX:
|
|
54
|
-
|
|
9
|
+
return (_jsxs(Box, { marginBottom: 1, paddingX: 0, paddingY: 0, children: [_jsxs(Box, { borderStyle: "round", borderColor: colors.primary, paddingX: 2, paddingY: 0, children: [_jsx(Text, { color: colors.primary, bold: true, children: "rl" }), isDevEnvironment && (_jsxs(Text, { color: colors.error, bold: true, children: [" ", "(dev)"] })), _jsx(Text, { color: colors.textDim, children: " \u203A " }), items.map((item, index) => {
|
|
10
|
+
// Limit label length to prevent Yoga layout engine errors
|
|
11
|
+
const MAX_LABEL_LENGTH = 80;
|
|
12
|
+
const truncatedLabel = item.label.length > MAX_LABEL_LENGTH
|
|
13
|
+
? item.label.substring(0, MAX_LABEL_LENGTH) + "..."
|
|
14
|
+
: item.label;
|
|
15
|
+
return (_jsxs(React.Fragment, { children: [_jsx(Text, { color: item.active ? colors.primary : colors.textDim, children: truncatedLabel }), index < items.length - 1 && (_jsx(Text, { color: colors.textDim, children: " \u203A " }))] }, index));
|
|
16
|
+
})] }), showVersionCheck && (_jsx(Box, { paddingX: 2, marginTop: 0, children: _jsx(UpdateNotification, {}) }))] }));
|
|
17
|
+
};
|