mr-sliy 1.0.0

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 (50) hide show
  1. package/.env.example +145 -0
  2. package/database/schema.sql +187 -0
  3. package/package.json +74 -0
  4. package/scripts/download-tree-sitter.js +171 -0
  5. package/scripts/postinstall.js +134 -0
  6. package/src/agent/agent.js +563 -0
  7. package/src/agent.js +87 -0
  8. package/src/cli/index.js +1643 -0
  9. package/src/config/index.js +232 -0
  10. package/src/engine/dualModeEngine.js +486 -0
  11. package/src/index.js +165 -0
  12. package/src/middlewares/errorHandler.js +166 -0
  13. package/src/middlewares/index.js +23 -0
  14. package/src/routes/aiRoutes.js +117 -0
  15. package/src/routes/configRoutes.js +31 -0
  16. package/src/routes/index.js +75 -0
  17. package/src/routes/issueRoutes.js +195 -0
  18. package/src/routes/projectRoutes.js +46 -0
  19. package/src/routes/reportRoutes.js +40 -0
  20. package/src/routes/scanRoutes.js +245 -0
  21. package/src/routes/userRoutes.js +47 -0
  22. package/src/services/ast/parser.js +503 -0
  23. package/src/services/detection/detector.js +934 -0
  24. package/src/services/llm/providers.js +1107 -0
  25. package/src/services/rag/agent.js +375 -0
  26. package/src/services/vector/knowledgeBase.js +863 -0
  27. package/src/skills/Skill.js +38 -0
  28. package/src/skills/code-analysis/index.js +272 -0
  29. package/src/skills/code-detection/index.js +166 -0
  30. package/src/skills/code-detection/rules/console-log.js +45 -0
  31. package/src/skills/code-detection/rules/deep-nesting.js +76 -0
  32. package/src/skills/code-detection/rules/duplicate-code.js +57 -0
  33. package/src/skills/code-detection/rules/high-complexity.js +109 -0
  34. package/src/skills/code-detection/rules/index.js +59 -0
  35. package/src/skills/code-detection/rules/long-functions.js +54 -0
  36. package/src/skills/code-detection/rules/magic-numbers.js +48 -0
  37. package/src/skills/code-detection/rules/missing-comment.js +64 -0
  38. package/src/skills/code-detection/rules/null-check.js +71 -0
  39. package/src/skills/code-detection/rules/unnecessary-else.js +46 -0
  40. package/src/skills/code-detection/rules/unused-functions.js +57 -0
  41. package/src/skills/code-detection/rules/unused-imports.js +57 -0
  42. package/src/skills/code-detection/rules/unused-variables.js +54 -0
  43. package/src/skills/code-optimization/index.js +319 -0
  44. package/src/skills/index.js +152 -0
  45. package/src/utils/crypto.js +212 -0
  46. package/src/utils/database.js +125 -0
  47. package/src/utils/helpers.js +226 -0
  48. package/src/utils/logger.js +202 -0
  49. package/src/utils/mysql.js +198 -0
  50. package/src/utils/response.js +124 -0
