rlm-cli 0.2.21 → 0.2.23
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/dist/cli.js +57 -2
- package/dist/env.js +5 -2
- package/dist/main.js +3 -2
- package/dist/repl.js +28 -5
- package/dist/runtime.py +4 -0
- package/dist/viewer.js +2 -6
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -114,16 +114,60 @@ async function fetchUrl(url) {
|
|
|
114
114
|
// ── Main ────────────────────────────────────────────────────────────────────
|
|
115
115
|
async function main() {
|
|
116
116
|
const args = parseArgs();
|
|
117
|
-
//
|
|
117
|
+
// Provider → env var mapping
|
|
118
|
+
const providerKeys = {
|
|
119
|
+
anthropic: "ANTHROPIC_API_KEY", openai: "OPENAI_API_KEY",
|
|
120
|
+
google: "GEMINI_API_KEY", "google-gemini-cli": "GEMINI_API_KEY",
|
|
121
|
+
groq: "GROQ_API_KEY", xai: "XAI_API_KEY",
|
|
122
|
+
mistral: "MISTRAL_API_KEY", openrouter: "OPENROUTER_API_KEY",
|
|
123
|
+
};
|
|
124
|
+
const defaultModels = {
|
|
125
|
+
anthropic: "claude-sonnet-4-6", openai: "gpt-4o",
|
|
126
|
+
google: "gemini-2.5-flash", groq: "llama-3.3-70b-versatile",
|
|
127
|
+
xai: "grok-4", mistral: "mistral-large-latest", openrouter: "claude-sonnet-4-6",
|
|
128
|
+
};
|
|
129
|
+
// Resolve model — ensure provider has an API key
|
|
118
130
|
let model;
|
|
131
|
+
let resolvedProvider = "";
|
|
119
132
|
const allModelIds = [];
|
|
120
133
|
for (const provider of getProviders()) {
|
|
121
134
|
const providerModels = getModels(provider);
|
|
122
135
|
for (const m of providerModels) {
|
|
123
136
|
allModelIds.push(m.id);
|
|
124
137
|
if (!model && m.id === args.modelId) {
|
|
125
|
-
|
|
138
|
+
const key = providerKeys[provider] || `${provider.toUpperCase().replace(/-/g, "_")}_API_KEY`;
|
|
139
|
+
if (process.env[key]) {
|
|
140
|
+
model = m;
|
|
141
|
+
resolvedProvider = provider;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// Fallback: if default model's provider has no key, pick one that does
|
|
147
|
+
if (!model) {
|
|
148
|
+
for (const [prov, envKey] of Object.entries(providerKeys)) {
|
|
149
|
+
if (!process.env[envKey])
|
|
150
|
+
continue;
|
|
151
|
+
const fallbackId = defaultModels[prov];
|
|
152
|
+
if (!fallbackId)
|
|
153
|
+
continue;
|
|
154
|
+
for (const p of getProviders()) {
|
|
155
|
+
if (p !== prov)
|
|
156
|
+
continue;
|
|
157
|
+
for (const m of getModels(p)) {
|
|
158
|
+
if (m.id === fallbackId) {
|
|
159
|
+
model = m;
|
|
160
|
+
resolvedProvider = prov;
|
|
161
|
+
args.modelId = fallbackId;
|
|
162
|
+
console.error(`Note: using ${fallbackId} (${prov}) — set RLM_MODEL to override`);
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (model)
|
|
167
|
+
break;
|
|
126
168
|
}
|
|
169
|
+
if (model)
|
|
170
|
+
break;
|
|
127
171
|
}
|
|
128
172
|
}
|
|
129
173
|
if (!model) {
|
|
@@ -134,6 +178,17 @@ async function main() {
|
|
|
134
178
|
// Load context
|
|
135
179
|
let context;
|
|
136
180
|
if (args.file) {
|
|
181
|
+
try {
|
|
182
|
+
const stat = fs.statSync(args.file);
|
|
183
|
+
if (stat.isDirectory()) {
|
|
184
|
+
console.error(`Error: "${args.file}" is a directory. Use the interactive mode (\`rlm\`) for directory loading.`);
|
|
185
|
+
process.exit(1);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
catch (err) {
|
|
189
|
+
console.error(`Error: could not access "${args.file}": ${err.message}`);
|
|
190
|
+
process.exit(1);
|
|
191
|
+
}
|
|
137
192
|
console.error(`Reading context from file: ${args.file}`);
|
|
138
193
|
try {
|
|
139
194
|
context = fs.readFileSync(args.file, "utf-8");
|
package/dist/env.js
CHANGED
|
@@ -14,10 +14,13 @@ function loadEnvFile(filePath) {
|
|
|
14
14
|
if (!fs.existsSync(filePath))
|
|
15
15
|
return;
|
|
16
16
|
const content = fs.readFileSync(filePath, "utf-8");
|
|
17
|
-
for (const
|
|
18
|
-
|
|
17
|
+
for (const rawLine of content.split("\n")) {
|
|
18
|
+
let trimmed = rawLine.trim();
|
|
19
19
|
if (!trimmed || trimmed.startsWith("#"))
|
|
20
20
|
continue;
|
|
21
|
+
// Strip leading "export " (common in .env files)
|
|
22
|
+
if (trimmed.startsWith("export "))
|
|
23
|
+
trimmed = trimmed.slice(7);
|
|
21
24
|
const eqIndex = trimmed.indexOf("=");
|
|
22
25
|
if (eqIndex === -1)
|
|
23
26
|
continue;
|
package/dist/main.js
CHANGED
|
@@ -31,7 +31,7 @@ const HELP = `
|
|
|
31
31
|
\x1b[1mCONFIGURATION\x1b[0m
|
|
32
32
|
.env file:
|
|
33
33
|
ANTHROPIC_API_KEY=sk-ant-...
|
|
34
|
-
RLM_MODEL=claude-sonnet-4-
|
|
34
|
+
RLM_MODEL=claude-sonnet-4-6
|
|
35
35
|
|
|
36
36
|
rlm_config.yaml:
|
|
37
37
|
max_iterations: 20
|
|
@@ -125,7 +125,8 @@ async function main() {
|
|
|
125
125
|
}
|
|
126
126
|
default: {
|
|
127
127
|
if (command.startsWith("--")) {
|
|
128
|
-
// Flags without subcommand → assume "run"
|
|
128
|
+
// Flags without subcommand → assume "run", pass all args through
|
|
129
|
+
process.argv = [process.argv[0], process.argv[1], ...args];
|
|
129
130
|
await import("./cli.js");
|
|
130
131
|
}
|
|
131
132
|
else {
|
package/dist/repl.js
CHANGED
|
@@ -92,17 +92,35 @@ export class PythonRepl {
|
|
|
92
92
|
/** Gracefully shut down the Python subprocess. */
|
|
93
93
|
shutdown() {
|
|
94
94
|
if (this.proc && this.proc.exitCode === null) {
|
|
95
|
+
const proc = this.proc;
|
|
95
96
|
try {
|
|
96
97
|
this.send({ type: "shutdown" });
|
|
97
98
|
}
|
|
98
99
|
catch {
|
|
99
100
|
// stdin may already be closed
|
|
100
101
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
102
|
+
if (process.platform === "win32") {
|
|
103
|
+
// Windows: SIGTERM is ignored, kill immediately
|
|
104
|
+
try {
|
|
105
|
+
proc.kill("SIGKILL");
|
|
106
|
+
}
|
|
107
|
+
catch { /* already dead */ }
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
// Unix: give Python 500ms to exit gracefully, then force kill
|
|
111
|
+
const killTimer = setTimeout(() => {
|
|
112
|
+
try {
|
|
113
|
+
if (proc.exitCode === null)
|
|
114
|
+
proc.kill("SIGKILL");
|
|
115
|
+
}
|
|
116
|
+
catch { }
|
|
117
|
+
}, 500);
|
|
118
|
+
proc.on("exit", () => clearTimeout(killTimer));
|
|
119
|
+
try {
|
|
120
|
+
proc.kill("SIGTERM");
|
|
121
|
+
}
|
|
122
|
+
catch { /* already dead */ }
|
|
104
123
|
}
|
|
105
|
-
catch { /* already dead */ }
|
|
106
124
|
}
|
|
107
125
|
this.cleanup();
|
|
108
126
|
}
|
|
@@ -111,7 +129,12 @@ export class PythonRepl {
|
|
|
111
129
|
if (!this.proc || !this.proc.stdin || this.proc.stdin.destroyed) {
|
|
112
130
|
throw new Error("REPL subprocess is not running");
|
|
113
131
|
}
|
|
114
|
-
|
|
132
|
+
try {
|
|
133
|
+
this.proc.stdin.write(`${JSON.stringify(msg)}\n`);
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
throw new Error("REPL subprocess stdin write failed");
|
|
137
|
+
}
|
|
115
138
|
}
|
|
116
139
|
handleLine(line) {
|
|
117
140
|
const trimmed = line.trim();
|
package/dist/runtime.py
CHANGED
|
@@ -37,12 +37,16 @@ __final_result__ = None
|
|
|
37
37
|
def FINAL(x):
|
|
38
38
|
"""Set the final answer as a string and terminate the RLM loop."""
|
|
39
39
|
global __final_result__
|
|
40
|
+
if __final_result__ is not None:
|
|
41
|
+
print(f"[Warning] FINAL() called again — overwriting previous answer", file=sys.stderr)
|
|
40
42
|
__final_result__ = str(x)
|
|
41
43
|
|
|
42
44
|
|
|
43
45
|
def FINAL_VAR(x):
|
|
44
46
|
"""Set the final answer from a variable and terminate the RLM loop."""
|
|
45
47
|
global __final_result__
|
|
48
|
+
if __final_result__ is not None and x is not None:
|
|
49
|
+
print(f"[Warning] FINAL_VAR() called again — overwriting previous answer", file=sys.stderr)
|
|
46
50
|
__final_result__ = str(x) if x is not None else None
|
|
47
51
|
|
|
48
52
|
|
package/dist/viewer.js
CHANGED
|
@@ -341,13 +341,9 @@ function renderIteration(state) {
|
|
|
341
341
|
// Scroll indicator at top
|
|
342
342
|
if (from > 0) {
|
|
343
343
|
W(` ${c.dim}^ scroll up (${from} lines above)${c.reset}\n`);
|
|
344
|
-
for (let i = from + 1; i < to; i++)
|
|
345
|
-
W(allLines[i] + "\n");
|
|
346
|
-
}
|
|
347
|
-
else {
|
|
348
|
-
for (let i = from; i < to; i++)
|
|
349
|
-
W(allLines[i] + "\n");
|
|
350
344
|
}
|
|
345
|
+
for (let i = from; i < to; i++)
|
|
346
|
+
W(allLines[i] + "\n");
|
|
351
347
|
if (to < allLines.length) {
|
|
352
348
|
// Replace last visible line with scroll indicator
|
|
353
349
|
W(` ${c.dim}v scroll down (${allLines.length - to} lines below)${c.reset}\n`);
|