rlm-cli 0.2.20 → 0.2.22

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 CHANGED
@@ -77,7 +77,7 @@ function parseArgs() {
77
77
  }
78
78
  }
79
79
  if (!modelId) {
80
- modelId = process.env.RLM_MODEL || "claude-sonnet-4-5-20250929";
80
+ modelId = process.env.RLM_MODEL || "claude-sonnet-4-6";
81
81
  }
82
82
  if (positional.length === 0) {
83
83
  console.error("Error: query argument is required");
@@ -114,16 +114,60 @@ async function fetchUrl(url) {
114
114
  // ── Main ────────────────────────────────────────────────────────────────────
115
115
  async function main() {
116
116
  const args = parseArgs();
117
- // Resolve model by scanning all providers (stop at first match)
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
- model = m;
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 line of content.split("\n")) {
18
- const trimmed = line.trim();
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;
@@ -1127,11 +1127,32 @@ async function interactive() {
1127
1127
  }
1128
1128
  setupRl.close();
1129
1129
  }
1130
- // Resolve model
1130
+ // Resolve model — ensure the resolved provider actually has an API key
1131
1131
  const initialResolved = resolveModelWithProvider(currentModelId);
1132
1132
  if (initialResolved) {
1133
- currentModel = initialResolved.model;
1134
- currentProviderName = initialResolved.provider;
1133
+ const resolvedKey = providerEnvKey(initialResolved.provider);
1134
+ if (process.env[resolvedKey]) {
1135
+ // Provider has a key — use it
1136
+ currentModel = initialResolved.model;
1137
+ currentProviderName = initialResolved.provider;
1138
+ }
1139
+ }
1140
+ // If default model's provider has no key, fall back to a provider that does
1141
+ if (!currentModel) {
1142
+ const activeProvider = detectProvider();
1143
+ if (activeProvider !== "unknown") {
1144
+ const fallbackModel = getDefaultModelForProvider(activeProvider);
1145
+ if (fallbackModel) {
1146
+ const fallbackResolved = resolveModelWithProvider(fallbackModel);
1147
+ if (fallbackResolved) {
1148
+ currentModelId = fallbackModel;
1149
+ currentModel = fallbackResolved.model;
1150
+ currentProviderName = fallbackResolved.provider;
1151
+ const label = findSetupProvider(activeProvider)?.name || activeProvider;
1152
+ console.log(` ${c.dim}Using ${label} (${currentModelId})${c.reset}`);
1153
+ }
1154
+ }
1155
+ }
1135
1156
  }
1136
1157
  if (!currentModel) {
1137
1158
  console.log(`\n ${c.red}Model "${currentModelId}" not found.${c.reset}`);
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`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rlm-cli",
3
- "version": "0.2.20",
3
+ "version": "0.2.22",
4
4
  "description": "Standalone CLI for Recursive Language Models (RLMs) — implements Algorithm 1 from arXiv:2512.24601",
5
5
  "type": "module",
6
6
  "bin": {