agent-mp 0.5.22 → 0.5.24

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.
@@ -69,7 +69,7 @@ function rebuildCmd(cliName, model, flags) {
69
69
  }
70
70
  async function ask(prompt, rl, fi) {
71
71
  if (fi) {
72
- fi.println(chalk.cyan(` ${prompt}`));
72
+ fi.println(chalk.cyan(prompt));
73
73
  const answer = await fi.readLine();
74
74
  // Echo user response right-aligned (chat style, per line)
75
75
  const userLines = answer.trim().split('\n').filter(l => l.trim());
@@ -329,17 +329,25 @@ export class AgentEngine {
329
329
  if (!this.fi)
330
330
  return noop;
331
331
  const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
332
+ const dotFrames = ['· ', '·· ', '···'];
332
333
  let i = 0;
334
+ let ti = 0;
333
335
  const t0 = Date.now();
334
336
  const fi = this.fi;
337
+ let streaming = false;
335
338
  fi.startActivity(`${frames[0]} ${label} 0s`);
339
+ fi.setActivityLines([` ${dotFrames[0]} esperando respuesta...`]);
336
340
  const iv = setInterval(() => {
337
341
  const s = Math.floor((Date.now() - t0) / 1000);
342
+ ti++;
338
343
  fi.updateActivityHeader(`${frames[i++ % frames.length]} ${label} ${s}s`);
339
- }, 100);
344
+ if (!streaming) {
345
+ fi.setActivityLines([` ${dotFrames[ti % dotFrames.length]} esperando respuesta...`]);
346
+ }
347
+ }, 300);
340
348
  return {
341
349
  stop() { clearInterval(iv); fi.stopActivity(); },
342
- push(line) { fi.pushActivity(line); },
350
+ push(_chunk) { streaming = true; },
343
351
  };
344
352
  }
345
353
  /** Extract readable text lines from a qwen/CLI streaming chunk. */
@@ -409,7 +417,7 @@ INSTRUCCIONES:
409
417
  const model = this.coordinatorCmd.match(/(?:-m|--model)\s+(\S+)/)?.[1] || 'coder-model';
410
418
  const sp = this._startSpinner(`coordinador ${model}`);
411
419
  try {
412
- const result = await callQwenAPI(prompt, model, (c) => this._parseChunk(c).forEach(l => sp.push(l)));
420
+ const result = await callQwenAPI(prompt, model, (c) => sp.push(c));
413
421
  sp.stop();
414
422
  return result;
415
423
  }
@@ -620,26 +628,22 @@ INSTRUCCIONES:
620
628
  const ROLE_BINARIES = new Set(['agent-orch', 'agent-impl', 'agent-rev', 'agent-explorer']);
