@xagent-ai/cli 1.0.1 → 1.1.1
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/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- package/README.md +280 -280
- package/README_CN.md +3 -3
- package/dist/ai-client.d.ts.map +1 -1
- package/dist/ai-client.js +84 -82
- package/dist/ai-client.js.map +1 -1
- package/dist/auth.d.ts +0 -1
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +75 -105
- package/dist/auth.js.map +1 -1
- package/dist/cli.js +166 -13
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +3 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +48 -7
- package/dist/config.js.map +1 -1
- package/dist/context-compressor.d.ts +5 -5
- package/dist/context-compressor.js +8 -8
- package/dist/context-compressor.js.map +1 -1
- package/dist/gui-subagent/action-parser/actionParser.d.ts +7 -0
- package/dist/gui-subagent/action-parser/actionParser.d.ts.map +1 -1
- package/dist/gui-subagent/action-parser/actionParser.js +6 -3
- package/dist/gui-subagent/action-parser/actionParser.js.map +1 -1
- package/dist/gui-subagent/action-parser/constants.d.ts +6 -0
- package/dist/gui-subagent/action-parser/constants.d.ts.map +1 -1
- package/dist/gui-subagent/action-parser/constants.js +5 -3
- package/dist/gui-subagent/action-parser/constants.js.map +1 -1
- package/dist/gui-subagent/action-parser/index.d.ts +6 -0
- package/dist/gui-subagent/action-parser/index.d.ts.map +1 -1
- package/dist/gui-subagent/action-parser/index.js +5 -3
- package/dist/gui-subagent/action-parser/index.js.map +1 -1
- package/dist/gui-subagent/action-parser/types.d.ts +4 -0
- package/dist/gui-subagent/action-parser/types.d.ts.map +1 -1
- package/dist/gui-subagent/action-parser/types.js +3 -3
- package/dist/gui-subagent/agent/gui-agent.d.ts +39 -0
- package/dist/gui-subagent/agent/gui-agent.d.ts.map +1 -1
- package/dist/gui-subagent/agent/gui-agent.js +164 -89
- package/dist/gui-subagent/agent/gui-agent.js.map +1 -1
- package/dist/gui-subagent/agent/index.d.ts +1 -1
- package/dist/gui-subagent/agent/index.d.ts.map +1 -1
- package/dist/gui-subagent/agent/index.js.map +1 -1
- package/dist/gui-subagent/index.d.ts +27 -1
- package/dist/gui-subagent/index.d.ts.map +1 -1
- package/dist/gui-subagent/index.js +6 -0
- package/dist/gui-subagent/index.js.map +1 -1
- package/dist/logger.js +1 -1
- package/dist/logger.js.map +1 -1
- package/dist/mcp.d.ts +1 -0
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +140 -29
- package/dist/mcp.js.map +1 -1
- package/dist/remote-ai-client.d.ts +111 -0
- package/dist/remote-ai-client.d.ts.map +1 -0
- package/dist/remote-ai-client.js +558 -0
- package/dist/remote-ai-client.js.map +1 -0
- package/dist/sdk-output-adapter.d.ts +232 -0
- package/dist/sdk-output-adapter.d.ts.map +1 -0
- package/dist/sdk-output-adapter.js +636 -0
- package/dist/sdk-output-adapter.js.map +1 -0
- package/dist/sdk-session-v2.d.ts +13 -0
- package/dist/sdk-session-v2.d.ts.map +1 -0
- package/dist/sdk-session-v2.js +46 -0
- package/dist/sdk-session-v2.js.map +1 -0
- package/dist/sdk-session.d.ts +13 -0
- package/dist/sdk-session.d.ts.map +1 -0
- package/dist/sdk-session.js +48 -0
- package/dist/sdk-session.js.map +1 -0
- package/dist/session-manager.js +3 -3
- package/dist/session-manager.js.map +1 -1
- package/dist/session.d.ts +46 -3
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +564 -117
- package/dist/session.js.map +1 -1
- package/dist/skill-invoker.d.ts +40 -4
- package/dist/skill-invoker.d.ts.map +1 -1
- package/dist/skill-invoker.js +310 -1184
- package/dist/skill-invoker.js.map +1 -1
- package/dist/skill-loader.d.ts +15 -1
- package/dist/skill-loader.d.ts.map +1 -1
- package/dist/skill-loader.js +49 -32
- package/dist/skill-loader.js.map +1 -1
- package/dist/slash-commands.d.ts +4 -2
- package/dist/slash-commands.d.ts.map +1 -1
- package/dist/slash-commands.js +149 -15
- package/dist/slash-commands.js.map +1 -1
- package/dist/smart-approval.d.ts +2 -1
- package/dist/smart-approval.d.ts.map +1 -1
- package/dist/smart-approval.js +29 -3
- package/dist/smart-approval.js.map +1 -1
- package/dist/system-prompt-generator.d.ts +4 -5
- package/dist/system-prompt-generator.d.ts.map +1 -1
- package/dist/system-prompt-generator.js +131 -81
- package/dist/system-prompt-generator.js.map +1 -1
- package/dist/tools.d.ts +17 -6
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +264 -211
- package/dist/tools.js.map +1 -1
- package/dist/types.d.ts +0 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +0 -1
- package/dist/types.js.map +1 -1
- package/docs/architecture/mcp-integration-guide.md +194 -131
- package/docs/architecture/overview.md +169 -93
- package/docs/architecture/tool-system-design.md +56 -11
- package/docs/cli/commands.md +238 -189
- package/docs/smart-mode.md +281 -257
- package/docs/third-party-models.md +247 -256
- package/package.json +6 -2
- package/src/ai-client.ts +107 -105
- package/src/auth.ts +82 -116
- package/src/cancellation.ts +1 -1
- package/src/cli.ts +178 -13
- package/src/config.ts +57 -8
- package/src/context-compressor.ts +8 -8
- package/src/gui-subagent/action-parser/actionParser.ts +6 -3
- package/src/gui-subagent/action-parser/constants.ts +5 -3
- package/src/gui-subagent/action-parser/index.ts +5 -3
- package/src/gui-subagent/action-parser/types.ts +3 -3
- package/src/gui-subagent/agent/gui-agent.ts +210 -103
- package/src/gui-subagent/agent/index.ts +1 -1
- package/src/gui-subagent/index.ts +26 -2
- package/src/index.ts +18 -18
- package/src/logger.ts +1 -1
- package/src/mcp.ts +149 -30
- package/src/remote-ai-client.ts +671 -0
- package/src/session-manager.ts +3 -3
- package/src/session.ts +742 -178
- package/src/skill-invoker.ts +340 -1293
- package/src/skill-loader.ts +55 -34
- package/src/slash-commands.ts +165 -15
- package/src/smart-approval.ts +34 -3
- package/src/system-prompt-generator.ts +145 -88
- package/src/tools.ts +309 -224
- package/src/types.ts +0 -1
- package/scripts/init-skills-path.js +0 -58
package/src/skill-loader.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from 'fs/promises';
|
|
2
2
|
import fsSync from 'fs';
|
|
3
3
|
import path from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
4
5
|
import { WorkflowConfig } from './workflow.js';
|
|
5
6
|
import { getConfigManager } from './config.js';
|
|
6
7
|
|
|
@@ -72,34 +73,8 @@ export class SkillLoader {
|
|
|
72
73
|
}
|
|
73
74
|
|
|
74
75
|
private detectSkillsPath(): string {
|
|
75
|
-
//
|
|
76
|
-
|
|
77
|
-
const scriptDir = path.dirname(process.argv[1]);
|
|
78
|
-
|
|
79
|
-
// Possible locations relative to where xagent script is installed
|
|
80
|
-
const possiblePaths = [
|
|
81
|
-
path.join(scriptDir, '..', 'skills', 'skills'),
|
|
82
|
-
path.join(scriptDir, '..', '..', 'skills', 'skills'),
|
|
83
|
-
path.join(scriptDir, 'skills', 'skills'),
|
|
84
|
-
path.join(scriptDir, '..', '..', '..', 'skills', 'skills'),
|
|
85
|
-
// Also try process.cwd() as fallback
|
|
86
|
-
path.join(process.cwd(), 'skills', 'skills')
|
|
87
|
-
];
|
|
88
|
-
|
|
89
|
-
for (const p of possiblePaths) {
|
|
90
|
-
try {
|
|
91
|
-
const resolvedPath = path.resolve(p);
|
|
92
|
-
const stat = fsSync.statSync(resolvedPath);
|
|
93
|
-
if (stat.isDirectory()) {
|
|
94
|
-
return resolvedPath;
|
|
95
|
-
}
|
|
96
|
-
} catch {
|
|
97
|
-
continue;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Ultimate fallback
|
|
102
|
-
return path.join(process.cwd(), 'skills', 'skills');
|
|
76
|
+
// Skills folder is always at {xagent_root}/skills/skills
|
|
77
|
+
return path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'skills', 'skills');
|
|
103
78
|
}
|
|
104
79
|
|
|
105
80
|
async loadAllSkills(): Promise<SkillInfo[]> {
|
|
@@ -211,7 +186,7 @@ export class SkillLoader {
|
|
|
211
186
|
|
|
212
187
|
try {
|
|
213
188
|
const content = await fs.readFile(skillMdPath, 'utf-8');
|
|
214
|
-
const parsed = this.
|
|
189
|
+
const parsed = this._parseSkillMarkdown(content);
|
|
215
190
|
|
|
216
191
|
if (parsed.name === skillId) {
|
|
217
192
|
this.skillDirectories.set(skillId, categoryPath);
|
|
@@ -265,10 +240,25 @@ export class SkillLoader {
|
|
|
265
240
|
|
|
266
241
|
try {
|
|
267
242
|
const content = await fs.readFile(skillMdPath, 'utf-8');
|
|
268
|
-
const parsed = this.
|
|
243
|
+
const parsed = this._parseSkillMarkdown(content);
|
|
269
244
|
|
|
270
245
|
if (parsed.name) {
|
|
271
246
|
this.skillDirectories.set(parsed.name, categoryPath);
|
|
247
|
+
|
|
248
|
+
// Also populate loadedSkills so getSkill() can find it
|
|
249
|
+
const skillInfo: SkillInfo = {
|
|
250
|
+
id: parsed.name,
|
|
251
|
+
name: parsed.name,
|
|
252
|
+
description: parsed.description || '',
|
|
253
|
+
license: parsed.license || 'Unknown',
|
|
254
|
+
version: parsed.version || '1.0.0',
|
|
255
|
+
author: parsed.author || 'Anonymous',
|
|
256
|
+
category: category.name,
|
|
257
|
+
markdown: content,
|
|
258
|
+
skillsPath: categoryPath
|
|
259
|
+
};
|
|
260
|
+
this.loadedSkills.set(parsed.name, skillInfo);
|
|
261
|
+
|
|
272
262
|
skillIds.push(parsed.name);
|
|
273
263
|
}
|
|
274
264
|
} catch (error) {
|
|
@@ -320,7 +310,7 @@ export class SkillLoader {
|
|
|
320
310
|
|
|
321
311
|
try {
|
|
322
312
|
const content = await fs.readFile(skillMdPath, 'utf-8');
|
|
323
|
-
const parsed = this.
|
|
313
|
+
const parsed = this._parseSkillMarkdown(content);
|
|
324
314
|
|
|
325
315
|
if (!parsed.name) {
|
|
326
316
|
const warning: SkillLoadWarning = {
|
|
@@ -360,7 +350,7 @@ export class SkillLoader {
|
|
|
360
350
|
}
|
|
361
351
|
}
|
|
362
352
|
|
|
363
|
-
private
|
|
353
|
+
private _parseSkillMarkdown(content: string): { name: string; description: string; license?: string; version?: string; author?: string } {
|
|
364
354
|
const result = {
|
|
365
355
|
name: '',
|
|
366
356
|
description: '',
|
|
@@ -371,10 +361,27 @@ export class SkillLoader {
|
|
|
371
361
|
|
|
372
362
|
// Normalize line endings to LF for consistent parsing
|
|
373
363
|
const normalizedContent = content.replace(/\r\n/g, '\n');
|
|
364
|
+
|
|
365
|
+
// Try to extract frontmatter - support both formats:
|
|
366
|
+
// 1. Standard YAML: ---name: docx...--- 2. No opening ---: name: docx...
|
|
367
|
+
let frontmatter = '';
|
|
368
|
+
let contentStart = 0;
|
|
369
|
+
|
|
374
370
|
const frontmatterMatch = normalizedContent.match(/^---\n([\s\S]*?)\n---/);
|
|
375
|
-
|
|
376
371
|
if (frontmatterMatch) {
|
|
377
|
-
|
|
372
|
+
// Standard format with --- at start and end
|
|
373
|
+
frontmatter = frontmatterMatch[1];
|
|
374
|
+
contentStart = frontmatterMatch[0].length;
|
|
375
|
+
} else {
|
|
376
|
+
// Check for format without opening --- (just YAML at the start)
|
|
377
|
+
const yamlMatch = normalizedContent.match(/^([\s\S]*?)\n---/);
|
|
378
|
+
if (yamlMatch) {
|
|
379
|
+
frontmatter = yamlMatch[1];
|
|
380
|
+
contentStart = yamlMatch[0].length;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (frontmatter) {
|
|
378
385
|
const lines = frontmatter.split('\n');
|
|
379
386
|
|
|
380
387
|
let currentKey = '';
|
|
@@ -425,6 +432,20 @@ export class SkillLoader {
|
|
|
425
432
|
return this.loadedSkills.get(skillId);
|
|
426
433
|
}
|
|
427
434
|
|
|
435
|
+
/**
|
|
436
|
+
* Get the directory path for a skill
|
|
437
|
+
*/
|
|
438
|
+
getSkillDirectory(skillId: string): string | undefined {
|
|
439
|
+
return this.skillDirectories.get(skillId);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Public method to parse skill markdown frontmatter
|
|
444
|
+
*/
|
|
445
|
+
parseSkillMarkdown(content: string): { name: string; description: string; license?: string; version?: string; author?: string } {
|
|
446
|
+
return this._parseSkillMarkdown(content);
|
|
447
|
+
}
|
|
448
|
+
|
|
428
449
|
listSkills(): SkillInfo[] {
|
|
429
450
|
return Array.from(this.loadedSkills.values());
|
|
430
451
|
}
|
package/src/slash-commands.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import inquirer from 'inquirer';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import ora from 'ora';
|
|
4
|
-
import { ExecutionMode, ChatMessage, InputType, ToolCall, Checkpoint, AgentConfig, CompressionConfig } from './types.js';
|
|
4
|
+
import { ExecutionMode, ChatMessage, InputType, ToolCall, Checkpoint, AgentConfig, CompressionConfig, AuthType } from './types.js';
|
|
5
5
|
import { AIClient, Message, detectThinkingKeywords, getThinkingTokens } from './ai-client.js';
|
|
6
6
|
import { getToolRegistry } from './tools.js';
|
|
7
7
|
import { getAgentManager } from './agents.js';
|
|
@@ -14,6 +14,7 @@ import { getContextCompressor, ContextCompressor, CompressionResult } from './co
|
|
|
14
14
|
import { getConversationManager, ConversationManager } from './conversation.js';
|
|
15
15
|
import { icons, colors } from './theme.js';
|
|
16
16
|
import { SystemPromptGenerator } from './system-prompt-generator.js';
|
|
17
|
+
import { AuthService } from './auth.js';
|
|
17
18
|
|
|
18
19
|
const logger = getLogger();
|
|
19
20
|
|
|
@@ -40,14 +41,14 @@ export class SlashCommandHandler {
|
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
/**
|
|
43
|
-
*
|
|
44
|
+
* Set callback for clearing conversation
|
|
44
45
|
*/
|
|
45
46
|
setClearCallback(callback: () => void): void {
|
|
46
47
|
this.onClearCallback = callback;
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
/**
|
|
50
|
-
*
|
|
51
|
+
* Set callback for system prompt update
|
|
51
52
|
*/
|
|
52
53
|
setSystemPromptUpdateCallback(callback: () => Promise<void>): void {
|
|
53
54
|
this.onSystemPromptUpdate = callback;
|
|
@@ -84,6 +85,9 @@ export class SlashCommandHandler {
|
|
|
84
85
|
case 'auth':
|
|
85
86
|
await this.handleAuth();
|
|
86
87
|
break;
|
|
88
|
+
case 'login':
|
|
89
|
+
await this.handleLogin();
|
|
90
|
+
break;
|
|
87
91
|
case 'mode':
|
|
88
92
|
await this.handleMode(args);
|
|
89
93
|
break;
|
|
@@ -96,6 +100,9 @@ export class SlashCommandHandler {
|
|
|
96
100
|
case 'mcp':
|
|
97
101
|
await this.handleMcp(args);
|
|
98
102
|
break;
|
|
103
|
+
case 'vlm':
|
|
104
|
+
await this.handleVlm();
|
|
105
|
+
break;
|
|
99
106
|
case 'memory':
|
|
100
107
|
await this.handleMemory(args);
|
|
101
108
|
break;
|
|
@@ -111,9 +118,9 @@ export class SlashCommandHandler {
|
|
|
111
118
|
case 'theme':
|
|
112
119
|
await this.handleTheme();
|
|
113
120
|
break;
|
|
114
|
-
case 'language':
|
|
115
|
-
|
|
116
|
-
|
|
121
|
+
// case 'language':
|
|
122
|
+
// await this.handleLanguage();
|
|
123
|
+
// break;
|
|
117
124
|
case 'about':
|
|
118
125
|
await this.handleAbout();
|
|
119
126
|
break;
|
|
@@ -212,12 +219,12 @@ export class SlashCommandHandler {
|
|
|
212
219
|
detail: 'Enable/disable AI thinking process display',
|
|
213
220
|
example: '/think on\n/think off\n/think display compact'
|
|
214
221
|
},
|
|
215
|
-
{
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
},
|
|
222
|
+
// {
|
|
223
|
+
// cmd: '/language [zh|en]',
|
|
224
|
+
// desc: 'Switch language',
|
|
225
|
+
// detail: 'Switch between Chinese and English interface',
|
|
226
|
+
// example: '/language zh\n/language en'
|
|
227
|
+
// },
|
|
221
228
|
{
|
|
222
229
|
cmd: '/theme',
|
|
223
230
|
desc: 'Switch theme',
|
|
@@ -240,6 +247,12 @@ export class SlashCommandHandler {
|
|
|
240
247
|
detail: 'Manage Model Context Protocol servers',
|
|
241
248
|
example: '/mcp list\n/mcp add server-name'
|
|
242
249
|
},
|
|
250
|
+
{
|
|
251
|
+
cmd: '/vlm',
|
|
252
|
+
desc: 'Configure VLM for GUI Agent',
|
|
253
|
+
detail: 'Configure Vision-Language Model for browser/desktop automation',
|
|
254
|
+
example: '/vlm'
|
|
255
|
+
},
|
|
243
256
|
{
|
|
244
257
|
cmd: '/tools [verbose|simple]',
|
|
245
258
|
desc: 'Manage tool display',
|
|
@@ -334,13 +347,13 @@ export class SlashCommandHandler {
|
|
|
334
347
|
}
|
|
335
348
|
|
|
336
349
|
private async handleClear(): Promise<void> {
|
|
337
|
-
//
|
|
350
|
+
// Clear local conversation history
|
|
338
351
|
this.conversationHistory = [];
|
|
339
352
|
|
|
340
|
-
//
|
|
353
|
+
// Clear ConversationManager 中的当前对话
|
|
341
354
|
await this.conversationManager.clearCurrentConversation();
|
|
342
355
|
|
|
343
|
-
//
|
|
356
|
+
// Call callback to notify InteractiveSession 清空对话
|
|
344
357
|
if (this.onClearCallback) {
|
|
345
358
|
this.onClearCallback();
|
|
346
359
|
}
|
|
@@ -393,6 +406,143 @@ export class SlashCommandHandler {
|
|
|
393
406
|
}
|
|
394
407
|
}
|
|
395
408
|
|
|
409
|
+
private async handleLogin(): Promise<void> {
|
|
410
|
+
logger.section('Login to xAgent');
|
|
411
|
+
|
|
412
|
+
const authConfig = this.configManager.getAuthConfig();
|
|
413
|
+
const currentAuthType = authConfig.type;
|
|
414
|
+
|
|
415
|
+
if (currentAuthType !== AuthType.OAUTH_XAGENT) {
|
|
416
|
+
console.log(chalk.yellow('\n⚠️ Current authentication type is not OAuth xAgent.'));
|
|
417
|
+
const { proceed } = await inquirer.prompt([
|
|
418
|
+
{
|
|
419
|
+
type: 'confirm',
|
|
420
|
+
name: 'proceed',
|
|
421
|
+
message: 'Do you want to switch to OAuth xAgent authentication?',
|
|
422
|
+
default: false
|
|
423
|
+
}
|
|
424
|
+
]);
|
|
425
|
+
|
|
426
|
+
if (!proceed) {
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Switch to OAuth xAgent
|
|
431
|
+
await this.configManager.setAuthConfig({
|
|
432
|
+
selectedAuthType: AuthType.OAUTH_XAGENT,
|
|
433
|
+
apiKey: '',
|
|
434
|
+
refreshToken: '',
|
|
435
|
+
baseUrl: ''
|
|
436
|
+
});
|
|
437
|
+
await this.configManager.save('global');
|
|
438
|
+
console.log(chalk.green('✅ Switched to OAuth xAgent authentication.'));
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
console.log(chalk.cyan('\n🔐 Starting OAuth xAgent login...'));
|
|
442
|
+
console.log(chalk.gray(' A browser will open for you to complete authentication.\n'));
|
|
443
|
+
|
|
444
|
+
try {
|
|
445
|
+
const authService = new AuthService({
|
|
446
|
+
type: AuthType.OAUTH_XAGENT,
|
|
447
|
+
apiKey: '',
|
|
448
|
+
baseUrl: '',
|
|
449
|
+
refreshToken: ''
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
const success = await authService.authenticate();
|
|
453
|
+
|
|
454
|
+
if (success) {
|
|
455
|
+
const newConfig = this.configManager.getAuthConfig();
|
|
456
|
+
console.log(chalk.green('\n✅ Login successful!'));
|
|
457
|
+
console.log(chalk.cyan(` Token saved to: ~/.xagent/settings.json`));
|
|
458
|
+
console.log(chalk.gray(' You can now use xAgent CLI with remote AI services.\n'));
|
|
459
|
+
} else {
|
|
460
|
+
console.log(chalk.red('\n❌ Login failed or was cancelled.'));
|
|
461
|
+
}
|
|
462
|
+
} catch (error: any) {
|
|
463
|
+
console.log(chalk.red(`\n❌ Login error: ${error.message || 'Unknown error'}`));
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
private async handleVlm(): Promise<void> {
|
|
468
|
+
logger.section('VLM Configuration for GUI Agent');
|
|
469
|
+
|
|
470
|
+
// Show current VLM config
|
|
471
|
+
const currentVlmConfig = {
|
|
472
|
+
model: this.configManager.get('guiSubagentModel'),
|
|
473
|
+
baseUrl: this.configManager.get('guiSubagentBaseUrl'),
|
|
474
|
+
apiKey: this.configManager.get('guiSubagentApiKey') ? '***' : ''
|
|
475
|
+
};
|
|
476
|
+
|
|
477
|
+
console.log(chalk.cyan('\n📊 Current VLM Configuration:\n'));
|
|
478
|
+
console.log(` Model: ${chalk.yellow(currentVlmConfig.model || 'Not configured')}`);
|
|
479
|
+
console.log(` Base URL: ${chalk.yellow(currentVlmConfig.baseUrl || 'Not configured')}`);
|
|
480
|
+
console.log(` API Key: ${chalk.yellow(currentVlmConfig.apiKey || 'Not configured')}`);
|
|
481
|
+
console.log();
|
|
482
|
+
|
|
483
|
+
const { action } = await inquirer.prompt([
|
|
484
|
+
{
|
|
485
|
+
type: 'list',
|
|
486
|
+
name: 'action',
|
|
487
|
+
message: 'Select action:',
|
|
488
|
+
choices: [
|
|
489
|
+
{ name: 'Configure VLM', value: 'configure' },
|
|
490
|
+
{ name: 'Remove VLM configuration', value: 'remove' },
|
|
491
|
+
{ name: 'Back', value: 'back' }
|
|
492
|
+
]
|
|
493
|
+
}
|
|
494
|
+
]);
|
|
495
|
+
|
|
496
|
+
if (action === 'back') {
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
if (action === 'remove') {
|
|
501
|
+
const { confirm } = await inquirer.prompt([
|
|
502
|
+
{
|
|
503
|
+
type: 'confirm',
|
|
504
|
+
name: 'confirm',
|
|
505
|
+
message: 'Are you sure you want to remove VLM configuration?',
|
|
506
|
+
default: false
|
|
507
|
+
}
|
|
508
|
+
]);
|
|
509
|
+
|
|
510
|
+
if (confirm) {
|
|
511
|
+
await this.configManager.set('guiSubagentModel', '');
|
|
512
|
+
await this.configManager.set('guiSubagentBaseUrl', '');
|
|
513
|
+
await this.configManager.set('guiSubagentApiKey', '');
|
|
514
|
+
await this.configManager.save('global');
|
|
515
|
+
console.log(chalk.green('✅ VLM configuration removed successfully!'));
|
|
516
|
+
}
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
if (action === 'configure') {
|
|
521
|
+
// Use AuthService to configure VLM
|
|
522
|
+
const authService = new AuthService({
|
|
523
|
+
type: 'openai_compatible' as any,
|
|
524
|
+
apiKey: '',
|
|
525
|
+
baseUrl: '',
|
|
526
|
+
modelName: ''
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
const vlmConfig = await authService.configureAndValidateVLM();
|
|
530
|
+
|
|
531
|
+
if (vlmConfig) {
|
|
532
|
+
// Save VLM configuration
|
|
533
|
+
await this.configManager.set('guiSubagentModel', vlmConfig.model);
|
|
534
|
+
await this.configManager.set('guiSubagentBaseUrl', vlmConfig.baseUrl);
|
|
535
|
+
await this.configManager.set('guiSubagentApiKey', vlmConfig.apiKey);
|
|
536
|
+
await this.configManager.save('global');
|
|
537
|
+
console.log(chalk.green('✅ VLM configuration saved successfully!'));
|
|
538
|
+
console.log(chalk.cyan(` Model: ${vlmConfig.model}`));
|
|
539
|
+
console.log(chalk.cyan(` Base URL: ${vlmConfig.baseUrl}`));
|
|
540
|
+
} else {
|
|
541
|
+
console.log(chalk.red('❌ VLM configuration failed or cancelled'));
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
396
546
|
private async handleMode(args: string[]): Promise<void> {
|
|
397
547
|
const modes = Object.values(ExecutionMode);
|
|
398
548
|
const currentMode = this.configManager.getApprovalMode() || this.configManager.getExecutionMode();
|
package/src/smart-approval.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import inquirer from 'inquirer';
|
|
2
2
|
import { AIClient, Message } from './ai-client.js';
|
|
3
3
|
import { getConfigManager } from './config.js';
|
|
4
|
+
import { AuthType } from './types.js';
|
|
4
5
|
import { getLogger } from './logger.js';
|
|
5
6
|
import { colors, icons } from './theme.js';
|
|
6
7
|
|
|
@@ -303,20 +304,26 @@ export class BlacklistChecker {
|
|
|
303
304
|
*/
|
|
304
305
|
export class AIApprovalChecker {
|
|
305
306
|
private aiClient: AIClient | null = null;
|
|
307
|
+
private isRemoteMode: boolean = false;
|
|
306
308
|
|
|
307
309
|
constructor() {
|
|
308
310
|
this.initializeAIClient();
|
|
309
311
|
}
|
|
310
312
|
|
|
311
313
|
/**
|
|
312
|
-
* Initialize AI client
|
|
314
|
+
* Initialize AI client(s)
|
|
313
315
|
*/
|
|
314
316
|
private async initializeAIClient(): Promise<void> {
|
|
315
317
|
try {
|
|
316
318
|
const configManager = getConfigManager();
|
|
317
319
|
const authConfig = configManager.getAuthConfig();
|
|
318
320
|
|
|
319
|
-
if (
|
|
321
|
+
// Check if Remote mode (OAuth XAGENT)
|
|
322
|
+
this.isRemoteMode = authConfig.type === AuthType.OAUTH_XAGENT;
|
|
323
|
+
|
|
324
|
+
// Remote mode: AI review handled by remote LLM, no local AIClient needed
|
|
325
|
+
// Local mode: use local AIClient
|
|
326
|
+
if (!this.isRemoteMode && authConfig.apiKey) {
|
|
320
327
|
this.aiClient = new AIClient(authConfig);
|
|
321
328
|
}
|
|
322
329
|
} catch (error) {
|
|
@@ -328,11 +335,21 @@ export class AIApprovalChecker {
|
|
|
328
335
|
* Use AI for intelligent review
|
|
329
336
|
*/
|
|
330
337
|
async check(context: ToolCallContext): Promise<{ approved: boolean; analysis: string; riskLevel: RiskLevel }> {
|
|
338
|
+
// In Remote mode, the remote LLM has already approved the tool_calls
|
|
339
|
+
// Local AI review approves directly, no need to repeat
|
|
340
|
+
if (this.isRemoteMode) {
|
|
341
|
+
return {
|
|
342
|
+
approved: true,
|
|
343
|
+
analysis: 'Remote mode: tool approval handled by remote LLM',
|
|
344
|
+
riskLevel: RiskLevel.LOW
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
331
348
|
if (!this.aiClient) {
|
|
332
349
|
// If AI client is not initialized, default to medium risk, requires user confirmation
|
|
333
350
|
return {
|
|
334
351
|
approved: false,
|
|
335
|
-
analysis: 'AI review not available, requires manual user confirmation',
|
|
352
|
+
analysis: 'AI review not available (no local LLM configured), requires manual user confirmation',
|
|
336
353
|
riskLevel: RiskLevel.MEDIUM
|
|
337
354
|
};
|
|
338
355
|
}
|
|
@@ -392,6 +409,20 @@ Please return results in JSON format:
|
|
|
392
409
|
};
|
|
393
410
|
} catch (error: any) {
|
|
394
411
|
logger.error('AI approval check failed', error instanceof Error ? error.message : String(error));
|
|
412
|
+
|
|
413
|
+
// In Remote mode, remote LLM already approved, local failure means auto-approve
|
|
414
|
+
const configManager = getConfigManager();
|
|
415
|
+
const authConfig = configManager.getAuthConfig();
|
|
416
|
+
const isRemoteMode = authConfig.type === AuthType.OAUTH_XAGENT;
|
|
417
|
+
|
|
418
|
+
if (isRemoteMode) {
|
|
419
|
+
return {
|
|
420
|
+
approved: true,
|
|
421
|
+
analysis: 'Remote mode: approved (remote LLM handled approval)',
|
|
422
|
+
riskLevel: RiskLevel.LOW
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
|
|
395
426
|
return {
|
|
396
427
|
approved: false,
|
|
397
428
|
analysis: `AI review failed: ${error.message}, requires manual confirmation`,
|