v2.1.3 first build was 540MB (vs Stanley 153MB). Investigation:
- Our zip: app/core/node_modules = 1200MB unzipped (467 dirs)
- Stanley: node_modules = 365MB unzipped (361 dirs)
Single biggest offender: @node-llama-cpp at 701MB (local LLM binary,
pulled transitively via clawdbot when we pre-installed bundled deps).
Stanley only `npm install openclaw` (transitive resolution gets ~360
dirs, no bundled plugin deps). OpenClaw lazy-stages plugin deps at
runtime. We were forcing pre-install of all 36 bundled deps which
pulled @node-llama-cpp + clawdbot tree.
Fix:
- Drop the "pre-install bundled deps" loop entirely
- Keep belt-and-suspenders prune of @node-llama-cpp in case it shows
up via a different transitive path
The `cd "$core_dir"` for npm install changed cwd, so the trailing
`du -sh "$stage_dir"` (with stage_dir as relative path) failed:
du: cannot access 'dist/u-claw-portable-windows-v2.1.3'
Fix: prefix stage_dir with $(pwd) so it's absolute.
Surfaced by v2.1.3 release run that successfully npm-installed all
764 packages in 70s but exited 1 on the cosmetic du line.
Root cause: portable zip was 1MB skeleton; first launch triggered
`npm install` of OpenClaw + 36 bundled deps on customer's USB drive.
On exFAT-formatted USB this hangs/takes 20+ minutes, gateway never
binds 18789, dashboard shows "Gateway 未启动".
Fix follows StanleyChanH/openclaw-offline-package pattern (153MB
single-platform zip, gateway starts directly):
- release.yml: only build Windows bundle (~150-200MB). Mac users
fall back to setup.sh. Reasoning: 99% of customers are Windows;
bundling 3 platforms × Node ~30MB + 3× node_modules dedup pain
is what blew the previous attempt past GitHub's 2GB cap (see
5916ff5 history). Smaller scope = simpler + safer.
- Windows-Start.bat / Mac-Start.command: warn if node_modules
missing; fallback `npm install` adds --ignore-scripts --no-audit
--no-fund for speed.
- Release notes: NTFS U-disk recommendation.
Lessons applied (from 5916ff5):
- Stay on linux runner with --ignore-scripts (Stanley pattern)
- Drop mac-x64 / mac-arm64 Node from bundle (setup.sh handles Mac)
- rm -rf node-llama-cpp / sharp / canvas explicitly
- Hard size guard: fail CI if zip > 1500MB (buffer below 2GB cap)
Three changes for v2.1.2:
1. bootstrap-xiapan now writes "agents.defaults.model.primary/fallbacks"
(uclaw-cloud/deepseek-v4-flash + 3 fallbacks) and an empty models[],
matching the ClawX commercial schema. Lets OpenClaw runtime auto-discover
models from /v1/models instead of hard-coding a list that drifts.
2. config-server tries ports 18788..18798 with EADDRINUSE fallback and
persists the live port to data/.openclaw/runtime.json so future tooling
can discover it. Fixes silent failure when a second portable instance
started while 18788 was occupied.
3. u-claw-app/package.json version 2.0.0 -> 2.1.2 so .exe/.dmg filenames
align with the release tag.
OpenClaw 2026.4.29 renamed the per-model field from "label" to "name". The
old field is now an Unrecognized key, which fails schema validation at
gateway startup and exits OpenClaw before any HTTP listener binds.
Symptom (v2.1.0 first-launch):
- models.providers.uclaw-cloud.models.0.name: Invalid input: expected string, received undefined
- models.providers.uclaw-cloud.models.0: Unrecognized key: "label"
- throwInvalidConfig() at io-DaEsZ_NY.js:2901 -> process exit
Found via end-to-end H: drive test of v2.1.0 portable zip.
Real bug found in v2.1.0: ubuntu-runner zip preserved LF line endings, which
breaks Windows batch parsing. cmd.exe mis-tokenized 'setlocal' as 'local',
trashed env vars, and produced 'URL: //node--win-x64.zip' on Node download.
Symptom: Windows users who unzip and double-click setup.bat see a flood of
'XXX is not recognized as a command' errors followed by curl 'No host part'.
Fix: convert .bat/.cmd/.ps1 to CRLF, ensure .sh/.command/.mjs stay LF, before
zipping in the portable-skeleton job.
The previous job ran setup.{sh,bat} on the runner which downloaded Node.js +
3.4 GB of OpenClaw deps (clawdbot, @sliverp, node-llama-cpp), pushing the
Windows zip past GitHub's 2 GB single-file limit.
The portable folder is designed to fetch deps on first launch via setup.sh —
ship just the source skeleton (~1 MB) and let the user-side script populate
app/runtime + app/core/node_modules from China mirrors at first launch.
This also collapses portable-windows + portable-mac into one ubuntu-runner
job since the skeleton is platform-agnostic.
- 3-create-persistence.ps1: chain dd && mkfs.ext4 with && (was ;, so dd
failures still ran mkfs and produced bogus images); fix \wsl$\$wslDistro
variable expansion ambiguity using \${wslDistro}; mirror docker-desktop
branch with the same && chaining
- setup-openclaw.sh: guard mktemp failure; ensure partial node tarballs are
cleaned via EXIT trap; tolerate hosts with no non-loopback NIC under
pipefail; don't let test-installation.sh failure exit the post-install
- install.sh: add `set -o pipefail`; surface npm install failures (was
hidden by `| tail -5`); JSON-escape user-provided API_KEY before writing
to openclaw.json so keys with backslashes/quotes don't break the config
- Add SECURITY.md: private disclosure channel + scope
Two real bugs in the portable launchers:
1. Windows-Install.bat: xcopy reads from %TEMP%\node-<ver>-win-x64
but Expand-Archive extracts into %TEMP%\node-extract\node-<ver>-win-x64.
Result: empty runtime\node-win-x64\ on networked install path.
2. Mac-Start.command + Windows-Start.bat: legacy-config sync block
was placed AFTER the default-config block, so the "config.json
exists but openclaw.json doesn't" branch was unreachable. Existing
USB users with config.json never got their settings migrated;
they got the empty default instead.
Reordered so legacy migration runs first, default only as fallback.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When users run setup.ps1 from PowerShell with cwd outside the repo
(e.g. C:\Users\Admin>), npm 11 walks up looking for package.json and
errors with ENOENT before honoring --prefix. setup.bat already pushd's
into core; setup.ps1 was missing the equivalent Push-Location guard.
Fixes#27 (setup.ps1 ENOENT package.json reported by lawler-code).