621
629
  const tryRoleBinaryCreds = async (cliName, model) => {
622
630
  const credsPath = path.join(os.homedir(), `.${cliName}`, 'oauth_creds.json');
623
- if (!(await fileExists(credsPath))) {
624
- log.warn(`${cliName} has no credentials — run: ${cliName} --login`);
625
- return null;
631
+ const hasOAuthCreds = await fileExists(credsPath);
632
+ if (!hasOAuthCreds) {
633
+ // No role OAuth creds — try global API key config before giving up
634
+ const { loadApiKeyConfig } = await import('../utils/qwen-auth.js');
635
+ const apiKeyCfg = await loadApiKeyConfig();
636
+ if (!apiKeyCfg) {
637
+ log.warn(`${cliName} has no credentials — run: agent-mp setup api-key or ${cliName} --login`);
638
+ return null;
639
+ }
640
+ // Fall through: callQwenAPIFromCreds will use the API key config
626
641
  }
627
642
  const sp = this._startSpinner(`${cliName} ${model}`);
628
- let lineBuf = '';
629
- const onChunk = (delta) => {
630
- lineBuf += delta;
631
- const lines = lineBuf.split('\n');
632
- lineBuf = lines.pop() || '';
633
- for (const l of lines) {
634
- if (l.trim())
635
- sp.push(l.trim());
636
- }
637
- };
643
+ const onChunk = (delta) => sp.push(delta);
638
644
  try {
639
645
  log.info(`${cliName}: calling Qwen API with own credentials (${model})`);
640
646
  const result = await callQwenAPIFromCreds(rolePrompt, model, credsPath, onChunk);
641
- if (lineBuf.trim())
642
- sp.push(lineBuf.trim());
643
647
  sp.stop();
644
648
  return result;
645
649
  }
@@ -696,20 +700,9 @@ INSTRUCCIONES:
696
700
  const fb = this.config.fallback_global;
697
701
  log.warn(`Using global fallback: ${fb.cli} (${fb.model})`);
698
702
  const sp = this._startSpinner(`${fb.cli} ${fb.model} (fallback)`);
699
- let lineBuf = '';
700
- const onChunk = (delta) => {
701
- lineBuf += delta;
702
- const lines = lineBuf.split('\n');
703
- lineBuf = lines.pop() || '';
704
- for (const l of lines) {
705
- if (l.trim())
706
- sp.push(l.trim());
707
- }
708
- };
703
+ const onChunk = (delta) => sp.push(delta);
709
704
  try {
710
705
  const globalResult = await callQwenAPI(rolePrompt, fb.model, onChunk);
711
- if (lineBuf.trim())
712
- sp.push(lineBuf.trim());
713
706
  sp.stop();
714
707
  trackTokens(globalResult, fb.cli, fb.model);
715
708
  return globalResult;
package/dist/index.js CHANGED
@@ -61,8 +61,8 @@ REPL commands (type inside the session):
61
61
  /run orch <task> Run only orchestrator
62
62
  /run impl <id> Run only implementor
63
63
  /run rev <id> Run only reviewer
64
- /login Login (OAuth)
65
- /logout Logout and clear credentials
64
+ /login Configure API key
65
+ /logout Clear API key and credentials
66
66
  /models [cli] List available models
67
67
  /tasks List all tasks
68
68
  /clear Clear screen
@@ -100,25 +100,32 @@ if (nativeRole) {
100
100
  console.log(chalk.dim(` Version: ${PKG_NAME} --version\n`));
101
101
  process.exit(0);
102
102
  }
103
- // --login: OAuth login for this role's account
103
+ // --login: configure API key for this role
104
104
  if (args.includes('--login') || args.includes('login')) {
105
- const { qwenLogin, fetchQwenModels } = await import('./utils/qwen-auth.js');
106
- const { loadCliConfig, saveCliConfig } = await import('./utils/config.js');
107
- const ok = await qwenLogin();
108
- if (ok) {
109
- // Fetch and cache available models so /setup can show them
110
- const models = await fetchQwenModels();
111
- if (models.length) {
112
- const cliConfig = await loadCliConfig();
113
- cliConfig.availableModels = models;
114
- if (!cliConfig.coordinatorModel)
115
- cliConfig.coordinatorModel = models[0];
116
- await saveCliConfig(cliConfig);
117
- console.log(chalk.green(` ✓ Models cached: ${models.slice(0, 3).join(', ')}${models.length > 3 ? '...' : ''}`));
118
- console.log(chalk.dim(` Default model: ${cliConfig.coordinatorModel}`));
119
- }
105
+ const { saveApiKeyConfig, loadApiKeyConfig, DEFAULT_API_BASE_URL } = await import('./utils/qwen-auth.js');
106
+ const rl = (await import('readline')).createInterface({ input: process.stdin, output: process.stdout });
107
+ const ask = (q) => new Promise((res) => rl.question(q, res));
108
+ const existing = await loadApiKeyConfig();
109
+ console.log(chalk.bold.cyan(`\n ${PKG_NAME} API Key Setup\n`));
110
+ if (existing) {
111
+ console.log(chalk.dim(` Current: ${existing.provider} / ${existing.model}`));
112
+ }
113
+ const apiKey = await ask(` API Key${existing ? ' [Enter to keep]' : ''}: `);
114
+ const model = await ask(` Model [${existing?.model ?? 'qwen-plus'}]: `);
115
+ rl.close();
116
+ const cfg = {
117
+ provider: existing?.provider ?? 'openai-compatible',
118
+ api_key: apiKey.trim() || existing?.api_key || '',
119
+ base_url: DEFAULT_API_BASE_URL,
120
+ model: model.trim() || existing?.model || 'qwen-plus',
121
+ };
122
+ if (!cfg.api_key) {
123
+ console.log(chalk.red(' API key is required.'));
124
+ process.exit(1);
120
125
  }
121
- process.exit(ok ? 0 : 1);
126
+ await saveApiKeyConfig(cfg);
127
+ console.log(chalk.green(`\n ✓ API key saved — ${cfg.provider} / ${cfg.model}\n`));
128
+ process.exit(0);
122
129
  }
123
130
  // --status: show auth status
124
131
  if (args.includes('--status') || args.includes('status')) {
@@ -1,41 +1,39 @@
1
+ /**
2
+ * Inline prompt, styled like Gemini/Qwen CLI.
3
+ * Draws at the current cursor line; when output arrives we erase the area
4
+ * (line-by-line, \x1b[2K) and redraw below. No absolute positioning.
5
+ */
1
6
  export declare class FixedInput {
2
- private buf;
3
7
  private history;
4
- private histIdx;
5
8
  private origLog;
6
- private _pasting;
7
- private _pasteAccum;
8
- private _drawPending;
9
9
  private _activityHeader;
10
10
  private _activityLines;
11
- private get rows();
11
+ private _inputBuffer;
12
+ private _cursorPos;
13
+ private _pasting;
14
+ private _pasteAccum;
15
+ private _resolveInput?;
16
+ private _inputActive;
17
+ private _areaRows;
18
+ private _cursorRow;
19
+ private _onResize?;
12
20
  get cols(): number;
13
- private get _reservedRows();
14
- private get scrollBottom();
15
- private _contentRows;
16
21
  setup(): void;
17
22
  teardown(): void;
18
- redrawBox(): void;
19
23
  suspend(): () => void;
20
- /** Enter activity mode: show the 5-line log box instead of the input box. */
21
24
  startActivity(header: string): void;
22
- /** Update the header line (spinner frame + elapsed time) without clearing lines. */
23
25
  updateActivityHeader(header: string): void;
24
- /**
25
- * Append a line to the activity log (keeps last ACTIVITY_LINES lines).
26
- * Strips ANSI codes and skips blank or pure-JSON lines.
27
- */
28
26
  pushActivity(rawLine: string): void;
29
- /** Leave activity mode and restore the normal input box. */
27
+ /** Replace all content lines at once (for streaming preview). */
28
+ setActivityLines(lines: string[]): void;
30
29
  stopActivity(): void;
31
- readLine(): Promise<string>;
32
30
  println(text: string): void;
33
31
  printSeparator(): void;
34
- private _scheduleDraw;
35
- private _setScrollRegion;
36
- private _clearReserved;
37
- private _drawBox;
38
- private _drawActivityBox;
39
- private _drawInputBox;
40
- private _wrapText;
32
+ redrawBox(): void;
33
+ readLine(): Promise<string>;
34
+ private _commitPaste;
35
+ /** Build a string that erases the currently-drawn area and leaves the cursor at col 0 of the top row. */
36
+ private _buildClear;
37
+ private _clearArea;
38
+ private _redraw;
41
39
  }