Files
qiming/qimingclaw/crates/agent-electron-client/scripts/prepare/prepare-gui-server.js

146 lines
5.8 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
/**
* 从 node_modules 复制 agent-gui-server 到 resources/
*
* 前提:
* 1. pnpm install 已执行workspace 链接生效)
* 2. agent-gui-server 已构建npm run build
*
* 产物:
* resources/agent-gui-server/
* ├── dist/index.js — esbuild CLI bundleESM原生依赖标记为 external
* ├── dist/lib.bundle.cjs — esbuild SDK bundleCJS供 Electron runtime require
* ├── node_modules/ — external 原生依赖sharp, @nut-tree-fork/*, clipboardy
* └── package.json — 精简版name/version/main/bin
*
* 打包时 electron-builder extraResources 会打包到
* .app/Contents/Resources/agent-gui-server/
*/
const path = require('path');
const fs = require('fs');
const { execSync } = require('child_process');
const { getProjectRoot } = require('../utils/project-paths');
const projectRoot = getProjectRoot();
const srcDir = path.join(projectRoot, 'node_modules', 'agent-gui-server');
const destDir = path.join(projectRoot, 'resources', 'agent-gui-server');
// esbuild 中标记为 external 的原生依赖(需要在运行时可用)
const NATIVE_EXTERNALS = ['sharp', '@nut-tree-fork/nut-js', 'clipboardy'];
function main() {
// 1. 验证 node_modules 中存在 agent-gui-server
if (!fs.existsSync(path.join(srcDir, 'package.json'))) {
console.error('[prepare-gui-server] node_modules 中未找到 agent-gui-server');
console.error('[prepare-gui-server] 请先执行 pnpm install');
process.exit(1);
}
const srcPkg = JSON.parse(fs.readFileSync(path.join(srcDir, 'package.json'), 'utf8'));
console.log(`[prepare-gui-server] 源码版本: ${srcPkg.name}@${srcPkg.version}`);
// 2. 验证必要文件存在
const srcIndexJs = path.join(srcDir, 'dist', 'index.js');
if (!fs.existsSync(srcIndexJs)) {
console.error(`[prepare-gui-server] CLI 入口不存在: ${srcIndexJs}`);
console.error('[prepare-gui-server] 请先在 crates/agent-gui-server 中执行 npm run build');
process.exit(1);
}
// 3. 检查目标是否已是最新版本
const destPkgPath = path.join(destDir, 'package.json');
const destLibBundle = path.join(destDir, 'dist', 'lib.bundle.cjs');
if (fs.existsSync(destPkgPath) && fs.existsSync(destLibBundle)) {
try {
const destPkg = JSON.parse(fs.readFileSync(destPkgPath, 'utf8'));
if (destPkg.version === srcPkg.version) {
const destIndexJs = path.join(destDir, 'dist', 'index.js');
if (fs.existsSync(destIndexJs)) {
const srcSize = fs.statSync(srcIndexJs).size;
const destSize = fs.statSync(destIndexJs).size;
if (srcSize === destSize) {
console.log(`[prepare-gui-server] ${srcPkg.version} 已是最新,跳过`);
return;
}
}
}
} catch {
// 目标损坏,重新复制
}
}
// 4. 清理并创建目标目录
console.log('[prepare-gui-server] 复制到 resources/agent-gui-server/...');
if (fs.existsSync(destDir)) {
fs.rmSync(destDir, { recursive: true });
}
fs.mkdirSync(path.join(destDir, 'dist'), { recursive: true });
// 5. 复制 dist/index.js (esbuild CLI bundle)
const destIndexJs = path.join(destDir, 'dist', 'index.js');
fs.copyFileSync(srcIndexJs, destIndexJs);
fs.chmodSync(destIndexJs, 0o755);
console.log(` dist/index.js (${(fs.statSync(destIndexJs).size / 1024).toFixed(0)} KB)`);
// 5b. 复制 dist/lib.bundle.cjs (esbuild CJS SDK bundle, for Electron runtime require)
const srcLibBundle = path.join(srcDir, 'dist', 'lib.bundle.cjs');
if (fs.existsSync(srcLibBundle)) {
fs.copyFileSync(srcLibBundle, destLibBundle);
console.log(` dist/lib.bundle.cjs (${(fs.statSync(destLibBundle).size / 1024).toFixed(0)} KB)`);
} else {
console.warn('[prepare-gui-server] dist/lib.bundle.cjs not found — CJS SDK bundle will be unavailable');
}
// 6. 安装 external 原生依赖到 resources/agent-gui-server/node_modules/
// 使用 npm install 确保原生模块为当前平台编译
const depsToInstall = [];
for (const dep of NATIVE_EXTERNALS) {
const ver = srcPkg.dependencies?.[dep];
if (ver) {
depsToInstall.push(`${dep}@${ver.replace(/^[\^~]/, '')}`);
}
}
if (depsToInstall.length > 0) {
// 先写一个临时 package.json 让 npm install 工作
const tmpPkg = { name: 'agent-gui-server-runtime', version: '0.0.0', private: true };
fs.writeFileSync(destPkgPath, JSON.stringify(tmpPkg, null, 2) + '\n');
const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
// CI 交叉编译支持TARGET_ARCH 环境变量指定目标架构(如 macOS arm64 runner 构建 x64 包)
// npm install 通过 --cpu 标志安装目标架构的原生模块
const targetArch = process.env.TARGET_ARCH;
const archFlag = targetArch ? ` --cpu=${targetArch}` : '';
const archLabel = targetArch ? ` (target: ${targetArch})` : '';
console.log(` 安装原生依赖${archLabel}: ${depsToInstall.join(', ')}...`);
try {
execSync(`${npmCmd} install --no-save${archFlag} ${depsToInstall.join(' ')}`, {
cwd: destDir,
stdio: 'pipe',
});
console.log(' node_modules/ (原生依赖安装完成)');
} catch (e) {
console.error('[prepare-gui-server] 原生依赖安装失败:', e.stderr?.toString() || e.message);
process.exit(1);
}
}
// 7. 生成最终精简版 package.json
const slimPkg = {
name: srcPkg.name,
version: srcPkg.version,
type: 'module',
main: './dist/lib.bundle.cjs',
bin: { 'agent-gui-server': './dist/index.js' },
};
fs.writeFileSync(destPkgPath, JSON.stringify(slimPkg, null, 2) + '\n');
console.log(' package.json (slim)');
console.log(`[prepare-gui-server] ✓ resources/agent-gui-server/ (${srcPkg.version})`);
}
main();