#!/bin/bash # Build ../dist/DoTouchAI-win/ + a portable zip from the cached Windows # Electron binary + the cross-platform app sources. Win10/11 x64 target. # # What this produces (and what it cannot, on a macOS build host): # - a runnable portable folder: unzip → run "DoTouchAI.exe" # - the .exe keeps Electron's default icon (rcedit is a Windows tool we # cannot run on macOS); the window + tray use assets/icon.ico at runtime # - unsigned → SmartScreen warns on first launch ("更多信息 → 仍要运行") # - execution must be verified on a real Windows machine; this host cannot # run .exe, so the build is structural-only (files present + JS compiles) set -euo pipefail cd "$(dirname "$0")/.." VERSION="$(node -p "require('./package.json').version")" OUT="../dist" APP="$OUT/DoTouchAI-win" ZIP="$OUT/DoTouchAI-${VERSION}-win-x64.zip" # Locate the cached Windows Electron binary (same version pinned in devDeps). ELECTRON_ZIP=$(ls "$HOME/Library/Caches/electron/"*/electron-v*-win32-x64.zip 2>/dev/null | head -1) if [ -z "$ELECTRON_ZIP" ]; then echo "ERROR: electron win32-x64 zip not in cache. Run: npm i -D electron@42.3.3" >&2 exit 1 fi # Windows-native sherpa-onnx binary (offline ASR). Fetch on first build, cache. SHERPA_CACHE="../.sherpa-win-cache" if [ ! -d "$SHERPA_CACHE/sherpa-onnx-win-x64" ]; then echo "fetching sherpa-onnx-win-x64 (one-time)…" rm -rf "$SHERPA_CACHE" mkdir -p "$SHERPA_CACHE" ( cd "$SHERPA_CACHE" && npm pack sherpa-onnx-win-x64@1.13.6 \ --pack-destination "$SHERPA_CACHE" --cache "$SHERPA_CACHE/npm-cache" >/dev/null 2>&1 ) tar -xzf "$SHERPA_CACHE"/sherpa-onnx-win-x64-*.tgz -C "$SHERPA_CACHE" mv "$SHERPA_CACHE/package" "$SHERPA_CACHE/sherpa-onnx-win-x64" rm -f "$SHERPA_CACHE"/*.tgz fi rm -rf "$APP" mkdir -p "$APP" # 1. Windows Electron runtime (electron.exe + resources + locales + DLLs). unzip -q "$ELECTRON_ZIP" -d "$APP" mv "$APP/electron.exe" "$APP/DoTouchAI.exe" # 2. Swap our app for Electron's default shell. rm -f "$APP/resources/default_app.asar" mkdir -p "$APP/resources/app/assets" "$APP/resources/app/plugins" cp main.js env.js service.js updater.js plugins.js package.json "$APP/resources/app/" cp assets/icon.ico "$APP/resources/app/assets/" node -e "require('fs').writeFileSync('$APP/resources/app/build.json', JSON.stringify({version:'$VERSION', platform:'win32-x64', builtAt:new Date().toLocaleString('zh-CN',{hour12:false})}, null, 2) + '\n')" # 3. Bundled plugins (voice suite) — Windows native binary variant. VOICE_SRC="/Users/eastudio/Documents/DSWorkSpace" DEP_SRC="$HOME/.dsh/profiles/web/node_modules" BUNDLED="$APP/resources/app/plugins" cp -R "$VOICE_SRC/dsh-voice-client" "$BUNDLED/dsh-voice-client" rm -rf "$BUNDLED/dsh-voice-client/node_modules" cp -R "$VOICE_SRC/dsh-voice-server" "$BUNDLED/dsh-voice-server" rm -rf "$BUNDLED/dsh-voice-server/node_modules" cp -R "$DEP_SRC/sherpa-onnx-node" "$BUNDLED/sherpa-onnx-node" cp -R "$SHERPA_CACHE/sherpa-onnx-win-x64" "$BUNDLED/sherpa-onnx-win-x64" # .node + DLLs cp -R "$DEP_SRC/ws" "$BUNDLED/ws" node -e "require('fs').writeFileSync('$BUNDLED/manifest.json', JSON.stringify({ 'dsh-voice-client': { id: 'dsh-voice-client', name: 'dsh-voice-client' }, 'dsh-voice-server': { id: 'dsh-voice-server', name: 'dsh-voice-server/lib/index-v6.js' }, 'sherpa-onnx-node': { dep: true }, 'sherpa-onnx-win-x64': { dep: true }, 'ws': { dep: true } }, null, 2) + '\n')" echo "app: $(du -sh "$APP" | cut -f1) — exe + plugins: $(ls "$BUNDLED" | tr '\n' ' ')" # 4. Portable zip (python3 — no resource-fork noise, cross-platform). rm -f "$ZIP" python3 -c " import zipfile, os src='$APP' with zipfile.ZipFile('$ZIP','w',zipfile.ZIP_DEFLATED, compresslevel=6) as z: for root,dirs,files in os.walk(src): for f in files: p=os.path.join(root,f) arc=os.path.relpath(p, os.path.dirname(src)) z.write(p, arc) " echo "built: $ZIP ($(du -sh "$ZIP" | cut -f1))"