Repository navigation
feat: Windows/macOS packaging groundwork - #40
Merged
Merged
Conversation
LlamaLibraryLocator accepts an optional package:path context so Windows and macOS path shapes can be unit-tested on any host. Production keeps the host context; only the tests pass a foreign one.
BundledRuntimes.directories accepts macOsBundle and reads Contents/Resources/runtime as well as Contents/MacOS/runtime. Reverse path order makes Resources win deterministically; the library and llama-server locators enable it when the detected platform is macOS.
Create the fixture as llama-server explicitly, since the macOS locator never looks for the .exe name, and skip the test on Windows where the POSIX executability check cannot pass. The path logic stays covered there by the BundledRuntimes and library-locator tests. Thread p.Context through BundledRuntimes so a foreign path context really does render the bundled runtime roots, and pin the Contents/Resources over Contents/MacOS precedence at both locator levels.
Name <exeDir>/runtime in the resolution order and the macOS Contents/Resources root with its precedence over Contents/MacOS.
tool/package_linux.sh resolves the repo root, changes there and execs package_app.dart with --os linux, forwarding arguments and the exit code (usage errors still exit 2). The melos workspace gains package:linux, package:windows and package:macos scripts so each platform builds through the same Dart tool without a cd or MELOS_ROOT_PATH.
A new package job builds the desktop app for its target, checks the staged layout with --check, writes the archive, then verifies it the way a user would: Expand-Archive plus llama_pulse.exe on Windows, ditto plus an executable main binary and a strict codesign check on macOS. The macOS matrix entry also asserts the built app is not sandboxed. Archives upload on main and workflow_dispatch only; the gate and test matrix are untouched.
The new CI package job found that a Flutter macOS release app keeps flutter_assets under Contents/Frameworks/App.framework, not Contents/ Resources, so the required entries now point at that path (verifyLayout follows the framework's Resources symlink). The macOS test fakes grow the App.framework and the Info.plist files codesign needs for nested frameworks, which also stops the real ditto test from failing at signing. Two pre-existing Windows test failures are fixed too: _joinEntry splits POSIX-style entries before joining so reported paths use the host separator throughout, and the Flutter launcher follows the host rather than the target, since a Windows host has flutter.bat.
Both expectations assumed a POSIX host: the build test pinned the bare flutter launcher, and the deleted-entry test joined a POSIX-style required entry without splitting it. They now accept flutter.bat and use host separators, so the Windows test matrix runs the packaging suite.
11 tasks done
Factor the pinned-release backend lookup into runtimeSelectionProblems and call it in runPackageApp before the filesystem check, so a typo or an unpublished backend exits 2 with the valid list instead of reporting a missing install.
Reject empty --bundle/--out, refuse a stage inside the bundle, resolve a symlinked bundle root, and turn FileSystemException/UnsupportedError into error: lines with exit 1 (2 for an unsupported ABI). Tests cover an occupied --out, the nested stage, empty paths and a symlinked bundle.
The entitlement read no longer ignores a missing, mispathed or unsigned app: the step asserts the built app exists and fails when codesign cannot read it. Only the grep for com.apple.security.app-sandbox stays conditional.
Run verifyLayout and the executable-bit check after ditto extraction on every host and gate only codesign on macOS. Stream Windows ZIP writes and verification so a CUDA-sized bundle is not buffered whole, and explain the synthesised tar listing in the Linux test.
Make the exit-code table true after the backend-validation change, state that CI packages runtime-free bundles and that a runtime-bearing macOS bundle is unverified, attribute the earlier Linux GUI check to PR #28, and fix the host/env-gated skip wording.
Windows reports a synthetic file mode whose bits are not POSIX permissions, so the extracted macOS executable was flagged on the Windows test runner. Keep the layout check and the POSIX executable-bit check; skip only the bit on Windows, which has no executable bit.
The packager skipped backend validation for any tag other than v0.8.0, so `--runtime v0.7.0 bogus` fell through to the install check and exited 1. Resolve the release by tag through a new RuntimeRelease.forTag/releases seam (a second pin cannot silently bypass validation), reject an unknown tag with exit 2, move the stage-inside-bundle check above the install check so the documented exit-code matrix holds, and drop the now-unreachable UnsupportedError catch.
ZipFileEncoder.addFileSync opens an InputFileStream per entry and never closes it, so a runtime-bearing Windows bundle could exhaust file handles. Use the async addFile, which closes the stream, delete the archive when the write fails instead of leaving a truncated file at the final path, and cover a zero-byte plus a 5 MiB entry round-trip.
CI packages runtime-free bundles, so only that layout and the ad-hoc signature are CI-verified; the Contents/Resources runtime placement is unit-tested only. Name both open #39 checklist items (the pathContext seam and the runtime-bearing CI bundle), record the unknown-tag and usage-error precedence in docs/packaging.md, and refresh the measured counts to 1288 (889 core plus two skips, 233 CLI, 166 app).
…ag + pin) Integrates the runtime archive fix and the build-script commit check from PR #38 after it merged into main. Conflicts resolved: - runtime_release.dart: keep RuntimeRelease.releases/forTag from this branch and assetTag on every asset from main. tag stays v0.8.0 (install identity), assetTag is b11960 (download release). - runtime_release_test.dart and docs/runtime.md merged cleanly and now cover both sides: nightly URLs plus releases/forTag, and the bundled-runtime resolution order. docs/status.md, AGENT.md and README.md record 1291 tests (892 core plus two host/env-gated skips, 233 CLI, 166 app) measured on this merge.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Steps 1–9 of the Windows/macOS packaging track (#39). This PR adds the path seams, the macOS sandbox decision, a packaging tool that stages a bundle, writes the platform archive and verifies it, CI jobs for both targets, and the documentation and checkpoint refresh. Issue #39's final
ServerBinaryLocatorImplpathContextitem is not part of this PR.Step 1 — path-context seam in the library locator
LlamaLibraryLocatortakes an optionalpackage:pathContext. Everyjoin/normalize/isAbsolute/dirname/separatorcall now goes through_path, which defaults top.context. Production is unchanged; tests pass awindowsorposixcontext to exercise Windows and macOS path shapes on Linux.<exeDir>\lib\llama.dll,<exeDir>\llama.dll,<exeDir>\..\Frameworks\llama.dll, the configlib\llama.dll, barellama.dlllast.lib/andFrameworks/dylibs, then the bundled runtime, then config, then barelibllama.dylib.Step 2 — macOS
Contents/Resources/runtimeBundledRuntimes.directoriesgainsmacOsBundle:. It reads<exeDir>/runtimealways and<exeDir>/../Resources/runtimewhen set. Roots are deduplicated and sorted reverse path order, soContents/Resources/runtimedeterministically wins overContents/MacOS/runtime. Unreadable roots and entries are still skipped.LlamaLibraryLocatorandServerBinaryLocatorImplenable the extra root when the detected OS ismacos.Resulting macOS resolution order: config runtime (selected first, then newest) →
<exeDir>/lib/<exeDir>//<exeDir>/../Frameworks→ bundledContents/Resources/runtimebeforeContents/MacOS/runtime→ configlib→ barelibllama.dylib.Step 3 — macOS release build runs outside the App Sandbox
com.apple.security.app-sandboxis removed from bothRelease.entitlementsandDebugProfile.entitlements; the remaining DebugProfile entitlements (cs.allow-jit,network.server) stay. A comment in each file records why: LlamaPulse supervises an externalllama-serverprocess and opens.gguffiles the user picks anywhere on disk, and Mac App Store distribution is out of scope. No other entitlement orInfo.plistkey changes —macos/Runner/Info.plisthas no sandbox key, and there is noRunner.entitlementsvariant.Step 4 — cross-platform packaging skeleton
New, under
packages/llama_pulse_core/tool/:package_app.dart—ArgParser-based entry point. Steps 5–6 wire it to the staging and archive writer; exit 0 success/--help/--check, 1 a missing bundle or runtime or a layout/archive problem, 2 usage or validation error.packaging/package_target.dart—PackageTarget { linux, windows, macos }with executable names, archive kind (tar.gzon Linux,zipelsewhere), default Flutter bundle directory per target,artifactId, runtime-platform mapping and the runtime directory inside the stage (Contents/Resources/runtime/...on macOS).packaging/package_plan.dart— pubspec version parsing (drops+build, rejects malformed values), stage/archive names,archFromAbi()viadart:ffi, repo-root discovery fromPlatform.script, exact required entries per target andverifyLayout.packaging/process_runner.dart—ProcessRunner/SystemProcessRunnerseam andflutter.baton Windows.Frozen CLI surface:
Stage and archive names are version-derived and Linux reproduces today's name exactly:
llama-pulse-0.1.0-linux-x64llama-pulse-0.1.0-linux-x64.tar.gzllama-pulse-0.1.0-windows-x64llama-pulse-0.1.0-windows-x64.zipllama-pulse-0.1.0-macos-arm64llama-pulse-0.1.0-macos-arm64.zipllama-pulse-0.1.0-macos-x64llama-pulse-0.1.0-macos-x64.zipStill a no-op by design: staging the Flutter output, copying a managed runtime, writing archives and wiring CI are steps 5–8 in a later PR with the same frozen surface.
Dependency note
argsmoves from the CLI's runtime dependency into core's dev_dependencies only; the CLI keeps it too. It is already in the workspace dependency graph, so no new package enters the resolution andpubspec.lockstays untracked.archive,pathandtestwere already used by core; no other dependency changes.Step 5 — staging
packaging/staging.dartimplementsstage(...):*.pdb,*.expand*.lib; macOS copiesllama_pulse.appas one unit, so the framework symlinks underContents/Frameworksstay symlinks.README.mdandLICENSEbeside the bundle: stage root on Linux/Windows, beside the.appon macOS, never insideContents/.tag(^[A-Za-z0-9._-]+$, no..) andbackend(^[a-z0-9]+$), requiresllama-server/llama-server.exeandruntime.yaml, checks the pinnedv0.8.0backend againstRuntimeRelease.v080, and copies the tree verbatim toruntime/<tag>/<backend>(macOS:Contents/Resources/runtime/...). Rejections list the valid backends for the platform.codesign --force --deep --sign - <app>after adding resources.flutter build <os> --releaseinapps/llama_pulseunless--skip-build(flutter.baton Windows).verifyLayoutnow reports wrong-type entries as well as missing ones, one problem per entry.Step 6 — archives
packaging/archive_writer.dart:tar -C <out> -czf <archive> <stage>(system tar keeps the executable bit), thentar -tzfverification.package:archiveZIP over the stage with/separators; verification re-opens it withZipDecoderand checks the stage prefix and required entries.ditto -c -k --sequesterRsrc --keepParent <stage> <archive>; verification extracts withditto -x -kinto a temp dir, checks the executable bit and runscodesign --verify --deep --stricton a macOS host.package_app.dartnow builds, stages, archives and verifies, printingcreated <archive> (size, verified)plus a run hint.--checkstages into a temporary directory and verifies it without writing an archive. Any layout or archive problem exits 1 with a readable message.Step 7 — Linux shim and melos scripts
tool/package_linux.shis now a thin compatibility shim: it resolves the repo root from its own location,cds there andexecsdart run packages/llama_pulse_core/tool/package_app.dart --os linux "$@". Arguments and the exit code pass through, so usage errors still exit 2 and--helpprints the tool help.The workspace pubspec gains cross-shell scripts with no
cdand no$MELOS_ROOT_PATH:Verified on Linux:
tool/package_linux.sh --skip-build --bundle apps/llama_pulse/build/linux/x64/debug/bundle --out /tmp/opencode/dist-shimproducedllama-pulse-0.1.0-linux-x64.tar.gz(31.0 MiB, verified) andtar -tzflistsllama-pulse-0.1.0-linux-x64/llama_pulse;--bad-flagexits 2;dart run melos run package:linux -- --skip-build --checkstages and verifies.One melos 8.10.0 limitation:
melos run <script> -- --helpcannot forward--help, because melos treats any--helpargument as a request for its own help and loads an empty config (lib/src/command_runner.dart,_shouldUseEmptyConfig). Usetool/package_linux.sh --helpor the directdart run .../package_app.dart --help, which both print the tool help.Step 8 — CI packaging jobs
A new
packagejob, separate fromgateand thetestmatrix (both untouched), runs amacos-latest/macosand awindows-latest/windowsleg with the workflow's pinned Flutter (FLUTTER_VERSION3.47.7). Each leg checks out, installs Flutter, resolves the workspace, runsflutter build <target> --releaseinapps/llama_pulse, runs the packager twice —--checkfor the staged layout, then without--checkto write the archive — and verifies the archive natively:shell: pwsh):Expand-Archiveintodist/verify, then assert allama_pulse.exeexists.ditto -x -kintodist/verify, assertllama_pulse.app/Contents/MacOS/llama_pulseis executable andcodesign --verify --deep --strictpasses. A second step grepscodesign -d --entitlements :-of the built app forcom.apple.security.app-sandboxand fails if present.actions/upload-artifact@v4uploadsdist/*.zipasllama-pulse-<target>with 7-day retention, gated onrefs/heads/mainpushes andworkflow_dispatch.PackageTarget.defaultBundleDiralready matches the Flutter release output for both targets (build/windows/x64/runner/Release,build/macos/Build/Products/Release/llama_pulse.app), so no Dart default changed.Follow-up fixes from the first CI run
requiredEntriesnow looks for the macOSflutter_assetsunderContents/Frameworks/App.framework/Resources/, where Flutter actually puts them; the firstPackage (macos)run failed on the oldContents/Resourcespath. The test fakes grow matchingApp.frameworkbundles and frameworkInfo.plistfiles socodesignaccepts the nested code._joinEntrysplits POSIX-style entries before joining, so reported paths use the host separator throughout, and the Flutter launcher follows the host (flutter.baton Windows) rather than the target.a failed abort of a start fails start and keeps the child) failed once and passed on the next run; it is a timing test unrelated to packaging.Step 9 — documentation
docs/packaging.mdis restructured around the three targets: concrete trees for Linux, Windows and macOS; per-host requirements; theruntime/<tag>/<backend>(macOSContents/Resources/runtime) placement; the exact backends per platform; the Dart tool's flags and exit codes;--check;tar.gz/zipand thedittomacOS archive; and the fact thatruntime.yamlis copied, never generated.ditto -x -kverification and the quarantine consequence, and states explicitly that signing/notarisation is out of scope.windows-latest/macos-latest; macOS archive correctness cannot be verified on the Linux workstation.README.mdgains a "Package it" table for the three targets, drops the "Windows/macOS bundles are not part of it yet" wording, updates thetool/line and the measured test count.AGENT.mdgains thepackage:linux|windows|macoscommands and the direct Dart entry point (--skip-build --check), the newpackageCI job with artifacts only onmain/workflow_dispatch, the packaging definition-of-done item, the Windows/macOS CI-only note, and theargsdev-dependency justification.docs/status.mdis refreshed to this branch's baseline and measured counts and gains the Windows/macOS packaging entry, landmines and the remaining plan.ServerBinaryLocatorImplpathContextitem and CI verification of a runtime-bearing macOS bundle) stay open. The pinned checkpoint Project status & roadmap (checkpoint 2026-11-09) #16 is refreshed after merge, not in this PR.Remaining for the operator
docs/status.mdwith the merged commit.Review fixes
.appexists and fails ifcodesign -d --entitlements :-cannot read it; only the grep forcom.apple.security.app-sandboxremains conditional. Flutter's ad-hoc app with no sandbox entitlement exits 0 and prints nothing, so the step still passes on 0.8.0.runtimeSelectionProblemsinstaging.dartresolves the tag throughRuntimeRelease.forTagand validates the backend against it;runPackageAppcalls it before touching the filesystem. An unknown tag (v0.7.0) exits 2 with "unknown runtime tag v0.7.0; this build packages v0.8.0", an unknown (cda13) or unpublished (metalon Windows) backend exits 2 and lists the valid backends; a valid but missing install still exits 1 with "install the runtime first". A stage path inside--bundleis refused (exit 2) before the install check, so the documented matrix holds even when both are wrong.docs/packaging.md's exit-code matrix matches.codesign --force --deepsigns the runtime's dylibs underContents/Resourcesand--verify --deep --strictthen passes is unproven.docs/packaging.mdsays so, and issue chore(packaging): Windows and macOS desktop bundles #39 carries a checklist item for it.ZipFileEncoder/InputFileStreamwrite each file incrementally andZipDecoder().decodeStream(...)verifies from the file's central directory instead ofreadAsBytesSync. A CUDA-sized bundle no longer sits in memory in one piece,addFilecloses each entry's stream, and a failed write deletes the truncated archive. The installedarchive4.6.0 supports both APIs cleanly, so this is the real fix, not a documented ceiling.runPackageAppcatchesFileSystemException(create/delete/copy/chmod, unwritable/occupied--out), printserror: …and returns 1. ThearchFromAbiUnsupportedErroris handled where it is raised (exit 2 for an unsupported ABI), so an unrelatedUnsupportedErrorfrom a later call is no longer swallowed as a usage error. Tests occupy--outwith a file.--bundle/--outexit 2; a stage path inside--bundle(including equality) exits 2; a--bundlesymlink to a directory is resolved before copying; the shim'scdto the repo root is documented so relative paths anchor there._verifyDittonow runsverifyLayouton any host and the executable-bit check on every POSIX host (Windows has no executable bit); onlycodesignis still gated on macOS, and the real-dittotest stays macOS-only. The Linux verifier test carries a comment explaining that itstar -tzflisting is synthesised fromrequiredEntries, with the Linux gate job covering the real-tar gap.Review follow-ups (second pass)
docs/status.mdno longer claims CI verifies theContents/Resourcesruntime placement: CI packages runtime-free bundles, so only that layout and the ad-hoc signature are CI-covered; the placement is unit-tested pending issue chore(packaging): Windows and macOS desktop bundles #39. Both open chore(packaging): Windows and macOS desktop bundles #39 checklist items are named in the checkpoint and the remaining plan, and issue chore(packaging): Windows and macOS desktop bundles #39's footer note says items 10–11.RuntimeReleasegainedreleases/forTag, soruntimeSelectionProblemsresolves the tag instead of comparing tov080; a second pin cannot silently skip validation. Updated the tests anddocs/packaging.md.--outplus a missing runtime exits 2 as documented.UnsupportedErrorcatch around the staging/archive block is gone; only thearchFromAbicall keeps its own handler._writeZipis async and usesaddFile, which closes the per-entryInputFileStream; a failed write deletes the truncated archive instead of leaving it at the final path. Streaming (no whole-archive buffering) is unchanged.runPackageAppexit 1 for an installed-but-missing runtime (cuda13) with the install hint; unknown tag (v0.7.0) exits 2; a nested--outbeats a missing runtime; a zero-byte and a 5 MiB ZIP entry round-trip;RuntimeRelease.forTagresolves the pin and rejects unknown tags.Tests
library_locator_test.dart: Windows context candidates, macOS posix candidates, a macOS bundle library underContents/Resourcesresolves, and a config runtime still wins over it.bundled_runtimes_test.dart:Contents/Resourcesis listed only withmacOsBundle: true,Resourcessorts beforeMacOS, and the resources root is computed correctly.server_binary_locator_test.dart: a bundledllama-serverunderContents/Resources/runtime/...is located (existing fake version reader).test/tool/packaging/package_target_test.dart: executable names, archive kinds, default bundle dirs, runtime-platform/artifact mapping and runtime stage dirs.test/tool/packaging/package_plan_test.dart: version parsing including+buildand malformed input, all stage/archive names, exact required entries per target, runtime entries,verifyLayoutsuccess/failure, ABI mapping, repo-root discovery.test/domain/runtime/runtime_release_test.dart: the v0.8.0 pin (tag, build, commit, digests, sizes), per-platform backend coverage and archive names,releases/forTag(the pin resolves, an unknown tag is null), CUDA cudart archives, and platform naming.test/tool/packaging/package_app_test.dart:--helpexits 0 and lists every flag; unknown flag and missing--runtimevalue exit 2;--skip-buildwith a nonexistent bundle exits 1 with no writes;--runtimerefusal onmacos-x64/linux-arm64; an unknown tag (v0.7.0), an unknown backend (cda13) and an unpublished backend (metalon Windows) exit 2 before the install check and list the valid options; a published but missing runtime (cuda13) exits 1 with the install hint; empty--bundle/--outand a stage inside the bundle exit 2, the latter even when the runtime is also missing; an occupied--outexits 1 witherror:; a symlinked--bundleis resolved;--checkstages, verifies and creates no output directory; a non-check Windows run writes and verifies the ZIP; a layout failure exits 1.test/tool/packaging/staging_test.dart: every target layout stages and verifies clean;.pdb/.exp/.libare excluded; README/LICENSE placement; framework symlinks survive; the runtime lands at the documented path; failure modes (missing bundle, missing server binary, missingruntime.yaml,..tag, unknown tag, unknown backend,metalon Windows,cuda13on macOS,macos-x64);verifyLayoutflags each deleted entry; the exactflutter build <os> --releasecommand and the macOS-onlycodesigncall.test/tool/packaging/archive_writer_test.dart: streamed Windows ZIP round-trip with a single stage prefix, plus a zero-byte and a 5 MiB entry that survive byte for byte; exact Linuxtar -C/-tzfand macOSditto -c/-xargv; thedittofake now verifies the extracted layout and executable bit on any host; a realdittoarchive test guarded byPlatform.isMacOS.Verification
cd packages/llama_pulse_core && dart test test/tool/packaging→ 78 passed, 1 skipped (the real macOS archive test).dart run packages/llama_pulse_core/tool/package_app.dart --help→ exit 0.--os windows --skip-build --bundle /tmp/opencode/missing→ clear error, exit 1, no path created.dart run melos analyze,dart run melos run format:check,dart run melos run gentwice (0 outputs written),dart run melos run gen:catalog:checkand fulldart run melos run checkare green.dart run packages/llama_pulse_core/tool/package_app.dart --os linux --skip-build --bundle apps/llama_pulse/build/linux/x64/release/bundle --out /tmp/opencode/dist-smokeproducedllama-pulse-0.1.0-linux-x64.tar.gz(10.2 MiB, verified).tar -tzflists the expected prefix, extraction succeeds andllama_pulseis executable.origin/main(which brought PR fix(runtime): download the pinned archives from the nightly release #38's new runtime-asset tests): core 892 passed (+2 skipped), CLI 233, app 166 — 1291 tests. The packaging suite alone is 78 passed (+1 skipped). The extra skip is the real-dittoarchive test.gate, thetestmatrix (Linux, macOS, Windows), the standalone Windows CLI compile, andPackage (macOS)/Package (Windows).Merge with main (runtime asset URLs, PR #38)
origin/mainmoved toc72c5ec(the PR #38 merge) after this branch was cut, so the branch merges it in as8549e0e.runtime_release.dart: both sides kept.RuntimeRelease.v080,releasesandforTagstay, and every asset still carriesassetTag: b11960from fix(runtime): download the pinned archives from the nightly release #38.tagremainsv0.8.0(install identity, manifests, attestation,<config>/runtime/<tag>/<backend>), and the download URL is built fromassetTag.runtime_release_test.dartmerged cleanly and now asserts both: URLs under/download/b11960/,assetTag == 'b11960', no/download/v0.8.0/, install tagv0.8.0, andreleases/forTagresolving the pin.docs/runtime.mdkeeps both edits: the nightly-release/assetTagexplanation and this branch's bundled-runtime resolution order.tool/build_llama_cpp.shtakes main's pinned-commit check unchanged;BUILD.mdandRUNTIME_ASSETS.mdkeep fix(runtime): download the pinned archives from the nightly release #38's text.docs/status.md/AGENT.md/README.mdkeep this branch's checkpoint and fold in fix(runtime): download the pinned archives from the nightly release #38: theassetTagfix, the pinned-commit check, baselinec72c5ec, fix(runtime): download the pinned archives from the nightly release #37 closed, and the re-measured 1291 count.RuntimeRelease.forTag('v0.8.0')is thev080instance, the Linux CPU asset carriestag=v0.8.0,assetTag=b11960, and its URL ishttps://github.com/ggml-org/llama.cpp/releases/download/b11960/llama-b11960-bin-ubuntu-x64.tar.gz.Refs #39