build-desktop.mjs 3.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546
  1. import { build } from 'esbuild'
  2. import { execFileSync } from 'node:child_process'
  3. import { buildRunner } from './build-runner.mjs'
  4. import { cp, mkdir, rm, readdir, readFile, writeFile, realpath, chmod } from 'node:fs/promises'
  5. import { resolve } from 'node:path'
  6. import { sealRuntime } from '../bin/lib/runtime-release.mjs'
  7. const root = resolve(import.meta.dirname, '..'), out = resolve(root, '.desktop-build')
  8. await rm(out, { recursive: true, force: true })
  9. await mkdir(out, { recursive: true })
  10. await build({ entryPoints: [resolve(root, 'src/server/index.ts')], outfile: resolve(out, 'server.mjs'),
  11. bundle: true, platform: 'node', format: 'esm', target: 'node24', external: ['vite'],
  12. banner: { js: "import { createRequire as __createRequire } from 'node:module'; const require = __createRequire(import.meta.url);" },
  13. define: { 'process.env.NODE_ENV': '"production"' }, sourcemap: true,
  14. })
  15. await buildRunner(out)
  16. await build({ entryPoints: [resolve(root, 'src/server/githubReleases.ts')], outfile: resolve(out, 'github-release.mjs'),
  17. bundle: true, platform: 'node', format: 'esm', target: 'node24' })
  18. const swiftTarget=process.arch==='arm64'?'arm64-apple-macosx12.0':'x86_64-apple-macosx12.0'
  19. execFileSync('xcrun',['swiftc','-O','-target',swiftTarget,resolve(root,'desktop/keychain-helper.swift'),'-o',resolve(out,'keychain-helper')],{stdio:'inherit'})
  20. execFileSync('xcrun',['swiftc','-O','-target',swiftTarget,'-D','DEVELOPMENT_HELPER',resolve(root,'desktop/keychain-helper.swift'),'-o',resolve(out,'keychain-helper-dev')],{stdio:'inherit'})
  21. await cp(resolve(root, 'dist'), resolve(out, 'ui'), { recursive: true })
  22. await cp(resolve(root, 'release.json'), resolve(out, 'release.json'))
  23. await cp(resolve(root, 'public/icons/icon-512.png'), resolve(root, 'desktop/icon.png'))
  24. const shell = resolve(out, 'shell')
  25. await mkdir(shell)
  26. for (const file of await readdir(resolve(root, 'desktop'))) {
  27. if (file.endsWith('.test.mjs') || !/\.(js|mjs|cjs|html|css|png)$/.test(file)) continue
  28. await cp(resolve(root, 'desktop', file), resolve(shell, file))
  29. }
  30. for (const file of ['boot.html', 'boot.js', 'boot.css', 'preload.cjs']) await readFile(resolve(shell, file))
  31. await cp(resolve(root,'build-info.json'),resolve(out,'build-info.json'))
  32. const manifest = JSON.parse(await readFile(resolve(root, 'release.json'), 'utf8'))
  33. const service = resolve(out, 'web-service')
  34. await mkdir(service)
  35. for (const file of ['server.mjs', 'ui', 'release.json', 'build-info.json', 'hermes_worker.py', 'guest_proxy.py', 'computer-image', 'third-party']) await cp(resolve(out, file), resolve(service, file), { recursive: true })
  36. await cp(resolve(root, 'bin'), resolve(service, 'bin'), { recursive: true })
  37. await cp(await realpath(process.execPath), resolve(service, 'node'))
  38. await cp(resolve(await realpath(process.execPath), '../../LICENSE'), resolve(service, 'NODE-LICENSE.txt'))
  39. await chmod(resolve(service, 'node'), 0o755)
  40. await writeFile(resolve(service, 'package.json'), JSON.stringify({ name: 'hermes-yaoyao', version: manifest.webVersion, type: 'module' }))
  41. sealRuntime(service)
  42. await writeFile(resolve(shell, 'package.json'), JSON.stringify({ name: 'hermes-yaoyao-desktop', version: manifest.webVersion,
  43. productName: '夭夭', description: '夭夭:本地服务、多 Agent 团队与电脑工作空间',
  44. author: 'YaoYao contributors', license: 'BSL-1.1', main: 'main.mjs', type: 'module' }, null, 2))
  45. console.log('桌面服务和界面已打包到 .desktop-build')