import { execFileSync } from 'node:child_process'; import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const desktopRoot = path.resolve(__dirname, '..'); const outputDir = path.join(desktopRoot, 'src-tauri', 'resources', 'openclaw-runtime'); const dryRun = process.argv.includes('--dry-run'); function log(message) { console.log(`[prepare-openclaw-runtime] ${message}`); } function readFirstExistingPath(commandNames) { for (const commandName of commandNames) { try { const stdout = execFileSync('where.exe', [commandName], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], }); const firstMatch = stdout .split(/\r?\n/) .map((line) => line.trim()) .find(Boolean); if (firstMatch) { return firstMatch; } } catch { continue; } } return null; } function ensureFileExists(filePath, label) { if (!filePath || !fs.existsSync(filePath) || !fs.statSync(filePath).isFile()) { throw new Error(`${label} 不存在:${filePath || '(empty)'}`); } } function ensureDirExists(dirPath, label) { if (!dirPath || !fs.existsSync(dirPath) || !fs.statSync(dirPath).isDirectory()) { throw new Error(`${label} 不存在:${dirPath || '(empty)'}`); } } function resolveOpenClawBin() { const override = process.env.OPENCLAW_BIN; if (override) { return path.resolve(override); } const resolved = readFirstExistingPath(['openclaw.cmd', 'openclaw']); if (!resolved) { throw new Error('未找到 openclaw 入口。请先安装 OpenClaw,或设置 OPENCLAW_BIN。'); } return resolved; } function resolvePackageDir(openclawBinPath) { const override = process.env.OPENCLAW_PACKAGE_DIR; if (override) { return path.resolve(override); } return path.join(path.dirname(openclawBinPath), 'node_modules', 'openclaw'); } function resolveNodeExe(openclawBinPath) { const override = process.env.OPENCLAW_NODE_EXE; if (override) { return path.resolve(override); } const bundledNode = path.join(path.dirname(openclawBinPath), 'node.exe'); if (fs.existsSync(bundledNode)) { return bundledNode; } const resolved = readFirstExistingPath(['node.exe', 'node']); if (!resolved) { throw new Error('未找到 node.exe。请先安装 Node.js,或设置 OPENCLAW_NODE_EXE。'); } return resolved; } function cleanOutputDirectory(dirPath) { if (!fs.existsSync(dirPath)) { return; } for (const entry of fs.readdirSync(dirPath)) { fs.rmSync(path.join(dirPath, entry), { recursive: true, force: true }); } } function writeCmdLauncher(dirPath) { const launcher = [ '@ECHO off', 'SETLOCAL', 'SET "_prog=%~dp0\\node.exe"', '"%_prog%" "%~dp0\\node_modules\\openclaw\\openclaw.mjs" %*', '', ].join('\r\n'); fs.writeFileSync(path.join(dirPath, 'openclaw.cmd'), launcher, 'utf8'); } function stageRuntime() { const openclawBinPath = resolveOpenClawBin(); const packageDir = resolvePackageDir(openclawBinPath); const nodeExePath = resolveNodeExe(openclawBinPath); const packageJsonPath = path.join(packageDir, 'package.json'); const entryPath = path.join(packageDir, 'openclaw.mjs'); ensureFileExists(openclawBinPath, 'OpenClaw 入口'); ensureDirExists(packageDir, 'OpenClaw 包目录'); ensureFileExists(packageJsonPath, 'OpenClaw package.json'); ensureFileExists(entryPath, 'OpenClaw 入口脚本'); ensureFileExists(nodeExePath, 'Node.js 可执行文件'); const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); const destinationPackageDir = path.join(outputDir, 'node_modules', 'openclaw'); const manifest = { source: { openclawBinPath, packageDir, nodeExePath, }, stagedAt: new Date().toISOString(), version: packageJson.version ?? null, }; log(`OpenClaw version: ${packageJson.version || 'unknown'}`); log(`Source bin: ${openclawBinPath}`); log(`Source package: ${packageDir}`); log(`Source node.exe: ${nodeExePath}`); log(`Target dir: ${outputDir}`); if (dryRun) { log('Dry run 完成,未写入任何文件。'); return; } fs.mkdirSync(outputDir, { recursive: true }); cleanOutputDirectory(outputDir); fs.mkdirSync(path.join(outputDir, 'node_modules'), { recursive: true }); fs.copyFileSync(nodeExePath, path.join(outputDir, 'node.exe')); fs.cpSync(packageDir, destinationPackageDir, { recursive: true, force: true }); writeCmdLauncher(outputDir); fs.writeFileSync(path.join(outputDir, 'runtime-manifest.json'), JSON.stringify(manifest, null, 2), 'utf8'); log('OpenClaw runtime 已写入 src-tauri/resources/openclaw-runtime'); } try { stageRuntime(); } catch (error) { const message = error instanceof Error ? error.message : String(error); console.error(`[prepare-openclaw-runtime] ${message}`); process.exit(1); }