package/.env.example ADDED
@@ -0,0 +1,145 @@
1
+ # ==================== 服务配置 ====================
2
+ # 服务端口
3
+ PORT=3000
4
+ # 运行环境: development | production | test
5
+ NODE_ENV=development
6
+ # 服务主机地址
7
+ HOST=localhost
8
+
9
+ # ==================== 数据库配置 ====================
10
+ # SQLite数据库文件路径
11
+ DB_PATH=./database/code_optimizer.db
12
+ # 数据库连接池最大连接数
13
+ DB_POOL_MAX=10
14
+ # 数据库连接池最小连接数
15
+ DB_POOL_MIN=2
16
+
17
+ # ==================== MySQL配置(云端知识库同步,可选) ====================
18
+ # 是否启用MySQL云同步: true | false
19
+ MYSQL_ENABLED=false
20
+ # MySQL主机地址
21
+ MYSQL_HOST=
22
+ # MySQL端口
23
+ MYSQL_PORT=3306
24
+ # MySQL用户名
25
+ MYSQL_USER=
26
+ # MySQL密码
27
+ MYSQL_PASSWORD=
28
+ # MySQL数据库名
29
+ MYSQL_DATABASE=code_optimizer
30
+ # MySQL连接池最大连接数
31
+ MYSQL_CONNECTION_LIMIT=10
32
+ # 启动时自动同步: true | false
33
+ MYSQL_SYNC_ON_STARTUP=true
34
+
35
+ # ==================== AI配置(在线模式) ====================
36
+ # OpenAI
37
+ OPENAI_API_KEY=
38
+ OPENAI_BASE_URL=https://api.openai.com/v1
39
+ OPENAI_MODEL=gpt-4
40
+
41
+ # Claude (Anthropic)
42
+ CLAUDE_API_KEY=
43
+ CLAUDE_MODEL=claude-3-sonnet-20240229
44
+
45
+ # Ollama (本地大模型)
46
+ OLLAMA_URL=http://localhost:11434
47
+ OLLAMA_MODEL=codellama
48
+
49
+ # Azure OpenAI
50
+ AZURE_OPENAI_ENDPOINT=
51
+ AZURE_OPENAI_KEY=
52
+ AZURE_DEPLOYMENT_NAME=gpt-4
53
+
54
+ # Google Gemini
55
+ GEMINI_API_KEY=
56
+ GEMINI_MODEL=gemini-1.5-pro
57
+
58
+ # 阿里通义千问
59
+ TONGYI_API_KEY=
60
+ TONGYI_MODEL=qwen-plus
61
+
62
+ # 字节豆包
63
+ DOUBAO_API_KEY=
64
+ DOUBAO_MODEL=Doubao-7B
65
+
66
+ # 百度文心一言
67
+ WENXIN_API_KEY=
68
+ WENXIN_SECRET_KEY=
69
+ WENXIN_MODEL=ernie-3.5
70
+
71
+ # DeepSeek
72
+ DEEPSEEK_API_KEY=
73
+ DEEPSEEK_BASE_URL=https://api.deepseek.com/v1
74
+ DEEPSEEK_MODEL=deepseek-chat
75
+
76
+ # 智谱AI (ChatGLM)
77
+ ZHIPU_API_KEY=
78
+ ZHIPU_BASE_URL=https://open.bigmodel.cn/api/paas/v4
79
+ ZHIPU_MODEL=glm-4
80
+
81
+ # Moonshot AI (Kimi)
82
+ MOONSHOT_API_KEY=
83
+ MOONSHOT_BASE_URL=https://api.moonshot.cn/v1
84
+ MOONSHOT_MODEL=moonshot-v1-8k
85
+
86
+ # AI调用超时时间(毫秒)
87
+ AI_TIMEOUT=30000
88
+ # AI最大输出token数
89
+ AI_MAX_TOKENS=2000
90
+ # AI温度参数 (0-1)
91
+ AI_TEMPERATURE=0.7
92
+
93
+ # ==================== AST扫描配置 ====================
94
+ # 最大文件大小(字节)
95
+ MAX_FILE_SIZE=1048576
96
+ # 扫描超时时间(毫秒)
97
+ SCAN_TIMEOUT=60000
98
+ # 是否启用并行扫描
99
+ ENABLE_PARALLEL_SCAN=true
100
+ # 并行扫描最大并发数
101
+ MAX_PARALLEL_JOBS=4
102
+ # 扫描文件扩展名(逗号分隔)
103
+ SCAN_EXTENSIONS=.js,.ts,.jsx,.tsx,.py,.java,.go,.rs
104
+ # 排除目录(逗号分隔)
105
+ EXCLUDE_DIRS=node_modules,dist,build,out,.git,coverage
106
+ # 排除文件(逗号分隔)
107
+ EXCLUDE_FILES=min.js,min.css,.d.ts
108
+
109
+ # ==================== 检测规则配置 ====================
110
+ DETECT_UNUSED_VARIABLES=true
111
+ DETECT_UNUSED_IMPORTS=true
112
+ DETECT_UNUSED_FUNCTIONS=true
113
+ DETECT_MAGIC_NUMBERS=true
114
+ MAX_FUNCTION_LINES=50
115
+ MAX_CYCLOMATIC_COMPLEXITY=10
116
+ MAX_NESTING_DEPTH=4
117
+ ENABLE_DEEP_NESTING_CHECK=true
118
+ ENABLE_NULL_CHECK=true
119
+ ENABLE_CONSOLE_LOG_CHECK=true
120
+ ENABLE_DUPLICATE_CODE_CHECK=true
121
+ ENABLE_COMMENT_CHECK=true
122
+
123
+ # ==================== 日志配置 ====================
124
+ # 日志级别: error | warn | info | debug
125
+ LOG_LEVEL=warn
126
+ # 日志文件路径
127
+ LOG_FILE=./logs/app.log
128
+ # 单个日志文件最大大小(字节)
129
+ LOG_MAX_SIZE=5242880
130
+ # 最大日志文件数量
131
+ LOG_MAX_FILES=5
132
+
133
+ # ==================== CORS配置 ====================
134
+ # 允许的源(逗号分隔)
135
+ CORS_ORIGINS=http://localhost:5173,http://localhost:3000,http://localhost:1420
136
+
137
+ # ==================== 速率限制 ====================
138
+ # API速率限制(每15分钟请求数)
139
+ RATE_LIMIT_MAX=100
140
+
141
+ # ==================== 默认运行模式 ====================
142
+ # offline: 仅离线AST检测
143
+ # online: AST + AI优化
144
+ # auto: 自动选择
145
+ DEFAULT_MODE=auto
@@ -0,0 +1,187 @@
1
+ -- ============================================
2
+ -- 代码优化智能体数据库架构设计
3
+ -- 版本: 1.0.0
4
+ -- 描述: 包含8张核心数据表
5
+ -- ============================================
6
+
7
+ -- 1. 用户表 (sys_user)
8
+ CREATE TABLE IF NOT EXISTS sys_user (
9
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
10
+ username VARCHAR(50) NOT NULL UNIQUE,
11
+ password_hash VARCHAR(255) NOT NULL,
12
+ email VARCHAR(100),
13
+ role VARCHAR(20) NOT NULL DEFAULT 'operator',
14
+ status VARCHAR(20) NOT NULL DEFAULT 'active',
15
+ last_login_at DATETIME,
16
+ login_count INTEGER DEFAULT 0,
17
+ created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
18
+ updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
19
+ );
20
+
21
+ -- 2. 操作日志表 (sys_oper_log)
22
+ CREATE TABLE IF NOT EXISTS sys_oper_log (
23
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
24
+ user_id INTEGER,
25
+ username VARCHAR(50),
26
+ operation_type VARCHAR(50) NOT NULL,
27
+ operation_desc TEXT,
28
+ request_method VARCHAR(10),
29
+ request_url VARCHAR(255),
30
+ request_params TEXT,
31
+ response_status INTEGER,
32
+ ip_address VARCHAR(50),
33
+ user_agent TEXT,
34
+ duration_ms INTEGER,
35
+ created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
36
+ );
37
+
38
+ -- 3. 系统配置表 (sys_config)
39
+ CREATE TABLE IF NOT EXISTS sys_config (
40
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
41
+ config_key VARCHAR(100) NOT NULL UNIQUE,
42
+ config_value TEXT,
43
+ config_type VARCHAR(50),
44
+ description TEXT,
45
+ is_public BOOLEAN DEFAULT 0,
46
+ created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
47
+ updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
48
+ );
49
+
50
+ -- 4. 扫描项目表 (scan_project)
51
+ CREATE TABLE IF NOT EXISTS scan_project (
52
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
53
+ project_name VARCHAR(255) NOT NULL,
54
+ project_path VARCHAR(500) NOT NULL,
55
+ project_type VARCHAR(50),
56
+ language VARCHAR(50),
57
+ framework VARCHAR(100),
58
+ description TEXT,
59
+ total_files INTEGER DEFAULT 0,
60
+ total_lines INTEGER DEFAULT 0,
61
+ scan_count INTEGER DEFAULT 0,
62
+ last_scan_at DATETIME,
63
+ user_id INTEGER,
64
+ status VARCHAR(20) DEFAULT 'active',
65
+ created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
66
+ updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
67
+ );
68
+
69
+ -- 5. 扫描任务表 (scan_task)
70
+ CREATE TABLE IF NOT EXISTS scan_task (
71
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
72
+ project_id INTEGER,
73
+ task_name VARCHAR(255),
74
+ scan_mode VARCHAR(20) NOT NULL,
75
+ scan_type VARCHAR(50) NOT NULL,
76
+ target_path VARCHAR(500),
77
+ file_count INTEGER DEFAULT 0,
78
+ scanned_files INTEGER DEFAULT 0,
79
+ issue_count INTEGER DEFAULT 0,
80
+ issue_critical INTEGER DEFAULT 0,
81
+ issue_high INTEGER DEFAULT 0,
82
+ issue_medium INTEGER DEFAULT 0,
83
+ issue_low INTEGER DEFAULT 0,
84
+ status VARCHAR(20) DEFAULT 'pending',
85
+ progress INTEGER DEFAULT 0,
86
+ started_at DATETIME,
87
+ completed_at DATETIME,
88
+ duration_ms INTEGER,
89
+ error_message TEXT,
90
+ user_id INTEGER,
91
+ created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
92
+ );
93
+
94
+ -- 6. 代码缺陷表 (code_issue)
95
+ CREATE TABLE IF NOT EXISTS code_issue (
96
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
97
+ task_id INTEGER NOT NULL,
98
+ project_id INTEGER,
99
+ file_path VARCHAR(500) NOT NULL,
100
+ file_name VARCHAR(255),
101
+ language VARCHAR(50),
102
+ issue_type VARCHAR(50) NOT NULL,
103
+ severity VARCHAR(20) NOT NULL,
104
+ message TEXT NOT NULL,
105
+ suggestion TEXT,
106
+ line_start INTEGER NOT NULL,
107
+ line_end INTEGER,
108
+ column_start INTEGER,
109
+ column_end INTEGER,
110
+ code_snippet TEXT,
111
+ ast_node_type VARCHAR(100),
112
+ is_fixed BOOLEAN DEFAULT 0,
113
+ fixed_at DATETIME,
114
+ fixed_by_user_id INTEGER,
115
+ fix_suggestion TEXT,
116
+ ai_optimized BOOLEAN DEFAULT 0,
117
+ created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
118
+ );
119
+
120
+ -- 7. AI优化记录表 (ai_optimize_record)
121
+ CREATE TABLE IF NOT EXISTS ai_optimize_record (
122
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
123
+ issue_id INTEGER NOT NULL,
124
+ task_id INTEGER,
125
+ original_code TEXT NOT NULL,
126
+ optimized_code TEXT,
127
+ explanation TEXT,
128
+ optimization_type VARCHAR(50),
129
+ ai_model VARCHAR(100),
130
+ tokens_used INTEGER,
131
+ api_latency_ms INTEGER,
132
+ user_rating INTEGER,
133
+ user_feedback TEXT,
134
+ is_applied BOOLEAN DEFAULT 0,
135
+ applied_at DATETIME,
136
+ created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
137
+ );
138
+
139
+ -- 8. 报告导出表 (code_report)
140
+ CREATE TABLE IF NOT EXISTS code_report (
141
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
142
+ task_id INTEGER NOT NULL,
143
+ project_id INTEGER,
144
+ report_name VARCHAR(255) NOT NULL,
145
+ report_type VARCHAR(50),
146
+ file_path VARCHAR(500),
147
+ file_size_kb REAL,
148
+ summary TEXT,
149
+ include_ai_suggestions BOOLEAN DEFAULT 1,
150
+ user_id INTEGER,
151
+ created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
152
+ );
153
+
154
+ -- ============================================
155
+ -- 索引设计
156
+ -- ============================================
157
+
158
+ CREATE INDEX IF NOT EXISTS idx_user_username ON sys_user(username);
159
+ CREATE INDEX IF NOT EXISTS idx_user_status ON sys_user(status);
160
+
161
+ CREATE INDEX IF NOT EXISTS idx_oper_log_user_id ON sys_oper_log(user_id);
162
+ CREATE INDEX IF NOT EXISTS idx_oper_log_operation_type ON sys_oper_log(operation_type);
163
+ CREATE INDEX IF NOT EXISTS idx_oper_log_created_at ON sys_oper_log(created_at);
164
+
165
+ CREATE INDEX IF NOT EXISTS idx_config_key ON sys_config(config_key);
166
+
167
+ CREATE INDEX IF NOT EXISTS idx_project_user_id ON scan_project(user_id);
168
+ CREATE INDEX IF NOT EXISTS idx_project_status ON scan_project(status);
169
+
170
+ CREATE INDEX IF NOT EXISTS idx_task_project_id ON scan_task(project_id);
171
+ CREATE INDEX IF NOT EXISTS idx_task_user_id ON scan_task(user_id);
172
+ CREATE INDEX IF NOT EXISTS idx_task_status ON scan_task(status);
173
+ CREATE INDEX IF NOT EXISTS idx_task_created_at ON scan_task(created_at);
174
+
175
+ CREATE INDEX IF NOT EXISTS idx_issue_task_id ON code_issue(task_id);
176
+ CREATE INDEX IF NOT EXISTS idx_issue_project_id ON code_issue(project_id);
177
+ CREATE INDEX IF NOT EXISTS idx_issue_type ON code_issue(issue_type);
178
+ CREATE INDEX IF NOT EXISTS idx_issue_severity ON code_issue(severity);
179
+ CREATE INDEX IF NOT EXISTS idx_issue_is_fixed ON code_issue(is_fixed);
180
+
181
+ CREATE INDEX IF NOT EXISTS idx_ai_optimize_issue_id ON ai_optimize_record(issue_id);
182
+ CREATE INDEX IF NOT EXISTS idx_ai_optimize_task_id ON ai_optimize_record(task_id);
183
+ CREATE INDEX IF NOT EXISTS idx_ai_optimize_created_at ON ai_optimize_record(created_at);
184
+
185
+ CREATE INDEX IF NOT EXISTS idx_report_task_id ON code_report(task_id);
186
+ CREATE INDEX IF NOT EXISTS idx_report_project_id ON code_report(project_id);
187
+ CREATE INDEX IF NOT EXISTS idx_report_user_id ON code_report(user_id);
package/package.json ADDED
@@ -0,0 +1,74 @@
1
+ {
2
+ "name": "mr-sliy",
3
+ "version": "1.0.0",
4
+ "description": "Mr.Sliy - 基于Tree-sitter与RAG的多语言代码优化智能体",
5
+ "main": "src/agent.js",
6
+ "bin": {
7
+ "mr-sliy": "src/agent.js",
8
+ "sliy": "src/agent.js"
9
+ },
10
+ "files": [
11
+ "src/",
12
+ "database/schema.sql",
13
+ "scripts/",
14
+ ".env.example"
15
+ ],
16
+ "scripts": {
17
+ "postinstall": "node scripts/postinstall.js",
18
+ "start": "node src/agent.js",
19
+ "agent": "node src/agent.js",
20
+ "server": "node src/index.js",
21
+ "dev": "nodemon src/agent.js",
22
+ "test": "jest",
23
+ "test:coverage": "jest --coverage",
24
+ "lint": "eslint src/",
25
+ "init": "node database/init.js",
26
+ "seed": "node database/seed.js",
27
+ "setup": "npm run init && npm run seed",
28
+ "download-wasm": "node scripts/download-tree-sitter.js"
29
+ },
30
+ "keywords": [
31
+ "ast",
32
+ "code-analysis",
33
+ "tree-sitter",
34
+ "rag",
35
+ "code-optimization",
36
+ "ai",
37
+ "agent",
38
+ "llm"
39
+ ],
40
+ "author": "",
41
+ "license": "MIT",
42
+ "dependencies": {
43
+ "better-sqlite3": "^9.2.2",
44
+ "cors": "^2.8.5",
45
+ "dotenv": "^16.3.1",
46
+ "express": "^4.18.2",
47
+ "express-rate-limit": "^7.1.5",
48
+ "helmet": "^7.1.0",
49
+ "mysql2": "^3.22.5",
50
+ "tree-sitter-c": "^0.21.0",
51
+ "tree-sitter-c-sharp": "^0.21.0",
52
+ "tree-sitter-cpp": "^0.21.0",
53
+ "tree-sitter-go": "^0.21.0",
54
+ "tree-sitter-java": "^0.21.0",
55
+ "tree-sitter-javascript": "^0.21.2",
56
+ "tree-sitter-php": "^0.21.0",
57
+ "tree-sitter-python": "^0.21.0",
58
+ "tree-sitter-ruby": "^0.21.0",
59
+ "tree-sitter-rust": "^0.21.0",
60
+ "tree-sitter-typescript": "^0.21.0",
61
+ "uuid": "^9.0.1",
62
+ "web-tree-sitter": "^0.20.8",
63
+ "winston": "^3.11.0"
64
+ },
65
+ "devDependencies": {
66
+ "eslint": "^8.56.0",
67
+ "jest": "^29.7.0",
68
+ "nodemon": "^3.0.2",
69
+ "supertest": "^6.3.4"
70
+ },
71
+ "engines": {
72
+ "node": ">=18.0.0"
73
+ }
74
+ }
@@ -0,0 +1,171 @@
1
+ /**
2
+ * Tree-sitter WASM下载脚本
3
+ * 下载常用语言的Tree-sitter解析器WASM文件
4
+ * 支持:JavaScript、TypeScript、Python、Java、Go、Rust、C、C++、C#、Ruby、PHP等
5
+ */
6
+
7
+ const fs = require('fs');
8
+ const path = require('path');
9
+ const https = require('https');
10
+ const http = require('http');
11
+
12
+ const WASM_DIR = path.join(__dirname, '..', 'wasm');
13
+
14
+ const languageUrls = {
15
+ javascript: 'https://github.com/tree-sitter/tree-sitter-javascript/releases/download/v0.21.2/tree-sitter-javascript.wasm',
16
+ typescript: 'https://github.com/tree-sitter/tree-sitter-typescript/releases/download/v0.21.0/tree-sitter-typescript.wasm',
17
+ python: 'https://github.com/tree-sitter/tree-sitter-python/releases/download/v0.21.0/tree-sitter-python.wasm',
18
+ java: 'https://github.com/tree-sitter/tree-sitter-java/releases/download/v0.21.0/tree-sitter-java.wasm',
19
+ go: 'https://github.com/tree-sitter/tree-sitter-go/releases/download/v0.21.0/tree-sitter-go.wasm',
20
+ rust: 'https://github.com/tree-sitter/tree-sitter-rust/releases/download/v0.21.0/tree-sitter-rust.wasm',
21
+ c: 'https://github.com/tree-sitter/tree-sitter-c/releases/download/v0.21.0/tree-sitter-c.wasm',
22
+ cpp: 'https://github.com/tree-sitter/tree-sitter-cpp/releases/download/v0.21.0/tree-sitter-cpp.wasm',
23
+ c_sharp: 'https://github.com/tree-sitter/tree-sitter-c-sharp/releases/download/v0.21.0/tree-sitter-c-sharp.wasm',
24
+ ruby: 'https://github.com/tree-sitter/tree-sitter-ruby/releases/download/v0.21.0/tree-sitter-ruby.wasm',
25
+ php: 'https://github.com/tree-sitter/tree-sitter-php/releases/download/v0.21.0/tree-sitter-php.wasm',
26
+ swift: 'https://github.com/tree-sitter/tree-sitter-swift/releases/download/v0.21.0/tree-sitter-swift.wasm',
27
+ kotlin: 'https://github.com/tree-sitter/tree-sitter-kotlin/releases/download/v0.21.0/tree-sitter-kotlin.wasm',
28
+ scala: 'https://github.com/tree-sitter/tree-sitter-scala/releases/download/v0.21.0/tree-sitter-scala.wasm'
29
+ };
30
+
31
+ const downloadFile = (url, dest) => {
32
+ return new Promise((resolve, reject) => {
33
+ const protocol = url.startsWith('https') ? https : http;
34
+ const file = fs.createWriteStream(dest);
35
+
36
+ protocol.get(url, (response) => {
37
+ if (response.statusCode === 301 || response.statusCode === 302) {
38
+ resolve(downloadFile(response.headers.location, dest));
39
+ return;
40
+ }
41
+
42
+ if (response.statusCode !== 200) {
43
+ reject(new Error(`下载失败: ${response.statusCode}`));
44
+ return;
45
+ }
46
+
47
+ response.pipe(file);
48
+
49
+ file.on('finish', () => {
50
+ file.close(() => resolve(dest));
51
+ });
52
+
53
+ file.on('error', (err) => {
54
+ fs.unlink(dest, () => reject(err));
55
+ });
56
+ }).on('error', (err) => {
57
+ reject(err);
58
+ });
59
+ });
60
+ };
61
+
62
+ const downloadWithRetry = async (url, dest, retries = 3) => {
63
+ for (let i = 0; i < retries; i++) {
64
+ try {
65
+ return await downloadFile(url, dest);
66
+ } catch (error) {
67
+ console.log(` 下载失败 (${i + 1}/${retries}): ${error.message}`);
68
+ if (i === retries - 1) throw error;
69
+ await new Promise(r => setTimeout(r, 2000));
70
+ }
71
+ }
72
+ };
73
+
74
+ const downloadAllWasm = async (languages) => {
75
+ if (!fs.existsSync(WASM_DIR)) {
76
+ fs.mkdirSync(WASM_DIR, { recursive: true });
77
+ }
78
+
79
+ const langs = languages || Object.keys(languageUrls);
80
+ let successCount = 0;
81
+ let failCount = 0;
82
+
83
+ for (const lang of langs) {
84
+ const url = languageUrls[lang];
85
+ if (!url) continue;
86
+
87
+ const dest = path.join(WASM_DIR, `tree-sitter-${lang}.wasm`);
88
+
89
+ if (fs.existsSync(dest)) {
90
+ const size = fs.statSync(dest).size;
91
+ if (size > 0) {
92
+ successCount++;
93
+ continue;
94
+ }
95
+ }
96
+
97
+ try {
98
+ await downloadWithRetry(url, dest);
99
+ successCount++;
100
+ } catch (error) {
101
+ failCount++;
102
+ }
103
+ }
104
+
105
+ return { success: successCount, fail: failCount };
106
+ };
107
+
108
+ const main = async () => {
109
+ console.log('=== Tree-sitter WASM下载脚本 ===\n');
110
+
111
+ if (!fs.existsSync(WASM_DIR)) {
112
+ fs.mkdirSync(WASM_DIR, { recursive: true });
113
+ console.log(`创建目录: ${WASM_DIR}\n`);
114
+ }
115
+
116
+ const args = process.argv.slice(2);
117
+ const languages = args.length > 0 ? args : Object.keys(languageUrls);
118
+
119
+ console.log(`准备下载 ${languages.length} 个语言解析器...\n`);
120
+
121
+ let successCount = 0;
122
+ let failCount = 0;
123
+
124
+ for (const lang of languages) {
125
+ const url = languageUrls[lang];
126
+ if (!url) {
127
+ console.log(` ⚠️ 不支持的语言: ${lang}`);
128
+ continue;
129
+ }
130
+
131
+ const dest = path.join(WASM_DIR, `tree-sitter-${lang}.wasm`);
132
+
133
+ if (fs.existsSync(dest)) {
134
+ const size = fs.statSync(dest).size;
135
+ if (size > 0) {
136
+ console.log(` ✅ ${lang} (已存在, ${(size / 1024).toFixed(1)} KB)`);
137
+ successCount++;
138
+ continue;
139
+ }
140
+ }
141
+
142
+ try {
143
+ process.stdout.write(` 📥 ${lang}... `);
144
+ await downloadWithRetry(url, dest);
145
+ const size = fs.statSync(dest).size;
146
+ console.log(`完成 (${(size / 1024).toFixed(1)} KB)`);
147
+ successCount++;
148
+ } catch (error) {
149
+ console.log(`失败: ${error.message}`);
150
+ failCount++;
151
+ }
152
+ }
153
+
154
+ console.log('\n=== 下载完成 ===');
155
+ console.log(`成功: ${successCount}, 失败: ${failCount}`);
156
+
157
+ const existingFiles = fs.readdirSync(WASM_DIR).filter(f => f.endsWith('.wasm'));
158
+ if (existingFiles.length > 0) {
159
+ console.log(`\n已安装的语言解析器:`);
160
+ existingFiles.forEach(f => {
161
+ const size = fs.statSync(path.join(WASM_DIR, f)).size;
162
+ console.log(` - ${f} (${(size / 1024).toFixed(1)} KB)`);
163
+ });
164
+ }
165
+ };
166
+
167
+ if (require.main === module) {
168
+ main().catch(console.error);
169
+ }
170
+
171
+ module.exports = { downloadAllWasm, downloadFile, downloadWithRetry, languageUrls, WASM_DIR };