aggroot 1.3.8 → 1.3.9
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/dist/index.cjs +461 -461
- package/dist/native/better_sqlite3.node +0 -0
- package/install-linux.sh +18 -2
- package/install-windows.bat +20 -2
- package/package.json +7 -4
- package/scripts/import-meta-shim.js +3 -0
- package/scripts/install-browsers.mjs +132 -0
- package/scripts/obfuscate.mjs +185 -0
- package/scripts/verify-no-source.mjs +83 -0
|
Binary file
|
package/install-linux.sh
CHANGED
|
@@ -77,8 +77,24 @@ else
|
|
|
77
77
|
fi
|
|
78
78
|
echo ""
|
|
79
79
|
|
|
80
|
-
# 步骤 3:
|
|
81
|
-
echo -e "${YELLOW}[3/
|
|
80
|
+
# 步骤 3: 安装浏览器
|
|
81
|
+
echo -e "${YELLOW}[3/4] 安装 Playwright 浏览器...${NC}"
|
|
82
|
+
export PLAYWRIGHT_DOWNLOAD_HOST=https://npmmirror.com/mirrors/playwright
|
|
83
|
+
if ! npx playwright install chromium; then
|
|
84
|
+
echo -e "${YELLOW}淘宝镜像失败,尝试官方源...${NC}"
|
|
85
|
+
unset PLAYWRIGHT_DOWNLOAD_HOST
|
|
86
|
+
if ! npx playwright install chromium; then
|
|
87
|
+
echo -e "${YELLOW}⚠️ 警告: 浏览器安装失败,将在首次使用时自动下载${NC}"
|
|
88
|
+
else
|
|
89
|
+
echo -e "${GREEN}✅ 浏览器安装完成${NC}"
|
|
90
|
+
fi
|
|
91
|
+
else
|
|
92
|
+
echo -e "${GREEN}✅ 浏览器安装完成${NC}"
|
|
93
|
+
fi
|
|
94
|
+
echo ""
|
|
95
|
+
|
|
96
|
+
# 步骤 4: 构建项目
|
|
97
|
+
echo -e "${YELLOW}[4/4] 构建项目...${NC}"
|
|
82
98
|
if ! npm run build:dev; then
|
|
83
99
|
echo -e "${RED}❌ 错误: 构建失败${NC}"
|
|
84
100
|
echo "请检查 TypeScript 配置"
|
package/install-windows.bat
CHANGED
|
@@ -70,8 +70,26 @@ if %errorlevel% neq 0 (
|
|
|
70
70
|
)
|
|
71
71
|
echo.
|
|
72
72
|
|
|
73
|
-
REM 步骤 3:
|
|
74
|
-
echo [3/
|
|
73
|
+
REM 步骤 3: 安装浏览器
|
|
74
|
+
echo [3/4] 安装 Playwright 浏览器...
|
|
75
|
+
set PLAYWRIGHT_DOWNLOAD_HOST=https://npmmirror.com/mirrors/playwright
|
|
76
|
+
call npx playwright install chromium
|
|
77
|
+
if %errorlevel% neq 0 (
|
|
78
|
+
echo 淘宝镜像失败,尝试官方源...
|
|
79
|
+
set PLAYWRIGHT_DOWNLOAD_HOST=
|
|
80
|
+
call npx playwright install chromium
|
|
81
|
+
if !errorlevel! neq 0 (
|
|
82
|
+
echo ⚠️ 警告: 浏览器安装失败,将在首次使用时自动下载
|
|
83
|
+
) else (
|
|
84
|
+
echo ✅ 浏览器安装完成
|
|
85
|
+
)
|
|
86
|
+
) else (
|
|
87
|
+
echo ✅ 浏览器安装完成
|
|
88
|
+
)
|
|
89
|
+
echo.
|
|
90
|
+
|
|
91
|
+
REM 步骤 4: 构建项目
|
|
92
|
+
echo [4/4] 构建项目...
|
|
75
93
|
call npm run build:dev
|
|
76
94
|
if %errorlevel% neq 0 (
|
|
77
95
|
echo ❌ 错误: 构建失败
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aggroot",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.9",
|
|
4
4
|
"description": "AggRoot - AI Assistant (Node.js/TypeScript Version)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"files": [
|
|
35
35
|
"dist/",
|
|
36
36
|
"share/",
|
|
37
|
+
"scripts/",
|
|
37
38
|
".env.example",
|
|
38
39
|
"install-windows.bat",
|
|
39
40
|
"install-linux.sh",
|
|
@@ -70,7 +71,6 @@
|
|
|
70
71
|
"@xenova/transformers": "^2.17.0",
|
|
71
72
|
"ajv": "^8.18.0",
|
|
72
73
|
"ajv-formats": "^3.0.1",
|
|
73
|
-
"better-sqlite3": "^12.8.0",
|
|
74
74
|
"chalk": "^5.6.2",
|
|
75
75
|
"chokidar": "^5.0.0",
|
|
76
76
|
"cli-table3": "^0.6.5",
|
|
@@ -88,12 +88,11 @@
|
|
|
88
88
|
"ora": "^8.1.0",
|
|
89
89
|
"pino": "^9.5.0",
|
|
90
90
|
"pino-pretty": "^11.2.0",
|
|
91
|
-
"playwright": "^1.
|
|
91
|
+
"playwright": "^1.60.0",
|
|
92
92
|
"pptxgenjs": "^4.0.1",
|
|
93
93
|
"react": "^19.2.4",
|
|
94
94
|
"react-reconciler": "^0.31.0",
|
|
95
95
|
"scheduler": "^0.27.0",
|
|
96
|
-
"sharp": "^0.34.5",
|
|
97
96
|
"simple-git": "^3.26.0",
|
|
98
97
|
"string-width": "^8.2.1",
|
|
99
98
|
"tiktoken": "^1.0.22",
|
|
@@ -104,6 +103,10 @@
|
|
|
104
103
|
"web-tree-sitter": "^0.26.8",
|
|
105
104
|
"zod": "^3.25.76"
|
|
106
105
|
},
|
|
106
|
+
"optionalDependencies": {
|
|
107
|
+
"better-sqlite3": "^12.8.0",
|
|
108
|
+
"sharp": "^0.34.5"
|
|
109
|
+
},
|
|
107
110
|
"devDependencies": {
|
|
108
111
|
"@eslint/js": "^9.13.0",
|
|
109
112
|
"@types/ajv": "^0.0.5",
|
|
@@ -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,185 @@
|
|
|
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
|
+
copyFileSync(srcPath, destPath);
|
|
114
|
+
console.log(`[bundle] Copied native: ${dest}`);
|
|
115
|
+
} else {
|
|
116
|
+
console.warn(`[bundle] Native module not found: ${src} — users will need build tools`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Step 2: Remove original directory structure
|
|
121
|
+
console.log('[bundle] Cleaning up original files...');
|
|
122
|
+
|
|
123
|
+
const filesToKeep = new Set(['index.bundled.js', 'web-tree-sitter.wasm', 'tree-sitter-cpp.wasm', 'tree-sitter-c.wasm', 'cli.cjs']);
|
|
124
|
+
const dirsToKeep = new Set(['native']);
|
|
125
|
+
|
|
126
|
+
function cleanDir(dir) {
|
|
127
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
128
|
+
for (const entry of entries) {
|
|
129
|
+
const fullPath = join(dir, entry.name);
|
|
130
|
+
if (entry.isDirectory()) {
|
|
131
|
+
if (!dirsToKeep.has(entry.name)) {
|
|
132
|
+
rmSync(fullPath, { recursive: true, force: true });
|
|
133
|
+
}
|
|
134
|
+
} else if (!filesToKeep.has(entry.name)) {
|
|
135
|
+
rmSync(fullPath, { force: true });
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
cleanDir(DIST_DIR);
|
|
140
|
+
|
|
141
|
+
// Step 3: Fix shebang and write final file
|
|
142
|
+
// Remove shebang from bundle body (it gets displaced by processing)
|
|
143
|
+
let bundleContent = readFileSync(BUNDLE_OUTPUT, 'utf-8');
|
|
144
|
+
if (bundleContent.startsWith('#!')) {
|
|
145
|
+
bundleContent = bundleContent.substring(bundleContent.indexOf('\n') + 1);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const finalPath = join(DIST_DIR, 'index.cjs');
|
|
149
|
+
writeFileSync(finalPath, '#!/usr/bin/env node\n' + bundleContent, 'utf-8');
|
|
150
|
+
rmSync(BUNDLE_OUTPUT, { force: true });
|
|
151
|
+
|
|
152
|
+
const finalSize = statSync(finalPath).size;
|
|
153
|
+
console.log(`[bundle] Done: dist/index.cjs (${(finalSize / 1024).toFixed(0)} KB)`);
|
|
154
|
+
console.log('[bundle] Complete — single minified file, no source exposure');
|
|
155
|
+
|
|
156
|
+
// Step 4: Generate CLI wrapper that sets --max-old-space-size=4096
|
|
157
|
+
// The shebang #!/usr/bin/env node cannot pass flags, so we need a wrapper
|
|
158
|
+
// that re-spawns node with the larger heap limit when needed.
|
|
159
|
+
// Use .cjs extension so Node treats it as CommonJS regardless of package.json "type"
|
|
160
|
+
const cliWrapperPath = join(DIST_DIR, 'cli.cjs');
|
|
161
|
+
const cliWrapperContent = `#!/usr/bin/env node
|
|
162
|
+
// AggRoot CLI wrapper - ensures Node.js heap is large enough for long sessions.
|
|
163
|
+
const v8 = require('node:v8');
|
|
164
|
+
const path = require('node:path');
|
|
165
|
+
const { spawn } = require('node:child_process');
|
|
166
|
+
const heapLimitMB = Math.round(v8.getHeapStatistics().heap_size_limit / 1024 / 1024);
|
|
167
|
+
|
|
168
|
+
if (heapLimitMB < 3500 && !process.env.AGGROOT_SPAWNED) {
|
|
169
|
+
// Re-spawn with larger heap, inheriting stdio for seamless UX
|
|
170
|
+
const child = spawn(process.execPath, [
|
|
171
|
+
'--max-old-space-size=4096',
|
|
172
|
+
'--no-warnings',
|
|
173
|
+
path.resolve(__dirname, 'index.cjs'),
|
|
174
|
+
...process.argv.slice(2),
|
|
175
|
+
], {
|
|
176
|
+
stdio: 'inherit',
|
|
177
|
+
env: { ...process.env, AGGROOT_SPAWNED: '1' },
|
|
178
|
+
});
|
|
179
|
+
child.on('exit', (code) => process.exit(code ?? 0));
|
|
180
|
+
} else {
|
|
181
|
+
require('./index.cjs');
|
|
182
|
+
}
|
|
183
|
+
`;
|
|
184
|
+
writeFileSync(cliWrapperPath, cliWrapperContent, 'utf-8');
|
|
185
|
+
console.log(`[bundle] CLI wrapper: dist/cli.cjs`);
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Verify that dist/ contains no original source code after obfuscation.
|
|
5
|
+
*
|
|
6
|
+
* Checks that:
|
|
7
|
+
* 1. Only expected files exist in dist/
|
|
8
|
+
* 2. No .js files contain readable source patterns (comments, multi-line functions, etc.)
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { readdirSync, readFileSync, statSync } from 'node:fs';
|
|
12
|
+
import { join } from 'node:path';
|
|
13
|
+
|
|
14
|
+
const DIST_DIR = join(process.cwd(), 'dist');
|
|
15
|
+
|
|
16
|
+
const ALLOWED_FILES = new Set([
|
|
17
|
+
'index.cjs',
|
|
18
|
+
'cli.cjs',
|
|
19
|
+
'web-tree-sitter.wasm',
|
|
20
|
+
'tree-sitter-cpp.wasm',
|
|
21
|
+
'tree-sitter-c.wasm',
|
|
22
|
+
'native/better_sqlite3.node',
|
|
23
|
+
]);
|
|
24
|
+
|
|
25
|
+
// Collect all files recursively
|
|
26
|
+
function walkDir(dir) {
|
|
27
|
+
const files = [];
|
|
28
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
29
|
+
const fullPath = join(dir, entry.name);
|
|
30
|
+
if (entry.isDirectory()) {
|
|
31
|
+
files.push(...walkDir(fullPath));
|
|
32
|
+
} else {
|
|
33
|
+
files.push(fullPath);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return files;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const allFiles = walkDir(DIST_DIR);
|
|
40
|
+
const violations = [];
|
|
41
|
+
|
|
42
|
+
// Check 1: No unexpected files or directories
|
|
43
|
+
for (const file of allFiles) {
|
|
44
|
+
const relative = file.slice(DIST_DIR.length + 1).replace(/\\/g, '/');
|
|
45
|
+
if (!ALLOWED_FILES.has(relative)) {
|
|
46
|
+
violations.push(`Unexpected file in dist/: ${relative}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Check 2: The bundled .cjs file should not contain readable source patterns
|
|
51
|
+
const cjsPath = join(DIST_DIR, 'index.cjs');
|
|
52
|
+
if (statSync(cjsPath, { throwIfNoEntry: false })) {
|
|
53
|
+
const content = readFileSync(cjsPath, 'utf-8');
|
|
54
|
+
|
|
55
|
+
// Real ESM import/export at the top level (line starts with import/export).
|
|
56
|
+
// Inside minified code these only appear within strings — never at the start of a line.
|
|
57
|
+
if (/^import\s/m.test(content) || /^export\s/m.test(content)) {
|
|
58
|
+
violations.push('index.cjs contains top-level ES module syntax (bundling may have failed)');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// A real sourceMappingURL directive starts at the beginning of a line (after optional whitespace)
|
|
62
|
+
if (/^\s*\/\/[#@]\s*sourceMappingURL/m.test(content)) {
|
|
63
|
+
violations.push('index.cjs contains source map reference');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Original TypeScript source directories should not appear as-is in the bundle.
|
|
67
|
+
// esbuild minification flattens everything — seeing "src/" paths as module references means
|
|
68
|
+
// the original file structure leaked.
|
|
69
|
+
const srcPathRefs = content.match(/require\(["']\.\/src\//g) || [];
|
|
70
|
+
if (srcPathRefs.length > 0) {
|
|
71
|
+
violations.push(`index.cjs contains ${srcPathRefs.length} require("./src/...") reference(s) — original directory structure leaked`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (violations.length > 0) {
|
|
76
|
+
console.error('[verify] Source exposure detected:');
|
|
77
|
+
for (const v of violations) {
|
|
78
|
+
console.error(` - ${v}`);
|
|
79
|
+
}
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
console.log('[verify] OK — no source exposure in dist/');
|