@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/auth.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import axios from 'axios';
|
|
2
2
|
import open from 'open';
|
|
3
3
|
import inquirer from 'inquirer';
|
|
4
|
-
import
|
|
4
|
+
import https from 'https';
|
|
5
5
|
import { AuthConfig, AuthType } from './types.js';
|
|
6
6
|
import { getLogger } from './logger.js';
|
|
7
7
|
|
|
@@ -140,75 +140,47 @@ export class AuthService {
|
|
|
140
140
|
}
|
|
141
141
|
|
|
142
142
|
async authenticate(): Promise<boolean> {
|
|
143
|
+
let result: boolean;
|
|
143
144
|
switch (this.authConfig.type) {
|
|
144
145
|
case AuthType.OAUTH_XAGENT:
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
return await this.authenticateWithApiKey();
|
|
146
|
+
result = await this.authenticateWithXAgent();
|
|
147
|
+
break;
|
|
148
148
|
case AuthType.OPENAI_COMPATIBLE:
|
|
149
|
-
|
|
149
|
+
result = await this.authenticateWithOpenAICompatible();
|
|
150
|
+
break;
|
|
150
151
|
default:
|
|
151
152
|
throw new Error(`Unknown auth type: ${this.authConfig.type}`);
|
|
152
153
|
}
|
|
154
|
+
|
|
155
|
+
return result;
|
|
153
156
|
}
|
|
154
157
|
|
|
155
158
|
private async authenticateWithXAgent(): Promise<boolean> {
|
|
156
159
|
logger.info('Authenticating with xAgent...', 'Please complete the authentication in your browser');
|
|
157
160
|
|
|
158
161
|
try {
|
|
159
|
-
// 1.
|
|
162
|
+
// 1. Start HTTP server to receive callback
|
|
160
163
|
const token = await this.retrieveXAgentToken();
|
|
161
164
|
|
|
162
|
-
// 2.
|
|
163
|
-
this.authConfig.
|
|
164
|
-
|
|
165
|
+
// 2. 调用后端验证用户
|
|
166
|
+
const xagentApiBaseUrl = this.authConfig.xagentApiBaseUrl || 'https://154.8.140.52:443';
|
|
167
|
+
const httpsAgent = new https.Agent({ rejectUnauthorized: false });
|
|
168
|
+
const response = await axios.get(`${xagentApiBaseUrl}/api/auth/me`, {
|
|
169
|
+
headers: { 'Authorization': `Bearer ${token}` },
|
|
170
|
+
httpsAgent
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// 3. Set authentication configuration
|
|
174
|
+
this.authConfig.baseUrl = 'https://154.8.140.52:443/v1';
|
|
175
|
+
this.authConfig.xagentApiBaseUrl = xagentApiBaseUrl;
|
|
165
176
|
this.authConfig.apiKey = token;
|
|
177
|
+
this.authConfig.type = AuthType.OAUTH_XAGENT;
|
|
166
178
|
|
|
167
179
|
logger.success('Successfully authenticated with xAgent!');
|
|
168
|
-
logger.info(`LLM API: http://xagent-colife.net:3000/v1`);
|
|
169
|
-
logger.info(`VLM API: http://xagent-colife.net:3000/v3`);
|
|
170
180
|
return true;
|
|
171
181
|
} catch (error: any) {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
logger.error('Authentication timed out', 'The browser authentication took too long. Please try again.');
|
|
175
|
-
} else if (errorMsg.includes('No token') || errorMsg.includes('no token')) {
|
|
176
|
-
logger.error('Authentication was cancelled or failed', 'No token was received from the browser. Please try again.');
|
|
177
|
-
} else {
|
|
178
|
-
logger.error('xAgent authentication failed', errorMsg || 'Check your network connection and try again');
|
|
179
|
-
}
|
|
180
|
-
return false;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
private async authenticateWithApiKey(): Promise<boolean> {
|
|
185
|
-
logger.info('Authenticating with API Key...');
|
|
186
|
-
|
|
187
|
-
const answers = await inquirer.prompt([
|
|
188
|
-
{
|
|
189
|
-
type: 'input',
|
|
190
|
-
name: 'apiKey',
|
|
191
|
-
message: 'Enter your xAgent API Key:',
|
|
192
|
-
validate: (input: string) => {
|
|
193
|
-
if (!input || input.trim().length === 0) {
|
|
194
|
-
return 'API Key cannot be empty';
|
|
195
|
-
}
|
|
196
|
-
return true;
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
]);
|
|
200
|
-
|
|
201
|
-
const apiKey = answers.apiKey as string;
|
|
202
|
-
|
|
203
|
-
this.authConfig.apiKey = apiKey.trim();
|
|
204
|
-
this.authConfig.baseUrl = 'https://apis.xagent.cn/v1';
|
|
205
|
-
|
|
206
|
-
const isValid = await this.validateApiKey();
|
|
207
|
-
if (isValid) {
|
|
208
|
-
logger.success('API Key verified successfully!', 'You can now start using xAgent CLI');
|
|
209
|
-
return true;
|
|
210
|
-
} else {
|
|
211
|
-
logger.error('Invalid API Key, please try again.', 'Make sure you entered the correct API Key');
|
|
182
|
+
logger.error('Authentication failed', error.message || 'Unknown error');
|
|
183
|
+
logger.debug('Full error:', JSON.stringify(error.response?.data || error.message));
|
|
212
184
|
return false;
|
|
213
185
|
}
|
|
214
186
|
}
|
|
@@ -323,6 +295,7 @@ export class AuthService {
|
|
|
323
295
|
this.authConfig.baseUrl = baseUrl;
|
|
324
296
|
this.authConfig.apiKey = (apiKey as string).trim();
|
|
325
297
|
this.authConfig.modelName = modelName;
|
|
298
|
+
this.authConfig.type = AuthType.OPENAI_COMPATIBLE;
|
|
326
299
|
|
|
327
300
|
const isValid = await this.validateApiKey();
|
|
328
301
|
if (isValid) {
|
|
@@ -402,87 +375,80 @@ export class AuthService {
|
|
|
402
375
|
}
|
|
403
376
|
|
|
404
377
|
private async retrieveXAgentToken(): Promise<string> {
|
|
405
|
-
//
|
|
406
|
-
const
|
|
407
|
-
const
|
|
378
|
+
// Use xagentApiBaseUrl from config, fallback to default
|
|
379
|
+
const webBaseUrl = this.authConfig.xagentApiBaseUrl || 'https://154.8.140.52';
|
|
380
|
+
const authUrl = `${webBaseUrl}/login`;
|
|
381
|
+
// Callback URL tells frontend where to store token
|
|
382
|
+
const callbackUrl = 'https://154.8.140.52:443/callback';
|
|
383
|
+
|
|
384
|
+
// 如果已有保存的token,通过URL参数传给Web页面
|
|
385
|
+
const existingToken = this.authConfig.apiKey;
|
|
386
|
+
const existingRefreshToken = this.authConfig.refreshToken;
|
|
387
|
+
|
|
388
|
+
// 构建登录URL - 如果已有token也传给Web
|
|
389
|
+
let loginUrl = `${authUrl}?callback=${encodeURIComponent(callbackUrl)}`;
|
|
390
|
+
if (existingToken) {
|
|
391
|
+
loginUrl += `&existingToken=${encodeURIComponent(existingToken)}`;
|
|
392
|
+
if (existingRefreshToken) {
|
|
393
|
+
loginUrl += `&existingRefreshToken=${encodeURIComponent(existingRefreshToken)}`;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
408
396
|
|
|
409
|
-
|
|
410
|
-
|
|
397
|
+
// Open browser for login, then poll server for token
|
|
398
|
+
await open(loginUrl);
|
|
399
|
+
logger.info('Waiting for authentication...', 'Please complete login in your browser');
|
|
411
400
|
|
|
412
|
-
//
|
|
401
|
+
// Poll server to get token
|
|
413
402
|
return new Promise((resolve, reject) => {
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
403
|
+
const pollInterval = 2000; // Poll every 2 seconds
|
|
404
|
+
const maxWaitTime = 30 * 60 * 1000; // 30 minutes timeout
|
|
405
|
+
const startTime = Date.now();
|
|
406
|
+
|
|
407
|
+
const poll = async () => {
|
|
408
|
+
if (Date.now() - startTime > maxWaitTime) {
|
|
409
|
+
logger.warn('Authentication timeout after 30 minutes');
|
|
410
|
+
reject(new Error('Authentication timeout'));
|
|
411
|
+
return;
|
|
421
412
|
}
|
|
422
|
-
};
|
|
423
|
-
|
|
424
|
-
const serverCallback = (req: any, res: any) => {
|
|
425
|
-
logger.debug(`[OAuth] Received request: ${req.url}`);
|
|
426
413
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
const
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
414
|
+
try {
|
|
415
|
+
// Create HTTPS agent that ignores certificate errors (for IP-based access)
|
|
416
|
+
const httpsAgent = new https.Agent({ rejectUnauthorized: false });
|
|
417
|
+
|
|
418
|
+
const response = await axios.get(`${webBaseUrl}/api/cli/get-token`, {
|
|
419
|
+
timeout: 10000,
|
|
420
|
+
httpsAgent
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
if (response.data.token) {
|
|
424
|
+
logger.success('Authentication successful! Received token');
|
|
425
|
+
logger.debug('[CLI-Auth] Token stored, key:', response.data.token.substring(0, 20) + '...');
|
|
438
426
|
// Save refresh token if provided
|
|
439
|
-
if (refreshToken) {
|
|
440
|
-
this.authConfig.refreshToken = refreshToken;
|
|
441
|
-
logger.debug(`[OAuth] Refresh token saved`);
|
|
427
|
+
if (response.data.refreshToken) {
|
|
428
|
+
this.authConfig.refreshToken = response.data.refreshToken;
|
|
442
429
|
}
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
}
|
|
450
|
-
resolve(token);
|
|
430
|
+
resolve(response.data.token);
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
} catch (error: any) {
|
|
434
|
+
if (error.response?.status === 404) {
|
|
435
|
+
// Token not ready yet, continue polling
|
|
451
436
|
} else {
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
res.writeHead(400, { 'Content-Type': 'text/html' });
|
|
455
|
-
res.end('<h1>Authentication Failed: No token</h1>');
|
|
456
|
-
if (server) {
|
|
457
|
-
server.close();
|
|
458
|
-
}
|
|
459
|
-
reject(new Error('No token received'));
|
|
437
|
+
console.error('[CLI-Auth] Polling error:', error.message);
|
|
460
438
|
}
|
|
461
439
|
}
|
|
440
|
+
|
|
441
|
+
// Continue polling
|
|
442
|
+
setTimeout(poll, pollInterval);
|
|
462
443
|
};
|
|
463
|
-
|
|
464
|
-
server = http.createServer(serverCallback);
|
|
465
444
|
|
|
466
|
-
//
|
|
467
|
-
|
|
468
|
-
logger.warn('[OAuth] Authentication timeout after 30 minutes');
|
|
469
|
-
if (server) {
|
|
470
|
-
server.close();
|
|
471
|
-
}
|
|
472
|
-
reject(new Error('Authentication timeout'));
|
|
473
|
-
}, 1800000); // 30 minutes
|
|
474
|
-
|
|
475
|
-
server.listen(8080, async () => {
|
|
476
|
-
logger.info('Waiting for authentication...', 'Opening browser for login...');
|
|
477
|
-
const fullUrl = `${authUrl}?callback=${encodeURIComponent(callbackUrl)}`;
|
|
478
|
-
logger.debug(`[OAuth] Full URL: ${fullUrl}`);
|
|
479
|
-
await open(fullUrl);
|
|
480
|
-
});
|
|
445
|
+
// Start polling
|
|
446
|
+
setTimeout(poll, pollInterval);
|
|
481
447
|
});
|
|
482
448
|
}
|
|
483
449
|
|
|
484
450
|
getAuthConfig(): AuthConfig {
|
|
485
|
-
return { ...this.authConfig };
|
|
451
|
+
return { ...this.authConfig, type: this.authConfig.type };
|
|
486
452
|
}
|
|
487
453
|
|
|
488
454
|
updateAuthConfig(config: Partial<AuthConfig>): void {
|
package/src/cancellation.ts
CHANGED
package/src/cli.ts
CHANGED
|
@@ -10,6 +10,17 @@ import { getMCPManager } from './mcp.js';
|
|
|
10
10
|
import { getLogger, setConfigProvider } from './logger.js';
|
|
11
11
|
import { theme, icons, colors } from './theme.js';
|
|
12
12
|
import { getCancellationManager } from './cancellation.js';
|
|
13
|
+
import { readFileSync } from 'fs';
|
|
14
|
+
import { dirname, join } from 'path';
|
|
15
|
+
import { fileURLToPath } from 'url';
|
|
16
|
+
|
|
17
|
+
// Get current directory
|
|
18
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
19
|
+
const __dirname = dirname(__filename);
|
|
20
|
+
|
|
21
|
+
// Read package.json
|
|
22
|
+
const packageJsonPath = join(__dirname, '../package.json');
|
|
23
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
13
24
|
|
|
14
25
|
const logger = getLogger();
|
|
15
26
|
|
|
@@ -26,10 +37,72 @@ setConfigProvider(() => {
|
|
|
26
37
|
|
|
27
38
|
const program = new Command();
|
|
28
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Format error message for user-friendly display
|
|
42
|
+
*/
|
|
43
|
+
function formatError(error: unknown): { message: string; suggestion: string } {
|
|
44
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
45
|
+
|
|
46
|
+
// Network errors
|
|
47
|
+
if (errorMessage.includes('ENOTFOUND') || errorMessage.includes('ECONNREFUSED')) {
|
|
48
|
+
return {
|
|
49
|
+
message: 'Unable to connect to the server',
|
|
50
|
+
suggestion: 'Please check your network connection and try again.'
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
if (errorMessage.includes('ETIMEDOUT') || errorMessage.includes('ECONNRESET')) {
|
|
54
|
+
return {
|
|
55
|
+
message: 'Connection timed out',
|
|
56
|
+
suggestion: 'The server may be busy. Please wait a moment and try again.'
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
// Authentication errors
|
|
60
|
+
if (errorMessage.includes('401') || errorMessage.includes('Unauthorized') || errorMessage.includes('invalid token')) {
|
|
61
|
+
return {
|
|
62
|
+
message: 'Authentication failed',
|
|
63
|
+
suggestion: 'Please login again using: xagent auth'
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
// Token expired
|
|
67
|
+
if (errorMessage.includes('token') && errorMessage.includes('expired')) {
|
|
68
|
+
return {
|
|
69
|
+
message: 'Session expired',
|
|
70
|
+
suggestion: 'Please login again using: xagent auth'
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
// Permission errors
|
|
74
|
+
if (errorMessage.includes('EACCES') || errorMessage.includes('permission denied')) {
|
|
75
|
+
return {
|
|
76
|
+
message: 'Permission denied',
|
|
77
|
+
suggestion: 'Please check your file permissions or run with appropriate privileges.'
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
// File not found
|
|
81
|
+
if (errorMessage.includes('ENOENT') || errorMessage.includes('not found')) {
|
|
82
|
+
return {
|
|
83
|
+
message: 'File or resource not found',
|
|
84
|
+
suggestion: 'Please check the path and try again.'
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
// Invalid JSON
|
|
88
|
+
if (errorMessage.includes('JSON') || errorMessage.includes('parse')) {
|
|
89
|
+
return {
|
|
90
|
+
message: 'Invalid data format',
|
|
91
|
+
suggestion: 'The configuration file may be corrupted. Please check the file content.'
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Default friendly message
|
|
96
|
+
return {
|
|
97
|
+
message: 'An error occurred',
|
|
98
|
+
suggestion: 'Please try again. If the problem persists, check your configuration.'
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
29
102
|
program
|
|
30
103
|
.name('xagent')
|
|
31
104
|
.description('AI-powered command-line assistant')
|
|
32
|
-
.version(
|
|
105
|
+
.version(packageJson.version)
|
|
33
106
|
.option('-h, --help', 'Show help');
|
|
34
107
|
|
|
35
108
|
program
|
|
@@ -144,9 +217,10 @@ program
|
|
|
144
217
|
console.log(colors.success(`Agent ${options.remove} removed successfully`));
|
|
145
218
|
console.log('');
|
|
146
219
|
} catch (error: any) {
|
|
220
|
+
const { message, suggestion } = formatError(error);
|
|
147
221
|
console.log('');
|
|
148
|
-
console.log(colors.error(`Failed to remove agent: ${
|
|
149
|
-
console.log(colors.textMuted(
|
|
222
|
+
console.log(colors.error(`Failed to remove agent: ${message}`));
|
|
223
|
+
console.log(colors.textMuted(suggestion));
|
|
150
224
|
console.log('');
|
|
151
225
|
}
|
|
152
226
|
} else {
|
|
@@ -207,9 +281,10 @@ program
|
|
|
207
281
|
console.log(colors.success(`MCP server ${options.remove} removed successfully`));
|
|
208
282
|
console.log('');
|
|
209
283
|
} catch (error: any) {
|
|
284
|
+
const { message, suggestion } = formatError(error);
|
|
210
285
|
console.log('');
|
|
211
|
-
console.log(colors.error(`Failed to remove MCP server: ${
|
|
212
|
-
console.log(colors.textMuted(
|
|
286
|
+
console.log(colors.error(`Failed to remove MCP server: ${message}`));
|
|
287
|
+
console.log(colors.textMuted(suggestion));
|
|
213
288
|
console.log('');
|
|
214
289
|
}
|
|
215
290
|
} else {
|
|
@@ -238,8 +313,9 @@ program
|
|
|
238
313
|
console.log(colors.textMuted('You can now run "xagent start" to begin'));
|
|
239
314
|
console.log('');
|
|
240
315
|
} catch (error: any) {
|
|
241
|
-
|
|
242
|
-
console.log(colors.
|
|
316
|
+
const { message, suggestion } = formatError(error);
|
|
317
|
+
console.log(colors.error(`Initialization failed: ${message}`));
|
|
318
|
+
console.log(colors.textMuted(suggestion));
|
|
243
319
|
console.log('');
|
|
244
320
|
process.exit(1);
|
|
245
321
|
}
|
|
@@ -285,9 +361,10 @@ program
|
|
|
285
361
|
console.log(colors.success(`Workflow ${options.add} added successfully!`));
|
|
286
362
|
console.log('');
|
|
287
363
|
} catch (error: any) {
|
|
364
|
+
const { message, suggestion } = formatError(error);
|
|
288
365
|
console.log('');
|
|
289
|
-
console.log(colors.error(
|
|
290
|
-
console.log(colors.textMuted(
|
|
366
|
+
console.log(colors.error(message));
|
|
367
|
+
console.log(colors.textMuted(suggestion));
|
|
291
368
|
console.log('');
|
|
292
369
|
process.exit(1);
|
|
293
370
|
}
|
|
@@ -298,9 +375,10 @@ program
|
|
|
298
375
|
console.log(colors.success(`Workflow ${options.remove} removed successfully!`));
|
|
299
376
|
console.log('');
|
|
300
377
|
} catch (error: any) {
|
|
378
|
+
const { message, suggestion } = formatError(error);
|
|
301
379
|
console.log('');
|
|
302
|
-
console.log(colors.error(
|
|
303
|
-
console.log(colors.textMuted(
|
|
380
|
+
console.log(colors.error(message));
|
|
381
|
+
console.log(colors.textMuted(suggestion));
|
|
304
382
|
console.log('');
|
|
305
383
|
process.exit(1);
|
|
306
384
|
}
|
|
@@ -326,7 +404,7 @@ program
|
|
|
326
404
|
console.log('');
|
|
327
405
|
console.log(colors.border(separator));
|
|
328
406
|
console.log('');
|
|
329
|
-
console.log(` ${icons.info} ${colors.textMuted('Version:')} ${colors.primaryBright(
|
|
407
|
+
console.log(` ${icons.info} ${colors.textMuted('Version:')} ${colors.primaryBright(packageJson.version)}`);
|
|
330
408
|
console.log(` ${icons.code} ${colors.textMuted('Node.js:')} ${colors.textMuted(process.version)}`);
|
|
331
409
|
console.log(` ${icons.bolt} ${colors.textMuted('Platform:')} ${colors.textMuted(process.platform + ' ' + process.arch)}`);
|
|
332
410
|
console.log('');
|
|
@@ -349,10 +427,70 @@ program
|
|
|
349
427
|
console.log('');
|
|
350
428
|
|
|
351
429
|
try {
|
|
430
|
+
const configManager = getConfigManager();
|
|
431
|
+
const authConfig = configManager.getAuthConfig();
|
|
432
|
+
|
|
433
|
+
// Get GUI-specific VLM configuration
|
|
434
|
+
const baseUrl = configManager.get('guiSubagentBaseUrl') || configManager.get('baseUrl') || '';
|
|
435
|
+
const apiKey = configManager.get('guiSubagentApiKey') || configManager.get('apiKey') || '';
|
|
436
|
+
const modelName = configManager.get('guiSubagentModel') || configManager.get('modelName') || '';
|
|
437
|
+
|
|
438
|
+
// Determine mode: local (openai_compatible) or remote
|
|
439
|
+
const isLocalMode = authConfig.type === 'openai_compatible';
|
|
440
|
+
|
|
441
|
+
if (isLocalMode) {
|
|
442
|
+
// Local mode: require baseUrl configuration
|
|
443
|
+
if (!baseUrl) {
|
|
444
|
+
console.log(colors.error('No VLM API URL configured for GUI subagent.'));
|
|
445
|
+
console.log(colors.textMuted('Please run "xagent auth" and configure guiSubagentBaseUrl.'));
|
|
446
|
+
console.log('');
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
console.log(colors.info(`${icons.brain} Using local VLM configuration`));
|
|
450
|
+
console.log(colors.textMuted(` Model: ${modelName}`));
|
|
451
|
+
console.log(colors.textMuted(` Base URL: ${baseUrl}`));
|
|
452
|
+
console.log('');
|
|
453
|
+
} else {
|
|
454
|
+
// Remote mode
|
|
455
|
+
console.log(colors.info(`${icons.brain} Using remote VLM service`));
|
|
456
|
+
console.log(colors.textMuted(` Auth Type: ${authConfig.type}`));
|
|
457
|
+
console.log('');
|
|
458
|
+
}
|
|
459
|
+
|
|
352
460
|
const { createGUISubAgent } = await import('./gui-subagent/index.js');
|
|
353
461
|
|
|
462
|
+
// Create remoteVlmCaller for remote mode (uses full messages for consistent behavior)
|
|
463
|
+
let remoteVlmCaller: ((messages: any[], systemPrompt: string) => Promise<string>) | undefined;
|
|
464
|
+
|
|
465
|
+
if (!isLocalMode && authConfig.baseUrl) {
|
|
466
|
+
const remoteBaseUrl = `${authConfig.baseUrl}/api/agent/vlm`;
|
|
467
|
+
remoteVlmCaller = async (messages: any[], _systemPrompt: string): Promise<string> => {
|
|
468
|
+
const response = await fetch(remoteBaseUrl, {
|
|
469
|
+
method: 'POST',
|
|
470
|
+
headers: {
|
|
471
|
+
'Content-Type': 'application/json',
|
|
472
|
+
'Authorization': `Bearer ${authConfig.apiKey || ''}`,
|
|
473
|
+
},
|
|
474
|
+
body: JSON.stringify({
|
|
475
|
+
messages
|
|
476
|
+
}),
|
|
477
|
+
});
|
|
478
|
+
if (!response.ok) {
|
|
479
|
+
const errorText = await response.text();
|
|
480
|
+
throw new Error(`Remote VLM error: ${response.status} - ${errorText}`);
|
|
481
|
+
}
|
|
482
|
+
const result = await response.json() as { response?: string; content?: string; message?: string };
|
|
483
|
+
return result.response || result.content || result.message || '';
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
|
|
354
487
|
const guiAgent = await createGUISubAgent({
|
|
355
488
|
headless: options.headless ?? false,
|
|
489
|
+
model: isLocalMode ? modelName : undefined,
|
|
490
|
+
modelBaseUrl: isLocalMode ? baseUrl : undefined,
|
|
491
|
+
modelApiKey: isLocalMode ? apiKey : undefined,
|
|
492
|
+
remoteVlmCaller,
|
|
493
|
+
isLocalMode,
|
|
356
494
|
});
|
|
357
495
|
|
|
358
496
|
console.log(colors.success('✅ GUI Subagent initialized successfully!'));
|
|
@@ -371,8 +509,10 @@ program
|
|
|
371
509
|
console.log(colors.primaryBright('Use the GUI tools in the interactive session to control the computer.'));
|
|
372
510
|
console.log('');
|
|
373
511
|
} catch (error: any) {
|
|
512
|
+
const { message, suggestion } = formatError(error);
|
|
374
513
|
console.log('');
|
|
375
|
-
console.log(colors.error(`Failed to start GUI Subagent: ${
|
|
514
|
+
console.log(colors.error(`Failed to start GUI Subagent: ${message}`));
|
|
515
|
+
console.log(colors.textMuted(suggestion));
|
|
376
516
|
console.log('');
|
|
377
517
|
}
|
|
378
518
|
});
|
|
@@ -382,3 +522,28 @@ program.parse(process.argv);
|
|
|
382
522
|
if (!process.argv.slice(2).length) {
|
|
383
523
|
program.outputHelp();
|
|
384
524
|
}
|
|
525
|
+
|
|
526
|
+
// ============================================================
|
|
527
|
+
// Global error handling - prevent crashes from uncaught errors
|
|
528
|
+
// ============================================================
|
|
529
|
+
|
|
530
|
+
// Handle uncaught promise rejections
|
|
531
|
+
process.on('unhandledRejection', (reason: any, promise: Promise<any>) => {
|
|
532
|
+
console.error('\n❌ An unexpected error occurred');
|
|
533
|
+
if (reason instanceof Error) {
|
|
534
|
+
console.error(` ${reason.message}`);
|
|
535
|
+
} else if (reason) {
|
|
536
|
+
console.error(` ${String(reason)}`);
|
|
537
|
+
}
|
|
538
|
+
console.error('\n If this problem persists, please report this issue.');
|
|
539
|
+
console.error('');
|
|
540
|
+
process.exit(1);
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
// Handle uncaught exceptions
|
|
544
|
+
process.on('uncaughtException', (error: Error) => {
|
|
545
|
+
console.error('\n❌ Critical error - application will exit');
|
|
546
|
+
console.error(` ${error.message}`);
|
|
547
|
+
console.error('');
|
|
548
|
+
process.exit(1);
|
|
549
|
+
});
|
package/src/config.ts
CHANGED
|
@@ -10,12 +10,12 @@ const DEFAULT_SETTINGS: Settings = {
|
|
|
10
10
|
theme: 'Default',
|
|
11
11
|
selectedAuthType: AuthType.OAUTH_XAGENT,
|
|
12
12
|
apiKey: '',
|
|
13
|
-
// LLM API -
|
|
13
|
+
// LLM API - for main conversation and task processing (OpenAI compatible format)
|
|
14
14
|
baseUrl: 'http://xagent-colife.net:3000/v1',
|
|
15
15
|
modelName: 'Qwen3-Coder',
|
|
16
|
-
// xAgent API -
|
|
16
|
+
// xAgent API - for token validation and other backend calls (without /v1)
|
|
17
17
|
xagentApiBaseUrl: 'http://xagent-colife.net:3000',
|
|
18
|
-
// VLM API -
|
|
18
|
+
// VLM API - for GUI automation (browser/desktop operations)
|
|
19
19
|
guiSubagentModel: 'Qwen3-Coder',
|
|
20
20
|
guiSubagentBaseUrl: 'http://xagent-colife.net:3000/v3',
|
|
21
21
|
guiSubagentApiKey: '',
|
|
@@ -66,18 +66,32 @@ export class ConfigManager {
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
async load(): Promise<Settings> {
|
|
69
|
+
logger.debug('[CONFIG] ========== load() 开始 ==========');
|
|
70
|
+
logger.debug('[CONFIG] globalConfigPath:', this.globalConfigPath);
|
|
71
|
+
logger.debug('[CONFIG] projectConfigPath:', this.projectConfigPath);
|
|
72
|
+
logger.debug('[CONFIG] 检查文件是否存在...');
|
|
73
|
+
|
|
69
74
|
try {
|
|
70
75
|
const globalConfig = this.readConfigFile(this.globalConfigPath);
|
|
76
|
+
logger.debug('[CONFIG] globalConfig 读取成功:', JSON.stringify(globalConfig, null, 2));
|
|
77
|
+
|
|
71
78
|
this.settings = { ...DEFAULT_SETTINGS, ...globalConfig };
|
|
79
|
+
logger.debug('[CONFIG] 合并后的 settings:', JSON.stringify(this.settings, null, 2));
|
|
72
80
|
|
|
73
81
|
if (this.projectConfigPath) {
|
|
74
82
|
const projectConfig = this.readConfigFile(this.projectConfigPath);
|
|
83
|
+
logger.debug('[CONFIG] projectConfig 读取成功:', JSON.stringify(projectConfig, null, 2));
|
|
75
84
|
this.settings = { ...this.settings, ...projectConfig };
|
|
76
85
|
}
|
|
77
86
|
|
|
87
|
+
logger.debug('[CONFIG] 最终 settings.apiKey:', this.settings.apiKey ? this.settings.apiKey.substring(0, 30) + '...' : 'empty');
|
|
88
|
+
logger.debug('[CONFIG] 最终 settings.refreshToken:', this.settings.refreshToken ? 'exists' : 'empty');
|
|
89
|
+
logger.debug('[CONFIG] ========== load() 结束 ==========');
|
|
90
|
+
|
|
78
91
|
return this.settings;
|
|
79
92
|
} catch (error) {
|
|
80
|
-
logger.
|
|
93
|
+
logger.debug('[CONFIG] load() 捕获到错误:', error instanceof Error ? error.message : String(error));
|
|
94
|
+
logger.debug('[CONFIG] ========== load() 结束 (使用默认值) ==========');
|
|
81
95
|
return { ...DEFAULT_SETTINGS };
|
|
82
96
|
}
|
|
83
97
|
}
|
|
@@ -111,7 +125,7 @@ export class ConfigManager {
|
|
|
111
125
|
}
|
|
112
126
|
|
|
113
127
|
getAuthConfig() {
|
|
114
|
-
|
|
128
|
+
const result = {
|
|
115
129
|
type: this.settings.selectedAuthType,
|
|
116
130
|
apiKey: this.settings.apiKey,
|
|
117
131
|
refreshToken: this.settings.refreshToken,
|
|
@@ -121,10 +135,30 @@ export class ConfigManager {
|
|
|
121
135
|
searchApiKey: this.settings.searchApiKey,
|
|
122
136
|
showAIDebugInfo: this.settings.showAIDebugInfo
|
|
123
137
|
};
|
|
138
|
+
|
|
139
|
+
logger.debug('[CONFIG] getAuthConfig() 返回:');
|
|
140
|
+
logger.debug(' - type:', result.type);
|
|
141
|
+
logger.debug(' - apiKey:', result.apiKey ? result.apiKey.substring(0, 30) + '...' : 'empty');
|
|
142
|
+
logger.debug(' - refreshToken:', result.refreshToken ? 'exists' : 'empty');
|
|
143
|
+
logger.debug(' - baseUrl:', result.baseUrl);
|
|
144
|
+
logger.debug(' - xagentApiBaseUrl:', result.xagentApiBaseUrl);
|
|
145
|
+
|
|
146
|
+
return result;
|
|
124
147
|
}
|
|
125
148
|
|
|
126
|
-
async setAuthConfig(config: Partial<Settings>): Promise<void> {
|
|
127
|
-
|
|
149
|
+
async setAuthConfig(config: Partial<Settings> & { xagentApiBaseUrl?: string }): Promise<void> {
|
|
150
|
+
// Extract xagentApiBaseUrl separately since it's not in Settings
|
|
151
|
+
const { xagentApiBaseUrl, type, ...otherConfig } = config as any;
|
|
152
|
+
|
|
153
|
+
// Map 'type' to 'selectedAuthType' (AuthConfig uses 'type', Settings uses 'selectedAuthType')
|
|
154
|
+
if (type !== undefined) {
|
|
155
|
+
this.settings.selectedAuthType = type;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
Object.assign(this.settings, otherConfig);
|
|
159
|
+
if (xagentApiBaseUrl !== undefined) {
|
|
160
|
+
this.settings.xagentApiBaseUrl = xagentApiBaseUrl;
|
|
161
|
+
}
|
|
128
162
|
await this.save('global');
|
|
129
163
|
}
|
|
130
164
|
|
|
@@ -221,7 +255,22 @@ export class ConfigManager {
|
|
|
221
255
|
}
|
|
222
256
|
|
|
223
257
|
getWorkspacePath(): string | undefined {
|
|
224
|
-
|
|
258
|
+
if (this.settings.workspacePath) {
|
|
259
|
+
return this.settings.workspacePath;
|
|
260
|
+
}
|
|
261
|
+
// Auto-detect: ~/.xagent/workspace
|
|
262
|
+
const detectedPath = path.join(os.homedir(), '.xagent', 'workspace');
|
|
263
|
+
|
|
264
|
+
// Ensure directory exists
|
|
265
|
+
try {
|
|
266
|
+
if (!fs.existsSync(detectedPath)) {
|
|
267
|
+
fs.mkdirSync(detectedPath, { recursive: true });
|
|
268
|
+
}
|
|
269
|
+
} catch {
|
|
270
|
+
// Ignore errors - caller will handle missing path
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return detectedPath;
|
|
225
274
|
}
|
|
226
275
|
|
|
227
276
|
setWorkspacePath(path: string): void {
|