agent-rev 0.3.1 → 0.3.2

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.
@@ -7,7 +7,7 @@ import chalk from 'chalk';
7
7
  import { execSync } from 'child_process';
8
8
  import { CLI_REGISTRY } from '../types.js';
9
9
  import { writeJson, ensureDir, readJson, listDir, fileExists } from '../utils/fs.js';
10
- import { loadAuth, saveAuth, loadCliConfig, saveCliConfig, loadProjectConfig, PKG_NAME } from '../utils/config.js';
10
+ import { loadAuth, saveAuth, loadCliConfig, saveCliConfig, loadProjectConfig } from '../utils/config.js';
11
11
  import { log } from '../utils/logger.js';
12
12
  import { AgentEngine, ExitError } from '../core/engine.js';
13
13
  import { qwenAuthStatus, QWEN_AGENT_HOME, fetchQwenModels } from '../utils/qwen-auth.js';
@@ -1227,17 +1227,8 @@ export async function runRole(role, arg, model) {
1227
1227
  }
1228
1228
  case 'explorer':
1229
1229
  case 'exp': {
1230
- const explorerCli = config.roles.explorer?.cli;
1231
- if (explorerCli && explorerCli === PKG_NAME) {
1232
- // This binary IS the configured explorer CLI — avoid recursion.
1233
- // Call Qwen API directly using own credentials.
1234
- const result = await engine.runExplorerDirect(arg || undefined);
1235
- console.log(result);
1236
- }
1237
- else {
1238
- const result = await engine.runExplorer(arg || undefined);
1239
- console.log(result);
1240
- }
1230
+ const result = await engine.runExplorer(arg || undefined);
1231
+ console.log(result);
1241
1232
  break;
1242
1233
  }
1243
1234
  case 'coordinator':
@@ -6,7 +6,7 @@ import { CLI_REGISTRY } from '../types.js';
6
6
  import { writeJson, readJson, fileExists, writeFile, readFile } from '../utils/fs.js';
7
7
  import { log } from '../utils/logger.js';
8
8
  import chalk from 'chalk';
9
- import { QWEN_AGENT_HOME, callQwenAPI } from '../utils/qwen-auth.js';
9
+ import { callQwenAPI, callQwenAPIFromCreds } from '../utils/qwen-auth.js';
10
10
  import * as fs from 'fs/promises';
11
11
  /** Thrown when a slash command inside a conversation requests exit */
