rlm-cli 0.2.17 → 0.2.18
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 +2 -0
- package/dist/cli.js +13 -1
- package/dist/interactive.js +21 -9
- package/dist/repl.js +11 -2
- package/dist/viewer.js +10 -2
- package/package.json +1 -1
package/README.md
CHANGED
package/dist/cli.js
CHANGED
|
@@ -91,9 +91,15 @@ function parseArgs() {
|
|
|
91
91
|
return { modelId, file, url, useStdin, verbose, query };
|
|
92
92
|
}
|
|
93
93
|
// ── Helpers ─────────────────────────────────────────────────────────────────
|
|
94
|
+
const MAX_STDIN_BYTES = 50 * 1024 * 1024; // 50MB
|
|
94
95
|
async function readStdin() {
|
|
95
96
|
const chunks = [];
|
|
97
|
+
let total = 0;
|
|
96
98
|
for await (const chunk of process.stdin) {
|
|
99
|
+
total += chunk.length;
|
|
100
|
+
if (total > MAX_STDIN_BYTES) {
|
|
101
|
+
throw new Error(`stdin exceeds ${MAX_STDIN_BYTES / 1024 / 1024}MB limit`);
|
|
102
|
+
}
|
|
97
103
|
chunks.push(chunk);
|
|
98
104
|
}
|
|
99
105
|
return Buffer.concat(chunks).toString("utf-8");
|
|
@@ -129,7 +135,13 @@ async function main() {
|
|
|
129
135
|
let context;
|
|
130
136
|
if (args.file) {
|
|
131
137
|
console.error(`Reading context from file: ${args.file}`);
|
|
132
|
-
|
|
138
|
+
try {
|
|
139
|
+
context = fs.readFileSync(args.file, "utf-8");
|
|
140
|
+
}
|
|
141
|
+
catch (err) {
|
|
142
|
+
console.error(`Error: could not read file "${args.file}": ${err.message}`);
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
133
145
|
}
|
|
134
146
|
else if (args.url) {
|
|
135
147
|
console.error(`Fetching context from URL: ${args.url}`);
|
package/dist/interactive.js
CHANGED
|
@@ -652,11 +652,11 @@ function isBinaryFile(filePath) {
|
|
|
652
652
|
if (BINARY_EXTENSIONS.has(ext))
|
|
653
653
|
return true;
|
|
654
654
|
// Quick null-byte check on first 512 bytes
|
|
655
|
+
let fd;
|
|
655
656
|
try {
|
|
656
|
-
|
|
657
|
+
fd = fs.openSync(filePath, "r");
|
|
657
658
|
const buf = Buffer.alloc(512);
|
|
658
659
|
const bytesRead = fs.readSync(fd, buf, 0, 512, 0);
|
|
659
|
-
fs.closeSync(fd);
|
|
660
660
|
for (let i = 0; i < bytesRead; i++) {
|
|
661
661
|
if (buf[i] === 0)
|
|
662
662
|
return true;
|
|
@@ -665,9 +665,19 @@ function isBinaryFile(filePath) {
|
|
|
665
665
|
catch { /* unreadable → skip */
|
|
666
666
|
return true;
|
|
667
667
|
}
|
|
668
|
+
finally {
|
|
669
|
+
if (fd !== undefined)
|
|
670
|
+
try {
|
|
671
|
+
fs.closeSync(fd);
|
|
672
|
+
}
|
|
673
|
+
catch { }
|
|
674
|
+
}
|
|
668
675
|
return false;
|
|
669
676
|
}
|
|
670
|
-
|
|
677
|
+
const MAX_DIR_DEPTH = 30;
|
|
678
|
+
function walkDir(dir, depth = 0) {
|
|
679
|
+
if (depth > MAX_DIR_DEPTH)
|
|
680
|
+
return [];
|
|
671
681
|
const results = [];
|
|
672
682
|
let entries;
|
|
673
683
|
try {
|
|
@@ -679,11 +689,13 @@ function walkDir(dir) {
|
|
|
679
689
|
for (const entry of entries) {
|
|
680
690
|
if (entry.name.startsWith(".") && entry.name !== ".env")
|
|
681
691
|
continue;
|
|
692
|
+
if (entry.isSymbolicLink())
|
|
693
|
+
continue;
|
|
682
694
|
const full = path.join(dir, entry.name);
|
|
683
695
|
if (entry.isDirectory()) {
|
|
684
696
|
if (SKIP_DIRS.has(entry.name))
|
|
685
697
|
continue;
|
|
686
|
-
results.push(...walkDir(full));
|
|
698
|
+
results.push(...walkDir(full, depth + 1));
|
|
687
699
|
}
|
|
688
700
|
else if (entry.isFile()) {
|
|
689
701
|
if (!isBinaryFile(full))
|
|
@@ -694,12 +706,12 @@ function walkDir(dir) {
|
|
|
694
706
|
}
|
|
695
707
|
return results;
|
|
696
708
|
}
|
|
697
|
-
function simpleGlobMatch(pattern, filePath) {
|
|
698
|
-
// Expand {a,b,c} braces into alternatives
|
|
709
|
+
function simpleGlobMatch(pattern, filePath, _braceDepth = 0) {
|
|
710
|
+
// Expand {a,b,c} braces into alternatives (with depth limit)
|
|
699
711
|
const braceMatch = pattern.match(/\{([^}]+)\}/);
|
|
700
|
-
if (braceMatch) {
|
|
701
|
-
const alternatives = braceMatch[1].split(",");
|
|
702
|
-
return alternatives.some((alt) => simpleGlobMatch(pattern.replace(braceMatch[0], alt.trim()), filePath));
|
|
712
|
+
if (braceMatch && _braceDepth < 5) {
|
|
713
|
+
const alternatives = braceMatch[1].split(",").slice(0, 50);
|
|
714
|
+
return alternatives.some((alt) => simpleGlobMatch(pattern.replace(braceMatch[0], alt.trim()), filePath, _braceDepth + 1));
|
|
703
715
|
}
|
|
704
716
|
// Convert glob to regex
|
|
705
717
|
let regex = "^";
|
package/dist/repl.js
CHANGED
|
@@ -32,13 +32,18 @@ export class PythonRepl {
|
|
|
32
32
|
return;
|
|
33
33
|
const runtimePath = path.join(path.dirname(fileURLToPath(import.meta.url)), "runtime.py");
|
|
34
34
|
const pythonCmd = process.platform === "win32" ? "python" : "python3";
|
|
35
|
+
const homeDir = os.homedir();
|
|
35
36
|
this.proc = spawn(pythonCmd, [runtimePath], {
|
|
36
37
|
stdio: ["pipe", "pipe", "pipe"],
|
|
37
38
|
env: {
|
|
38
39
|
// Only pass what Python actually needs — not API keys or secrets
|
|
39
40
|
PATH: process.env.PATH,
|
|
40
|
-
HOME:
|
|
41
|
+
HOME: homeDir,
|
|
42
|
+
USERPROFILE: homeDir, // Windows uses USERPROFILE
|
|
41
43
|
PYTHONUNBUFFERED: "1",
|
|
44
|
+
// Windows needs SystemRoot/SYSTEMROOT for Python to find DLLs
|
|
45
|
+
...(process.env.SystemRoot ? { SystemRoot: process.env.SystemRoot } : {}),
|
|
46
|
+
...(process.env.SYSTEMROOT ? { SYSTEMROOT: process.env.SYSTEMROOT } : {}),
|
|
42
47
|
},
|
|
43
48
|
});
|
|
44
49
|
this.rl = readline.createInterface({ input: this.proc.stdout });
|
|
@@ -93,7 +98,11 @@ export class PythonRepl {
|
|
|
93
98
|
catch {
|
|
94
99
|
// stdin may already be closed
|
|
95
100
|
}
|
|
96
|
-
|
|
101
|
+
// SIGTERM is ignored on Windows; use SIGKILL as fallback
|
|
102
|
+
try {
|
|
103
|
+
this.proc.kill(process.platform === "win32" ? "SIGKILL" : "SIGTERM");
|
|
104
|
+
}
|
|
105
|
+
catch { /* already dead */ }
|
|
97
106
|
}
|
|
98
107
|
this.cleanup();
|
|
99
108
|
}
|
package/dist/viewer.js
CHANGED
|
@@ -614,9 +614,17 @@ function syntaxHighlight(code) {
|
|
|
614
614
|
async function main() {
|
|
615
615
|
// Enter alternate screen buffer so output never scrolls the main terminal
|
|
616
616
|
W(c.altScreenOn);
|
|
617
|
-
// Ensure we always
|
|
618
|
-
const cleanup = () =>
|
|
617
|
+
// Ensure we always restore terminal on exit (alt screen, cursor, raw mode)
|
|
618
|
+
const cleanup = () => {
|
|
619
|
+
try {
|
|
620
|
+
process.stdin.setRawMode(false);
|
|
621
|
+
}
|
|
622
|
+
catch { }
|
|
623
|
+
W(c.showCursor, c.altScreenOff);
|
|
624
|
+
};
|
|
619
625
|
process.on("exit", cleanup);
|
|
626
|
+
process.on("SIGINT", () => { cleanup(); process.exit(0); });
|
|
627
|
+
process.on("SIGTERM", () => { cleanup(); process.exit(0); });
|
|
620
628
|
let filePath = process.argv[2];
|
|
621
629
|
if (!filePath) {
|
|
622
630
|
const files = listTrajectories();
|