fix(ai): resolve sidecar spawn path and auto-install the llama.cpp engine (I73) - #88
Conversation
…gine (I73)
The in-app llama-server spawn never worked: shell().sidecar("binaries/
llama-server") resolves <exe_dir>/binaries/llama-server, but tauri-build
and the bundler place the file at <exe_dir>/llama-server — every
sidecar_start since V2-P1 failed with ENOENT, surfaced as "AI failed to
start" / "AI model crashed". Day-one bug (plugin pinned at 2.3.5 since
V1-P1), masked until I72 let model downloads complete.
Fix + hardening, treated as one production story:
- Spawn via shell().command() on absolute paths: bundled binary at
<exe_dir>/llama-server(.exe) (size-gated), then a managed install at
data_dir/engine/<tag>-<triple>/; spawn failures fall through candidates.
- New commands/engine.rs: when nothing resolves, sidecar_start downloads
the pinned llama.cpp b9095 asset for the current triple (SHA-256
verified, pins lockstep-tested against scripts/fetch-llama-server.sh),
extracts llama-server + companion libs (flattened, filtered, traversal-
proof), installs atomically, and spawns it. Concurrent installs
coalesce; failures persist in engine_info.last_error.
- engine_auto_install setting (default ON, absent-key-safe) + Settings →
AI "AI engine" row: status, live download progress (role=status), and
Install/Reinstall (stops the sidecar first; locked during sessions).
- engine_not_installed sentinel (auto-install off) maps to a calm
"Install it in Settings → AI" toast, not a crash string.
- build.rs writes a debug-profile-only placeholder so fresh checkouts
compile and run with zero manual steps; release-profile builds still
hard-fail without real binaries.
- Windows spawn failures name the missing VC++ redistributable
(vcruntime140.dll probe) with the aka.ms install link.
Verified live on macOS: installed-bundle binary spawns via the exact new
resolution (--version, exit 0); placeholder build compiles + launches;
pinned archives download, hash-match, extract, and run. GUI walkthrough
is user-walked (dev-binary keychain prompt blocks machine-driving).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Warning Review limit reached
Next review available in: 36 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (9)
📝 WalkthroughWalkthroughThe PR adds a pinned, checksum-verified llama-server auto-installer, absolute-path sidecar resolution with managed fallback, frontend engine controls and progress reporting, persisted auto-install settings, debug placeholders, and related documentation and tests. ChangesAI engine lifecycle
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant SessionView
participant SidecarRuntime
participant sidecar_start
participant EngineCommands
SessionView->>SidecarRuntime: start engineAutoInstall
SidecarRuntime->>sidecar_start: invoke startup command
sidecar_start->>EngineCommands: resolve or install engine
EngineCommands-->>sidecar_start: return executable path
sidecar_start-->>SidecarRuntime: return port or engine_not_installed
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches✨ Simplify code
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (4)
tests/unit/ai-sidecar.test.ts (2)
176-178: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsider covering the auto-install-off path too.
Every stub pins
getEngineAutoInstalltotrue, so nothing exercisesengineAutoInstall: falseforwarding or theERR_ENGINE_NOT_INSTALLEDsentinel surfacing throughlastError— the two behaviors this cohort actually adds on the JS side.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/unit/ai-sidecar.test.ts` around lines 176 - 178, Extend the ai-sidecar tests around start() to cover getEngineAutoInstall returning false, asserting engineAutoInstall: false is forwarded to sidecar_start and ERR_ENGINE_NOT_INSTALLED is surfaced through lastError. Keep the existing true-path coverage unchanged.
27-31: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
startCalls's element type is now stale.It omits
engineAutoInstalleven thoughpush(params)carries it, so a future drift in the forwarded payload wouldn't be caught at type level.♻️ Widen the recorded type
const startCalls: Array<{ modelPath: string mmprojPath: string | null ctxSize: number + engineAutoInstall: boolean }> = []Also applies to: 98-98
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/unit/ai-sidecar.test.ts` around lines 27 - 31, Update the startCalls element type and its recorded entries to include the engineAutoInstall field forwarded by push(params), matching the parameter shape used by the startup call so future payload drift is caught by TypeScript.tests/unit/settings-migration.test.ts (1)
160-171: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valuePrefer the exported key constant over the string literal.
fakeStore({ engine_auto_install: false })hardcodes the key; usingSETTINGS_KEY_ENGINE_AUTO_INSTALLkeeps the test pinned to the same contracthydrateValuesFromStorereads.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/unit/settings-migration.test.ts` around lines 160 - 171, Update the fakeStore input in the “defaults engineAutoInstall on when missing and preserves an explicit off” test to use the exported SETTINGS_KEY_ENGINE_AUTO_INSTALL constant instead of the hardcoded engine_auto_install string, preserving the existing false-value assertion.src-tauri/src/commands/engine.rs (1)
305-326: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winStaging dir and partial archive leak on failure paths.
If
extract_archivefails, the??returns beforefs::remove_file(&archive_path), leaving both.download-*and a partial.tmp-*behind. Same for thestaged_binarymetadata error at Line 317. They're swept by the nextcleanup_stale, but a failed install then leaves a full archive (~100 MB) on disk indefinitely if the user never retries.♻️ Remove the archive before propagating extraction errors
let staging = root.join(format!(".tmp-{ENGINE_TAG}-{TARGET_TRIPLE}")); - { + let extract_result = { let archive_path = archive_path.clone(); let staging = staging.clone(); tauri::async_runtime::spawn_blocking(move || { extract_archive(&archive_path, &staging, binary_name()) }) .await - .map_err(|e| format!("extract task: {e}"))??; - } - let _ = fs::remove_file(&archive_path); + .map_err(|e| format!("extract task: {e}")) + .and_then(|r| r) + }; + let _ = fs::remove_file(&archive_path); + if let Err(e) = extract_result { + let _ = fs::remove_dir_all(&staging); + return Err(e); + } let staged_binary = staging.join(binary_name()); - let staged_len = fs::metadata(&staged_binary) - .map_err(|e| format!("{} missing from archive: {e}", binary_name()))? - .len(); + let staged_len = match fs::metadata(&staged_binary) { + Ok(m) => m.len(), + Err(e) => { + let _ = fs::remove_dir_all(&staging); + return Err(format!("{} missing from archive: {e}", binary_name())); + } + };🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src-tauri/src/commands/engine.rs` around lines 305 - 326, Update the archive extraction flow around extract_archive so cleanup runs before propagating extraction errors: remove archive_path and staging on any extract failure, including partial archives. Also clean up both archive_path and staging when fs::metadata(&staged_binary) fails, while preserving the existing validation and success path.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@CHANGELOG.md`:
- Around line 40-52: Update the changelog section describing the AI engine
installation to say installation is attempted when the default-enabled setting
permits it and may fail, replacing unconditional “automatically” and “No error,
no manual step” claims. Also update README.md lines 330-334 to state that debug
builds auto-install only while the setting remains enabled; when disabled, users
must provide or manually install the engine.
In `@src-tauri/src/commands/sidecar.rs`:
- Around line 185-195: Update the sidecar_start handler to acquire the shared
EngineState.gate using its existing engine state before resolve_candidates and
keep the guard through the spawn operation. This must serialize presence checks
and process startup with ensure_installed/forced reinstall, while preserving the
current installation and error-handling behavior.
In `@src/strings.ts`:
- Around line 1218-1245: Update the AI privacy messaging to limit “Nothing
leaves your computer” to camera and screen data, and explicitly disclose that
the llama.cpp engine may be downloaded from GitHub. Keep the disclosure
consistent with the engine.auto.help text and the default auto-install flow in
the engine strings.
---
Nitpick comments:
In `@src-tauri/src/commands/engine.rs`:
- Around line 305-326: Update the archive extraction flow around extract_archive
so cleanup runs before propagating extraction errors: remove archive_path and
staging on any extract failure, including partial archives. Also clean up both
archive_path and staging when fs::metadata(&staged_binary) fails, while
preserving the existing validation and success path.
In `@tests/unit/ai-sidecar.test.ts`:
- Around line 176-178: Extend the ai-sidecar tests around start() to cover
getEngineAutoInstall returning false, asserting engineAutoInstall: false is
forwarded to sidecar_start and ERR_ENGINE_NOT_INSTALLED is surfaced through
lastError. Keep the existing true-path coverage unchanged.
- Around line 27-31: Update the startCalls element type and its recorded entries
to include the engineAutoInstall field forwarded by push(params), matching the
parameter shape used by the startup call so future payload drift is caught by
TypeScript.
In `@tests/unit/settings-migration.test.ts`:
- Around line 160-171: Update the fakeStore input in the “defaults
engineAutoInstall on when missing and preserves an explicit off” test to use the
exported SETTINGS_KEY_ENGINE_AUTO_INSTALL constant instead of the hardcoded
engine_auto_install string, preserving the existing false-value assertion.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: edd09be6-9944-452d-951a-1be161441250
⛔ Files ignored due to path filters (1)
src-tauri/Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (20)
ARCHITECTURE.mdCHANGELOG.mdISSUES.mdREADME.mdsrc-tauri/Cargo.tomlsrc-tauri/build.rssrc-tauri/src/commands/engine.rssrc-tauri/src/commands/mod.rssrc-tauri/src/commands/models.rssrc-tauri/src/commands/sidecar.rssrc-tauri/src/lib.rssrc/features/ai/engine.tssrc/features/ai/index.tssrc/features/ai/sidecar.tssrc/features/session/SessionView.tsxsrc/features/settings/categories/AiCategory.tsxsrc/stores/settingsStore.tssrc/strings.tstests/unit/ai-sidecar.test.tstests/unit/settings-migration.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (3)
- GitHub Check: Rust (Windows)
- GitHub Check: Rust (macOS)
- GitHub Check: Frontend
🧰 Additional context used
📓 Path-based instructions (9)
tests/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use Vitest for unit, integration, and AI-evaluation tests; do not assume RTL/jsdom-based component tests are available.
Files:
tests/unit/settings-migration.test.tstests/unit/ai-sidecar.test.ts
**/*.{ts,tsx,js,jsx,css,json,md,rs,toml,yml,yaml}
📄 CodeRabbit inference engine (CLAUDE.md)
Run repository formatting before committing multi-file or subagent work; Prettier formatting must pass for the whole tree.
Files:
tests/unit/settings-migration.test.tssrc-tauri/src/commands/mod.rssrc-tauri/Cargo.tomlREADME.mdsrc-tauri/src/commands/models.rssrc-tauri/build.rssrc/features/session/SessionView.tsxARCHITECTURE.mdsrc/features/ai/index.tssrc-tauri/src/lib.rsCHANGELOG.mdsrc/features/ai/engine.tstests/unit/ai-sidecar.test.tssrc/strings.tssrc/features/ai/sidecar.tsISSUES.mdsrc/stores/settingsStore.tssrc/features/settings/categories/AiCategory.tsxsrc-tauri/src/commands/engine.rssrc-tauri/src/commands/sidecar.rs
src-tauri/**/*.rs
📄 CodeRabbit inference engine (CLAUDE.md)
src-tauri/**/*.rs: Rust changes must preserve compatibility with persisted local data and peer-facing contracts.
Rust changes requirecargo test,cargo fmt --check, andcargo clippybefore review.
Files:
src-tauri/src/commands/mod.rssrc-tauri/src/commands/models.rssrc-tauri/build.rssrc-tauri/src/lib.rssrc-tauri/src/commands/engine.rssrc-tauri/src/commands/sidecar.rs
src-tauri/{Cargo.toml,Cargo.lock,tauri.conf.json}
📄 CodeRabbit inference engine (CLAUDE.md)
Release version bumps must update the
src-tauriversion entries inCargo.toml,Cargo.lock, andtauri.conf.jsonin lockstep with the frontend package version.
Files:
src-tauri/Cargo.toml
src/**/*.{ts,tsx,css}
📄 CodeRabbit inference engine (CLAUDE.md)
src/**/*.{ts,tsx,css}: Frontend code must use design tokens rather than raw visual values; token compliance is enforced byscripts/check-tokens.ts.
Maintain WCAG AA contrast for every text/background pairing in both themes, provide information without relying on color alone, and respect the global reduced-motion kill switch.
Files:
src/features/session/SessionView.tsxsrc/features/ai/index.tssrc/features/ai/engine.tssrc/strings.tssrc/features/ai/sidecar.tssrc/stores/settingsStore.tssrc/features/settings/categories/AiCategory.tsx
src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
src/**/*.{ts,tsx}: New motion sites must be gated by the global reduced-motion behavior by default.
Peer wire formats and identity derivation are cross-version contracts; coordinate changes so older peers remain compatible and stored identity/data are not stranded.
The application must remain local-only with no telemetry; never instruct users to paste model files or BIP39 mnemonics into an AI service.
Do not add comments unless the rationale is non-obvious; preserve established provenance tags such asI9,F6, orPR-27when fixing traceable code.
Avoid unrelated refactors, hypothetical abstractions, and adjacent cleanup when implementing a feature or bug fix.
Files:
src/features/session/SessionView.tsxsrc/features/ai/index.tssrc/features/ai/engine.tssrc/strings.tssrc/features/ai/sidecar.tssrc/stores/settingsStore.tssrc/features/settings/categories/AiCategory.tsx
**/*.tsx
📄 CodeRabbit inference engine (CLAUDE.md)
Component behavior should be covered by Storybook and the axe-core gate rather than adding
*.test.tsxtests, because Vitest runs in node environment without RTL/jsdom; component-test scope requires deliberate approval.
Files:
src/features/session/SessionView.tsxsrc/features/settings/categories/AiCategory.tsx
CHANGELOG.md
📄 CodeRabbit inference engine (CLAUDE.md)
Update
CHANGELOG.mdas part of every release.
Files:
CHANGELOG.md
src/strings.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Toast and notification copy must be centralized in
src/strings.ts; prefer this module for other user-facing strings.
Files:
src/strings.ts
🔇 Additional comments (10)
ARCHITECTURE.md (1)
392-393: LGTM!CHANGELOG.md (1)
21-37: LGTM!ISSUES.md (1)
13-85: LGTM!Also applies to: 86-87
src-tauri/src/commands/engine.rs (1)
118-140: LGTM!Also applies to: 338-414, 539-686
src-tauri/src/commands/models.rs (1)
210-210: LGTM!src-tauri/src/lib.rs (1)
29-30: LGTM!Also applies to: 181-184, 295-295
src-tauri/src/commands/sidecar.rs (1)
264-283: LGTM!Also applies to: 450-540, 710-713
src-tauri/src/commands/mod.rs (1)
18-19: 🎯 Functional Correctness
sidecaris already gated correctly.
pub mod sidecar;is behind#[cfg(desktop)], matching the desktop-onlyenginegating.src-tauri/Cargo.toml (1)
39-41: 📐 Maintainability & Code QualityNo changes needed.
src/features/settings/categories/AiCategory.tsx (1)
351-389: 📐 Maintainability & Code QualityVerify Storybook/axe coverage for the new engine controls.
Please confirm the AI settings story covers missing, downloading, installed, unsupported, and session-locked states through the axe gate; do not add a
*.test.tsxcomponent test.As per coding guidelines, “Component behavior should be covered by Storybook and the axe-core gate rather than adding
*.test.tsxtests.”Source: Coding guidelines
| // I73 — the llama.cpp engine row + auto-install toggle. | ||
| engine: { | ||
| label: 'AI engine', | ||
| helpBundled: (version: string) => | ||
| `llama.cpp ${version} — included with this build.`, | ||
| helpManaged: (version: string) => | ||
| `llama.cpp ${version} — downloaded automatically.`, | ||
| helpMissingAuto: | ||
| "Not installed yet. It'll download automatically when AI starts.", | ||
| helpMissingManual: 'Not installed.', | ||
| helpUnsupported: 'No prebuilt engine for this computer yet.', | ||
| helpInstallError: (error: string) => `Couldn't install: ${error}`, | ||
| helpDownloading: (received: string, total: string) => | ||
| `Downloading the engine — ${received} of ${total}.`, | ||
| helpDownloadingIndeterminate: (received: string) => | ||
| `Downloading the engine — ${received} so far.`, | ||
| helpVerifying: 'Checking the download.', | ||
| helpExtracting: 'Unpacking the engine.', | ||
| installCta: 'Install now', | ||
| reinstallCta: 'Reinstall', | ||
| installedToast: 'AI engine installed.', | ||
| installErrorFallback: "Couldn't install the AI engine.", | ||
| installAria: 'Install the AI engine', | ||
| auto: { | ||
| label: 'Install engine automatically', | ||
| help: "Fetches the llama.cpp engine from GitHub if it's ever missing when AI starts. About 10–20 MB, verified against a pinned checksum.", | ||
| ariaLabel: 'Install engine automatically', | ||
| }, |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win
Sensitive Data Exposure (CWE-359)
Reachability: Internal
● Entry
src/features/settings/categories/AiCategory.tsx:64
AiCategory: I73 — engine presence + live install progress. The subscription stays
│
▼
● Sink
src/strings.ts
Clarify the AI privacy claim for engine downloads.
The new default flow fetches llama.cpp from GitHub, while the AI pane still says “Nothing leaves your computer.” Scope that statement to camera/screen data and disclose the engine-download network request consistently.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/strings.ts` around lines 1218 - 1245, Update the AI privacy messaging to
limit “Nothing leaves your computer” to camera and screen data, and explicitly
disclose that the llama.cpp engine may be downloaded from GitHub. Keep the
disclosure consistent with the engine.auto.help text and the default
auto-install flow in the engine strings.
…laceholder guard - engine_install now takes the install gate BEFORE stopping the sidecar and swaps the engine dir under the same hold; sidecar_start holds the gate across resolve + spawn. One lock-order invariant (gate before sidecar lock, never the reverse) closes the reinstall-vs-start race on the managed dir. - build.rs also creates a placeholder llama-runtime-<triple>/ dir: on a truly fresh clone the resources glob binaries/llama-runtime-*/* hard- fails with no match (masked earlier by a pre-fetched checkout). - build.rs release-profile arm now panics when the sidecar file on disk is a leftover debug placeholder (<4 MB) instead of bundling it. - do_install cleans the spent archive + staging dir on every failure path, not just the size gate. - Model-picker benchmark card maps the bare engine_not_installed sentinel to real copy; AI pane intro scopes "nothing leaves your computer" to captures and discloses engine/model downloads. - CHANGELOG/README describe auto-install as default-on and fallible. - Tests: auto-install-off forwarding + sentinel-through-lastError (plain-string rejection, as Tauri actually rejects); startCalls type widened; settings test uses the exported key constant. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
All review items addressed in 417046f:
Two additional fixes surfaced by a second review pass + a truly fresh worktree build:
|
Summary
Root cause (I73, Sev1): the in-app llama-server spawn has never worked in any build.
shell().sidecar("binaries/llama-server")resolves<exe_dir>/binaries/llama-server, but tauri-build (dev) and the bundler (release) strip the directory prefix and place the binary at<exe_dir>/llama-server— verified in bothtarget/debug/and the installedStudyVis.app/Contents/MacOS/. Everysidecar_startfailed withspawn llama-server: No such file or directory, shown as "AI failed to start" / "AI model crashed". Day-one bug (tauri-plugin-shell pinned at 2.3.5 since V1-P1), masked until I72 (v1.7.1) let model downloads complete; the 2026-07-26 attempt left a 0-bytellama-server.log— the child never ran.Fix + hardening:
shell().command()(same piped stdio +CREATE_NO_WINDOW, minus the broken exe-relative join). Preference order: bundled<exe_dir>/llama-server(.exe)(4 MB size gate filters the dev placeholder), then manageddata_dir/engine/<tag>-<triple>/. Spawn failures fall through candidates; the crash-restart watcher re-resolves but never downloads.src-tauri/src/commands/engine.rs): when no candidate resolves,sidecar_startdownloads the pinned llama.cpp b9095 release asset for the current triple — SHA-256-verified, streaming with throttledengine:progressevents — extractsllama-server+ companion libs (entries flattened to file names: traversal-proof by construction; other CLI tools skipped), and installs atomically (staging dir + rename). Concurrent installs coalesce behind a gate with a re-check (no "already in flight" errors). Pins are lockstep-tested againstscripts/fetch-llama-server.shincargo test.role="status"), Install/Reinstall button (stops the sidecar first — Windows can't swap loaded DLLs; locked during sessions), and an Install engine automatically toggle (engine_auto_install, default ON; absent-key hydration is default-safe for existing users).engine_not_installedsentinel → "The AI engine isn't installed. Install it in Settings → AI." toast with an Open-settings action, not a raw error. Windows spawn failure withvcruntime140.dllabsent → error names the VC++ redistributable with the aka.ms link.build.rswrites a debug-profile-only placeholder sidecar so fresh checkouts compile/run without the fetch script (release-profile builds still hard-fail; CI/release fetch real binaries first, unchanged).All four supported triples (mac-arm64, mac-x64, win-x64, linux-x64) have pinned assets + checksums; unsupported triples surface a calm "No prebuilt engine for this computer yet." state, never an error.
Test plan
Machine-verified (this Mac, Apple Silicon):
cargo test(87: incl. pin-lockstep vs fetch script, tar/zip extraction fixtures w/ traversal + filtering + exec bit, asset matrix),cargo clippy(correctness),cargo fmtnpm run build,lint,test(832),check-tokens,check-strings,check-contrast,check-a11y(axe over Storybook)llama-server --versionruns (Metal init,@loader_pathrpath confirmed → flat managed dir needs no env); the installed bundle's binary spawns via the exact new resolution (Contents/MacOS/llama-server+DYLD_FALLBACK_LIBRARY_PATH, exit 0) while the oldContents/MacOS/binaries/llama-serverpath provably doesn't exist; fresh-checkout placeholder build compiles and launchestauri dev(one-time watcher re-trigger noted in build.rs).User-walked (dev binary's keychain prompt blocks machine-driving the GUI):
src-tauri/binaries/llama-server-*stashed: row shows "Not installed yet…", Install now downloads with live progress and lands on "downloaded automatically"🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes
Documentation