12
12
  export class ExitError extends Error {
@@ -251,55 +251,45 @@ INSTRUCCIONES:
251
251
  const envOverride = {};
252
252
  let res;
253
253
  if (this.coordinatorCmd.startsWith('qwen')) {
254
- const corporateCreds = path.join(QWEN_AGENT_HOME, 'oauth_creds.json');
255
- const personalCreds = path.join(os.homedir(), '.qwen', 'oauth_creds.json');
256
- let corporateCredsContent = null;
254
+ // Use Qwen API directly — avoids the qwen CLI's own OAuth flow
255
+ // which causes mid-session auth popups and breaks display.
257
256
  try {
258
- corporateCredsContent = await fs.readFile(corporateCreds, 'utf-8');
257
+ const model = this.coordinatorCmd.match(/(?:-m|--model)\s+(\S+)/)?.[1] || 'coder-model';
258
+ return await callQwenAPI(prompt, model);
259
259
  }
260
- catch {
261
- console.log(chalk.red('\n ✗ No hay credenciales corporativas de Qwen.'));
260
+ catch (err) {
261
+ console.log(chalk.red(`\n ✗ Error de autenticación Qwen: ${err.message}`));
262
262
  console.log(chalk.yellow(' Ejecutá /login para autenticarte.\n'));
263
263
  return '';
264
264
  }
265
- let personalBackup = null;
266
- try {
267
- personalBackup = await fs.readFile(personalCreds, 'utf-8');
268
- }
269
- catch { }
270
- await fs.writeFile(personalCreds, corporateCredsContent);
271
- res = await runCli(this.coordinatorCmd, prompt, 600000, envOverride);
272
- if (personalBackup) {
273
- await fs.writeFile(personalCreds, personalBackup);
274
- }
275
- else {
276
- await fs.unlink(personalCreds).catch(() => { });
277
- }
278
265
  }
279
266
  else {
280
267
  res = await runCli(this.coordinatorCmd, prompt, 600000, envOverride);
281
268
  }
282
- // Extract readable text (Qwen CLI returns JSON events)
269
+ // Extract readable text search for JSON array even if there's prefix text
283
270
  let responseText = res.output.trim();
284
- try {
285
- const json = JSON.parse(res.output);
286
- if (Array.isArray(json)) {
287
- for (const item of json) {
288
- if (item.type === 'result' && typeof item.result === 'string') {
289
- responseText = item.result;
290
- break;
291
- }
292
- if (item.type === 'assistant' && item.message?.content?.length > 0) {
293
- const t = item.message.content.find((c) => c.type === 'text');
294
- if (t?.text) {
295
- responseText = t.text;
271
+ const arrayStart = res.output.indexOf('[{');
272
+ if (arrayStart !== -1) {
273
+ try {
274
+ const json = JSON.parse(res.output.slice(arrayStart));
275
+ if (Array.isArray(json)) {
276
+ for (const item of json) {
277
+ if (item.type === 'result' && typeof item.result === 'string') {
278
+ responseText = item.result;
296
279
  break;
297
280
  }
281
+ if (item.type === 'assistant' && item.message?.content?.length > 0) {
282
+ const t = item.message.content.find((c) => c.type === 'text');
283
+ if (t?.text) {
284
+ responseText = t.text;
285
+ break;
286
+ }
287
+ }
298
288
  }
299
289
  }
300
290
  }
291
+ catch { }
301
292
  }
302
- catch { }
303
293
  return responseText;
304
294
  };
305
295
  // Clarification loop — coordinator is only called when there is new user input
@@ -446,14 +436,34 @@ INSTRUCCIONES:
446
436
  }
447
437
  }
448
438
  };
449
- // Role binaries (agent-orch, agent-impl, etc.) are wrappers, not AI CLIs.
450
- // runExplorer handles them separately by spawning with the short task.
451
- // For orch/impl/rev the full cycle never spawns role binaries via runWithFallback.
439
+ // Role binaries (agent-orch, agent-impl, etc.) require an interactive TTY and can't
440
+ // be spawned as subprocesses. Instead, if they have their own Qwen credentials
441
+ // (~/.agent-<name>/oauth_creds.json from running `agent-<name> --login`), call
442
+ // the Qwen API directly with those creds. Otherwise skip to fallback.
452
443
  const ROLE_BINARIES = new Set(['agent-orch', 'agent-impl', 'agent-rev', 'agent-explorer']);
444
+ const tryRoleBinaryCreds = async (cliName, model) => {
445
+ const credsPath = path.join(os.homedir(), `.${cliName}`, 'oauth_creds.json');
446
+ if (!(await fileExists(credsPath))) {
447
+ log.warn(`${cliName} has no credentials — run: ${cliName} --login`);
448
+ return null;
449
+ }
450
+ try {
451
+ log.info(`${cliName}: calling Qwen API with own credentials (${model})`);
452
+ return await callQwenAPIFromCreds(rolePrompt, model, credsPath);
453
+ }
454
+ catch (err) {
455
+ log.warn(`${cliName} direct API call failed: ${err.message}`);
456
+ return null;
457
+ }
458
+ };
453
459
  // Try primary
454
460
  log.info(`Launching ${roleName}: ${role.cli} (${role.model})`);
455
461
  if (ROLE_BINARIES.has(role.cli)) {
456
- log.warn(`${role.cli} is a role binary — skipping to fallback`);
462
+ const directResult = await tryRoleBinaryCreds(role.cli, role.model);
463
+ if (directResult !== null) {
464
+ trackTokens(directResult, role.cli, role.model);
465
+ return directResult;
466
+ }
457
467
  }
458
468
  else {
459
469
  const primaryResult = await tryWithAutoRepair(role.cli, role.model, role.cmd);
@@ -463,12 +473,21 @@ INSTRUCCIONES:
463
473
  }
464
474
  }
465
475
  // Try individual fallback
466
- if (role.fallback && !ROLE_BINARIES.has(role.fallback.cli)) {
476
+ if (role.fallback) {
467
477
  log.warn(`Trying individual fallback for ${roleName}: ${role.fallback.cli} (${role.fallback.model})`);
468
- const fallbackResult = await tryWithAutoRepair(role.fallback.cli, role.fallback.model, role.fallback.cmd);
469
- if (fallbackResult !== null) {
470
- trackTokens(fallbackResult, role.fallback.cli, role.fallback.model);
471
- return fallbackResult;
478
+ if (ROLE_BINARIES.has(role.fallback.cli)) {
479
+ const directResult = await tryRoleBinaryCreds(role.fallback.cli, role.fallback.model);
480
+ if (directResult !== null) {
481
+ trackTokens(directResult, role.fallback.cli, role.fallback.model);
482
+ return directResult;
483
+ }
484
+ }
485
+ else {
486
+ const fallbackResult = await tryWithAutoRepair(role.fallback.cli, role.fallback.model, role.fallback.cmd);
487
+ if (fallbackResult !== null) {
488
+ trackTokens(fallbackResult, role.fallback.cli, role.fallback.model);
489
+ return fallbackResult;
490
+ }
472
491
  }
473
492
  }
474
493
  // Try global fallback
@@ -783,26 +802,7 @@ REGLAS:
783
802
  - NO modifiques archivos de aplicacion (solo .agent/context/)
784
803
  - NO ejecutes comandos que cambien estado (npm install, migraciones, etc.)
785
804
  - Si un directorio esta en node_modules, dist, .git: ignoralo`;
786
- const ROLE_BINS = new Set(['agent-orch', 'agent-impl', 'agent-rev', 'agent-explorer']);
787
- let res;
788
- if (ROLE_BINS.has(this.config.roles.explorer.cli)) {
789
- // Spawn the role binary as a separate process with its own credentials.
790
- // Pass only the short task — the binary builds its own full prompt internally.
791
- const explorerRole = this.config.roles.explorer;
792
- const spawnCmd = `${explorerRole.cli} --model ${explorerRole.model}`;
793
- log.info(`Spawning ${explorerRole.cli} (${explorerRole.model}) as separate process`);
794
- const result = await runCli(spawnCmd, effectiveTask);
795
- if (result.exitCode !== 0) {
796
- log.warn(`${explorerRole.cli} exited with code ${result.exitCode} — trying fallback`);
797
- res = await this.runWithFallback('explorer', prompt, 'Exploracion');
798
- }
799
- else {
800
- res = result.output;
801
- }
802
- }
803
- else {
804
- res = await this.runWithFallback('explorer', prompt, 'Exploracion');
805
- }
805
+ const res = await this.runWithFallback('explorer', prompt, 'Exploracion');
806
806
  const text = extractCliText(res);
807
807
  // Always save a timestamped explorer report
808
808
  try {
package/package.json CHANGED
@@ -1 +1 @@
1
- {"name":"agent-rev","version":"0.3.1","description":"agent-rev agent","type":"module","main":"./dist/index.js","files":["dist/"],"bin":{"agent-rev":"dist/index.js"},"scripts":{"build":"tsc"},"keywords":["ai","agent","cli"],"license":"MIT","dependencies":{"@anthropic-ai/sdk":"^0.39.0","@google/generative-ai":"^0.24.0","chalk":"^5.4.1","commander":"^13.1.0","open":"^11.0.0","openai":"^4.91.0"},"engines":{"node":">=18.0.0"}}
1
+ {"name":"agent-rev","version":"0.3.2","description":"agent-rev agent","type":"module","main":"./dist/index.js","files":["dist/"],"bin":{"agent-rev":"dist/index.js"},"scripts":{"build":"tsc"},"keywords":["ai","agent","cli"],"license":"MIT","dependencies":{"@anthropic-ai/sdk":"^0.39.0","@google/generative-ai":"^0.24.0","chalk":"^5.4.1","commander":"^13.1.0","open":"^11.0.0","openai":"^4.91.0"},"engines":{"node":">=18.0.0"}}