@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.
@@ -1,85 +0,0 @@
1
- /**
2
- * Memory Monitor - Track memory usage and manage GC
3
- * Helps prevent heap exhaustion during navigation
4
- */
5
- let lastMemoryUsage = null;
6
- let gcAttempts = 0;
7
- const MAX_GC_ATTEMPTS_PER_MINUTE = 5;
8
- let lastGCReset = Date.now();
9
- // Memory thresholds (in bytes)
10
- const HEAP_WARNING_THRESHOLD = 3.5e9; // 3.5 GB
11
- const HEAP_CRITICAL_THRESHOLD = 4e9; // 4 GB
12
- export function logMemoryUsage(label) {
13
- if (process.env.NODE_ENV === "development" || process.env.DEBUG_MEMORY) {
14
- const current = process.memoryUsage();
15
- const heapUsedMB = (current.heapUsed / 1024 / 1024).toFixed(2);
16
- const heapTotalMB = (current.heapTotal / 1024 / 1024).toFixed(2);
17
- const rssMB = (current.rss / 1024 / 1024).toFixed(2);
18
- let delta = "";
19
- if (lastMemoryUsage) {
20
- const heapDelta = current.heapUsed - lastMemoryUsage.heapUsed;
21
- const heapDeltaMB = (heapDelta / 1024 / 1024).toFixed(2);
22
- delta = ` (Δ ${heapDeltaMB}MB)`;
23
- }
24
- console.error(`[MEMORY] ${label}: Heap ${heapUsedMB}/${heapTotalMB}MB, RSS ${rssMB}MB${delta}`);
25
- // Warn if approaching limits
26
- if (current.heapUsed > HEAP_WARNING_THRESHOLD) {
27
- console.warn(`[MEMORY WARNING] Heap usage is high: ${heapUsedMB}MB (threshold: 3500MB)`);
28
- }
29
- lastMemoryUsage = current;
30
- }
31
- }
32
- export function getMemoryPressure() {
33
- const usage = process.memoryUsage();
34
- const heapUsedPercent = (usage.heapUsed / usage.heapTotal) * 100;
35
- if (usage.heapUsed > HEAP_CRITICAL_THRESHOLD || heapUsedPercent > 95)
36
- return "critical";
37
- if (usage.heapUsed > HEAP_WARNING_THRESHOLD || heapUsedPercent > 85)
38
- return "high";
39
- if (heapUsedPercent > 70)
40
- return "medium";
41
- return "low";
42
- }
43
- export function shouldTriggerGC() {
44
- const pressure = getMemoryPressure();
45
- return pressure === "high" || pressure === "critical";
46
- }
47
- /**
48
- * Force garbage collection if available and needed
49
- * Respects rate limiting to avoid GC thrashing
50
- */
51
- export function tryForceGC(reason) {
52
- // Reset GC attempt counter every minute
53
- const now = Date.now();
54
- if (now - lastGCReset > 60000) {
55
- gcAttempts = 0;
56
- lastGCReset = now;
57
- }
58
- // Rate limit GC attempts
59
- if (gcAttempts >= MAX_GC_ATTEMPTS_PER_MINUTE) {
60
- return false;
61
- }
62
- // Check if global.gc is available (requires --expose-gc flag)
63
- if (typeof global.gc === "function") {
64
- const beforeHeap = process.memoryUsage().heapUsed;
65
- global.gc();
66
- gcAttempts++;
67
- const afterHeap = process.memoryUsage().heapUsed;
68
- const freedMB = ((beforeHeap - afterHeap) / 1024 / 1024).toFixed(2);
69
- if (process.env.DEBUG_MEMORY) {
70
- console.error(`[MEMORY] Forced GC${reason ? ` (${reason})` : ""}: Freed ${freedMB}MB`);
71
- }
72
- return true;
73
- }
74
- return false;
75
- }
76
- /**
77
- * Monitor memory and trigger GC if needed
78
- * Call this after major operations like screen transitions
79
- */
80
- export function checkMemoryPressure() {
81
- const pressure = getMemoryPressure();
82
- if (pressure === "critical" || pressure === "high") {
83
- tryForceGC(`Memory pressure: ${pressure}`);
84
- }
85
- }
@@ -1,106 +0,0 @@
1
- /**
2
- * Process utilities wrapper for testability.
3
- *
4
- * This module provides wrappers around Node.js process functions
5
- * that can be easily mocked in tests.
6
- */
7
- /**
8
- * Default implementation using real process
9
- */
10
- const defaultProcess = {
11
- exit: (code) => {
12
- process.exit(code);
13
- },
14
- stdout: {
15
- write: (data) => process.stdout.write(data),
16
- get isTTY() {
17
- return process.stdout.isTTY;
18
- },
19
- },
20
- stderr: {
21
- write: (data) => process.stderr.write(data),
22
- },
23
- get env() {
24
- return process.env;
25
- },
26
- cwd: () => process.cwd(),
27
- };
28
- /**
29
- * Current process implementation - can be swapped for testing
30
- */
31
- let currentProcess = defaultProcess;
32
- /**
33
- * Get the current process wrapper
34
- */
35
- export function getProcess() {
36
- return currentProcess;
37
- }
38
- /**
39
- * Set a custom process wrapper (for testing)
40
- */
41
- export function setProcess(proc) {
42
- currentProcess = proc;
43
- }
44
- /**
45
- * Reset to the default process wrapper
46
- */
47
- export function resetProcess() {
48
- currentProcess = defaultProcess;
49
- }
50
- /**
51
- * Exit the process with an optional exit code
52
- */
53
- export function exitProcess(code) {
54
- return currentProcess.exit(code);
55
- }
56
- /**
57
- * Write to stdout
58
- */
59
- export function writeStdout(data) {
60
- return currentProcess.stdout.write(data);
61
- }
62
- /**
63
- * Write to stderr
64
- */
65
- export function writeStderr(data) {
66
- return currentProcess.stderr.write(data);
67
- }
68
- /**
69
- * Check if stdout is a TTY
70
- */
71
- export function isStdoutTTY() {
72
- return !!currentProcess.stdout.isTTY;
73
- }
74
- /**
75
- * Get environment variable
76
- */
77
- export function getEnv(key) {
78
- return currentProcess.env[key];
79
- }
80
- /**
81
- * Get current working directory
82
- */
83
- export function getCwd() {
84
- return currentProcess.cwd();
85
- }
86
- /**
87
- * Create a mock process for testing
88
- */
89
- export function createMockProcess(overrides = {}) {
90
- const exitFn = jest.fn();
91
- const stdoutWriteFn = jest.fn().mockReturnValue(true);
92
- const stderrWriteFn = jest.fn().mockReturnValue(true);
93
- const cwdFn = jest.fn().mockReturnValue("/mock/cwd");
94
- return {
95
- exit: overrides.exit ?? exitFn,
96
- stdout: overrides.stdout ?? {
97
- write: stdoutWriteFn,
98
- isTTY: true,
99
- },
100
- stderr: overrides.stderr ?? {
101
- write: stderrWriteFn,
102
- },
103
- env: overrides.env ?? {},
104
- cwd: overrides.cwd ?? cwdFn,
105
- };
106
- }
@@ -1,53 +0,0 @@
1
- import { shouldCheckForUpdates, updateCheckCache } from "./config.js";
2
- import { VERSION } from "../cli.js";
3
- /**
4
- * Check if a new version is available on npm
5
- * Uses caching to avoid checking too frequently
6
- */
7
- export async function checkForUpdates() {
8
- const currentVersion = VERSION;
9
- // Check if we should check for updates (respects cache)
10
- if (!shouldCheckForUpdates()) {
11
- return {
12
- isUpdateAvailable: false,
13
- latestVersion: null,
14
- currentVersion,
15
- };
16
- }
17
- try {
18
- // Fetch latest version from npm registry
19
- const response = await fetch("https://registry.npmjs.org/@runloop/rl-cli/latest", {
20
- headers: {
21
- Accept: "application/json",
22
- },
23
- });
24
- if (!response.ok) {
25
- // If fetch fails, don't show update message
26
- return {
27
- isUpdateAvailable: false,
28
- latestVersion: null,
29
- currentVersion,
30
- };
31
- }
32
- const data = (await response.json());
33
- const latestVersion = data.version;
34
- // Update the cache
35
- updateCheckCache();
36
- // Compare versions (simple string comparison works for semver)
37
- const isUpdateAvailable = latestVersion !== currentVersion;
38
- return {
39
- isUpdateAvailable,
40
- latestVersion,
41
- currentVersion,
42
- };
43
- }
44
- catch (error) {
45
- // Silently fail - don't show update message if check fails
46
- // This prevents network errors from disrupting the user experience
47
- return {
48
- isUpdateAvailable: false,
49
- latestVersion: null,
50
- currentVersion,
51
- };
52
- }
53
- }