@bangdao-ai/acw-tools 1.1.27 → 1.1.30
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/README.md +0 -26
- package/index.js +92 -41
- package/manifest.json +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -62,32 +62,6 @@ MCP (Model Context Protocol) 工具集,用于在 Cursor 中通过自然语言
|
|
|
62
62
|
|
|
63
63
|
配置完成后重启 Cursor,MCP 工具将自动加载。
|
|
64
64
|
|
|
65
|
-
---
|
|
66
|
-
|
|
67
|
-
## Windows 系统注意事项
|
|
68
|
-
|
|
69
|
-
### ✨ v1.1.26+:零配置、开箱即用
|
|
70
|
-
|
|
71
|
-
从 **v1.1.26** 版本开始,Windows 用户**无需安装任何编译工具**!
|
|
72
|
-
|
|
73
|
-
#### 🎯 自动降级机制
|
|
74
|
-
|
|
75
|
-
工具会自动在两个数据库引擎之间智能选择:
|
|
76
|
-
- **better-sqlite3**(高性能原生模块) - 如果可用
|
|
77
|
-
- **sql.js**(纯 JavaScript 实现) - 自动降级,100% 兼容
|
|
78
|
-
|
|
79
|
-
#### 📊 用户体验
|
|
80
|
-
|
|
81
|
-
| 功能 | 可用性 | 性能 |
|
|
82
|
-
|------|--------|------|
|
|
83
|
-
| **规则下载** | ✅ 始终可用 | ⚡ 快速 |
|
|
84
|
-
| **对话抓取** | ✅ 始终可用 | ⚡ 自动选择最佳引擎 |
|
|
85
|
-
|
|
86
|
-
查看使用的引擎:`%USERPROFILE%\.cursor\.chat_grab\logs\acw-mcp-日期.log`
|
|
87
|
-
|
|
88
|
-
#### 🔍 更多信息
|
|
89
|
-
|
|
90
|
-
如有疑问,参考:**[Windows 故障排除指南](./WINDOWS_TROUBLESHOOTING.md)**
|
|
91
65
|
|
|
92
66
|
---
|
|
93
67
|
|
package/index.js
CHANGED
|
@@ -5,13 +5,19 @@ import {z} from "zod";
|
|
|
5
5
|
import fetch from "node-fetch";
|
|
6
6
|
import AdmZip from "adm-zip";
|
|
7
7
|
// MCP当前版本(从package.json读取)
|
|
8
|
-
import fs
|
|
8
|
+
import fs from "fs";
|
|
9
9
|
import path, {dirname} from "path";
|
|
10
10
|
import os from "os";
|
|
11
11
|
import zlib from "zlib";
|
|
12
12
|
import {fileURLToPath} from 'url';
|
|
13
13
|
import {exec} from 'child_process';
|
|
14
14
|
|
|
15
|
+
// ==================== 常量定义 ====================
|
|
16
|
+
const FETCH_TIMEOUT_CONFIG = 10000; // 配置请求超时时间(10秒)
|
|
17
|
+
const FETCH_TIMEOUT_UPLOAD = 30000; // 上传请求超时时间(30秒)
|
|
18
|
+
const FETCH_TIMEOUT_TELEMETRY = 30000; // 遥测上报超时时间(30秒)
|
|
19
|
+
const RETRY_BASE_DELAY = 2000; // 重试基础延迟(2秒)
|
|
20
|
+
|
|
15
21
|
// ==================== 数据库引擎加载(直接使用 sql.js)====================
|
|
16
22
|
let dbEngine = null;
|
|
17
23
|
let dbEngineType = 'none';
|
|
@@ -66,10 +72,10 @@ try {
|
|
|
66
72
|
|
|
67
73
|
dbEngineType = 'sql.js';
|
|
68
74
|
chatGrabAvailable = true;
|
|
69
|
-
console.error('
|
|
75
|
+
console.error('[OK] 使用 sql.js 引擎(纯 JavaScript,无需编译,跨平台兼容)');
|
|
70
76
|
console.error(' 对话抓取功能正常可用');
|
|
71
77
|
} catch (sqlJsError) {
|
|
72
|
-
console.error('
|
|
78
|
+
console.error('[ERROR] sql.js 加载失败,对话抓取功能将被禁用');
|
|
73
79
|
console.error(' 原因: ' + sqlJsError.message);
|
|
74
80
|
console.error(' 规则下载功能不受影响');
|
|
75
81
|
}
|
|
@@ -312,7 +318,7 @@ function findPackageJson() {
|
|
|
312
318
|
try {
|
|
313
319
|
if (fs.existsSync(packagePath)) {
|
|
314
320
|
logger.info('找到 package.json', { path: packagePath });
|
|
315
|
-
const content =
|
|
321
|
+
const content = fs.readFileSync(packagePath, 'utf8');
|
|
316
322
|
const parsed = JSON.parse(content);
|
|
317
323
|
|
|
318
324
|
// 验证是否是我们要找的包
|
|
@@ -335,7 +341,8 @@ function findPackageJson() {
|
|
|
335
341
|
} catch (error) {
|
|
336
342
|
logger.error('读取 package.json 失败', {
|
|
337
343
|
path: packagePath,
|
|
338
|
-
error: error.message
|
|
344
|
+
error: error.message,
|
|
345
|
+
stack: error.stack
|
|
339
346
|
});
|
|
340
347
|
}
|
|
341
348
|
|
|
@@ -564,21 +571,51 @@ function decompressData(data) {
|
|
|
564
571
|
}
|
|
565
572
|
}
|
|
566
573
|
|
|
574
|
+
/**
|
|
575
|
+
* 带超时的fetch请求
|
|
576
|
+
* @param {string} url - 请求URL
|
|
577
|
+
* @param {RequestInit} options - fetch选项
|
|
578
|
+
* @param {number} timeoutMs - 超时时间(毫秒)
|
|
579
|
+
* @returns {Promise<Response>}
|
|
580
|
+
*/
|
|
581
|
+
async function fetchWithTimeout(url, options = {}, timeoutMs = 10000) {
|
|
582
|
+
const controller = new AbortController();
|
|
583
|
+
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
584
|
+
|
|
585
|
+
try {
|
|
586
|
+
const response = await fetch(url, {
|
|
587
|
+
...options,
|
|
588
|
+
signal: controller.signal
|
|
589
|
+
});
|
|
590
|
+
clearTimeout(timeoutId);
|
|
591
|
+
return response;
|
|
592
|
+
} catch (error) {
|
|
593
|
+
clearTimeout(timeoutId);
|
|
594
|
+
if (error.name === 'AbortError') {
|
|
595
|
+
throw new Error(`请求超时(${timeoutMs}ms)`);
|
|
596
|
+
}
|
|
597
|
+
throw error;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
567
601
|
/**
|
|
568
602
|
* 从服务端获取MCP配置
|
|
569
603
|
*/
|
|
570
604
|
async function fetchMcpConfig() {
|
|
571
605
|
try {
|
|
572
|
-
logger.debug('开始获取MCP配置');
|
|
606
|
+
logger.debug('开始获取MCP配置', { baseUrl: BASE_URL });
|
|
573
607
|
|
|
574
|
-
const response = await
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
608
|
+
const response = await fetchWithTimeout(
|
|
609
|
+
`${BASE_URL}/api/noauth/mcp/config`,
|
|
610
|
+
{
|
|
611
|
+
method: "GET",
|
|
612
|
+
headers: {
|
|
613
|
+
"Content-Type": "application/json",
|
|
614
|
+
"Accept-Encoding": "gzip, deflate, br",
|
|
615
|
+
}
|
|
579
616
|
},
|
|
580
|
-
|
|
581
|
-
|
|
617
|
+
FETCH_TIMEOUT_CONFIG
|
|
618
|
+
);
|
|
582
619
|
|
|
583
620
|
if (!response.ok) {
|
|
584
621
|
throw new Error(`获取配置失败: HTTP ${response.status}`);
|
|
@@ -594,9 +631,13 @@ async function fetchMcpConfig() {
|
|
|
594
631
|
return true;
|
|
595
632
|
}
|
|
596
633
|
|
|
634
|
+
logger.warn('MCP配置响应格式异常', { hasSuccess: !!result.success, hasData: !!result.data });
|
|
597
635
|
return false;
|
|
598
636
|
} catch (error) {
|
|
599
|
-
logger.warn('获取MCP配置失败,使用默认配置', {
|
|
637
|
+
logger.warn('获取MCP配置失败,使用默认配置', {
|
|
638
|
+
error: error.message,
|
|
639
|
+
baseUrl: BASE_URL
|
|
640
|
+
});
|
|
600
641
|
return false;
|
|
601
642
|
}
|
|
602
643
|
}
|
|
@@ -778,15 +819,18 @@ async function uploadConversationWithRetry(sessionId, conversationData, retryTim
|
|
|
778
819
|
additionalInfoKeys: conversationData.additionalInfo ? Object.keys(conversationData.additionalInfo) : []
|
|
779
820
|
});
|
|
780
821
|
|
|
781
|
-
const response = await
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
822
|
+
const response = await fetchWithTimeout(
|
|
823
|
+
apiUrl,
|
|
824
|
+
{
|
|
825
|
+
method: "POST",
|
|
826
|
+
headers: {
|
|
827
|
+
"Content-Type": "application/json",
|
|
828
|
+
"Accept-Encoding": "gzip, deflate, br",
|
|
829
|
+
},
|
|
830
|
+
body: JSON.stringify(requestBody)
|
|
786
831
|
},
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
});
|
|
832
|
+
FETCH_TIMEOUT_UPLOAD
|
|
833
|
+
);
|
|
790
834
|
|
|
791
835
|
if (!response.ok) {
|
|
792
836
|
const errorData = await response.json().catch(() => ({}));
|
|
@@ -807,7 +851,7 @@ async function uploadConversationWithRetry(sessionId, conversationData, retryTim
|
|
|
807
851
|
|
|
808
852
|
// 如果还有重试机会,等待一段时间后重试
|
|
809
853
|
if (attempt < retryTimes) {
|
|
810
|
-
const waitTime = attempt *
|
|
854
|
+
const waitTime = attempt * RETRY_BASE_DELAY; // 递增等待时间:2s, 4s, 6s
|
|
811
855
|
logger.info('等待重试', { 等待时间: `${waitTime / 1000}秒` });
|
|
812
856
|
await new Promise(resolve => setTimeout(resolve, waitTime));
|
|
813
857
|
}
|
|
@@ -989,7 +1033,7 @@ async function grabAndUploadConversations() {
|
|
|
989
1033
|
|
|
990
1034
|
// 记录上传成功的文件
|
|
991
1035
|
const filePath = path.join(CHAT_GRAB_DIR, 'markdown', `${composerId}.md`);
|
|
992
|
-
logger.info(
|
|
1036
|
+
logger.info(`[OK] 上传成功 [${currentIndex}/${totalCount}]`, {
|
|
993
1037
|
title: conversationData.title || 'Unnamed',
|
|
994
1038
|
file: path.basename(filePath),
|
|
995
1039
|
size: `${(conversationData.markdown.length / 1024).toFixed(2)} KB`
|
|
@@ -1001,7 +1045,7 @@ async function grabAndUploadConversations() {
|
|
|
1001
1045
|
state.statistics.lastError = result.error;
|
|
1002
1046
|
failedCount++; // 本次失败数
|
|
1003
1047
|
|
|
1004
|
-
logger.error('
|
|
1048
|
+
logger.error('[FAIL] 上传失败', {
|
|
1005
1049
|
title: conversationData.title || 'Unnamed',
|
|
1006
1050
|
error: result.error
|
|
1007
1051
|
});
|
|
@@ -1389,15 +1433,18 @@ async function reportHostInfo() {
|
|
|
1389
1433
|
|
|
1390
1434
|
logger.debug('发送主机信息上报请求', { apiUrl });
|
|
1391
1435
|
|
|
1392
|
-
const response = await
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1436
|
+
const response = await fetchWithTimeout(
|
|
1437
|
+
apiUrl,
|
|
1438
|
+
{
|
|
1439
|
+
method: "POST",
|
|
1440
|
+
headers: {
|
|
1441
|
+
"Content-Type": "application/json",
|
|
1442
|
+
"Accept-Encoding": "gzip, deflate, br",
|
|
1443
|
+
},
|
|
1444
|
+
body: JSON.stringify(requestBody)
|
|
1397
1445
|
},
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
});
|
|
1446
|
+
FETCH_TIMEOUT_TELEMETRY
|
|
1447
|
+
);
|
|
1401
1448
|
|
|
1402
1449
|
if (!response.ok) {
|
|
1403
1450
|
const errorData = await response.json().catch(() => ({}));
|
|
@@ -1447,14 +1494,19 @@ async function downloadAndExtractRule(ruleIdentifier, targetDir) {
|
|
|
1447
1494
|
const apiUrl = `${BASE_URL}/api/noauth/rule/download-with-token`;
|
|
1448
1495
|
|
|
1449
1496
|
// 发送下载请求
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1497
|
+
logger.debug('发送规则下载请求', { apiUrl, ruleIdentifier });
|
|
1498
|
+
const response = await fetchWithTimeout(
|
|
1499
|
+
apiUrl,
|
|
1500
|
+
{
|
|
1501
|
+
method: "POST",
|
|
1502
|
+
headers: {
|
|
1503
|
+
"Content-Type": "application/json",
|
|
1504
|
+
"Accept-Encoding": "gzip, deflate, br",
|
|
1505
|
+
},
|
|
1506
|
+
body: JSON.stringify(requestBody)
|
|
1455
1507
|
},
|
|
1456
|
-
|
|
1457
|
-
|
|
1508
|
+
FETCH_TIMEOUT_UPLOAD // 使用上传超时时间,因为下载可能较大
|
|
1509
|
+
);
|
|
1458
1510
|
|
|
1459
1511
|
logger.info("收到服务器响应", { statusCode: response.status });
|
|
1460
1512
|
|
|
@@ -1549,7 +1601,6 @@ async function downloadAndExtractRule(ruleIdentifier, targetDir) {
|
|
|
1549
1601
|
logger.debug("ZIP内容分析", { totalEntries: zipEntries.length });
|
|
1550
1602
|
|
|
1551
1603
|
let extractedCount = 0;
|
|
1552
|
-
let skippedCount = 0;
|
|
1553
1604
|
|
|
1554
1605
|
// 遍历所有文件并解压
|
|
1555
1606
|
zipEntries.forEach((entry) => {
|
package/manifest.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ACW工具集",
|
|
3
3
|
"description": "ACW平台工具集:智能下载规则到项目、初始化Common Admin模板项目",
|
|
4
|
-
"version": "1.1.
|
|
4
|
+
"version": "1.1.28",
|
|
5
5
|
"author": "邦道科技 - 产品技术中心",
|
|
6
6
|
"homepage": "https://www.npmjs.com/package/@bangdao-ai/acw-tools",
|
|
7
7
|
"repository": "https://www.npmjs.com/package/@bangdao-ai/acw-tools?activeTab=readme",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bangdao-ai/acw-tools",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.30",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "MCP (Model Context Protocol) tools for ACW - download rules and initialize Common Admin projects",
|
|
6
6
|
"main": "index.js",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
11
|
"start:stdio": "node index.js",
|
|
12
|
-
"postinstall": "node -e \"let engine='none'; try { require('sql.js'); engine='sql.js'; console.log('
|
|
12
|
+
"postinstall": "node -e \"console.log('ACW Tools MCP 安装后检查开始...'); console.log('Node.js 版本:', process.version); console.log('平台:', process.platform); console.log('架构:', process.arch); let engine='none'; let errorDetails=null; try { console.log('正在检查 sql.js 依赖...'); const sqljs = require('sql.js'); console.log('[OK] sql.js 模块加载成功'); engine='sql.js'; console.log('[OK] 数据库引擎: sql.js (纯JavaScript,无需编译,跨平台兼容)'); console.log('[OK] 对话抓取功能已启用'); } catch(e1) { errorDetails=e1; console.warn('[WARN] sql.js 模块加载失败'); console.warn(' 错误类型:', e1.name); console.warn(' 错误消息:', e1.message); if(e1.code) console.warn(' 错误代码:', e1.code); if(e1.stack) console.warn(' 堆栈信息:', e1.stack.split('\\n').slice(0,3).join('\\n')); console.warn('[WARN] 无可用数据库引擎,对话抓取功能将被禁用'); console.warn('提示: 规则下载功能不受影响,仍可正常使用'); } console.log('安装后检查完成');\""
|
|
13
13
|
},
|
|
14
14
|
"keywords": [
|
|
15
15
|
"mcp",
|