diff --git a/electron/package.json b/electron/package.json index bb833dc3..340bd191 100644 --- a/electron/package.json +++ b/electron/package.json @@ -28,6 +28,7 @@ "build": { "appId": "com.clusterlabs.openswarm", "productName": "OpenSwarm", + "electronLanguages": ["en"], "electronDownload": { "mirror": "https://github.com/castlabs/electron-releases/releases/download/v" }, @@ -125,8 +126,15 @@ ] }, { - "from": "build-staging/node", - "to": "node", + "from": "build-staging/node/${arch}", + "to": "node/${arch}", + "filter": [ + "**/*" + ] + }, + { + "from": "build-staging/uv-bin/${arch}", + "to": "backend/uv-bin", "filter": [ "**/*" ] diff --git a/frontend/public/onboarding-videos/Step1.mp4 b/frontend/public/onboarding-videos/Step1.mp4 deleted file mode 100644 index 56914855..00000000 Binary files a/frontend/public/onboarding-videos/Step1.mp4 and /dev/null differ diff --git a/frontend/public/onboarding-videos/Step2.mp4 b/frontend/public/onboarding-videos/Step2.mp4 deleted file mode 100644 index 9ac5cdaa..00000000 Binary files a/frontend/public/onboarding-videos/Step2.mp4 and /dev/null differ diff --git a/frontend/public/onboarding-videos/Step3.mp4 b/frontend/public/onboarding-videos/Step3.mp4 deleted file mode 100644 index 10af62cd..00000000 Binary files a/frontend/public/onboarding-videos/Step3.mp4 and /dev/null differ diff --git a/frontend/public/onboarding-videos/Step4.mp4 b/frontend/public/onboarding-videos/Step4.mp4 deleted file mode 100644 index e7010077..00000000 Binary files a/frontend/public/onboarding-videos/Step4.mp4 and /dev/null differ diff --git a/frontend/public/onboarding-videos/Step5.mp4 b/frontend/public/onboarding-videos/Step5.mp4 deleted file mode 100644 index bf63d51c..00000000 Binary files a/frontend/public/onboarding-videos/Step5.mp4 and /dev/null differ diff --git a/scripts/build-app-win.ps1 b/scripts/build-app-win.ps1 index f92fbba9..3273040d 100644 --- a/scripts/build-app-win.ps1 +++ b/scripts/build-app-win.ps1 @@ -328,7 +328,13 @@ function Copy-Excluded($Source, $Dest, $Exclude) { Copy-Excluded ` (Join-Path $ProjectRoot 'backend') (Join-Path $Staging 'backend') ` - @{ Dirs = @('__pycache__','.venv','tools','tests'); Files = @('*.pyc','.env','.env.*') } + @{ Dirs = @('__pycache__','.venv','data','uv-bin','tests'); Files = @('*.pyc','.env','.env.*') } +# data: backend/config/paths.py points DATA_ROOT at %APPDATA%/OpenSwarm/data in +# packaged mode and no code seeds from the bundle, so the entire shipped +# backend/data/ tree was dead weight (and was leaking the dev machine's +# auth.token + install_id + dev session artifacts). +# uv-bin: source dir holds the binary so dev works; staged separately below +# so extraResources can substitute ${arch} (matches the mac build). # Production .env: OAuth helper base URL + Google credentials. See # scripts/build-app.sh for the rationale; v1.0.29 cloud-proxied the OAuth flow, @@ -354,7 +360,14 @@ New-Item -ItemType Directory -Force -Path (Split-Path $ShipEnvPath -Parent) | Ou "GOOGLE_OAUTH_CLIENT_SECRET=$GoogleClientSecretShip" ) | Set-Content -Path $ShipEnvPath Write-Host "Staged production .env" -New-Item -ItemType Directory -Force -Path (Join-Path $Staging 'backend\data\tools') | Out-Null + +# Stage uv-bin into per-arch staging so package.json extraResources can +# substitute ${arch} and ship only the matching slice. Windows is x64-only +# today; matches the mac build's per-arch staging shape. +$UvStageX64 = Join-Path $Staging 'uv-bin\x64' +New-Item -ItemType Directory -Force -Path $UvStageX64 | Out-Null +Copy-Item -Force (Join-Path $UvBinDir 'uv.exe') (Join-Path $UvStageX64 'uv.exe') +Copy-Item -Force (Join-Path $UvBinDir 'uvx.exe') (Join-Path $UvStageX64 'uvx.exe') Copy-Excluded ` (Join-Path $ProjectRoot 'debugger') (Join-Path $Staging 'debugger') ` diff --git a/scripts/build-app.sh b/scripts/build-app.sh index 78834aeb..8322ae8f 100755 --- a/scripts/build-app.sh +++ b/scripts/build-app.sh @@ -366,13 +366,18 @@ echo "[4/5] Snapshotting source directories..." rsync -a \ --exclude='__pycache__' --exclude='**/__pycache__' \ --exclude='*.pyc' --exclude='.venv' \ - --exclude='data/tools' \ - --exclude='data/outputs_workspace' \ - --exclude='data/agent_history' --exclude='data/sessions' \ + --exclude='/data' \ + --exclude='/uv-bin' \ --exclude='apps/outputs/webapp_template_cache' \ --exclude='tests' --exclude='**/tests' \ --exclude='/.env' --exclude='/.env.*' \ "$PROJECT_ROOT/backend/" "$STAGING_DIR/backend/" +# /data: backend/config/paths.py points DATA_ROOT at ~/Library/Application Support/OpenSwarm/data +# in packaged mode and no code seeds from the bundle, so the entire shipped +# backend/data/ tree was dead weight (and was leaking the dev machine's +# auth.token + install_id + dev session artifacts). +# /uv-bin: source dir holds the universal binary so `bash run.sh` works on either +# host arch; we stage per-arch thin slices below so each DMG ships only its slice. # webapp_template_cache: a pre-built node_modules.tar.gz that gets shipped # to speed up first-app-create. Apple notarization extracts it and rejects # the build because upstream native binaries inside (esbuild, fsevents, etc.) @@ -411,8 +416,20 @@ GOOGLE_OAUTH_CLIENT_ID=${GOOGLE_CLIENT_ID_SHIP} GOOGLE_OAUTH_CLIENT_SECRET=${GOOGLE_CLIENT_SECRET_SHIP} EOF echo "Staged production .env" -# Create empty tools directory so the app has a place to write -mkdir -p "$STAGING_DIR/backend/data/tools" + +# Per-arch slice of the universal uv/uvx for shipping. The source-tree uv-bin/ +# stays universal so dev (`bash run.sh`) works on either host arch; thinning +# into staging means each per-arch DMG ships only its slice (~48 MB savings +# vs the 97 MB universal binary the build used to put in both DMGs). +echo "Slicing uv per-arch into staging..." +for arch in arm64 x64; do + lipo_arch=$arch + [[ "$arch" == "x64" ]] && lipo_arch=x86_64 + mkdir -p "$STAGING_DIR/uv-bin/$arch" + lipo "$UV_BIN_DIR/uv" -thin "$lipo_arch" -output "$STAGING_DIR/uv-bin/$arch/uv" + lipo "$UV_BIN_DIR/uvx" -thin "$lipo_arch" -output "$STAGING_DIR/uv-bin/$arch/uvx" + chmod +x "$STAGING_DIR/uv-bin/$arch/uv" "$STAGING_DIR/uv-bin/$arch/uvx" +done rsync -a \ --exclude='__pycache__' --exclude='**/__pycache__' \ diff --git a/scripts/build-python-env-win.ps1 b/scripts/build-python-env-win.ps1 index 7accac2e..ae0d332c 100644 --- a/scripts/build-python-env-win.ps1 +++ b/scripts/build-python-env-win.ps1 @@ -100,18 +100,31 @@ $ToStrip = @( (Join-Path $PythonEnvDir 'lib\python3.13\tkinter'), # Tk GUI toolkit — same (Join-Path $PythonEnvDir 'lib\python3.13\ensurepip'), # Pip bootstrap — backend never installs at runtime (Join-Path $PythonEnvDir 'lib\python3.13\turtledemo'), # Educational drawing examples + (Join-Path $PythonEnvDir 'lib\python3.13\pydoc_data'), # pydoc topics/keywords; only `help()` reads them + (Join-Path $PythonEnvDir 'lib\python3.13\_pyrepl'), # Python 3.13 interactive REPL, never started in packaged app (Join-Path $PythonEnvDir 'share') # Man pages / desktop integration ) foreach ($p in $ToStrip) { if (Test-Path $p) { Remove-Item -Recurse -Force $p -ErrorAction SilentlyContinue } } +$Sp = Join-Path $PythonEnvDir 'lib\python3.13\site-packages' +# pip itself: nothing in the packaged backend invokes it. uvx (used by +# MCPs) is a self-contained installer; the App Builder picks SYSTEM +# python via shutil.which (view_builder_templates.py:382), never this +# bundled one; backend code only mentions "pip install" in error strings. +Remove-Item -Recurse -Force (Join-Path $Sp 'pip') -ErrorAction SilentlyContinue +Get-ChildItem -Path $Sp -Directory -Filter 'pip-*.dist-info' -ErrorAction SilentlyContinue ` + | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue +# Launcher .exe shims for the now-removed tools. Windows installs them under +# Scripts\; ignore missing. +foreach ($exe in @('pip.exe','pip3.exe','pip3.13.exe','idle3.exe','idle3.13.exe','pydoc3.exe','pydoc3.13.exe')) { + $p = Join-Path $PythonEnvDir "Scripts\$exe" + if (Test-Path $p) { Remove-Item -Force $p -ErrorAction SilentlyContinue } +} # ----- Babel locale-data trim (~30 MB / ~900 files) ----- # Babel ships 1,084 CLDR locale .dat files. Trafilatura's transitive dep -# courlan/filters.py:184 calls Locale.parse(seg) on URL path segments — -# UnknownLocaleError IS caught at line 188 (graceful degradation: that URL -# just doesn't get language-filtered). Keeping the 20 most common base -# languages preserves filtering for the URLs we'll actually see. +# courlan/filters.py:184 calls Locale.parse(seg) on URL path segments. UnknownLocaleError IS caught at line 188, so stripped locales just skip language-filtering for that URL. $Sp = Join-Path $PythonEnvDir 'lib\python3.13\site-packages' $LocaleDir = Join-Path $Sp 'babel\locale-data' if (Test-Path $LocaleDir) { diff --git a/scripts/build-python-env.sh b/scripts/build-python-env.sh index a7150da0..71c5526f 100755 --- a/scripts/build-python-env.sh +++ b/scripts/build-python-env.sh @@ -130,6 +130,32 @@ rm -rf "$PYTHON_ENV_DIR/lib/python3.13/ensurepip" rm -rf "$PYTHON_ENV_DIR/lib/python3.13/turtledemo" # Man pages / desktop-integration files — embedded Python doesn't read these. rm -rf "$PYTHON_ENV_DIR/share" +# pip itself + launcher shims. Verified the packaged backend never invokes +# pip: uvx (used by MCPs) is a self-contained installer; the App Builder's +# view_builder_templates.py:382 picks SYSTEM python via shutil.which, never +# this bundled one; backend code only mentions "pip install" in error-message +# strings. `python -m venv` from this bundled env is also dead (ensurepip +# already stripped above) but nothing calls it. +rm -rf "$PYTHON_ENV_DIR/lib/python3.13/site-packages/pip" \ + "$PYTHON_ENV_DIR/lib/python3.13/site-packages"/pip-*.dist-info +rm -f "$PYTHON_ENV_DIR/bin/pip" "$PYTHON_ENV_DIR/bin/pip3" "$PYTHON_ENV_DIR/bin/pip3.13" \ + "$PYTHON_ENV_DIR/bin/idle3" "$PYTHON_ENV_DIR/bin/idle3.13" \ + "$PYTHON_ENV_DIR/bin/pydoc3" "$PYTHON_ENV_DIR/bin/pydoc3.13" +# pydoc_data: keyword/topic tables consumed only by stdlib `pydoc` / `help()`. +# Backend never starts a REPL or calls help(). +rm -rf "$PYTHON_ENV_DIR/lib/python3.13/pydoc_data" +# _pyrepl: Python 3.13's new interactive REPL implementation. We never +# spawn an interactive shell from the packaged build. +rm -rf "$PYTHON_ENV_DIR/lib/python3.13/_pyrepl" +# Tcl/Tk runtime shared libraries. python-build-standalone install_only_stripped +# ships these even after the `tkinter` Python package is stripped. With the +# `_tkinter` C extension absent (lib-dynload/ is empty in this build variant; +# verified `find python-env -name '_tkinter*.so'` returns nothing), no code +# path can load these libraries. PIL.ImageTk would import them but backend +# only does `from PIL import Image`, never ImageTk. +rm -rf "$PYTHON_ENV_DIR/lib/tcl8.6" "$PYTHON_ENV_DIR/lib/tk8.6" \ + "$PYTHON_ENV_DIR/lib/itcl4.2.4" "$PYTHON_ENV_DIR/lib/thread2.8.9" \ + "$PYTHON_ENV_DIR/lib/tcl8" # ----- Babel locale-data trim (~30 MB / ~900 files) ----- # Babel ships 1,084 CLDR locale .dat files (~30 MB). Our backend doesn't use