feat(computer): ship the Windows helper exe as a release asset with on-demand download#849
Conversation
…n-demand download setup --host failed for npm-installed CLIs with 'Windows helper exe not built' because the ~157MB exe never ships in the tarball and the only resolution paths were local build outputs (GitHub #547). Two halves: - computer-helper-win.yml now also triggers on v* tags (pushed by scripts/release.sh) and a new release-exe job publishes the self-contained single-file exe, smoke-tests that exact artifact, and uploads it + a sha256sum-format .sha256 as GitHub release assets (creating the release for the tag when missing; --clobber for re-runs). The branch/PR smoke jobs are skipped on tag runs since release-exe re-smokes the artifact it ships. - resolveWinHelperExe stays local-paths-first; setupRemoteHelper now falls through to ensureWinHelperExe, which downloads releases/download/v<cli-version>/computer-helper-win.exe, verifies its sha256 against the published .sha256 asset, and caches it under ~/.agents/.cache/computer/win-helper/v<version>/. Only the exact tag is consulted; a missing asset is a hard error naming that tag. Verified end-to-end against a temporary prerelease (deleted after): with no local exe and an empty cache, computer setup --host win-mini downloaded the asset, checksum-verified it, pushed it to win-mini, and start --host reported the daemon answering.
Code ReviewerVerdict: Ready to merge Build: Clean — Repo instructions read: root Changes that work well
Things to verify manually
Reviewed by Code Reviewer — actually ran the build and tests on this branch. |
Code ReviewerVerdict: Ready to merge (two non-blocking design/doc notes below) Build: Clean — Read for repo conventions before reviewing: root Changes that work well
Issues that need attention1. Publish/tag-push ordering leaves a window where
2. This is the same incorrect "staged into the npm tarball at release" claim that Things to verify manually
Reviewed by Code Reviewer — actually ran the build and tests on this branch. |
Code ReviewerVerdict: Changes requested (one real concurrency bug; rest is solid) Build: Clean — Read for context: root I also verified the PR's central technical claim independently: GitHub's Changes that work well
Issues that need attention1. Race condition: concurrent downloads for the same version corrupt/clobber each other — const partial = `${cached}.download`;
try {
await pipeline(Readable.fromWeb(exeRes.body ...), fs.createWriteStream(partial));
const actual = await sha256File(partial);
if (actual !== expected) throw new Error(...);
fs.renameSync(partial, cached);
} finally {
fs.rmSync(partial, { force: true });
}
This isn't a hypothetical the codebase hasn't already solved: Fix: suffix Nit — documentation left in the exact state this PR calls "wrong"The PR body says
Things to verify manually
Reviewed by Code Reviewer — actually ran the build and tests on this branch. |
Closes #547.
agents computer setup --hostfailed for anynpm i -guser withWindows helper exe not built. Run: bash scripts/build-win.sh— the ~157MBcomputer-helper-win.exenever ships in the npm tarball andresolveWinHelperExe()only checked local build-output paths.Half 1 — release plumbing (
.github/workflows/computer-helper-win.yml)on.pushnow also matchestags: ['v*'](the tagapps/cli/scripts/release.shpushes at the merge commit — no release.sh change needed). Per GitHub docs, "Path filters are not evaluated for pushes of tags", so the existingpathsfilter does not gate tag runs.release-exejob (if: github.ref_type == 'tag',contents: write): publishes the self-contained single-file exe with the exactbuild-win.shflags, smoke-tests that artifact (smoke/smoke.mjs), writes a sha256sum-format.sha256, creates the GitHub release for the tag if missing (--verify-tag), andgh release upload --clobbers both assets.if: github.ref_type != 'tag'— release-exe re-smokes the artifact it ships, so tag runs don't double-build.Half 2 — download fallback (
apps/cli/src/lib/ssh-tunnel.ts)resolveWinHelperExe()keeps local-path resolution first, unchanged.setupRemoteHelper()now callsensureWinHelperExe(): local build if present, elsedownloadWinHelperExe(getCliVersion())— fetchesreleases/download/v<version>/computer-helper-win.exe, verifies its sha256 against the published.sha256asset, streams to a.downloadpartial and renames only after the checksum passes, caches under~/.agents/.cache/computer/win-helper/v<version>/.v<version>tag is consulted. A missing asset is a hard error naming that tag (no computer-helper-win.exe release asset for tag v<X> (HTTP 404 on <url>)) — no fallback to other tags.Verification (real flow, quoted)
Temporary prerelease
v0.0.0-win-exe-testwith a locally cross-published exe +.sha256as assets; CLI version temporarily pointed at it; local exe paths absent; empty cache. Note: the issue plan said draft release, but draft assets are not served at the publicreleases/download/<tag>/URL (probed:404) — they are API-only until published — so the test used a short-lived published prerelease, deleted immediately after along with its tag (git ls-remote --tags origin | grep win-exe-test→ no match).vitest: 17/17 pass inssh-tunnel.test.ts(5 new: URL pinning,.sha256parsing, streaming sha256 vector, cache hit without network, and a real GitHub 404 asserting the error names the exact tag).actionlinton the workflow: clean.tsc --noEmit: clean.Final CI proof of the release job completes on the next real
v*tag — the job only fires on tag pushes, which this PR cannot trigger.Docs:
native/computer-win/README.mdcorrected (it claimed release scripts stage the exe into the npm tarball — they never did) + CHANGELOG entry under Unreleased.