Files
nodeagent/scripts/pack.mjs
T
jkooe 4c9b44288c fix(pack): esbuild 输入改用绝对路径,支持从任意目录调用
此前 pack.mjs 用相对路径 apps/agent/dist/index.js 作为 esbuild 输入,
从项目目录外调用(node <abs>/scripts/pack.mjs)时报
Could not resolve apps/agent/dist/index.js。

改为基于 __dirname 的绝对路径;README 补充两种调用方式并注明需在项目目录执行。

验证:cd /tmp && node <abs>/scripts/pack.mjs 可正常产出 32MB 安装包
2026-09-27 19:21:21 +08:00

116 lines
4.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
/**
* 构建 Windows 便携安装包。
*
* 用法: node scripts/pack.mjs [--node v22.20.0] [--out release]
*
* 产物: <out>/nodeagent-win-x64.zip
* ├── node.exe Node.js 运行时(免装 Node.js)
* ├── agent.mjs esbuild 打包的被控端(含全部依赖)
* ├── install.ps1 一键安装
* └── uninstall.ps1 一键卸载
*/
import { execFileSync, execSync } from 'node:child_process';
import { copyFileSync, createWriteStream, existsSync, mkdirSync, rmSync } from 'node:fs';
import { get } from 'node:https';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const root = join(__dirname, '..');
const args = process.argv.slice(2);
const getArg = (k, d) => {
const i = args.indexOf(k);
return i >= 0 && args[i + 1] ? args[i + 1] : d;
};
const NODE_VERSION = getArg('--node', 'v22.20.0');
const OUT_DIR = getArg('--out', join(root, 'release'));
const PKG_DIR = join(OUT_DIR, 'nodeagent-win-x64');
const MIRROR = process.env.NODEAGENT_NODE_MIRROR ?? 'https://npmmirror.com/mirrors/node';
const NODE_ZIP_URL = `${MIRROR}/${NODE_VERSION}/node-${NODE_VERSION}-win-x64.zip`;
const log = (msg) => console.log(msg);
const fail = (msg) => {
console.error(`✗ ${msg}`);
process.exit(1);
};
async function download(url, dest) {
log(` 下载 ${url}`);
return new Promise((resolve, reject) => {
const follow = (u, redirects = 0) => {
get(u, (res) => {
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
if (redirects > 5) return reject(new Error('重定向过多'));
return follow(res.headers.location, redirects + 1);
}
if (res.statusCode !== 200) return reject(new Error(`HTTP ${res.statusCode}`));
const file = createWriteStream(dest);
res.pipe(file);
file.on('finish', () => file.close(() => resolve()));
file.on('error', reject);
}).on('error', reject);
};
follow(url);
});
}
async function main() {
log('=== ① 构建 workspace ===');
execSync('pnpm -r build', { cwd: root, stdio: 'inherit' });
log('\n=== ② esbuild 打包被控端 ===');
mkdirSync(OUT_DIR, { recursive: true });
const bundleOut = join(OUT_DIR, 'agent.mjs');
const esbuild = join(root, 'node_modules', '.bin', 'esbuild');
execFileSync(
esbuild,
[
// 输入用绝对路径,保证从任意工作目录调用都能解析
join(root, 'apps', 'agent', 'dist', 'index.js'),
'--bundle',
'--platform=node',
'--target=node22',
'--format=esm',
'--banner:js=import { createRequire } from "node:module"; const require = createRequire(import.meta.url);',
`--outfile=${bundleOut}`,
],
{ stdio: 'inherit' },
);
log('\n=== ③ 下载 Node.js Windows 运行时 ===');
const tmpZip = join(OUT_DIR, 'node-runtime.zip');
if (existsSync(tmpZip)) {
log(' 已缓存,跳过下载');
} else {
await download(NODE_ZIP_URL, tmpZip);
}
log('\n=== ④ 组装便携包 ===');
rmSync(PKG_DIR, { recursive: true, force: true });
mkdirSync(PKG_DIR, { recursive: true });
execSync(`unzip -o -j "${tmpZip}" "*/node.exe" -d "${PKG_DIR}"`, { stdio: 'inherit' });
copyFileSync(bundleOut, join(PKG_DIR, 'agent.mjs'));
copyFileSync(join(root, 'scripts', 'install.ps1'), join(PKG_DIR, 'install.ps1'));
copyFileSync(join(root, 'scripts', 'uninstall.ps1'), join(PKG_DIR, 'uninstall.ps1'));
const files = execSync(`ls -la "${PKG_DIR}"`, { encoding: 'utf8' });
log(files.split('\n').slice(0, 8).join('\n'));
log('\n=== ⑤ 打 zip ===');
const zipOut = join(OUT_DIR, 'nodeagent-win-x64.zip');
rmSync(zipOut, { force: true });
execSync(`cd "${PKG_DIR}" && zip -q -r "${zipOut}" .`, { stdio: 'inherit' });
const size = execSync(`du -sh "${zipOut}"`, { encoding: 'utf8' }).split('\t')[0];
log(`\n✓ 完成: ${zipOut}(${size.trim()})`);
log(' 分发方式:解压后右键以管理员运行 install.ps1 即可');
}
try {
await main();
} catch (err) {
fail(err instanceof Error ? err.message : String(err));
}