openclawapi 1.3.3 → 1.3.5

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.
Files changed (2) hide show
  1. package/cli.js +199 -3
  2. package/package.json +1 -1
package/cli.js CHANGED
@@ -15,9 +15,7 @@ const ENDPOINTS = [
15
15
  { name: '国内节点2', url: 'https://yunyi.skem.cn' },
16
16
  { name: 'CF国外节点1', url: 'https://yunyi.cfd' },
17
17
  { name: 'CF国外节点2', url: 'https://cdn1.yunyi.cfd' },
18
- { name: 'CF国外节点3', url: 'https://cdn2.yunyi.cfd' },
19
- { name: '备用节点1', url: 'http://47.99.42.193' },
20
- { name: '备用节点2', url: 'http://47.97.100.10' }
18
+ { name: 'CF国外节点3', url: 'https://cdn2.yunyi.cfd' }
21
19
  ];
22
20
 
23
21
  // Claude 模型预设
@@ -191,6 +189,7 @@ async function main() {
191
189
  { name: '🟢 选择 Codex 节点', value: 'select_codex' },
192
190
  { name: '⚡ 激活 Claude', value: 'activate_claude' },
193
191
  { name: '⚡ 激活 Codex', value: 'activate_codex' },
192
+ { name: '🧪 测试连接', value: 'test_connection' },
194
193
  { name: '📋 查看当前配置', value: 'view_config' },
195
194
  { name: '🔄 恢复默认配置', value: 'restore' },
196
195
  { name: '❌ 退出', value: 'exit' }
@@ -213,6 +212,9 @@ async function main() {
213
212
  case 'activate_codex':
214
213
  await activate(paths, 'codex');
215
214
  break;
215
+ case 'test_connection':
216
+ await testConnection(paths);
217
+ break;
216
218
  case 'view_config':
217
219
  await viewConfig(paths);
218
220
  break;
@@ -377,6 +379,200 @@ async function activate(paths, type) {
377
379
  console.log(chalk.gray(` API Key: 已设置`));
378
380
  }
379
381
 
382
+ // ============ 测试连接 ============
383
+ async function testConnection(paths) {
384
+ console.log(chalk.cyan('🧪 测试 API 连接\n'));
385
+
386
+ const config = readConfig(paths.openclawConfig);
387
+
388
+ if (!config) {
389
+ console.log(chalk.yellow('配置文件不存在,请先选择节点'));
390
+ return;
391
+ }
392
+
393
+ // 检查当前激活的是哪个
394
+ const primary = config.agents?.defaults?.model?.primary || '';
395
+ const isClaudeActive = primary.startsWith('yunyi-claude');
396
+ const isCodexActive = primary.startsWith('yunyi-codex');
397
+
398
+ if (!isClaudeActive && !isCodexActive) {
399
+ console.log(chalk.yellow('⚠️ 请先激活 Claude 或 Codex'));
400
+ return;
401
+ }
402
+
403
+ const type = isClaudeActive ? 'claude' : 'codex';
404
+ const typeLabel = isClaudeActive ? 'Claude' : 'Codex';
405
+ const apiConfig = API_CONFIG[type];
406
+ const provider = config.models?.providers?.[apiConfig.providerName];
407
+
408
+ if (!provider) {
409
+ console.log(chalk.yellow(`⚠️ ${typeLabel} 节点未配置`));
410
+ return;
411
+ }
412
+
413
+ if (!provider.apiKey) {
414
+ console.log(chalk.yellow(`⚠️ ${typeLabel} API Key 未设置`));
415
+ return;
416
+ }
417
+
418
+ console.log(chalk.gray(`测试 ${typeLabel} API...`));
419
+ console.log(chalk.gray(`节点: ${provider.baseUrl}`));
420
+ console.log(chalk.gray(`模型: ${provider.models?.[0]?.id || 'N/A'}\n`));
421
+
422
+ try {
423
+ const startTime = Date.now();
424
+
425
+ if (type === 'claude') {
426
+ // Claude API 测试
427
+ const result = await testClaudeApi(provider.baseUrl, provider.apiKey, provider.models?.[0]?.id);
428
+ const latency = Date.now() - startTime;
429
+
430
+ if (result.success) {
431
+ console.log(chalk.green(`✅ Claude API 连接成功!`));
432
+ console.log(chalk.cyan(` 响应时间: ${latency}ms`));
433
+ console.log(chalk.gray(` 回复: ${result.message}`));
434
+ } else {
435
+ console.log(chalk.red(`❌ Claude API 连接失败`));
436
+ console.log(chalk.red(` 错误: ${result.error}`));
437
+ }
438
+ } else {
439
+ // Codex API 测试
440
+ const result = await testCodexApi(provider.baseUrl, provider.apiKey, provider.models?.[0]?.id);
441
+ const latency = Date.now() - startTime;
442
+
443
+ if (result.success) {
444
+ console.log(chalk.green(`✅ Codex API 连接成功!`));
445
+ console.log(chalk.cyan(` 响应时间: ${latency}ms`));
446
+ console.log(chalk.gray(` 回复: ${result.message}`));
447
+ } else {
448
+ console.log(chalk.red(`❌ Codex API 连接失败`));
449
+ console.log(chalk.red(` 错误: ${result.error}`));
450
+ }
451
+ }
452
+ } catch (error) {
453
+ console.log(chalk.red(`❌ 测试失败: ${error.message}`));
454
+ }
455
+ }
456
+
457
+ // Claude API 测试
458
+ function testClaudeApi(baseUrl, apiKey, model) {
459
+ return new Promise((resolve) => {
460
+ const urlObj = new URL(baseUrl);
461
+ const protocol = urlObj.protocol === 'https:' ? https : http;
462
+
463
+ const postData = JSON.stringify({
464
+ model: model || 'claude-sonnet-4-5',
465
+ max_tokens: 50,
466
+ messages: [{ role: 'user', content: 'Hi, reply with "OK" only.' }]
467
+ });
468
+
469
+ const options = {
470
+ hostname: urlObj.hostname,
471
+ port: urlObj.port || (urlObj.protocol === 'https:' ? 443 : 80),
472
+ path: urlObj.pathname,
473
+ method: 'POST',
474
+ timeout: 30000,
475
+ rejectUnauthorized: false,
476
+ headers: {
477
+ 'Content-Type': 'application/json',
478
+ 'x-api-key': apiKey,
479
+ 'anthropic-version': '2023-06-01',
480
+ 'Content-Length': Buffer.byteLength(postData)
481
+ }
482
+ };
483
+
484
+ const req = protocol.request(options, (res) => {
485
+ let data = '';
486
+ res.on('data', chunk => data += chunk);
487
+ res.on('end', () => {
488
+ try {
489
+ const json = JSON.parse(data);
490
+ if (json.content && json.content[0]) {
491
+ resolve({ success: true, message: json.content[0].text?.substring(0, 100) || 'OK' });
492
+ } else if (json.error) {
493
+ resolve({ success: false, error: json.error.message || JSON.stringify(json.error) });
494
+ } else {
495
+ resolve({ success: false, error: `HTTP ${res.statusCode}: ${data.substring(0, 200)}` });
496
+ }
497
+ } catch {
498
+ resolve({ success: false, error: `HTTP ${res.statusCode}: ${data.substring(0, 200)}` });
499
+ }
500
+ });
501
+ });
502
+
503
+ req.on('timeout', () => {
504
+ req.destroy();
505
+ resolve({ success: false, error: '请求超时 (30s)' });
506
+ });
507
+
508
+ req.on('error', (e) => {
509
+ resolve({ success: false, error: e.message });
510
+ });
511
+
512
+ req.write(postData);
513
+ req.end();
514
+ });
515
+ }
516
+
517
+ // Codex API 测试
518
+ function testCodexApi(baseUrl, apiKey, model) {
519
+ return new Promise((resolve) => {
520
+ const urlObj = new URL(baseUrl);
521
+ const protocol = urlObj.protocol === 'https:' ? https : http;
522
+
523
+ const postData = JSON.stringify({
524
+ model: model || 'gpt-5.2',
525
+ input: 'Hi, reply with "OK" only.'
526
+ });
527
+
528
+ const options = {
529
+ hostname: urlObj.hostname,
530
+ port: urlObj.port || (urlObj.protocol === 'https:' ? 443 : 80),
531
+ path: urlObj.pathname,
532
+ method: 'POST',
533
+ timeout: 30000,
534
+ rejectUnauthorized: false,
535
+ headers: {
536
+ 'Content-Type': 'application/json',
537
+ 'Authorization': `Bearer ${apiKey}`,
538
+ 'Content-Length': Buffer.byteLength(postData)
539
+ }
540
+ };
541
+
542
+ const req = protocol.request(options, (res) => {
543
+ let data = '';
544
+ res.on('data', chunk => data += chunk);
545
+ res.on('end', () => {
546
+ try {
547
+ const json = JSON.parse(data);
548
+ if (json.output && json.output[0]) {
549
+ const text = json.output[0].content?.[0]?.text || json.output[0].text || 'OK';
550
+ resolve({ success: true, message: text.substring(0, 100) });
551
+ } else if (json.error) {
552
+ resolve({ success: false, error: json.error.message || JSON.stringify(json.error) });
553
+ } else {
554
+ resolve({ success: false, error: `HTTP ${res.statusCode}: ${data.substring(0, 200)}` });
555
+ }
556
+ } catch {
557
+ resolve({ success: false, error: `HTTP ${res.statusCode}: ${data.substring(0, 200)}` });
558
+ }
559
+ });
560
+ });
561
+
562
+ req.on('timeout', () => {
563
+ req.destroy();
564
+ resolve({ success: false, error: '请求超时 (30s)' });
565
+ });
566
+
567
+ req.on('error', (e) => {
568
+ resolve({ success: false, error: e.message });
569
+ });
570
+
571
+ req.write(postData);
572
+ req.end();
573
+ });
574
+ }
575
+
380
576
  // ============ 查看配置 ============
381
577
  async function viewConfig(paths) {
382
578
  console.log(chalk.cyan('📋 当前配置\n'));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclawapi",
3
- "version": "1.3.3",
3
+ "version": "1.3.5",
4
4
  "description": "跨平台 OpenClaw/Clawdbot 配置管理工具 - 管理中转地址、模型切换、API Keys、测速优化",
5
5
  "main": "cli.js",
6
6
  "bin": {