#!/usr/bin/env node import fs from 'node:fs/promises'; import os from 'node:os'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { spawn } from 'node:child_process'; import { removeForbiddenPortalRuntimePaths } from '../release-gate/artifact.mjs'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const root = path.join(__dirname, '..'); const runtimeRoot = path.join(root, '.runtime', 'portal'); const distDir = path.join(root, 'dist'); const publicDir = path.join(root, 'public'); const nodeModulesDir = path.join(root, 'node_modules'); const schemaFile = path.join(root, 'schema.sql'); const esbuildBin = path.join(root, 'node_modules', '.pnpm', 'node_modules', '.bin', 'esbuild'); const skipBuild = process.argv.includes('--skip-build'); const skipNodeModules = process.argv.includes('--skip-node-modules'); const runtimeNodeTarget = process.env.PORTAL_RUNTIME_NODE_TARGET || 'node24'; const runtimeInstallMode = process.env.PORTAL_RUNTIME_INSTALL_MODE || 'bundle-node-modules'; const linuxArm64ResvgPackage = '@resvg/resvg-js-linux-arm64-gnu'; const linuxArm64Argon2Package = '@node-rs/argon2-linux-arm64-gnu'; const linuxArm64NativePackages = [ { hostPackage: '@resvg/resvg-js', targetPackage: linuxArm64ResvgPackage, }, { hostPackage: '@node-rs/argon2', targetPackage: linuxArm64Argon2Package, }, ]; const externalPackages = [ '@img/sharp-darwin-arm64', '@img/sharp-libvips-darwin-arm64', '@node-rs/argon2', '@resvg/resvg-js', 'chromium-bidi', 'chromium-bidi/*', 'debug', 'express', 'fsevents', 'http-proxy-middleware', 'jsonrepair', 'mysql2', 'mysql2/promise', 'playwright', 'playwright-core', 'qrcode', 'redis', 'sharp', 'undici', ]; function run(command, args, options = {}) { return new Promise((resolve, reject) => { const child = spawn(command, args, { cwd: root, stdio: 'inherit', env: process.env, ...options, }); child.on('exit', (code) => { if (code === 0) resolve(); else reject(new Error(`${command} ${args.join(' ')} exited with code ${code ?? 1}`)); }); child.on('error', reject); }); } async function exists(targetPath) { try { await fs.access(targetPath); return true; } catch { return false; } } async function remove(targetPath) { if (!(await exists(targetPath))) return; try { await fs.rm(targetPath, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 }); } catch (error) { if (error?.code !== 'ENOTEMPTY' && error?.code !== 'EBUSY') throw error; await run('rm', ['-rf', targetPath]); } } async function copyDir(source, target) { await fs.mkdir(path.dirname(target), { recursive: true }); await fs.cp(source, target, { recursive: true, dereference: false }); } async function writeFile(targetPath, content) { await fs.mkdir(path.dirname(targetPath), { recursive: true }); await fs.writeFile(targetPath, content, 'utf8'); } async function buildFrontend() { if (skipBuild) return; console.log('==> 构建 Portal 前端 dist'); const buildEnv = { ...process.env, VITE_PLAZA_BASE: process.env.VITE_PLAZA_BASE ?? process.env.PLAZA_PUBLIC_BASE ?? 'https://plaza.tkmind.cn', VITE_MINDSPACE_BASE: process.env.VITE_MINDSPACE_BASE ?? process.env.H5_PUBLIC_BASE_URL ?? 'https://m.tkmind.cn', }; await run('npm', ['run', 'build'], { env: buildEnv }); } async function bundleServer() { if (!(await exists(esbuildBin))) { throw new Error(`未找到 esbuild: ${esbuildBin}`); } console.log('==> 打包 Portal 后端为单文件 runtime'); const args = [ 'server.mjs', '--bundle', '--platform=node', '--format=esm', `--target=${runtimeNodeTarget}`, '--outfile=.runtime/portal/server.mjs', '--banner:js=import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);', ]; for (const pkg of externalPackages) { args.push(`--external:${pkg}`); } await run(esbuildBin, args); } async function bundleCanaryProxy() { if (!(await exists(esbuildBin))) { throw new Error(`未找到 esbuild: ${esbuildBin}`); } console.log('==> 打包用户级灰度路由器'); const args = [ 'memind-canary-proxy.mjs', '--bundle', '--platform=node', '--format=esm', `--target=${runtimeNodeTarget}`, '--outfile=.runtime/portal/memind-canary-proxy.mjs', '--banner:js=import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);', ]; for (const pkg of externalPackages) { args.push(`--external:${pkg}`); } await run(esbuildBin, args); } async function bundleDeepseekCompatProxy() { if (!(await exists(esbuildBin))) { throw new Error(`未找到 esbuild: ${esbuildBin}`); } console.log('==> 打包 DeepSeek 工具轮次兼容代理'); const args = [ 'deepseek-no-think-proxy.mjs', '--bundle', '--platform=node', '--format=esm', `--target=${runtimeNodeTarget}`, '--outfile=.runtime/portal/deepseek-no-think-proxy.mjs', '--banner:js=import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);', ]; for (const pkg of externalPackages) { args.push(`--external:${pkg}`); } await run(esbuildBin, args); } async function bundleSandboxMcp() { if (!(await exists(esbuildBin))) { throw new Error(`未找到 esbuild: ${esbuildBin}`); } console.log('==> 打包 sandbox MCP 为单文件 runtime'); const args = [ 'mindspace-sandbox-mcp.mjs', '--bundle', '--platform=node', '--format=esm', `--target=${runtimeNodeTarget}`, '--outfile=.runtime/portal/mindspace-sandbox-mcp.mjs', '--banner:js=import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);', ]; for (const pkg of externalPackages) { args.push(`--external:${pkg}`); } await run(esbuildBin, args); } async function bundleMindSearchMcp() { if (!(await exists(esbuildBin))) { throw new Error(`未找到 esbuild: ${esbuildBin}`); } console.log('==> 打包 MindSearch MCP 为单文件 runtime'); const args = [ 'tkmind-search-mcp.mjs', '--bundle', '--platform=node', '--format=esm', `--target=${runtimeNodeTarget}`, '--outfile=.runtime/portal/tkmind-search-mcp.mjs', '--banner:js=import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);', ]; for (const pkg of externalPackages) { args.push(`--external:${pkg}`); } await run(esbuildBin, args); } async function bundleExcelMcp() { if (!(await exists(esbuildBin))) { throw new Error(`未找到 esbuild: ${esbuildBin}`); } console.log('==> 打包 Excel Analyst MCP 为单文件 runtime'); const args = [ 'tkmind-excel-mcp.mjs', '--bundle', '--platform=node', '--format=esm', `--target=${runtimeNodeTarget}`, '--outfile=.runtime/portal/tkmind-excel-mcp.mjs', '--banner:js=import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);', ]; for (const pkg of externalPackages) { args.push(`--external:${pkg}`); } await run(esbuildBin, args); } async function bundleAgentRunWorker() { if (!(await exists(esbuildBin))) { throw new Error(`未找到 esbuild: ${esbuildBin}`); } console.log('==> 打包 agent run worker 为单文件 runtime'); const args = [ 'scripts/agent-run-worker.mjs', '--bundle', '--platform=node', '--format=esm', `--target=${runtimeNodeTarget}`, '--outfile=.runtime/portal/scripts/agent-run-worker.mjs', '--banner:js=import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);', ]; for (const pkg of externalPackages) { args.push(`--external:${pkg}`); } await run(esbuildBin, args); } async function copyRuntimeAssets() { console.log('==> 拷贝运行时静态资源'); await remove(runtimeRoot); await fs.mkdir(runtimeRoot, { recursive: true }); if (!(await exists(distDir))) { throw new Error(`缺少 dist 目录: ${distDir}`); } await copyDir(distDir, path.join(runtimeRoot, 'dist')); if (await exists(publicDir)) { await copyDir(publicDir, path.join(runtimeRoot, 'public')); } await removeForbiddenPortalRuntimePaths(runtimeRoot); if (await exists(schemaFile)) { await fs.copyFile(schemaFile, path.join(runtimeRoot, 'schema.sql')); } const skillsDir = path.join(root, 'skills'); if (await exists(skillsDir)) { console.log('==> 拷贝平台 skills 目录'); await copyDir(skillsDir, path.join(runtimeRoot, 'skills')); } } async function copyNodeModules() { if (skipNodeModules) return; if (!(await exists(nodeModulesDir))) { throw new Error(`缺少 node_modules 目录: ${nodeModulesDir}`); } const resolvedNodeModulesDir = await fs.realpath(nodeModulesDir); console.log('==> 拷贝生产运行依赖 node_modules'); await copyDir(resolvedNodeModulesDir, path.join(runtimeRoot, 'node_modules')); await rewriteNodeModulesSymlinks(path.join(runtimeRoot, 'node_modules'), resolvedNodeModulesDir); await installLinuxArm64NativePackages(); } async function readLinuxArm64NativePackageVersion({ hostPackage, targetPackage }) { const hostPackageJson = JSON.parse( await fs.readFile(path.join(nodeModulesDir, hostPackage, 'package.json'), 'utf8'), ); const version = hostPackageJson.optionalDependencies?.[targetPackage]; if (!version) { throw new Error(`无法从 ${hostPackage} 确定 ${targetPackage} 的版本`); } return version; } // The runtime artifact is assembled on macOS but runs in a Linux ARM64 Colima VM. // pnpm copies the host optional binaries only, so explicitly materialize every // Linux ARM64 native package that an externalized runtime dependency loads. async function installLinuxArm64NativePackage(nativePackage) { const { targetPackage } = nativePackage; const version = await readLinuxArm64NativePackageVersion(nativePackage); const stagingDir = await fs.mkdtemp(path.join(os.tmpdir(), 'memind-native-linux-arm64-')); const targetDir = path.join(runtimeRoot, 'node_modules', ...targetPackage.split('/')); try { console.log(`==> 补齐 Linux ARM64 原生依赖 ${targetPackage} (${version})`); await run('npm', ['pack', `${targetPackage}@${version}`, '--pack-destination', stagingDir]); const archive = (await fs.readdir(stagingDir)).find((name) => name.endsWith('.tgz')); if (!archive) throw new Error(`未生成 ${targetPackage} 安装包`); await remove(targetDir); await fs.mkdir(targetDir, { recursive: true }); await run('tar', ['-xzf', path.join(stagingDir, archive), '--strip-components=1', '-C', targetDir]); const files = await fs.readdir(targetDir); if (!files.some((name) => name.endsWith('.node'))) { throw new Error(`${targetPackage} 缺少原生 .node 文件`); } } finally { await remove(stagingDir); } } async function installLinuxArm64NativePackages() { for (const nativePackage of linuxArm64NativePackages) { await installLinuxArm64NativePackage(nativePackage); } } async function rewriteNodeModulesSymlinks(runtimeNodeModulesDir, sourceNodeModulesDir) { console.log('==> 重写 node_modules 顶层符号链接为 runtime 内相对路径'); const localNodeModulesRoot = `${sourceNodeModulesDir}${path.sep}`; async function visit(dir) { const entries = await fs.readdir(dir); for (const entryName of entries) { const fullPath = path.join(dir, entryName); const stats = await fs.lstat(fullPath); if (stats.isSymbolicLink()) { const linkTarget = await fs.readlink(fullPath); const normalizedTarget = path.normalize(linkTarget); if (!normalizedTarget.startsWith(localNodeModulesRoot)) continue; const relativeInsideNodeModules = normalizedTarget.slice(localNodeModulesRoot.length); const runtimeTarget = path.join(runtimeNodeModulesDir, relativeInsideNodeModules); const replacement = path.relative(path.dirname(fullPath), runtimeTarget); await fs.unlink(fullPath); await fs.symlink(replacement, fullPath); continue; } if (stats.isDirectory()) { await visit(fullPath); } } } await visit(runtimeNodeModulesDir); } async function copyMindspacePublicLinkTools() { console.log('==> 拷贝 MindSpace 公开页链接检查工具'); await fs.copyFile( path.join(root, 'mindspace-public-links.mjs'), path.join(runtimeRoot, 'mindspace-public-links.mjs'), ); await fs.mkdir(path.join(runtimeRoot, 'scripts'), { recursive: true }); await fs.copyFile( path.join(root, 'scripts', 'check-mindspace-public-links.mjs'), path.join(runtimeRoot, 'scripts', 'check-mindspace-public-links.mjs'), ); await fs.copyFile( path.join(root, 'scripts', 'repair-mindspace-public-downloads.mjs'), path.join(runtimeRoot, 'scripts', 'repair-mindspace-public-downloads.mjs'), ); } async function writeMetadata() { const packageJson = JSON.parse(await fs.readFile(path.join(root, 'package.json'), 'utf8')); const runtimeDependencies = Object.fromEntries( externalPackages .filter((pkg) => packageJson.dependencies?.[pkg]) .map((pkg) => [pkg, packageJson.dependencies[pkg]]), ); const runtimeOptionalDependencies = Object.fromEntries( await Promise.all( linuxArm64NativePackages.map(async (nativePackage) => [ nativePackage.targetPackage, await readLinuxArm64NativePackageVersion(nativePackage), ]), ), ); const runtimePackageJson = { name: `${packageJson.name}-portal-runtime`, private: true, type: 'module', engines: { node: '>=22', }, dependencies: runtimeDependencies, optionalDependencies: runtimeOptionalDependencies, }; await writeFile(path.join(runtimeRoot, 'package.json'), `${JSON.stringify(runtimePackageJson, null, 2)}\n`); const tunnelScript = path.join(root, 'scripts', 'memind-portal-tunnel.sh'); const runtimeScriptsDir = path.join(runtimeRoot, 'scripts'); await fs.mkdir(runtimeScriptsDir, { recursive: true }); if (await exists(tunnelScript)) { await fs.copyFile(tunnelScript, path.join(runtimeScriptsDir, 'memind-portal-tunnel.sh')); await fs.chmod(path.join(runtimeScriptsDir, 'memind-portal-tunnel.sh'), 0o755); } else { throw new Error(`缺少隧道脚本: ${tunnelScript}`); } const prodStartScript = path.join(root, 'scripts', 'run-memind-portal-prod.sh'); if (await exists(prodStartScript)) { await fs.copyFile(prodStartScript, path.join(runtimeScriptsDir, 'run-memind-portal-prod.sh')); await fs.chmod(path.join(runtimeScriptsDir, 'run-memind-portal-prod.sh'), 0o755); } else { throw new Error(`缺少 Portal 启动脚本: ${prodStartScript}`); } for (const scriptName of [ 'run-memind-portal-candidate.sh', 'run-memind-canary-proxy-prod.sh', 'run-deepseek-compat-proxy-candidate.sh', ]) { const source = path.join(root, 'scripts', scriptName); if (!(await exists(source))) { throw new Error(`缺少灰度运行脚本: ${source}`); } await fs.copyFile(source, path.join(runtimeScriptsDir, scriptName)); await fs.chmod(path.join(runtimeScriptsDir, scriptName), 0o755); } await fs.copyFile( path.join(root, 'scripts', 'goosed-canary.compose.yml'), path.join(runtimeScriptsDir, 'goosed-canary.compose.yml'), ); await fs.copyFile( path.join(root, 'scripts', 'load-env.mjs'), path.join(runtimeRoot, 'scripts', 'load-env.mjs'), ); await fs.copyFile( path.join(root, 'scripts', 'wechat-mp-menu.mjs'), path.join(runtimeRoot, 'scripts', 'wechat-mp-menu.mjs'), ); await fs.copyFile( path.join(root, 'scripts', 'check-stream-runtime.mjs'), path.join(runtimeRoot, 'scripts', 'check-stream-runtime.mjs'), ); await fs.copyFile( path.join(root, 'scripts', 'runtime-worker-drain.mjs'), path.join(runtimeRoot, 'scripts', 'runtime-worker-drain.mjs'), ); await fs.copyFile( path.join(root, 'scripts', 'runtime-worker-metrics.mjs'), path.join(runtimeRoot, 'scripts', 'runtime-worker-metrics.mjs'), ); await fs.copyFile( path.join(root, 'scripts', 'runtime-worker-heartbeat.mjs'), path.join(runtimeRoot, 'scripts', 'runtime-worker-heartbeat.mjs'), ); await fs.copyFile( path.join(root, 'scripts', 'runtime-slo-report.mjs'), path.join(runtimeRoot, 'scripts', 'runtime-slo-report.mjs'), ); await fs.copyFile( path.join(root, 'scripts', 'embed-memory-v2-local-hash.mjs'), path.join(runtimeRoot, 'scripts', 'embed-memory-v2-local-hash.mjs'), ); await fs.copyFile( path.join(root, 'scripts', 'install-runtime-metrics-agent.sh'), path.join(runtimeRoot, 'scripts', 'install-runtime-metrics-agent.sh'), ); await fs.copyFile( path.join(root, 'scripts', 'install-runtime-heartbeat-agent.sh'), path.join(runtimeRoot, 'scripts', 'install-runtime-heartbeat-agent.sh'), ); await fs.copyFile( path.join(root, 'scripts', 'install-runtime-slo-report-agent.sh'), path.join(runtimeRoot, 'scripts', 'install-runtime-slo-report-agent.sh'), ); await fs.copyFile( path.join(root, 'scripts', 'install-runtime-slo-soak-agent.sh'), path.join(runtimeRoot, 'scripts', 'install-runtime-slo-soak-agent.sh'), ); await fs.copyFile( path.join(root, 'scripts', 'install-agent-run-worker-agent.sh'), path.join(runtimeRoot, 'scripts', 'install-agent-run-worker-agent.sh'), ); await fs.copyFile( path.join(root, 'scripts', 'check-tool-runtime.mjs'), path.join(runtimeRoot, 'scripts', 'check-tool-runtime.mjs'), ); await fs.copyFile( path.join(root, 'scripts', 'check-agent-code-run-entry.mjs'), path.join(runtimeRoot, 'scripts', 'check-agent-code-run-entry.mjs'), ); await fs.copyFile( path.join(root, 'scripts', 'check-agent-run-worker.mjs'), path.join(runtimeRoot, 'scripts', 'check-agent-run-worker.mjs'), ); await fs.copyFile( path.join(root, 'scripts', 'agent-run-guard.mjs'), path.join(runtimeRoot, 'scripts', 'agent-run-guard.mjs'), ); await fs.copyFile( path.join(root, 'scripts', 'agent-run-guard-lib.mjs'), path.join(runtimeRoot, 'scripts', 'agent-run-guard-lib.mjs'), ); await fs.copyFile( path.join(root, 'scripts', 'install-agent-run-guard-agent.sh'), path.join(runtimeRoot, 'scripts', 'install-agent-run-guard-agent.sh'), ); await fs.copyFile( path.join(root, 'scripts', 'resume-agent-run-prod.sh'), path.join(runtimeRoot, 'scripts', 'resume-agent-run-prod.sh'), ); await fs.copyFile( path.join(root, 'scripts', 'ensure-goose-session-postgres.sh'), path.join(runtimeRoot, 'scripts', 'ensure-goose-session-postgres.sh'), ); await writeFile( path.join(runtimeRoot, 'RUNBOOK.txt'), [ 'Portal runtime artifact', '', 'This directory is meant to run on production without the source tree.', 'Required persisted items to inherit on the host:', ' .env', ' MindSpace/', ' data/', ' users/', ' .tailscale/', ' public/plaza-covers/', ' logs/', '', 'Bundled alongside server.mjs (required for sandbox-fs MCP):', ' mindspace-sandbox-mcp.mjs (esbuild bundle; includes schedule-service deps)', ' tkmind-search-mcp.mjs (esbuild bundle; required when MindSearch is enabled)', ' tkmind-excel-mcp.mjs (esbuild bundle; required when Excel Analyst is enabled)', ' wechat-mp.bundle.mjs (esbuild bundle; hot-swappable WeChat MP module)', ' memind-canary-proxy.mjs (immutable-identity stable/candidate router)', ' deepseek-no-think-proxy.mjs (DeepSeek V4 tool-round compatibility proxy)', '', 'Post-deploy validation (scripts/check-mindspace-public-links.mjs):', ' Scans MindSpace/*/public/*.html for missing relative href/src/cover targets.', '', `Runtime install mode: ${runtimeInstallMode}`, `Bundle target: ${runtimeNodeTarget}`, '', 'Platform agent skills (synced into user workspaces):', ' skills/', 'Key runtime differences must stay in .env, not in the artifact:', ' DATABASE_URL / MYSQL_*', ' H5_PUBLIC_BASE_URL', ' TKMIND_API_TARGETS / TKMIND_API_TARGET / TKMIND_API_TARGET_1', ' H5_USERS_ROOT / MINDSPACE_STORAGE_ROOT / MEMIND_SHARED_PUBLISH_ROOT', '', 'Frontend compile-time public URLs (baked into dist at build):', ' VITE_PLAZA_BASE defaults to https://plaza.tkmind.cn in build-portal-runtime.mjs', ' VITE_MINDSPACE_BASE defaults to H5_PUBLIC_BASE_URL or https://m.tkmind.cn', ' Do not build production dist with VITE_PLAZA_BASE=http://127.0.0.1:* in .env', '', 'Deployment and operations transport:', ' H5 public domain: m.tkmind.cn', ' Current public path: m.tkmind.cn -> 105 nginx -> 103 Portal :8081', ' No separate public test domain remains; use explicit local/test overrides only when needed.', ' Production H5 and WeChat links should use m.tkmind.cn unless a test rollout explicitly overrides H5_PUBLIC_BASE_URL.', ' Canary layout: stable :8081, candidate :18081, identity router :18080, candidate goosed :18015.', '', 'Streaming runtime operations:', ' node scripts/check-stream-runtime.mjs', ' node scripts/runtime-worker-drain.mjs status', ' node scripts/runtime-worker-drain.mjs reconcile', ' node scripts/runtime-worker-drain.mjs reconcile --apply', ' node scripts/runtime-worker-metrics.mjs status', ' node scripts/runtime-worker-metrics.mjs sample', ' bash scripts/install-runtime-metrics-agent.sh', ' node scripts/runtime-worker-heartbeat.mjs once', ' node scripts/runtime-worker-heartbeat.mjs serve', ' bash scripts/install-runtime-heartbeat-agent.sh', ' node scripts/runtime-slo-report.mjs', ' node scripts/runtime-slo-report.mjs --write-report', ' node scripts/runtime-slo-report.mjs --write-report --prune --retention-days 30', ' bash scripts/install-runtime-slo-report-agent.sh', ' bash scripts/install-runtime-slo-soak-agent.sh', ' node scripts/runtime-worker-drain.mjs drain goosed-3', ' node scripts/runtime-worker-drain.mjs undrain goosed-3', ' node scripts/check-tool-runtime.mjs', ' node scripts/check-agent-code-run-entry.mjs', ' curl -sk https://m.tkmind.cn/api/runtime/status # includes toolRuntime.queue', ' node scripts/agent-run-worker.mjs --status', ' node scripts/agent-run-worker.mjs --once', ' bash scripts/install-agent-run-worker-agent.sh # installs disabled by default', ' node scripts/check-agent-run-worker.mjs # read-only LaunchAgent/queue check', ' node scripts/agent-run-guard.mjs # dry-run auto-pause guard check', ' node scripts/agent-run-guard.mjs --apply # first-time pause only; repeat checks are no-ops', ' bash scripts/install-agent-run-guard-agent.sh # installs guard LaunchAgent', ' bash scripts/resume-agent-run-prod.sh # manual recovery after guard pause', '', ].join('\n'), ); } async function bundleWechatMp() { console.log('==> 打包 WeChat MP runtime bundle'); await run('node', ['scripts/build-wechat-mp-runtime.mjs']); } async function main() { await buildFrontend(); await copyRuntimeAssets(); await bundleServer(); await bundleCanaryProxy(); await bundleDeepseekCompatProxy(); await bundleWechatMp(); await bundleSandboxMcp(); await bundleMindSearchMcp(); await bundleExcelMcp(); await bundleAgentRunWorker(); if (runtimeInstallMode === 'bundle-node-modules' && !skipNodeModules) { await copyNodeModules(); } await copyMindspacePublicLinkTools(); await writeMetadata(); await fs.chmod(path.join(runtimeRoot, 'scripts', 'run-memind-portal-prod.sh'), 0o755); await fs.chmod(path.join(runtimeRoot, 'scripts', 'run-memind-portal-candidate.sh'), 0o755); await fs.chmod(path.join(runtimeRoot, 'scripts', 'run-memind-canary-proxy-prod.sh'), 0o755); await fs.chmod( path.join(runtimeRoot, 'scripts', 'run-deepseek-compat-proxy-candidate.sh'), 0o755, ); await fs.chmod(path.join(runtimeRoot, 'scripts', 'wechat-mp-menu.mjs'), 0o755); await fs.chmod(path.join(runtimeRoot, 'scripts', 'check-stream-runtime.mjs'), 0o755); await fs.chmod(path.join(runtimeRoot, 'scripts', 'runtime-worker-drain.mjs'), 0o755); await fs.chmod(path.join(runtimeRoot, 'scripts', 'runtime-worker-metrics.mjs'), 0o755); await fs.chmod(path.join(runtimeRoot, 'scripts', 'runtime-worker-heartbeat.mjs'), 0o755); await fs.chmod(path.join(runtimeRoot, 'scripts', 'runtime-slo-report.mjs'), 0o755); await fs.chmod(path.join(runtimeRoot, 'scripts', 'agent-run-worker.mjs'), 0o755); await fs.chmod(path.join(runtimeRoot, 'scripts', 'install-runtime-metrics-agent.sh'), 0o755); await fs.chmod(path.join(runtimeRoot, 'scripts', 'install-runtime-heartbeat-agent.sh'), 0o755); await fs.chmod(path.join(runtimeRoot, 'scripts', 'install-runtime-slo-report-agent.sh'), 0o755); await fs.chmod(path.join(runtimeRoot, 'scripts', 'install-runtime-slo-soak-agent.sh'), 0o755); await fs.chmod(path.join(runtimeRoot, 'scripts', 'install-agent-run-worker-agent.sh'), 0o755); await fs.chmod(path.join(runtimeRoot, 'scripts', 'agent-run-guard.mjs'), 0o755); await fs.chmod(path.join(runtimeRoot, 'scripts', 'install-agent-run-guard-agent.sh'), 0o755); await fs.chmod(path.join(runtimeRoot, 'scripts', 'resume-agent-run-prod.sh'), 0o755); await fs.chmod(path.join(runtimeRoot, 'scripts', 'ensure-goose-session-postgres.sh'), 0o755); await fs.chmod(path.join(runtimeRoot, 'scripts', 'check-tool-runtime.mjs'), 0o755); await fs.chmod(path.join(runtimeRoot, 'scripts', 'check-agent-run-worker.mjs'), 0o755); await fs.chmod(path.join(runtimeRoot, 'scripts', 'memind-portal-tunnel.sh'), 0o755); console.log(''); console.log(`Portal runtime 已生成: ${runtimeRoot}`); } main().catch((error) => { console.error(error instanceof Error ? error.message : error); process.exit(1); });