@aggroot-team/aggroot 1.7.2
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/.env.example +106 -0
- package/LICENSE +21 -0
- package/README.md +496 -0
- package/dist/cli.cjs +26 -0
- package/dist/index.cjs +45238 -0
- package/dist/native/better_sqlite3.node +0 -0
- package/dist/tree-sitter-c.wasm +0 -0
- package/dist/tree-sitter-cpp.wasm +0 -0
- package/dist/web-tree-sitter.wasm +0 -0
- package/install-linux.sh +123 -0
- package/install-windows.bat +121 -0
- package/package.json +155 -0
- package/scripts/build-all.mjs +42 -0
- package/scripts/fix-workspace-deps.mjs +41 -0
- package/scripts/generate-example-skills.ts +238 -0
- package/scripts/import-meta-shim.js +3 -0
- package/scripts/install-browsers.mjs +132 -0
- package/scripts/obfuscate.mjs +215 -0
- package/scripts/test-bash-parser.ts +37 -0
- package/scripts/test-parser2.ts +24 -0
- package/scripts/test-parser3.js +4 -0
- package/scripts/test-tokenizer.js +148 -0
- package/scripts/verify-no-source.mjs +83 -0
- package/share/agents/agent-coder.json +256 -0
- package/share/agents/agent-devops.json +61 -0
- package/share/agents/agent-writer.json +213 -0
- package/share/scripts/_utils.py +105 -0
- package/share/scripts/market_overview.py +244 -0
- package/share/scripts/stock_compare.py +244 -0
- package/share/scripts/stock_financial.py +211 -0
- package/share/scripts/stock_news.py +181 -0
- package/share/scripts/stock_quote.py +433 -0
- package/share/scripts/stock_technical.py +402 -0
- package/share/web/dist/assets/arc-DmQ0lviI.js +1 -0
- package/share/web/dist/assets/architectureDiagram-3BPJPVTR-KELFQLRt.js +36 -0
- package/share/web/dist/assets/blockDiagram-GPEHLZMM-D_2waPJB.js +132 -0
- package/share/web/dist/assets/c4Diagram-AAUBKEIU-BxaYriMP.js +10 -0
- package/share/web/dist/assets/channel-DppiAmuZ.js +1 -0
- package/share/web/dist/assets/chunk-2J33WTMH-D_wcMArK.js +1 -0
- package/share/web/dist/assets/chunk-4BX2VUAB-C4Mm8aE8.js +1 -0
- package/share/web/dist/assets/chunk-55IACEB6-Djo1jBPb.js +1 -0
- package/share/web/dist/assets/chunk-727SXJPM-D-9kzLrQ.js +206 -0
- package/share/web/dist/assets/chunk-AQP2D5EJ-BV58AHlG.js +231 -0
- package/share/web/dist/assets/chunk-FMBD7UC4-D3AHszVJ.js +15 -0
- package/share/web/dist/assets/chunk-ND2GUHAM-CCuQ63c7.js +1 -0
- package/share/web/dist/assets/chunk-QZHKN3VN-Dl9PXzs1.js +1 -0
- package/share/web/dist/assets/classDiagram-4FO5ZUOK-BnkzOZLJ.js +1 -0
- package/share/web/dist/assets/classDiagram-v2-Q7XG4LA2-BnkzOZLJ.js +1 -0
- package/share/web/dist/assets/cose-bilkent-S5V4N54A-BuRspznV.js +1 -0
- package/share/web/dist/assets/cytoscape.esm-CkSuTymj.js +321 -0
- package/share/web/dist/assets/dagre-BM42HDAG-D4fhww9y.js +4 -0
- package/share/web/dist/assets/defaultLocale-CrowFXzY.js +1 -0
- package/share/web/dist/assets/diagram-2AECGRRQ-dMi9m1J4.js +43 -0
- package/share/web/dist/assets/diagram-5GNKFQAL-CjRIIBA8.js +10 -0
- package/share/web/dist/assets/diagram-KO2AKTUF-gR1tXbmQ.js +3 -0
- package/share/web/dist/assets/diagram-LMA3HP47-CQkJCjQX.js +24 -0
- package/share/web/dist/assets/diagram-OG6HWLK6-DBEvIvu5.js +24 -0
- package/share/web/dist/assets/erDiagram-TEJ5UH35-ChwS3Oys.js +85 -0
- package/share/web/dist/assets/flowDiagram-I6XJVG4X-yYT7qyQr.js +162 -0
- package/share/web/dist/assets/ganttDiagram-6RSMTGT7-9TgEI2XC.js +292 -0
- package/share/web/dist/assets/gitGraphDiagram-PVQCEYII-PVcWiaLN.js +106 -0
- package/share/web/dist/assets/graph-D2o_JWn5.js +1 -0
- package/share/web/dist/assets/index-BWshepxw.js +407 -0
- package/share/web/dist/assets/index-aC35iubs.css +1 -0
- package/share/web/dist/assets/infoDiagram-5YYISTIA-C5_cYCEE.js +2 -0
- package/share/web/dist/assets/init-Gi6I4Gst.js +1 -0
- package/share/web/dist/assets/ishikawaDiagram-YF4QCWOH-Day2c-1K.js +70 -0
- package/share/web/dist/assets/journeyDiagram-JHISSGLW-BZdHoYic.js +139 -0
- package/share/web/dist/assets/kanban-definition-UN3LZRKU-DoEoEViV.js +89 -0
- package/share/web/dist/assets/katex-HP8lGamR.js +257 -0
- package/share/web/dist/assets/layout-UJbxhwF7.js +1 -0
- package/share/web/dist/assets/linear-z8LqGvHi.js +1 -0
- package/share/web/dist/assets/mindmap-definition-RKZ34NQL-DshJGF7V.js +96 -0
- package/share/web/dist/assets/ordinal-Cboi1Yqb.js +1 -0
- package/share/web/dist/assets/pieDiagram-4H26LBE5-MKBdIMr0.js +30 -0
- package/share/web/dist/assets/quadrantDiagram-W4KKPZXB-Rc8VkAHd.js +7 -0
- package/share/web/dist/assets/requirementDiagram-4Y6WPE33-DIvF0TEG.js +84 -0
- package/share/web/dist/assets/sankeyDiagram-5OEKKPKP-FTZOsw4G.js +40 -0
- package/share/web/dist/assets/sequenceDiagram-3UESZ5HK-BXnFdz67.js +162 -0
- package/share/web/dist/assets/stateDiagram-AJRCARHV-N_DEea7F.js +1 -0
- package/share/web/dist/assets/stateDiagram-v2-BHNVJYJU-9RyR03R8.js +1 -0
- package/share/web/dist/assets/timeline-definition-PNZ67QCA-nelw3asj.js +120 -0
- package/share/web/dist/assets/vennDiagram-CIIHVFJN-BkfB2ea3.js +34 -0
- package/share/web/dist/assets/wardley-L42UT6IY-ChtTMLrY.js +161 -0
- package/share/web/dist/assets/wardleyDiagram-YWT4CUSO-CEsObJvL.js +78 -0
- package/share/web/dist/assets/xychartDiagram-2RQKCTM6-DNog7YuY.js +7 -0
- package/share/web/dist/index.html +13 -0
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 生成示例 Skills 到桌面(带分类目录 + DESCRIPTION.md + 索引缓存)
|
|
3
|
+
*
|
|
4
|
+
* 运行: npx tsx scripts/generate-example-skills.ts
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as fs from 'node:fs';
|
|
8
|
+
import * as path from 'node:path';
|
|
9
|
+
import * as os from 'node:os';
|
|
10
|
+
import { createSkillManageTool } from '../src/domain/skill/skill-manager-tool.js';
|
|
11
|
+
import { createSkillRegistry, loadSkillsWithCache, invalidateIndexCache } from '../src/domain/skill/skill-loader.js';
|
|
12
|
+
|
|
13
|
+
const outputDir = path.join(os.homedir(), 'Desktop', 'generated-skills');
|
|
14
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
15
|
+
process.env.AGGROOT_HOME = outputDir;
|
|
16
|
+
|
|
17
|
+
const registry = createSkillRegistry();
|
|
18
|
+
const tool = createSkillManageTool(registry);
|
|
19
|
+
|
|
20
|
+
const skills: Array<{ name: string; category?: string; content: string }> = [
|
|
21
|
+
// ---- devops 分类 ----
|
|
22
|
+
{
|
|
23
|
+
name: 'deploy-to-staging',
|
|
24
|
+
category: 'devops',
|
|
25
|
+
content: `---
|
|
26
|
+
name: deploy-to-staging
|
|
27
|
+
description: Deploy the project to staging environment using docker
|
|
28
|
+
whenToUse: When you need to deploy the current project to the staging server
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
# Deploy to Staging
|
|
32
|
+
|
|
33
|
+
1. Run tests: \`npm test\`
|
|
34
|
+
2. Build Docker image: \`docker build -t myapp:staging .\`
|
|
35
|
+
3. Push to registry: \`docker push registry.example.com/myapp:staging\`
|
|
36
|
+
4. SSH to staging server: \`ssh deploy@staging.example.com\`
|
|
37
|
+
5. Pull and restart: \`docker-compose pull && docker-compose up -d\`
|
|
38
|
+
6. Verify: \`curl -f https://staging.example.com/health\`
|
|
39
|
+
|
|
40
|
+
## Pitfalls
|
|
41
|
+
- Always run tests before deploying
|
|
42
|
+
- Check that .env.staging has the correct DATABASE_URL
|
|
43
|
+
- If health check fails, rollback with \`docker-compose down && docker-compose up -d --no-pull\`
|
|
44
|
+
`,
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: 'rollback',
|
|
48
|
+
category: 'devops',
|
|
49
|
+
content: `---
|
|
50
|
+
name: rollback
|
|
51
|
+
description: Rollback a deployment to the previous version
|
|
52
|
+
whenToUse: When a deployment fails or causes issues in production
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
# Rollback Deployment
|
|
56
|
+
|
|
57
|
+
1. Check current version: \`docker-compose ps\`
|
|
58
|
+
2. Rollback to previous image: \`docker-compose down && docker-compose up -d --no-pull\`
|
|
59
|
+
3. Verify health: \`curl -f https://staging.example.com/health\`
|
|
60
|
+
4. Notify team in Slack
|
|
61
|
+
|
|
62
|
+
## Pitfalls
|
|
63
|
+
- Always verify the previous image is still available
|
|
64
|
+
- Don't rollback if database migration already ran
|
|
65
|
+
`,
|
|
66
|
+
},
|
|
67
|
+
// ---- debugging 分类 ----
|
|
68
|
+
{
|
|
69
|
+
name: 'debug-auth-401',
|
|
70
|
+
category: 'debugging',
|
|
71
|
+
content: `---
|
|
72
|
+
name: debug-auth-401
|
|
73
|
+
description: Debug and fix 401 authentication errors in test suites
|
|
74
|
+
whenToUse: When auth tests fail with 401 status code
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
# Debug Auth 401 Errors
|
|
78
|
+
|
|
79
|
+
1. Run the failing test with verbose output to identify which test and assertion failed
|
|
80
|
+
2. Read the auth middleware source code to understand the token validation logic
|
|
81
|
+
3. Check if the middleware correctly handles token formats (e.g., Bearer prefix stripping)
|
|
82
|
+
4. Fix the middleware to properly extract the token
|
|
83
|
+
5. Re-run tests to verify the fix
|
|
84
|
+
|
|
85
|
+
## Common Causes
|
|
86
|
+
- Middleware not stripping "Bearer " prefix from Authorization header
|
|
87
|
+
- Missing or incorrect token extraction logic
|
|
88
|
+
- Token not being passed in test fixtures
|
|
89
|
+
`,
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
name: 'debug-memory-leak',
|
|
93
|
+
category: 'debugging',
|
|
94
|
+
content: `---
|
|
95
|
+
name: debug-memory-leak
|
|
96
|
+
description: Investigate and fix memory leaks in Node.js applications
|
|
97
|
+
whenToUse: When the application shows increasing memory usage over time
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
# Debug Memory Leak
|
|
101
|
+
|
|
102
|
+
1. Take heap snapshot: \`node --inspect app.js\` then Chrome DevTools → Memory → Take Snapshot
|
|
103
|
+
2. Compare snapshots over time to find growing objects
|
|
104
|
+
3. Check for common patterns: event listeners not removed, closures holding references, global caches
|
|
105
|
+
4. Fix the leak and verify with load testing
|
|
106
|
+
|
|
107
|
+
## Common Causes
|
|
108
|
+
- Event listeners not removed on cleanup
|
|
109
|
+
- Closures capturing large objects
|
|
110
|
+
- Unbounded caches without TTL
|
|
111
|
+
`,
|
|
112
|
+
},
|
|
113
|
+
// ---- git 分类 ----
|
|
114
|
+
{
|
|
115
|
+
name: 'smart-commit',
|
|
116
|
+
category: 'git',
|
|
117
|
+
content: `---
|
|
118
|
+
name: smart-commit
|
|
119
|
+
description: Create a well-structured git commit with proper message
|
|
120
|
+
whenToUse: When you need to commit changes to git
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
# Smart Commit
|
|
124
|
+
|
|
125
|
+
1. Run \`git status\` to see all changes
|
|
126
|
+
2. Run \`git diff\` to understand what changed
|
|
127
|
+
3. Stage appropriate files with \`git add\`
|
|
128
|
+
4. Create commit with conventional commit format:
|
|
129
|
+
- feat: new feature
|
|
130
|
+
- fix: bug fix
|
|
131
|
+
- refactor: code refactoring
|
|
132
|
+
|
|
133
|
+
## Pitfalls
|
|
134
|
+
- Always review the diff before committing
|
|
135
|
+
- Never commit .env files or secrets
|
|
136
|
+
`,
|
|
137
|
+
},
|
|
138
|
+
// ---- 根级 skill(无分类)----
|
|
139
|
+
{
|
|
140
|
+
name: 'explain',
|
|
141
|
+
content: `---
|
|
142
|
+
name: explain
|
|
143
|
+
description: Explain code or concept in detail
|
|
144
|
+
whenToUse: When you need to understand how something works
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
# Explain
|
|
148
|
+
|
|
149
|
+
1. Read the relevant source files
|
|
150
|
+
2. Trace the execution flow
|
|
151
|
+
3. Summarize the key concepts
|
|
152
|
+
4. Provide examples if helpful
|
|
153
|
+
|
|
154
|
+
Provide explanation at the requested detail level.
|
|
155
|
+
`,
|
|
156
|
+
},
|
|
157
|
+
];
|
|
158
|
+
|
|
159
|
+
console.log('Generating skills with category structure...\n');
|
|
160
|
+
|
|
161
|
+
for (const skill of skills) {
|
|
162
|
+
const result = await tool.execute({
|
|
163
|
+
action: 'create',
|
|
164
|
+
name: skill.name,
|
|
165
|
+
content: skill.content,
|
|
166
|
+
category: skill.category,
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
if (result.success) {
|
|
170
|
+
console.log(` ✓ ${skill.name}${skill.category ? ` (category: ${skill.category})` : ''}`);
|
|
171
|
+
} else {
|
|
172
|
+
console.log(` ✗ ${skill.name}: ${result.stderr}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// 手动写 DESCRIPTION.md 文件到分类目录
|
|
177
|
+
const skillsDir = path.join(outputDir, 'skills');
|
|
178
|
+
const descriptions: Record<string, string> = {
|
|
179
|
+
devops: 'DevOps tools — deployment, rollback, and infrastructure management',
|
|
180
|
+
debugging: 'Debugging workflows — systematic approaches to finding and fixing bugs',
|
|
181
|
+
git: 'Git workflows — commit, branch, and version control best practices',
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
for (const [cat, desc] of Object.entries(descriptions)) {
|
|
185
|
+
const descPath = path.join(skillsDir, cat, 'DESCRIPTION.md');
|
|
186
|
+
if (fs.existsSync(path.join(skillsDir, cat))) {
|
|
187
|
+
fs.writeFileSync(descPath, `---\ndescription: ${desc}\n---\n`, 'utf-8');
|
|
188
|
+
console.log(` ✓ ${cat}/DESCRIPTION.md`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// 删除旧缓存并重新加载以生成 .index.json
|
|
193
|
+
await invalidateIndexCache(skillsDir);
|
|
194
|
+
|
|
195
|
+
console.log('\nLoading skills with cache...');
|
|
196
|
+
const { skills: loadedSkills, categories } = await loadSkillsWithCache(skillsDir, 'user');
|
|
197
|
+
|
|
198
|
+
console.log(`\nLoaded ${loadedSkills.length} skills, ${categories.size} categories:`);
|
|
199
|
+
for (const [cat, desc] of categories) {
|
|
200
|
+
console.log(` ${cat}: ${desc}`);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// 验证 .index.json
|
|
204
|
+
const indexPath = path.join(skillsDir, '.index.json');
|
|
205
|
+
if (fs.existsSync(indexPath)) {
|
|
206
|
+
const cache = JSON.parse(fs.readFileSync(indexPath, 'utf-8'));
|
|
207
|
+
console.log(`\n.index.json created:`);
|
|
208
|
+
console.log(` version: ${cache.version}`);
|
|
209
|
+
console.log(` skills: ${cache.skills.length}`);
|
|
210
|
+
console.log(` categories: ${Object.keys(cache.categories).join(', ')}`);
|
|
211
|
+
console.log(` manifest entries: ${Object.keys(cache.manifest).length}`);
|
|
212
|
+
for (const entry of cache.skills) {
|
|
213
|
+
console.log(` - ${entry.name} (category: ${entry.category || 'none'}, path: ${entry.filePath})`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// 列出生成的文件结构
|
|
218
|
+
function listFiles(dir: string, prefix = ''): void {
|
|
219
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
220
|
+
if (entry.name.startsWith('.') && entry.isFile()) {
|
|
221
|
+
// Show dot files (like .index.json) but with indicator
|
|
222
|
+
console.log(`${prefix}${entry.name} (cache)`);
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
if (entry.name.startsWith('.')) continue;
|
|
226
|
+
if (entry.isDirectory()) {
|
|
227
|
+
console.log(`${prefix}${entry.name}/`);
|
|
228
|
+
listFiles(path.join(dir, entry.name), `${prefix} `);
|
|
229
|
+
} else {
|
|
230
|
+
console.log(`${prefix}${entry.name}`);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
console.log('\nGenerated file structure:');
|
|
236
|
+
listFiles(skillsDir);
|
|
237
|
+
|
|
238
|
+
console.log(`\nOutput directory: ${outputDir}`);
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { spawn } from "child_process";
|
|
4
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { homedir } from "node:os";
|
|
7
|
+
|
|
8
|
+
const MIRRORS = [
|
|
9
|
+
{ host: "https://npmmirror.com/mirrors/playwright", name: "淘宝镜像" },
|
|
10
|
+
{ host: "", name: "官方源" },
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
// npm postinstall 会吞掉 stdout,用 stderr 输出状态让用户看到
|
|
14
|
+
function status(msg) {
|
|
15
|
+
process.stderr.write(`\n[browsers] ${msg}\n`);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 获取当前 playwright 包需要的 chromium 版本号
|
|
20
|
+
* 从 playwright-core 的 registry 中读取
|
|
21
|
+
*/
|
|
22
|
+
function getRequiredChromiumVersion() {
|
|
23
|
+
try {
|
|
24
|
+
// 尝试从 playwright-core 的 registry 文件读取
|
|
25
|
+
const registryPaths = [
|
|
26
|
+
join(import.meta.url.replace('file:///', '').replace(/\/scripts\/.*/, ''), 'node_modules', 'playwright-core', 'browsers.json'),
|
|
27
|
+
join(process.cwd(), 'node_modules', 'playwright-core', 'browsers.json'),
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
// 也检查全局安装路径
|
|
31
|
+
const globalPaths = [
|
|
32
|
+
join(homedir(), 'AppData', 'Roaming', 'npm', 'node_modules', 'aggroot', 'node_modules', 'playwright-core', 'browsers.json'),
|
|
33
|
+
join('/usr', 'local', 'lib', 'node_modules', 'aggroot', 'node_modules', 'playwright-core', 'browsers.json'),
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
for (const p of [...registryPaths, ...globalPaths]) {
|
|
37
|
+
const filePath = p.startsWith('/') ? p : p.replace(/\//g, '\\');
|
|
38
|
+
if (existsSync(filePath)) {
|
|
39
|
+
const data = JSON.parse(readFileSync(filePath, 'utf-8'));
|
|
40
|
+
const chromium = data.browsers?.find(b => b.name === 'chromium');
|
|
41
|
+
if (chromium?.revision) return chromium.revision;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
} catch { /* fallback to npx */ }
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 检查 Playwright Chromium 浏览器是否已安装且版本匹配
|
|
50
|
+
*/
|
|
51
|
+
function isChromiumInstalled() {
|
|
52
|
+
const playwrightDir = process.platform === 'win32'
|
|
53
|
+
? join(homedir(), 'AppData', 'Local', 'ms-playwright')
|
|
54
|
+
: join(homedir(), '.cache', 'ms-playwright');
|
|
55
|
+
|
|
56
|
+
if (!existsSync(playwrightDir)) return false;
|
|
57
|
+
|
|
58
|
+
// 查找已安装的 chromium 目录
|
|
59
|
+
const dirs = readdirSync(playwrightDir, { withFileTypes: true })
|
|
60
|
+
.filter(d => d.isDirectory() && d.name.startsWith('chromium-'))
|
|
61
|
+
.map(d => d.name);
|
|
62
|
+
|
|
63
|
+
if (dirs.length === 0) return false;
|
|
64
|
+
|
|
65
|
+
// 检查是否有可执行文件
|
|
66
|
+
const latestDir = dirs.sort().pop();
|
|
67
|
+
const exeName = process.platform === 'win32' ? 'chrome.exe' : 'chrome';
|
|
68
|
+
const possiblePaths = [
|
|
69
|
+
join(playwrightDir, latestDir, 'chrome-win64', exeName),
|
|
70
|
+
join(playwrightDir, latestDir, 'chrome-linux', exeName),
|
|
71
|
+
join(playwrightDir, latestDir, 'chrome-mac', 'Chromium.app', 'Contents', 'MacOS', 'Chromium'),
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
return possiblePaths.some(p => existsSync(p));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function tryInstall(mirror) {
|
|
78
|
+
return new Promise((resolve) => {
|
|
79
|
+
const env = { ...process.env };
|
|
80
|
+
if (mirror.host) {
|
|
81
|
+
env.PLAYWRIGHT_DOWNLOAD_HOST = mirror.host;
|
|
82
|
+
} else {
|
|
83
|
+
delete env.PLAYWRIGHT_DOWNLOAD_HOST;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
status(`正在下载 Playwright Chromium (${mirror.name})...`);
|
|
87
|
+
|
|
88
|
+
const child = spawn("npx", ["playwright", "install", "chromium"], {
|
|
89
|
+
stdio: "inherit",
|
|
90
|
+
shell: true,
|
|
91
|
+
env,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
let timedOut = false;
|
|
95
|
+
const timer = setTimeout(() => {
|
|
96
|
+
timedOut = true;
|
|
97
|
+
child.kill();
|
|
98
|
+
}, 120000);
|
|
99
|
+
|
|
100
|
+
child.on("close", (code) => {
|
|
101
|
+
clearTimeout(timer);
|
|
102
|
+
resolve(timedOut ? "timeout" : code);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
child.on("error", () => {
|
|
106
|
+
clearTimeout(timer);
|
|
107
|
+
resolve("error");
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
(async () => {
|
|
113
|
+
// 先检查是否已安装
|
|
114
|
+
if (isChromiumInstalled()) {
|
|
115
|
+
status("Playwright Chromium 已安装,跳过下载。");
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
status("Playwright Chromium 未安装,开始下载...");
|
|
120
|
+
|
|
121
|
+
for (const mirror of MIRRORS) {
|
|
122
|
+
const result = await tryInstall(mirror);
|
|
123
|
+
if (result === 0) {
|
|
124
|
+
status("浏览器安装完成。");
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
if (mirror === MIRRORS[0]) {
|
|
128
|
+
status(`${mirror.name}下载失败,尝试${MIRRORS[1].name}...`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
status("浏览器安装失败,将在首次使用时自动下载。");
|
|
132
|
+
})();
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Bundle dist/ into a single file using esbuild.
|
|
5
|
+
*
|
|
6
|
+
* Strategy:
|
|
7
|
+
* 1. tsc compiles TS → JS (no source maps, no declarations)
|
|
8
|
+
* 2. esbuild bundles + minifies all JS into dist/index.cjs
|
|
9
|
+
* - All variable names shortened
|
|
10
|
+
* - All whitespace removed
|
|
11
|
+
* - Dead code eliminated
|
|
12
|
+
* - No directory structure visible
|
|
13
|
+
* - No file names visible
|
|
14
|
+
*
|
|
15
|
+
* esbuild minification alone provides strong protection:
|
|
16
|
+
* - Variables become single-letter names (a, b, c...)
|
|
17
|
+
* - All comments and formatting stripped
|
|
18
|
+
* - Original module structure completely flattened
|
|
19
|
+
* - No source maps, no type declarations
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import * as esbuild from 'esbuild';
|
|
23
|
+
import { readFileSync, writeFileSync, readdirSync, rmSync, statSync, copyFileSync, mkdirSync, existsSync } from 'node:fs';
|
|
24
|
+
import { join, dirname } from 'node:path';
|
|
25
|
+
|
|
26
|
+
const DIST_DIR = join(process.cwd(), 'dist');
|
|
27
|
+
const ENTRY = join(DIST_DIR, 'index.js');
|
|
28
|
+
const BUNDLE_OUTPUT = join(DIST_DIR, 'index.bundled.js');
|
|
29
|
+
|
|
30
|
+
// Step 1: Bundle with esbuild
|
|
31
|
+
console.log('[bundle] Bundling with esbuild...');
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const result = await esbuild.build({
|
|
35
|
+
entryPoints: [ENTRY],
|
|
36
|
+
bundle: true,
|
|
37
|
+
platform: 'node',
|
|
38
|
+
target: 'node20',
|
|
39
|
+
format: 'cjs',
|
|
40
|
+
outfile: BUNDLE_OUTPUT,
|
|
41
|
+
// Replace import.meta.url with CJS equivalent
|
|
42
|
+
define: {
|
|
43
|
+
'import.meta.url': '__dirname_url__',
|
|
44
|
+
},
|
|
45
|
+
// Inject __dirname_url__ variable
|
|
46
|
+
inject: [join(process.cwd(), 'scripts', 'import-meta-shim.js')],
|
|
47
|
+
external: [
|
|
48
|
+
'sharp',
|
|
49
|
+
'playwright',
|
|
50
|
+
'@xenova/transformers',
|
|
51
|
+
'tiktoken',
|
|
52
|
+
'yoga-layout',
|
|
53
|
+
// Heavy dependencies externalized for faster startup
|
|
54
|
+
'pino-pretty',
|
|
55
|
+
'docx',
|
|
56
|
+
'exceljs',
|
|
57
|
+
'mammoth',
|
|
58
|
+
'pptxgenjs',
|
|
59
|
+
'openai',
|
|
60
|
+
// bindings is only used by better-sqlite3's fallback path (when nativeBinding is not provided)
|
|
61
|
+
// We always provide nativeBinding, so bindings is never actually called at runtime
|
|
62
|
+
'bindings',
|
|
63
|
+
],
|
|
64
|
+
minify: true,
|
|
65
|
+
legalComments: 'none',
|
|
66
|
+
treeShaking: true,
|
|
67
|
+
metafile: true,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
if (result.metafile) {
|
|
71
|
+
console.log(`[bundle] Input: ${Object.keys(result.metafile.inputs).length} modules`);
|
|
72
|
+
}
|
|
73
|
+
const outputSize = statSync(BUNDLE_OUTPUT).size;
|
|
74
|
+
console.log(`[bundle] Output: ${(outputSize / 1024).toFixed(0)} KB`);
|
|
75
|
+
} catch (error) {
|
|
76
|
+
console.error('[bundle] Failed:', error.message);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Step 1.5: Copy WASM files to dist/ (esbuild cannot bundle binary .wasm files)
|
|
81
|
+
const WASM_FILES = [
|
|
82
|
+
{ src: 'web-tree-sitter/web-tree-sitter.wasm', dest: 'web-tree-sitter.wasm' },
|
|
83
|
+
{ src: 'tree-sitter-cpp/tree-sitter-cpp.wasm', dest: 'tree-sitter-cpp.wasm' },
|
|
84
|
+
{ src: 'tree-sitter-c/tree-sitter-c.wasm', dest: 'tree-sitter-c.wasm' },
|
|
85
|
+
];
|
|
86
|
+
|
|
87
|
+
for (const { src, dest } of WASM_FILES) {
|
|
88
|
+
const srcPath = join(process.cwd(), 'node_modules', src);
|
|
89
|
+
const destPath = join(DIST_DIR, dest);
|
|
90
|
+
if (existsSync(srcPath)) {
|
|
91
|
+
copyFileSync(srcPath, destPath);
|
|
92
|
+
console.log(`[bundle] Copied WASM: ${dest}`);
|
|
93
|
+
} else {
|
|
94
|
+
console.warn(`[bundle] WASM not found: ${src}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Step 1.6: Copy pre-compiled native modules to dist/native/
|
|
99
|
+
// This avoids requiring users to have C++ build tools installed
|
|
100
|
+
const NATIVE_DIR = join(DIST_DIR, 'native');
|
|
101
|
+
const NATIVE_MODULES = [
|
|
102
|
+
{ src: 'better-sqlite3/build/Release/better_sqlite3.node', dest: 'better_sqlite3.node' },
|
|
103
|
+
];
|
|
104
|
+
|
|
105
|
+
if (!existsSync(NATIVE_DIR)) {
|
|
106
|
+
mkdirSync(NATIVE_DIR, { recursive: true });
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
for (const { src, dest } of NATIVE_MODULES) {
|
|
110
|
+
const srcPath = join(process.cwd(), 'node_modules', src);
|
|
111
|
+
const destPath = join(NATIVE_DIR, dest);
|
|
112
|
+
if (existsSync(srcPath)) {
|
|
113
|
+
try {
|
|
114
|
+
copyFileSync(srcPath, destPath);
|
|
115
|
+
console.log(`[bundle] Copied native: ${dest}`);
|
|
116
|
+
} catch (err) {
|
|
117
|
+
console.warn(`[bundle] Failed to copy native module ${dest}: ${err.code} — using existing file if present`);
|
|
118
|
+
}
|
|
119
|
+
} else {
|
|
120
|
+
// pnpm hoists native binaries into .pnpm store; the top-level node_modules
|
|
121
|
+
// symlink may have an empty build/ directory. Search the store directly.
|
|
122
|
+
let found = false;
|
|
123
|
+
const pnpmDir = join(process.cwd(), 'node_modules', '.pnpm');
|
|
124
|
+
if (existsSync(pnpmDir)) {
|
|
125
|
+
const entries = readdirSync(pnpmDir).filter(d => d.startsWith('better-sqlite3'));
|
|
126
|
+
for (const ver of entries) {
|
|
127
|
+
const candidate = join(pnpmDir, ver, 'node_modules', 'better-sqlite3', 'build', 'Release', 'better_sqlite3.node');
|
|
128
|
+
if (existsSync(candidate)) {
|
|
129
|
+
try {
|
|
130
|
+
copyFileSync(candidate, destPath);
|
|
131
|
+
console.log(`[bundle] Copied native: ${dest} (from pnpm store: ${ver})`);
|
|
132
|
+
found = true;
|
|
133
|
+
break;
|
|
134
|
+
} catch (err) {
|
|
135
|
+
console.warn(`[bundle] Failed to copy native module ${dest}: ${err.code}`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (!found) {
|
|
141
|
+
console.warn(`[bundle] Native module not found: ${src} — users will need build tools`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Step 2: Remove original directory structure
|
|
147
|
+
console.log('[bundle] Cleaning up original files...');
|
|
148
|
+
|
|
149
|
+
const filesToKeep = new Set(['index.bundled.js', 'web-tree-sitter.wasm', 'tree-sitter-cpp.wasm', 'tree-sitter-c.wasm', 'cli.cjs']);
|
|
150
|
+
const dirsToKeep = new Set(['native']);
|
|
151
|
+
|
|
152
|
+
function cleanDir(dir) {
|
|
153
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
154
|
+
for (const entry of entries) {
|
|
155
|
+
const fullPath = join(dir, entry.name);
|
|
156
|
+
if (entry.isDirectory()) {
|
|
157
|
+
if (!dirsToKeep.has(entry.name)) {
|
|
158
|
+
rmSync(fullPath, { recursive: true, force: true });
|
|
159
|
+
}
|
|
160
|
+
} else if (!filesToKeep.has(entry.name)) {
|
|
161
|
+
rmSync(fullPath, { force: true });
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
cleanDir(DIST_DIR);
|
|
166
|
+
|
|
167
|
+
// Step 3: Fix shebang and write final file
|
|
168
|
+
// Remove shebang from bundle body (it gets displaced by processing)
|
|
169
|
+
let bundleContent = readFileSync(BUNDLE_OUTPUT, 'utf-8');
|
|
170
|
+
if (bundleContent.startsWith('#!')) {
|
|
171
|
+
bundleContent = bundleContent.substring(bundleContent.indexOf('\n') + 1);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const finalPath = join(DIST_DIR, 'index.cjs');
|
|
175
|
+
writeFileSync(finalPath, '#!/usr/bin/env node\n' + bundleContent, 'utf-8');
|
|
176
|
+
rmSync(BUNDLE_OUTPUT, { force: true });
|
|
177
|
+
|
|
178
|
+
const finalSize = statSync(finalPath).size;
|
|
179
|
+
console.log(`[bundle] Done: dist/index.cjs (${(finalSize / 1024).toFixed(0)} KB)`);
|
|
180
|
+
console.log('[bundle] Complete — single minified file, no source exposure');
|
|
181
|
+
|
|
182
|
+
// Step 4: Generate CLI wrapper that sets --max-old-space-size=4096
|
|
183
|
+
// The shebang #!/usr/bin/env node cannot pass flags, so we need a wrapper
|
|
184
|
+
// that re-spawns node with the larger heap limit when needed.
|
|
185
|
+
// Use .cjs extension so Node treats it as CommonJS regardless of package.json "type"
|
|
186
|
+
const cliWrapperPath = join(DIST_DIR, 'cli.cjs');
|
|
187
|
+
const cliWrapperContent = `#!/usr/bin/env node
|
|
188
|
+
// AggRoot CLI wrapper - ensures Node.js heap is large enough for long sessions.
|
|
189
|
+
const v8 = require('node:v8');
|
|
190
|
+
const path = require('node:path');
|
|
191
|
+
const { spawn } = require('node:child_process');
|
|
192
|
+
const heapLimitMB = Math.round(v8.getHeapStatistics().heap_size_limit / 1024 / 1024);
|
|
193
|
+
|
|
194
|
+
if (heapLimitMB < 3500 && !process.env.AGGROOT_SPAWNED) {
|
|
195
|
+
// Re-spawn with larger heap, inheriting stdio for seamless UX
|
|
196
|
+
const child = spawn(process.execPath, [
|
|
197
|
+
'--max-old-space-size=4096',
|
|
198
|
+
'--no-warnings',
|
|
199
|
+
path.resolve(__dirname, 'index.cjs'),
|
|
200
|
+
...process.argv.slice(2),
|
|
201
|
+
], {
|
|
202
|
+
stdio: 'inherit',
|
|
203
|
+
env: { ...process.env, AGGROOT_SPAWNED: '1' },
|
|
204
|
+
});
|
|
205
|
+
child.on('error', (err) => {
|
|
206
|
+
console.error('Failed to re-spawn with larger heap:', err.message);
|
|
207
|
+
process.exit(1);
|
|
208
|
+
});
|
|
209
|
+
child.on('exit', (code) => process.exit(code ?? 0));
|
|
210
|
+
} else {
|
|
211
|
+
require('./index.cjs');
|
|
212
|
+
}
|
|
213
|
+
`;
|
|
214
|
+
writeFileSync(cliWrapperPath, cliWrapperContent, 'utf-8');
|
|
215
|
+
console.log(`[bundle] CLI wrapper: dist/cli.cjs`);
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { parseSource } from '../src/shared/utils/bash-parser.ts';
|
|
2
|
+
import { isSafeShellCommand } from '../src/domain/agent/services/approval.ts';
|
|
3
|
+
import { getToolRiskLevel, ToolRiskLevel } from '../src/domain/agent/services/approval.ts';
|
|
4
|
+
|
|
5
|
+
const commands = [
|
|
6
|
+
'cd /c/Users/zhuoz/Desktop/gui && ls -la build* 2>/dev/null || echo "No build directory found"',
|
|
7
|
+
'cd C:/Users/zhuoz/Desktop/gui && find build -name "libAutoJdv*" -o -name "AutoJdv.dll" 2>/dev/null | head -5',
|
|
8
|
+
'ls -la',
|
|
9
|
+
'echo hello',
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
for (const cmd of commands) {
|
|
13
|
+
console.log(`\n=== Testing: ${cmd.slice(0, 80)}... ===`);
|
|
14
|
+
|
|
15
|
+
const t0 = Date.now();
|
|
16
|
+
let astResult;
|
|
17
|
+
try {
|
|
18
|
+
astResult = parseSource(cmd);
|
|
19
|
+
} catch (e) {
|
|
20
|
+
console.log('parseSource THREW:', e);
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
const parseTime = Date.now() - t0;
|
|
24
|
+
console.log(`parseSource: type=${astResult?.type} children=${astResult?.children?.length} time=${parseTime}ms`);
|
|
25
|
+
|
|
26
|
+
const t1 = Date.now();
|
|
27
|
+
const safe = isSafeShellCommand(cmd);
|
|
28
|
+
const safeTime = Date.now() - t1;
|
|
29
|
+
console.log(`isSafeShellCommand: ${safe} time=${safeTime}ms`);
|
|
30
|
+
|
|
31
|
+
const t2 = Date.now();
|
|
32
|
+
const risk = getToolRiskLevel('Shell', { command: cmd });
|
|
33
|
+
const riskTime = Date.now() - t2;
|
|
34
|
+
console.log(`getToolRiskLevel: ${risk} time=${riskTime}ms`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
console.log('\nAll tests completed.');
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { parseSource } from '../src/shared/utils/bash-parser.ts';
|
|
2
|
+
|
|
3
|
+
const cmd = 'cd /c/Users/zhuoz/Desktop/gui && ls -la build* 2>/dev/null || echo "No build directory found"';
|
|
4
|
+
|
|
5
|
+
// Test 1: just parseSource with timeout guard
|
|
6
|
+
console.log('Testing parseSource...');
|
|
7
|
+
const start = Date.now();
|
|
8
|
+
|
|
9
|
+
// Use a worker-like approach: run in a separate microtask with a hard timeout
|
|
10
|
+
let result: any = 'TIMEOUT';
|
|
11
|
+
let done = false;
|
|
12
|
+
|
|
13
|
+
// We can't truly timeout sync code in JS, but let's at least see if it returns
|
|
14
|
+
try {
|
|
15
|
+
result = parseSource(cmd);
|
|
16
|
+
done = true;
|
|
17
|
+
} catch (e) {
|
|
18
|
+
result = 'ERROR: ' + e;
|
|
19
|
+
done = true;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const elapsed = Date.now() - start;
|
|
23
|
+
console.log('parseSource returned:', done ? 'yes' : 'no', 'time:', elapsed, 'ms');
|
|
24
|
+
console.log('result type:', result?.type, 'children:', result?.children?.length);
|