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.
- package/dist/commands/repl.d.ts +0 -2
- package/dist/commands/repl.js +187 -227
- package/dist/commands/setup.js +55 -0
- package/dist/core/engine.js +24 -31
- package/dist/index.js +26 -19
- package/dist/ui/input.d.ts +23 -25
- package/dist/ui/input.js +310 -248
- package/dist/utils/qwen-auth.d.ts +14 -4
- package/dist/utils/qwen-auth.js +81 -8
- package/package.json +1 -1
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
export declare const DEFAULT_API_BASE_URL = "https://dashscope-intl.aliyuncs.com/compatible-mode/v1";
|
|
2
|
+
export interface ApiKeyConfig {
|
|
3
|
+
provider: string;
|
|
4
|
+
api_key: string;
|
|
5
|
+
base_url: string;
|
|
6
|
+
model: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function getApiKeyConfigPath(): Promise<string>;
|
|
9
|
+
export declare function loadApiKeyConfig(): Promise<ApiKeyConfig | null>;
|
|
10
|
+
export declare function saveApiKeyConfig(cfg: ApiKeyConfig): Promise<void>;
|
|
11
|
+
export declare function fetchApiKeyModels(cfg?: ApiKeyConfig | null): Promise<string[]>;
|
|
1
12
|
/** Exported as const for backward compat */
|
|
2
13
|
export declare const QWEN_AGENT_HOME: string;
|
|
3
14
|
/** Dynamic getter (recommended for runtime changes) */
|
|
@@ -14,17 +25,16 @@ export declare function qwenLogin(): Promise<boolean>;
|
|
|
14
25
|
export declare function qwenAuthStatus(): Promise<{
|
|
15
26
|
authenticated: boolean;
|
|
16
27
|
email?: string;
|
|
28
|
+
method?: string;
|
|
17
29
|
}>;
|
|
18
30
|
export declare function fetchQwenModels(): Promise<string[]>;
|
|
19
31
|
export declare function getQwenAccessToken(): Promise<string | null>;
|
|
20
32
|
/**
|
|
21
|
-
* Call Qwen REST API directly
|
|
22
|
-
* No dependency on any qwen CLI binary.
|
|
33
|
+
* Call Qwen REST API directly. Tries API key first, falls back to OAuth.
|
|
23
34
|
*/
|
|
24
35
|
export declare function callQwenAPI(prompt: string, model?: string, onData?: (chunk: string) => void): Promise<string>;
|
|
25
36
|
/**
|
|
26
|
-
* Call Qwen API using
|
|
27
|
-
* Calls the Qwen REST API directly — no dependency on any qwen CLI binary.
|
|
37
|
+
* Call Qwen API using role-specific OAuth creds or global API key config.
|
|
28
38
|
*/
|
|
29
39
|
export declare function callQwenAPIFromCreds(prompt: string, model: string, credsPath: string, onData?: (chunk: string) => void): Promise<string>;
|
|
30
40
|
/** Check quota status by making a minimal test API call */
|
package/dist/utils/qwen-auth.js
CHANGED
|
@@ -2,7 +2,71 @@ import * as fs from 'fs/promises';
|
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import * as crypto from 'crypto';
|
|
4
4
|
import open from 'open';
|
|
5
|
+
import OpenAI from 'openai';
|
|
5
6
|
import { AGENT_HOME } from './config.js';
|
|
7
|
+
// ── API Key (OpenAI-compatible) ───────────────────────────────────────────────
|
|
8
|
+
export const DEFAULT_API_BASE_URL = 'https://dashscope-intl.aliyuncs.com/compatible-mode/v1';
|
|
9
|
+
export async function getApiKeyConfigPath() {
|
|
10
|
+
await fs.mkdir(AGENT_HOME, { recursive: true });
|
|
11
|
+
return path.join(AGENT_HOME, 'api_key.json');
|
|
12
|
+
}
|
|
13
|
+
export async function loadApiKeyConfig() {
|
|
14
|
+
try {
|
|
15
|
+
const content = await fs.readFile(await getApiKeyConfigPath(), 'utf-8');
|
|
16
|
+
const cfg = JSON.parse(content);
|
|
17
|
+
if (!cfg.api_key || !cfg.base_url)
|
|
18
|
+
return null;
|
|
19
|
+
return cfg;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export async function saveApiKeyConfig(cfg) {
|
|
26
|
+
await fs.writeFile(await getApiKeyConfigPath(), JSON.stringify(cfg, null, 2), 'utf-8');
|
|
27
|
+
}
|
|
28
|
+
export async function fetchApiKeyModels(cfg) {
|
|
29
|
+
const resolved = cfg ?? await loadApiKeyConfig();
|
|
30
|
+
if (!resolved?.api_key)
|
|
31
|
+
return [];
|
|
32
|
+
try {
|
|
33
|
+
const client = new OpenAI({ apiKey: resolved.api_key, baseURL: resolved.base_url });
|
|
34
|
+
const list = await client.models.list();
|
|
35
|
+
return list.data.map((m) => m.id).sort();
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
async function callWithApiKey(cfg, prompt, model, onData) {
|
|
42
|
+
const useModel = (model && model !== 'coder-model') ? model : cfg.model;
|
|
43
|
+
const client = new OpenAI({
|
|
44
|
+
apiKey: cfg.api_key,
|
|
45
|
+
baseURL: cfg.base_url,
|
|
46
|
+
});
|
|
47
|
+
if (!onData) {
|
|
48
|
+
const completion = await client.chat.completions.create({
|
|
49
|
+
model: useModel,
|
|
50
|
+
messages: [{ role: 'user', content: prompt }],
|
|
51
|
+
});
|
|
52
|
+
return completion.choices[0]?.message?.content ?? '';
|
|
53
|
+
}
|
|
54
|
+
let fullText = '';
|
|
55
|
+
const stream = await client.chat.completions.create({
|
|
56
|
+
model: useModel,
|
|
57
|
+
messages: [{ role: 'user', content: prompt }],
|
|
58
|
+
stream: true,
|
|
59
|
+
});
|
|
60
|
+
for await (const chunk of stream) {
|
|
61
|
+
const delta = chunk.choices[0]?.delta?.content ?? '';
|
|
62
|
+
if (delta) {
|
|
63
|
+
fullText += delta;
|
|
64
|
+
onData(delta);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return fullText;
|
|
68
|
+
}
|
|
69
|
+
// ── OAuth ─────────────────────────────────────────────────────────────────────
|
|
6
70
|
const QWEN_OAUTH_BASE_URL = 'https://chat.qwen.ai';
|
|
7
71
|
const QWEN_OAUTH_DEVICE_CODE_ENDPOINT = `${QWEN_OAUTH_BASE_URL}/api/v1/oauth2/device/code`;
|
|
8
72
|
const QWEN_OAUTH_TOKEN_ENDPOINT = `${QWEN_OAUTH_BASE_URL}/api/v1/oauth2/token`;
|
|
@@ -223,13 +287,16 @@ async function pollForToken(deviceCode, codeVerifier, interval, expiresIn) {
|
|
|
223
287
|
return null;
|
|
224
288
|
}
|
|
225
289
|
export async function qwenAuthStatus() {
|
|
290
|
+
const apiKeyCfg = await loadApiKeyConfig();
|
|
291
|
+
if (apiKeyCfg?.api_key) {
|
|
292
|
+
return { authenticated: true, method: 'apikey', email: `${apiKeyCfg.provider} / ${apiKeyCfg.model}` };
|
|
293
|
+
}
|
|
226
294
|
const token = await loadToken();
|
|
227
295
|
if (!token)
|
|
228
296
|
return { authenticated: false };
|
|
229
|
-
// Verify token is not expired (loadToken already refreshes if close to expiry)
|
|
230
297
|
if (token.expiresAt > 0 && token.expiresAt < Date.now())
|
|
231
298
|
return { authenticated: false };
|
|
232
|
-
return { authenticated: true };
|
|
299
|
+
return { authenticated: true, method: 'oauth' };
|
|
233
300
|
}
|
|
234
301
|
export async function fetchQwenModels() {
|
|
235
302
|
const token = await loadToken();
|
|
@@ -335,19 +402,21 @@ async function callQwenAPIWithToken(token, prompt, model, onData) {
|
|
|
335
402
|
return fullText;
|
|
336
403
|
}
|
|
337
404
|
/**
|
|
338
|
-
* Call Qwen REST API directly
|
|
339
|
-
* No dependency on any qwen CLI binary.
|
|
405
|
+
* Call Qwen REST API directly. Tries API key first, falls back to OAuth.
|
|
340
406
|
*/
|
|
341
407
|
export async function callQwenAPI(prompt, model = 'coder-model', onData) {
|
|
408
|
+
const apiKeyCfg = await loadApiKeyConfig();
|
|
409
|
+
if (apiKeyCfg) {
|
|
410
|
+
return callWithApiKey(apiKeyCfg, prompt, model, onData);
|
|
411
|
+
}
|
|
342
412
|
let token = await loadToken();
|
|
343
413
|
if (!token) {
|
|
344
|
-
throw new Error('
|
|
414
|
+
throw new Error('No auth configured. Run: agent-mp --login or agent-mp setup api-key');
|
|
345
415
|
}
|
|
346
416
|
try {
|
|
347
417
|
return await callQwenAPIWithToken(token, prompt, model, onData);
|
|
348
418
|
}
|
|
349
419
|
catch (err) {
|
|
350
|
-
// Quota errors: refresh won't help, propagate immediately
|
|
351
420
|
if (err.message?.startsWith('QWEN_QUOTA_EXCEEDED'))
|
|
352
421
|
throw err;
|
|
353
422
|
if (!err.message?.startsWith('QWEN_AUTH_EXPIRED'))
|
|
@@ -363,10 +432,14 @@ export async function callQwenAPI(prompt, model = 'coder-model', onData) {
|
|
|
363
432
|
}
|
|
364
433
|
}
|
|
365
434
|
/**
|
|
366
|
-
* Call Qwen API using
|
|
367
|
-
* Calls the Qwen REST API directly — no dependency on any qwen CLI binary.
|
|
435
|
+
* Call Qwen API using role-specific OAuth creds or global API key config.
|
|
368
436
|
*/
|
|
369
437
|
export async function callQwenAPIFromCreds(prompt, model, credsPath, onData) {
|
|
438
|
+
// Prefer global API key config over role-specific OAuth creds
|
|
439
|
+
const apiKeyCfg = await loadApiKeyConfig();
|
|
440
|
+
if (apiKeyCfg) {
|
|
441
|
+
return callWithApiKey(apiKeyCfg, prompt, model, onData);
|
|
442
|
+
}
|
|
370
443
|
const cliName = path.basename(path.dirname(credsPath)).replace(/^\./, '');
|
|
371
444
|
let raw;
|
|
372
445
|
try {
|