From 8f10576f2a96a338f887c860ae8e3ea142c6e202 Mon Sep 17 00:00:00 2001 From: TimLai666 Date: Sun, 19 Jul 2026 13:20:38 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat(vz):=20macOS=20=E5=BE=8C=E7=AB=AF?= =?UTF-8?q?=E9=A0=90=E8=A8=AD=E5=95=9F=E7=94=A8=E2=80=94=E2=80=94=E7=A7=BB?= =?UTF-8?q?=E9=99=A4=20CHEFER=5FVZ=5FEXPERIMENTAL=20env=20=E9=8E=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 實機驗證於 2026-07 在 Apple Silicon(macOS 26)全綠後,vz 已非實驗性。 移除 availability() 開頭的 CHEFER_VZ_EXPERIMENTAL env 鎖:macOS 上 vz 是 唯一執行後端,鎖著等於雙擊預設不能跑,違背單檔精神。availability() 改為 純依真實前提判定(host arch / 內嵌 appliance / 內嵌 helper / macOS 13+), 缺任一即誠實回報 Unavailable。 Intel(x86_64)走完全相同的程式路徑、只換 appliance arch,但實機 VZ 開機 僅在 Apple Silicon 驗過(GHA runner 兩種 arch 都開不了 VZ guest);此差異 以文件註記,不另上 runtime 鎖(否則會擋掉很可能可行的路徑)。 同批補上這輪以 computer-use 驅動實機視窗完成的 HID/關窗驗證回填: - HID:guest 內 xev 記到指標 motion、按鍵 press/release、忠實且各異的 keycode (數字排 10/11/12/13 一對一),證明 NSEvent→VZ USB HID→guest evdev 鏈完整; 字母鍵在 macOS 端被輸入法(注音)轉換屬 host artifact,非 chefer。 - 關窗:紅鈕 → windowWillClose → vm.stop → exit(0),無殘留 helper/VM。 Changed: - crates/vmm-backend/src/vz.rs, lib.rs:移除 env 鎖 + 註解改寫 - scripts/vz-smoke.sh:清掉已無作用的 CHEFER_VZ_EXPERIMENTAL=1 前綴 - README.md / docs/DESIGN.md / docs/HANDOFF-macos-vz.md:改為預設啟用、 補 HID/關窗已驗證、Intel caveat Co-Authored-By: Claude Opus 4.8 --- README.md | 10 +++++----- crates/vmm-backend/src/lib.rs | 4 ++-- crates/vmm-backend/src/vz.rs | 23 +++++++++-------------- docs/DESIGN.md | 6 +++--- docs/HANDOFF-macos-vz.md | 4 ++-- scripts/vz-smoke.sh | 7 +++---- 6 files changed, 24 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index f28b6d5..b3b2086 100644 --- a/README.md +++ b/README.md @@ -26,14 +26,14 @@ you can "cook" your containerized application into a portable single-file app, m | Capability | Linux | Windows | macOS | |---|---|---|---| -| **Run the single-file app** | ✅ rootless user namespaces, zero dependencies | ✅ WSL2 — a minimal Chefer-dedicated distro is auto-provisioned for the run and best-effort removed when the app exits; **or ✅ WHP (no WSL needed)** — boots a bundle-embedded Linux micro-VM via the Windows Hypervisor Platform (validated on real hardware) | ✅ Virtualization.framework micro-VM — validated on real Apple Silicon (still opt-in via `CHEFER_VZ_EXPERIMENTAL=1`) | +| **Run the single-file app** | ✅ rootless user namespaces, zero dependencies | ✅ WSL2 — a minimal Chefer-dedicated distro is auto-provisioned for the run and best-effort removed when the app exits; **or ✅ WHP (no WSL needed)** — boots a bundle-embedded Linux micro-VM via the Windows Hypervisor Platform (validated on real hardware) | ✅ Virtualization.framework micro-VM — validated on real Apple Silicon, enabled by default (Intel shares the same code path but its VZ boot is unverified on real hardware) | | **Backend** | `namespaces` (in-process guest-agent) | `wsl2` (preferred when present) → `whp` (no-WSL micro-VM; virtio-blk + virtio-net over MMIO) | `vz` (Linux appliance + musl guest-agent) | | **Multi-service apps** (`depends_on` topo order + optional health checks) | ✅ | ✅ | ✅ verified | | **Data persistence** (`persist_path` → host dir, survives restarts) | ✅ | ✅ verified | ✅ verified (virtiofs write-back; counter survives restarts) | | **Internal networking** (services reach each other via `127.0.0.1:`) | ✅ | ✅ | ✅ verified | | **Host port mapping — TCP** (`"host:guest"`, host≠guest proxied) | ✅ | ✅ verified | ✅ verified — the vz backend relays `127.0.0.1:host → :guest` itself (the cross-platform runtime proxy is skipped on macOS) | | **Host port mapping — UDP** | ✅ | ✅ verified — Chefer relays via the VM IP + an in-VM eth0→loopback bridge (WSL2's own forwarding is TCP-only) | ✅ verified (same VM-IP relay + in-VM bridge) | -| **GUI services** | ✅ X11 / Wayland socket passthrough | ✅ WSL2 via WSLg ([gui-demo](examples/gui-demo)); **✅ WHP (no WSL)** — bundle-embedded `cage` compositor on a virtio-gpu scanout shown in a **resizable** native window, with keyboard/mouse + bidirectional clipboard (text validated bidirectionally on real hardware; PNG images via the `PNG` format / `CF_DIB`, host read/convert path verified via `--clip-selftest`; resizing the window now triggers a **true guest re-modeset**: the in-guest resize watcher follows the virtio-gpu display change (drm hotplug → re-probe → wlr-output-management; needs the cage ≥ 0.2.0 GUI overlay) — the chain is verified end-to-end on the vz backend, WHP re-validation pending; older overlays fall back to scaling the picture) | ✅ validated on real Apple Silicon — `VZVirtualMachineView` window + the same in-guest `cage` path as WHP (compositor up 8/8 boots); clipboard verified (text both ways; PNG guest→host byte-exact, host→guest wire-confirmed). Resolution: the **initial** guest mode matches the window's *pixel* size (Retina 2× backing; `CHEFER_VZ_GUI_SIZE` honored — verified 1280×800→2560×1600 and 1024×600→2048×1200), and live resize **re-modesets the guest** with dynamic resolution opted in (`CHEFER_VZ_DYNAMIC_RESOLUTION=1`, macOS 14+, cage ≥ 0.2.0 overlay — verified on real hardware: `xdpyinfo` followed 2560×1600 → 2200×1400); true dynamic resolution is now the **default** on macOS 14+ — the guest re-modesets on window resize and applies the host's HiDPI output scale (pushed via kernel cmdline `chefer.gui_scale`) so cursor/UI sizes stay correct on Retina; verified on real hardware including hand-checks (cursor size, pointer mapping, upscale quality; `xdpyinfo` reports logical points 1280×800 → 1100×700 across a live resize). `CHEFER_VZ_DYNAMIC_RESOLUTION=0` (or macOS 13) falls back to view-scaling. Interactive HID / close-window remain hand-checks (`scripts/vz-smoke.sh --gui`). Caveat: the clipboard channel is sometimes silently delayed — suspected macOS Local Network privacy gating of the ad-hoc-signed helper | +| **GUI services** | ✅ X11 / Wayland socket passthrough | ✅ WSL2 via WSLg ([gui-demo](examples/gui-demo)); **✅ WHP (no WSL)** — bundle-embedded `cage` compositor on a virtio-gpu scanout shown in a **resizable** native window, with keyboard/mouse + bidirectional clipboard (text validated bidirectionally on real hardware; PNG images via the `PNG` format / `CF_DIB`, host read/convert path verified via `--clip-selftest`; resizing the window now triggers a **true guest re-modeset**: the in-guest resize watcher follows the virtio-gpu display change (drm hotplug → re-probe → wlr-output-management; needs the cage ≥ 0.2.0 GUI overlay) — the chain is verified end-to-end on the vz backend, WHP re-validation pending; older overlays fall back to scaling the picture) | ✅ validated on real Apple Silicon — `VZVirtualMachineView` window + the same in-guest `cage` path as WHP (compositor up 8/8 boots); clipboard verified (text both ways; PNG guest→host byte-exact, host→guest wire-confirmed). Resolution: the **initial** guest mode matches the window's *pixel* size (Retina 2× backing; `CHEFER_VZ_GUI_SIZE` honored — verified 1280×800→2560×1600 and 1024×600→2048×1200), and live resize **re-modesets the guest** with dynamic resolution opted in (`CHEFER_VZ_DYNAMIC_RESOLUTION=1`, macOS 14+, cage ≥ 0.2.0 overlay — verified on real hardware: `xdpyinfo` followed 2560×1600 → 2200×1400); true dynamic resolution is now the **default** on macOS 14+ — the guest re-modesets on window resize and applies the host's HiDPI output scale (pushed via kernel cmdline `chefer.gui_scale`) so cursor/UI sizes stay correct on Retina; verified on real hardware including hand-checks (cursor size, pointer mapping, upscale quality; `xdpyinfo` reports logical points 1280×800 → 1100×700 across a live resize). `CHEFER_VZ_DYNAMIC_RESOLUTION=0` (or macOS 13) falls back to view-scaling. Interactive HID + close-window verified on real hardware (driven through the macOS window: `xev` in the guest logged pointer motion, button press/release, and faithful distinct key events — number-row keycodes 10/11/12/13; letter keys are intercepted by whatever macOS input method is active, a host-side artifact, not the VZ path; the red close button tears the app down with a clean exit 0, no orphan VM). Caveat: the clipboard channel is sometimes silently delayed — suspected macOS Local Network privacy gating of the ad-hoc-signed helper | | **`crash: fail_fast`** (any non-zero exit tears down the app, code propagated) | ✅ | ✅ verified | ✅ verified (exit 7 propagated; interface-service exit-0 teardown too) | | **Data-dir migration** (`old_names`) | ✅ | ✅ | ✅ | | **Official chown/gosu images** (redis, postgres, …) | ✅ as root (no userns); ✅ rootless via `/etc/subuid` + `newuidmap` (falls back to single-uid without them) | ✅ verified — distro runs as real root, runs official redis/postgres as-is | ✅ (same real-root guest path as WHP) | @@ -183,7 +183,7 @@ single executable ── user double-clicks ──> chefer-runtime └─ pick platform backend: Linux → rootless namespaces (in-process) Windows → WSL2 (auto-provisioned minimal distro) → WHP (no-WSL Linux micro-VM via Windows Hypervisor Platform: virtio-blk bundle/data, virtio-net TCP/UDP port forwarding, outbound NAT) - macOS → Virtualization.framework micro-VM (validated on real Apple Silicon; opt-in via CHEFER_VZ_EXPERIMENTAL=1) + macOS → Virtualization.framework micro-VM (validated on real Apple Silicon; enabled by default) │ ▼ guest-agent: assemble rootfs from layers (whiteouts), @@ -263,6 +263,6 @@ Honest list of what doesn't work (yet): - [~] **Faster app startup** — *partly done.* The single file now **caches its extracted bundle in a content-hashed dir** (keyed on the payload sha256): the first run decompresses+verifies once, and **subsequent runs skip extraction entirely** (no re-open, re-decompress, or re-hash of the payload) — the big win for large images launched repeatedly. (`--no-cache` forces fresh temp extraction.) The guest-agent's per-service rootfs assembly is cached, **decompresses layers in parallel** on first assembly (a worker pool decompresses ahead while layers are still applied strictly in order, preserving overlay/whiteout semantics — the pure-Rust musl `ruzstd` decode is CPU-bound), and **on root backends (WSL2 / macOS VM / native-root) uses overlayfs**: each layer is extracted once into its own content-addressed read-only lowerdir (shared/deduped across services and images, OCI whiteouts converted to overlay whiteouts), then mounted as an overlay with a per-run writable upper — no merge-copy, instant mount, layers shared. Rootless native Linux keeps the merged path (rootless can't create overlay whiteout devices). Windows WSL distros are now best-effort removed when the app exits, so WSL import remains a per-run cost unless `CHEFER_KEEP_WSL_DISTRO=1` is set for local debugging or repeated-launch profiling. **GUI apps on the WHP/VZ micro-VM** now ship their `cage`/Xwayland/Mesa **overlay as a read-only squashfs that is mounted (via overlayfs) instead of decompressed** — the VM is fresh every boot and can't cache across runs, so this removes a ~200 MB per-launch unpack from every GUI start. - [x] **`depends_on` health checks** (wait-until-ready), not just start order — a service with `healthcheck` is polled inside its container namespaces until healthy before later services start; services without one are considered ready once spawned. - [x] **Rootless Linux support for chown/gosu images** via `newuidmap` + `/etc/subuid` delegation — when the host has the `uidmap` tools and subuid/subgid ranges for the user (most distros ship both), rootless services get a full uid-range mapping (rootless-Podman-style) and images that `chown`/`gosu` to a dedicated uid (official redis/postgres) run as-is; verified end-to-end with the official `redis` image as a non-root user. Falls back to the old single-uid map when delegation is unavailable. (The root backends — WSL2 / macOS VM / native-root — never needed this.) -- [x] **macOS VZ boot validated on real Apple Silicon** *(2026-07, Apple Silicon / macOS 26)* — `scripts/vz-smoke.sh` fully green on bare metal: VM boot + virtiofs + guest-agent, exit-code propagation, service supervision, host→guest **TCP and UDP** port relays, multi-service `depends_on` + in-app `127.0.0.1` networking, `persist_path` write-back across restarts, and default-`bridge` outbound (pasta → VZ NAT, DNS included). Real-hardware fixes that came out of it: tar-mode restore on extraction (bundled agents lost their exec bit), an arm64-appliance initramfs that shipped an x86-64 busybox (cross-arch `busybox-static` now fetched at build), the runtime port proxy double-binding against the vz relay, and Docker-parity `/etc/resolv.conf` injection into service containers (without it, bridge outbound had connectivity but no DNS). Note: VZ is virtualization, not emulation — guest arch must match host (Apple Silicon → `linux/arm64`, Intel → `linux/amd64`). The backend still requires `CHEFER_VZ_EXPERIMENTAL=1` — flipping it default-on is a separate decision. -- [x] **GUI apps on macOS** — *validated on real Apple Silicon (2026-07).* The in-guest half (kiosk `cage` + Xwayland on a virtio-gpu scanout) is shared with WHP; `chefer-vz-helper --gui` shows the VM in an AppKit window via `VZVirtualMachineView` (1280×800 default, `CHEFER_VZ_GUI_SIZE=WxH` override) with USB keyboard/absolute-pointer devices. Real-hardware results: the compositor came up **8/8 GUI boots** (the `WLR_LIBINPUT_NO_DEVICES` boot-race fix holds on VZ); X11 clients connect and render; the host↔guest **clipboard** works (text verified both ways; PNG guest→host byte-exact on `NSPasteboard`, host→guest wire-confirmed). Resolution: `automaticallyReconfiguresDisplay` (macOS 14+) does push the window's *pixel* size to the guest — the **initial** mode matches the window's Retina backing exactly (a 1280×800-point window yields a 2560×1600 guest mode; `CHEFER_VZ_GUI_SIZE=1024x600` yields 2048×1200) — and live resize now **re-modesets the guest**: `cage` doesn't follow mode changes by itself, so the guest agent's resize watcher listens for the drm hotplug, re-probes the new preferred mode, and applies it over wlr-output-management (needs the cage ≥ 0.2.0 overlay from the current `build-gui-overlay.sh`; verified end-to-end on real hardware with `CHEFER_VZ_DYNAMIC_RESOLUTION=1`: `xdpyinfo` followed 2560×1600 → 2200×1400). True dynamic resolution is now the **default** on macOS 14+: the guest re-modesets on window resize and applies the host's HiDPI output scale (the helper pushes the screen's `backingScaleFactor` via kernel cmdline `chefer.gui_scale`; the resize watcher sets it at startup and on every re-modeset), so Retina-pixel modes no longer halve Linux UI sizes — verified end-to-end on real hardware (boot `output scale -> 2`; `xdpyinfo` logical 1280×800 → 1100×700 across a live resize, never the physical pixel sizes; hand-checks passed: cursor/UI size, pointer mapping, upscale quality). `CHEFER_VZ_DYNAMIC_RESOLUTION=0` (or macOS 13, or an old cage <0.2.0 overlay where the guest can't re-modeset) falls back to view-scaling with the aspect lock. Interactive checks that still need a human hand (`scripts/vz-smoke.sh --gui` checklist): typing/clicking into the guest, close-window-ends-app. Known caveat: the clipboard channel (helper → `:55381` over VZ NAT) sometimes sits in a silent `.waiting` state for up to a minute or never connects — suspected macOS Local Network privacy gating of the ad-hoc-signed helper; needs a stable signing identity and a visible hint in the helper. Design in [docs/DESIGN.md](docs/DESIGN.md). +- [x] **macOS VZ boot validated on real Apple Silicon** *(2026-07, Apple Silicon / macOS 26)* — `scripts/vz-smoke.sh` fully green on bare metal: VM boot + virtiofs + guest-agent, exit-code propagation, service supervision, host→guest **TCP and UDP** port relays, multi-service `depends_on` + in-app `127.0.0.1` networking, `persist_path` write-back across restarts, and default-`bridge` outbound (pasta → VZ NAT, DNS included). Real-hardware fixes that came out of it: tar-mode restore on extraction (bundled agents lost their exec bit), an arm64-appliance initramfs that shipped an x86-64 busybox (cross-arch `busybox-static` now fetched at build), the runtime port proxy double-binding against the vz relay, and Docker-parity `/etc/resolv.conf` injection into service containers (without it, bridge outbound had connectivity but no DNS). Note: VZ is virtualization, not emulation — guest arch must match host (Apple Silicon → `linux/arm64`, Intel → `linux/amd64`). The backend is now **enabled by default** (the old `CHEFER_VZ_EXPERIMENTAL` opt-in is gone) — `availability()` gates on the real preconditions (host arch, embedded appliance, embedded helper, macOS 13+). Intel Macs take the identical code path with an x86_64 appliance, but their VZ boot hasn't been exercised on real hardware (no CI can — a GitHub runner can't boot a VZ guest on either arch). +- [x] **GUI apps on macOS** — *validated on real Apple Silicon (2026-07).* The in-guest half (kiosk `cage` + Xwayland on a virtio-gpu scanout) is shared with WHP; `chefer-vz-helper --gui` shows the VM in an AppKit window via `VZVirtualMachineView` (1280×800 default, `CHEFER_VZ_GUI_SIZE=WxH` override) with USB keyboard/absolute-pointer devices. Real-hardware results: the compositor came up **8/8 GUI boots** (the `WLR_LIBINPUT_NO_DEVICES` boot-race fix holds on VZ); X11 clients connect and render; the host↔guest **clipboard** works (text verified both ways; PNG guest→host byte-exact on `NSPasteboard`, host→guest wire-confirmed). Resolution: `automaticallyReconfiguresDisplay` (macOS 14+) does push the window's *pixel* size to the guest — the **initial** mode matches the window's Retina backing exactly (a 1280×800-point window yields a 2560×1600 guest mode; `CHEFER_VZ_GUI_SIZE=1024x600` yields 2048×1200) — and live resize now **re-modesets the guest**: `cage` doesn't follow mode changes by itself, so the guest agent's resize watcher listens for the drm hotplug, re-probes the new preferred mode, and applies it over wlr-output-management (needs the cage ≥ 0.2.0 overlay from the current `build-gui-overlay.sh`; verified end-to-end on real hardware with `CHEFER_VZ_DYNAMIC_RESOLUTION=1`: `xdpyinfo` followed 2560×1600 → 2200×1400). True dynamic resolution is now the **default** on macOS 14+: the guest re-modesets on window resize and applies the host's HiDPI output scale (the helper pushes the screen's `backingScaleFactor` via kernel cmdline `chefer.gui_scale`; the resize watcher sets it at startup and on every re-modeset), so Retina-pixel modes no longer halve Linux UI sizes — verified end-to-end on real hardware (boot `output scale -> 2`; `xdpyinfo` logical 1280×800 → 1100×700 across a live resize, never the physical pixel sizes; hand-checks passed: cursor/UI size, pointer mapping, upscale quality). `CHEFER_VZ_DYNAMIC_RESOLUTION=0` (or macOS 13, or an old cage <0.2.0 overlay where the guest can't re-modeset) falls back to view-scaling with the aspect lock. **Input + close-window verified on real hardware** by driving the macOS window directly: `xev` inside the guest logged pointer motion, button press/release, and faithful *distinct* key events (number-row keycodes 10/11/12/13 arrived one-for-one) — proving the NSEvent → VZ USB HID → guest evdev path is intact; letter keys get transformed by whatever macOS input source is active (Zhuyin/注音 on the test machine) *before* reaching the VM view, so that mismapping is host-side, not chefer's. Clicking the red close button ran `windowWillClose → vm.stop → exit(0)`: runtime reported `exit code = 0` with no orphan helper/VM. Known caveat: the clipboard channel (helper → `:55381` over VZ NAT) sometimes sits in a silent `.waiting` state for up to a minute or never connects — suspected macOS Local Network privacy gating of the ad-hoc-signed helper; needs a stable signing identity and a visible hint in the helper. Design in [docs/DESIGN.md](docs/DESIGN.md). - [x] **GPU access / CUDA compute** — *opt-in `gpu: true`; validated end-to-end on both WSL2 and native NVIDIA Linux.* Set `gpu: true` on a service and Chefer binds the host's render/compute device nodes into that service's container — on **native Linux** `/dev/nvidia*` + `/dev/dri`; on the **Windows WSL2 backend** `/dev/dxg` + a bind of the host `/usr/lib/wsl/lib` **and `/usr/lib/wsl/drivers`** (its host-version-matched `libdxcore`/`libcuda`/… ) → CUDA/DirectML/OpenCL/Vulkan work with nothing extra in the image. The same passthrough also covers **NVENC/NVDEC video** and OpenCL/ROCm compute; **GL/Vulkan *rendering* does not work in a headless container** (see the native note below). **Validated end-to-end on real WSL2** (GeForce GT 1030) — the container sees `/dev/dxg` + `/usr/lib/wsl/lib` + `/usr/lib/wsl/drivers`, runs `nvidia-smi` with live telemetry, and executes a `nvcc`-built CUDA `vectorAdd` on the GPU (`sm_61`, 1048576 elems, 0 mismatches); binding `/usr/lib/wsl/lib` alone is not enough — its `libcuda`/`libnvidia-ml` are loader shims that dlopen the real driver from `/usr/lib/wsl/drivers`, so both must be bound (else `nvidia-smi` reports "Driver Not Loaded"). `/dev/dri` is bound when present (absent on some WSL kernels; CUDA uses `/dev/dxg`). It is a **clear startup error** on the non-WSL Windows (WHP) micro-VM and the macOS VM — a bare WHP/VZ VM can't tap Microsoft/Apple GPU paravirtualization, so GPU compute stays **native-Linux + WSL2 only**. **Native Linux** — host-driver-version-matched userspace lib injection (à la the NVIDIA Container Toolkit) reads the driver version from `/proc/driver/nvidia/version` and binds the matching NVIDIA driver libs (`libcuda`/`libnvidia-*`/`libnvcuvid`/`libnvoptix`/GL, matching `nvidia-container-cli list`) into `/run/chefer-nvidia` (versioned + real DT_SONAME + `.so`) plus `LD_LIBRARY_PATH`. **Validated on a GeForce RTX 4070** (driver 570.211.01): a `nvcc`-built CUDA vectorAdd from a stock `nvidia/cuda` image that ships no libcuda ran on the GPU (`sm_89`, 1048576 elems, 0 mismatches), and `ffmpeg -c:v h264_nvenc` encoded in-container (NVENC, exit 0). **OpenGL/Vulkan *rendering* does not work in a headless container**, though: NVIDIA's Vulkan ICD (`libGLX_nvidia` → `libnvidia-glcore`) needs a real graphics/X environment (it references Xorg's `ErrorF` and glibc-2.34-removed `__malloc_hook`), so it falls back to `llvmpipe` — and `docker --gpus all` with `NVIDIA_DRIVER_CAPABILITIES=all` (the nvidia-container-toolkit reference) fails identically on the same headless host, so this is a host/driver limitation, not a Chefer one (Chefer's GUI path renders via X11/Wayland socket passthrough + software rasterization instead). No path to Apple NPU / CoreML from a Linux guest. diff --git a/crates/vmm-backend/src/lib.rs b/crates/vmm-backend/src/lib.rs index 92dfeab..e24d088 100644 --- a/crates/vmm-backend/src/lib.rs +++ b/crates/vmm-backend/src/lib.rs @@ -3,8 +3,8 @@ //! 對應 docs/DESIGN.md §6「vmm-backend」節: //! - Linux:`namespaces` 後端(in-process 呼叫 guest-agent lib)。 //! - Windows:`wsl2` 後端(chefer 專用 distro 內跑 bundle 內嵌的 musl guest-agent)。 -//! - macOS:`vz` 後端(內附 Swift helper 驅動 Virtualization.framework;實機驗證前 -//! 預設 `Unavailable`,需 `CHEFER_VZ_EXPERIMENTAL=1` 啟用)。 +//! - macOS:`vz` 後端(內附 Swift helper 驅動 Virtualization.framework;已於 Apple Silicon +//! 實機驗證,`availability()` 依真實前提決定可用性,無實驗性 env 鎖)。 //! //! 公開 API:[`Availability`]、[`RunOptions`]、[`AppRunContext`]、[`ExecBackend`]、 //! [`backends`]、[`run_app`];另提供 [`cleanup_distros`] 供清理舊版殘留 WSL distro。 diff --git a/crates/vmm-backend/src/vz.rs b/crates/vmm-backend/src/vz.rs index 36922e6..0cbffac 100644 --- a/crates/vmm-backend/src/vz.rs +++ b/crates/vmm-backend/src/vz.rs @@ -7,10 +7,13 @@ //! 開機由內附的 **Swift helper**(`agents/chefer-vz-helper-`)驅動:本後端只負責 //! spawn helper、把它的 stdout(接著 guest 序列 console)串接解析 `CHEFER_GUEST_IP` / //! `CHEFER_GUEST_EXIT` 標記,並依 guest IP 起 host→guest 埠轉發。實際的 -//! Virtualization.framework 呼叫全在 helper(Swift)內,僅能在實體 Mac 驗證 VM 真的開得起來。 +//! Virtualization.framework 呼叫全在 helper(Swift)內。 //! -//! **誠實回報**:在實機驗證通過前 `availability()` 預設 `Unavailable`,需 -//! `CHEFER_VZ_EXPERIMENTAL=1` 才啟用。 +//! macOS 上 vz 是**唯一**的執行後端,且已於實體 Apple Silicon(macOS 26)完整驗證通過 +//! (2026-07),故不再上實驗性 env 鎖——`availability()` 依真實前提(host arch、bundle +//! 內嵌 appliance、bundle 內嵌 helper、macOS 13+)決定可用性,缺任一即誠實回報 +//! `Unavailable`。Intel(x86_64)走完全相同的程式路徑(appliance 換 arch),但實機 VZ 開機 +//! 目前僅在 Apple Silicon 驗過(GHA runner 開不了 VZ guest,Intel 亦然)。 use std::io::{BufRead, BufReader}; use std::net::{Ipv4Addr, SocketAddr, UdpSocket}; @@ -32,17 +35,9 @@ impl ExecBackend for VzBackend { } fn availability(&self, ctx: &AppRunContext) -> Availability { - // 實機驗證前預設不可用;需 CHEFER_VZ_EXPERIMENTAL=1 才啟用(誠實不偽稱可執行)。 - if std::env::var("CHEFER_VZ_EXPERIMENTAL").ok().as_deref() != Some("1") { - return Availability::Unavailable( - "The macOS (vz) backend is experimental and not yet validated on real hardware. \ - To try it on a Mac, set CHEFER_VZ_EXPERIMENTAL=1 (the app must embed a \ - chefer-vz-helper signed with the com.apple.security.virtualization entitlement, \ - or you can point CHEFER_VZ_HELPER at a locally built one)." - .to_string(), - ); - } - + // 依真實前提決定可用性(host arch、內嵌 appliance、內嵌 helper、macOS 版本)—— + // 已於 Apple Silicon 實機驗證,不再上實驗性 env 鎖(macOS 上 vz 是唯一後端, + // 鎖著等於預設不能跑)。缺任一前提即誠實回報 Unavailable。 let host_arch = std::env::consts::ARCH; let Some(arch) = vz_util::host_guest_arch(host_arch) else { return Availability::Unavailable(format!( diff --git a/docs/DESIGN.md b/docs/DESIGN.md index 3f68950..e6d8e4f 100644 --- a/docs/DESIGN.md +++ b/docs/DESIGN.md @@ -292,7 +292,7 @@ pub fn run_app(ctx: &AppRunContext) -> anyhow::Result; // 取第一個 Avai - **appliance 相容**:需要能在 WHP 環境開機的 appliance。kernel config 加 `CONFIG_VIRTIO_MMIO`/`_BLK`/`_NET`/`CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES`;cmdline 追加各 `virtio_mmio.device=...`。**init 改為自適應**(偵測 `/dev/hvc0` → vz 路徑掛 virtiofs;否則 WHP 路徑掛 `/dev/vda`(bundle ro)+`/dev/vdb`(data rw)、console 用 ttyS0),維持「whp 與 vz 共用一份 appliance」。init 另負責 `bridge` 出網(pasta)的環境前提:開機早期 `switch_root` 到 tmpfs 根(rootfs 上 `pivot_root` EINVAL)、確保 `/dev/net/tun` 存在且 0666;kernel config 含 `CONFIG_TUN`。 - **分階段里程碑(每個可獨立驗證;✅=已實機達成)**:✅ M1 transport(WHvEmulator 接線 + virtqueue)→ ✅ M2 virtio-blk(bundle ro)(guest 從 `/dev/vda` 讀 bundle 解 tar)→ ✅ M3 virtio-blk(data rw) 關機回寫(**實機達成**:helper 第二顆 virtio-blk vdb(base `0xD000_0400`/IRQ 7)以 `pack_dir(data_dir)` 補零到容量上限為 backing;guest 開機 `dd /dev/vdb | tar -x`、關機 `tar data_dir | dd of=/dev/vdb`,helper 關機 `unpack_image(vdb)→data_dir`;本機 WHP 實測 persist counter 跨重啟 1→2→3。**限制**:只在 guest 乾淨關機(服務全退出)時回寫,長駐 server 被 kill/timeout 不回寫;vdb 容量固定 `CHEFER_WHP_DATA_MIB`(預設 256MiB);tar 解壓不刪除 host 端已移除的檔)→ ✅ M4 virtio-net + host→guest 埠轉發(**實機達成**:net 裝置接上 run loop/MMIO 模擬器、smoltcp user-mode gateway、helper 綁 `[::1]:guest`、guest 靜態 IP `10.0.2.15`、guest-agent eth0→loopback TCP 橋接;本機 WHP 實測 redis bundle,host `redis PING` 經整條鏈得 `+PONG`)→ ✅ M5 appliance 自適應 init(偵測 /dev/hvc0 → vz virtiofs;否則 WHP `dd /dev/vda | tar -x`)+ kernel config + build → ✅ M6 端到端真 bundle(實機 `CHEFER_GUEST_EXIT=0`,`interface_mode: none` 服務)。**剩餘**:guest 主動對外 outbound NAT(🚧 分階段:✅ **M7-a 出網 UDP(實機達成)**——`whp-helper/src/virtio/nat.rs` 解析 guest IPv4/UDP frame + 合成回程 frame;net_backend NAT 引擎(per-flow host UDP socket)+ `drain_tx` 分流(外部 dst → NAT、其餘 → smoltcp)+ 回程注入 guest rx;本機 WHP 實測 `network: shared` 服務 `nslookup example.com 1.1.1.1` 經整條 NAT 鏈解析成功(`CHEFER_GUEST_EXIT=0`)。✅ **M7-b 出網 TCP(實機達成)**——drain_tx 對外部 dst 的 TCP 仍交 smoltcp,但 SYN 先 `nat_tcp_syn` 預註冊:把 dst IP 動態加進 iface `ip_addrs`(X/32;`.cargo/config.toml` 設 `SMOLTCP_IFACE_MAX_ADDR_COUNT=64` 提高容量 + idle dst LRU 驅逐)、建 smoltcp listen socket(dst,port)、**背景執行緒** connect host `TcpStream`(不阻塞 VM loop);`tcp_nat_pump` 完成連線後雙向橋接 smoltcp socket ↔ host stream;本機 WHP 實測 `network: shared` 服務 `wget http://example.com`(DNS UDP NAT 解析 + TCP NAT 連 104.20.x:80)成功(`CHEFER_GUEST_EXIT=0`)。✅ **M7-c bridge 模式出網(實機達成,預設模式全通)**——不需取代 pasta:與其他後端同一條 pasta 路徑(app netns → pasta tap → guest root netns socket → eth0 → helper NAT)。實機打通靠三個 guest 端修正:① appliance kernel 補 `CONFIG_TUN`(x86_64 defconfig 不含,pasta 無 tap 可建);② appliance init 補 `/dev/net/tun` 節點 0666(devtmpfs 對 misc 裝置預設 0600,pasta 以非特權 uid 開不了)並於開機早期 `switch_root` 到 tmpfs 根(initramfs 的 rootfs 上 `pivot_root` 一律 EINVAL,pasta 自我沙箱會直接退出——此問題 vz 同 appliance 也會中,一併修掉);③ guest-agent `start_pasta` 對 exec `EACCES` 補救(Windows host 打包無法記錄 unix 執行位 → bundle 內 pasta 是 0644;複製到 tmp 補 0755 重試)。本機 WHP 實測預設 `bridge` app:alpine 服務 `wget http://example.com`(DNS UDP + HTTP TCP 經 pasta→NAT 鏈)`CHEFER_GUEST_EXIT=0`;redis bundle 宣告埠 `+PONG`、未宣告埠拒絕。✅ **M7-d 容器內 DNS(10.0.2.3 pivot;實機達成)**——後查(實體 Mac vz 工作時發現):M7-b/c 當時容器內**沒有任何 resolv.conf 來源**(裸 alpine 不自帶、repo 內無注入程式碼;musl 缺檔會 fallback `127.0.0.1:53`,封包根本到不了 NAT——本機以 alpine 拔掉 resolv.conf 實證 `wget: bad address`),故當時的 wget 解析必是測試另行提供了 nameserver(未記錄入 repo);NAT 轉發機制的驗證仍有效,但「裸 image 開箱即可解析」**尚未成立**。正式來源已補上:kernel cmdline `ip=` dns0=`10.0.2.3` + helper DNS pivot(見 §「容器內 DNS」),host 端邏輯有跨平台單元測試;**本機 WHP 實機驗證通過**(2026-07-11,Windows 11 Pro 26200:裸 alpine:3.20、不含任何 resolv.conf 手段,預設 `bridge` 與 `shared` 皆容器內斷言 nslookup Server=`10.0.2.3` + `wget http://example.com`,`CHEFER_GUEST_EXIT=0`;trace `dns: upstreams` = host Wi-Fi DNS `192.168.1.1`(GetNetworkParams 探測正確),`CHEFER_WHP_DNS=1.1.1.1` 覆寫後 upstreams 跟隨;VPN/內網 hostname 情境未涵蓋——驗證機無 VPN,詳 whp-virtio-roadmap M7-d)。)。剩餘:GUI 顯示通道(virtio-gpu,另一條線)。 - **驗證邊界(誠實)**:同 vz——**GitHub runner 開不了 WHP**,transport/virtqueue 純邏輯進 CI 單元測試,真開 VM 的 e2e 只能在實機(Windows + 硬體虛擬化 + WHP 功能)手動跑。前置:`CHEFER_BACKEND=whp` 後端覆寫開關(已實作)讓有 WSL 的機器能選到 whp。 -- **macOS**(`vz`,Apple Virtualization.framework):macOS 沒有現成 Linux,必須自行開一台輕量 Linux micro-VM 來跑容器。設計如下(**狀態:契約已定 + 跨平台純邏輯已實作並測試;Linux appliance 建置與 QEMU E2E 已納入 scripts/CI;開機改採內附 Swift helper(見下),其 swiftc 編譯與 Rust 後端 compile-check 已納入 CI,VM 真正開機仍待在實體 Mac 上驗證**——在實機驗證通過前 `availability()` 預設 `Unavailable`,需 `CHEFER_VZ_EXPERIMENTAL=1` 才啟用,不偽稱可執行)。 +- **macOS**(`vz`,Apple Virtualization.framework):macOS 沒有現成 Linux,必須自行開一台輕量 Linux micro-VM 來跑容器。設計如下(**狀態:已於實體 Apple Silicon(macOS 26,2026-07)完整驗證通過**——開機/exit code/TCP+UDP 埠轉發/bridge 出網+DNS/多服務/persist/GUI 顯示+HID+剪貼簿+動態解析度全綠;`availability()` 依真實前提(host arch、內嵌 appliance、內嵌 helper、macOS 13+)決定可用性、**無實驗性 env 鎖**(macOS 上 vz 是唯一後端)。Intel(x86_64)走完全相同的程式路徑、appliance 換 arch,但實機 VZ 開機僅在 Apple Silicon 驗過(GHA runner 兩種 arch 都開不了 VZ guest)。) - **Appliance(kit 內附預編 kernel)**:kit 與 macOS 目標的 bundle 內附一份精簡 Linux 開機組合: - `vm/chefer-vmlinuz-`:預編 Linux kernel(含 virtio-blk/virtiofs/virtio-net/overlayfs)。 - `vm/chefer-initramfs-`:最小 initramfs,其 `/init` 掛載 virtiofs 共享的 bundle 與 data 後,`exec` 內附的 guest-agent `run --bundle /mnt/bundle --data /mnt/data`。 @@ -305,7 +305,7 @@ pub fn run_app(ctx: &AppRunContext) -> anyhow::Result; // 取第一個 Avai - `CHEFER_GUEST_IP=`:開機掛載後、跑 app 前印出 guest eth0 的對外 IPv4,供 host 規劃 host→guest 埠轉發。 - **網路 / 埠映射**:`VZNATNetworkDeviceAttachment` 給 guest NAT IP(host 在同一 NAT 子網可直接連到 guest);但 host→guest **不自動轉送**,故所有宣告的 ports(TCP 與 UDP)都由 vz 後端在 host 端 relay `127.0.0.1:host → :guest`(`guest_ip` 取自上面的 console 標記;轉發清單由 `vz_util::forward_ports` 規劃)。UDP 另以 `--udp-bridge` 叫 guest-agent 在 VM 內補 eth0→loopback 橋接(appliance init 已帶此旗標)。**已於實體 Apple Silicon 驗證(2026-07)**:vz 開機 shim、TCP relay(host `curl` 通)與 UDP relay(host datagram 進到 app netns 服務)皆實測通過。 - **程式碼簽章 / entitlement**:呼叫 Virtualization.framework 的程序是 **`chefer-vz-helper`**(非 chefer-runtime 本身),故 helper 在 macOS 上**必須**以 `com.apple.security.virtualization` entitlement 簽章,否則建立 VM 會在執行期失敗。release 流程建好 helper 後即以此 entitlement 簽章再嵌進 kit/bundle(macho 的簽章隨檔案複製保留,執行期解壓後仍有效);自行於 Mac 測試時可 ad-hoc 簽:`codesign --entitlements vz-helper.entitlements -s - chefer-vz-helper-`。 - - **實驗性開關(誠實回報)**:`availability()` 預設維持 `Unavailable`,需 `CHEFER_VZ_EXPERIMENTAL=1` 明確啟用。**實機驗證已於 2026-07 在 Apple Silicon(macOS 26)完成**(開機/exit code/TCP+UDP 埠轉發/bridge 出網+DNS/多服務/persist/GUI 顯示與剪貼簿);把 gate 放寬成預設可用屬**改變對外預設行為**,另行決策後再動。 + - **可用性判定(誠實回報,無實驗性 env 鎖)**:**實機驗證已於 2026-07 在 Apple Silicon(macOS 26)完成**(開機/exit code/TCP+UDP 埠轉發/bridge 出網+DNS/多服務/persist/GUI 顯示+HID+剪貼簿+動態解析度),故不再上 `CHEFER_VZ_EXPERIMENTAL` 這道 env 鎖(macOS 上 vz 是唯一執行後端,鎖著等於雙擊預設不能跑,違背單檔精神)。`availability()` 改為純依真實前提回報:① host arch 支援(x86_64/aarch64)② bundle 內嵌 appliance(`vm/chefer-vmlinuz-`+`-initramfs`)③ bundle 內嵌 helper(`agents/chefer-vz-helper-`,或 `CHEFER_VZ_HELPER` 指向本機自建)④ macOS ≥13(virtiofs)——缺任一即 `Unavailable` 附可行動訊息。**Intel(x86_64)取捨**:走完全相同的 Rust/Swift 路徑、只換 appliance arch,但實機 VZ 開機僅在 Apple Silicon 驗過(CI 兩種 arch 都開不了 VZ guest);此差異以文件註記,不另上 runtime 鎖(否則會擋掉很可能可行的路徑)。 - **純邏輯(跨平台可測,`vz_util.rs` 內)**:appliance 檔在 bundle 內的查找(`layout::vm_dir` / `kernel_name` / `initramfs_name`)、kernel command line 組裝、VM 資源(CPU/RAM)計算、guest 路徑映射、錯誤訊息——皆為純函式並有單元測試(在 Windows/Linux 上即可跑)。 - **Swift helper(`vz-helper/`,僅 macOS 編譯)**:以 swiftc 編成 `chefer-vz-helper-`,驅動上述組態並開機。只能在實體 Mac 驗證 VM 真的開得起來(GHA macOS runner 為巢狀虛擬化、開不了 VZ guest,CI 僅 swiftc compile-check helper、cargo compile-check Rust 後端)。Rust 後端找 helper 的順序:bundle 內 `agents/chefer-vz-helper-` > `CHEFER_VZ_HELPER` env(供實機驗證時直接指向自建 helper,免整條 release 管線)。 - **Appliance 建置**:`vm/chefer-vmlinuz-*` 與 `vm/chefer-initramfs-*` 由獨立的 Linux 建置流程產生,並可在 **Linux + QEMU**(同樣 virtio 開機)上做 E2E 驗證,與 macOS 無關——這是讓 vz 後端可信的關鍵前置,且不需 Mac。 @@ -319,7 +319,7 @@ pub fn run_app(ctx: &AppRunContext) -> anyhow::Result; // 取第一個 Avai - **Host(vz 後端,`#[cfg(target_os = "macos")]`)——已實作於 `vz-helper/main.swift` 的 `--gui` 模式**:VM 組態加 `VZVirtioGraphicsDeviceConfiguration`(一個 scanout,預設 1280×800、`CHEFER_VZ_GUI_SIZE=WxH` 覆寫)+ `VZUSBKeyboardConfiguration` + `VZUSBScreenCoordinatePointingDeviceConfiguration`(絕對座標,同 WHP 的 tablet 選擇);開一個 AppKit 視窗(標題 = app 名,`--gui-title`)承載 `VZVirtualMachineView` 綁該 VM(VM 綁主 queue、`NSApplication` 事件迴圈)→ 顯示 guest framebuffer,VZ 自動把鍵鼠 HID 轉進 guest。**關窗 = app 結束**(與 WHP 同語意;先試 `vm.stop`、2 秒保底退出,runtime 對「helper 乾淨退出且無 exit 標記」回 0)。vz.rs 依 manifest 有 gui/both 服務傳 `--gui`/`--gui-title`(`vz_util::helper_args`,含單元測試)。GUI 模式下 console 經 helper 內部 pipe tee 直通 stdout(runtime 標記解析不變)並掃 `CHEFER_GUEST_IP` 以啟動剪貼簿;非 gui 維持 headless 原路徑(console 直寫 stdout)。 - **生命週期 / 輸入**:`cage` 內 app 結束 → 沿用 guest-agent 既有「介面服務結束=收掉 app」→ VM 關、視窗關。鍵鼠走 VZ→USB HID→evdev→cage,免額外處理。 - **剪貼簿(host↔guest,vz 與 whp 共用設計)——vz host 端已實作(`ClipboardHost`,`vz-helper/main.swift`)**:**不走 vsock**(whp 得再寫一個 virtio-vsock 裝置模型,成本高)——改走**既有網路通道**:guest-agent 起剪貼簿同步服務(guest 側以 wl-clipboard 對接 cage 的 Wayland 剪貼簿;與 WHP 完全共用 `clipboard.rs`),host 側經該後端既有的 host→guest 通道連入(vz:**直連 guest IP:55381**(NAT 可達,IP 自 console `CHEFER_GUEST_IP` 標記);whp:helper 的 `[::1]` 轉發),雙向同步。安全:以 **kernel cmdline 帶入的隨機 token** 握手(vz helper 以 `SecRandomCopyBytes` 產生、附 `chefer.clip_token`/`chefer.clip_port`),未帶 token 的連線一律拒絕。線協定與 WHP 同版:token 行 + ``(kind 0=text、1=PNG,上限 32MiB);macOS 端 NSPasteboard 讀寫 `.png`(現代 app)與 `.tiff`(傳統 app,`NSBitmapImageRep` 轉換——對應 whp 的 "PNG"/CF_DIB 雙格式),回音以 changeCount + (kind,payload) 記憶抑制。**`.waiting` 可見性(實機發現後補)**:NWConnection 連 guest:55381 偶發卡在 `.waiting` 數十秒或永不成功(同一時間 shell 的 `nc` 可秒連),疑為 macOS「區域網路」隱私權對 ad-hoc 簽章 helper 的閘門;此狀態原本落在 stateUpdateHandler 的 default 分支完全靜默。現在 helper 對 `.waiting` 印一次性 stderr 提示(引導到 系統設定 → 隱私權與安全性 → 區域網路 放行)並在 3 秒未 ready 時棄連重連(新連線常可立即成功);`build-vz-helper.sh` 另支援 `CHEFER_CODESIGN_IDENTITY` 以穩定簽章身分讓 TCC 記住授權(預設仍 ad-hoc)。 - - **分期**:① vz 後端先點亮(非 GUI)✅ → ② appliance 加 virtio-gpu + cage + Xwayland,先在 **QEMU** 驗 GUI app 能算繪 ✅ → ③ 真 Mac 接 `VZVirtualMachineView` + HID —— ✅ **實體 Apple Silicon 驗證通過(2026-07)**:視窗顯示、X11 client 連線算繪、compositor 8/8 次開機全部就緒(`WLR_LIBINPUT_NO_DEVICES` boot-race 修復在 vz 上成立);鍵鼠實際輸入(VZ 原生 HID 注入)與關窗語意仍屬人工檢核項 → ④ 剪貼簿/縮放 —— ✅ **剪貼簿實機驗證**:文字雙向、PNG guest→host(NSPasteboard 位元組相符)、PNG host→guest(線上送達)皆通過;**縮放(動態解析度)實測結論(兩段式)**:`automaticallyReconfiguresDisplay` 確實把視窗**像素**尺寸推進 guest——**開機時** guest 模式=視窗 Retina backing(1280×800 點視窗 → guest 2560×1600;`CHEFER_VZ_GUI_SIZE=1024x600` → 2048×1200);但**執行中拖拉視窗不會 re-modeset、只有畫面縮放**(實機驗證:拖拉全程 guest 維持 2560×1600)——與 WHP 相同的 guest 端限制:`cage` kiosk compositor 啟動後不跟隨模式變更——**guest 側跟隨已補上(vz/WHP 共用)**:guest-agent 的 **resize watcher(`resize.rs`)**監聽 drm uevent → `GETCONNECTOR` re-probe 取新 preferred mode → `wlr-randr --custom-mode` 經 **wlr-output-management** 要求 cage re-modeset(overlay 需 **cage ≥0.2.0**=Alpine 3.22,`build-gui-overlay.sh` 已升版並附 wlr-randr;舊 overlay 下 watcher 自動停用)。實機 e2e(Apple Silicon):`CHEFER_VZ_DYNAMIC_RESOLUTION=1` 下視窗縮放 → console `gui: resize: Virtual-1 -> 2200x1400` → guest 內 xdpyinfo dimensions 2560×1600 → 2200×1400 跟隨。**預設取捨(實測後定案)**:guest 不 re-modeset 的情況下開 `automaticallyReconfiguresDisplay` 反而讓 view 停止縮放(等一個不會來的 re-modeset,拖拉時畫面尺寸不同步),且它推給 guest 的是 Retina **實體像素**尺寸、Linux guest 無 HiDPI 概念 → UI/游標縮半——故**當時**預設 scanout=視窗「點」數、關 `automaticallyReconfiguresDisplay`、由 view 等比縮放+鎖長寬比(Retina 上 2× 放大略軟但尺寸正確;此模式現保留為 `CHEFER_VZ_DYNAMIC_RESOLUTION=0`/macOS 13 的保底路徑——搭到舊 overlay(cage <0.2.0、guest 不 re-modeset)時仍可用);guest 端 re-modeset 由 resize watcher 補上(見上)後全鏈可用;翻預設原 gated on 的 **HiDPI output scale 已補上**:helper 於動態解析度模式把開機當下螢幕的 backingScaleFactor 以 **`chefer.gui_scale=`** 附進 kernel cmdline(同 clip_token 作法;視窗跨螢幕拖移的 scale 變化不追、恆用開機值),guest 的 resize watcher 啟動時即對現行輸出補設 output scale(開機初始模式已是 Retina 像素尺寸、之後未必再有 drm 事件),其後每次 `--custom-mode` 同一次 apply 一併重設(cage 0.2.0 的 wlr-output-management apply 走 `wlr_output_head_v1_state_apply`,支援 scale)——邏輯尺寸回到「點」數、游標/UI 尺寸正常,X11 surface 由 compositor 放大(預期視覺等同預設 view-scaling,但比例自由、無 letterbox)。**實機 e2e(Apple Silicon,2026-07-12)機器可驗項全過**:開機 console `gui: resize: Virtual-1 output scale -> 2` → guest 內 `xdpyinfo` 回報**邏輯**尺寸 1280×800(實體模式 2560×1600);`CHEFER_VZ_GUI_TEST_RESIZE` 縮放 1100×700pt → `gui: resize: Virtual-1 -> 2200x1400 (scale 2)` → `xdpyinfo` 跟隨 1100×700——cage 0.2.0 對 mode+scale 同次 apply 無誤。**人工目測項(游標/UI 尺寸、座標映射、Xwayland 放大畫質;拖拉中細黑邊確認為 debounce 前暫態)亦於 2026-07-12 實機通過 → 真動態解析度已翻為 macOS 14+ 預設**,`CHEFER_VZ_DYNAMIC_RESOLUTION=0` 退回 view 縮放保底(檢核清單見 `vz-smoke.sh --gui`)。helper 另提供 `CHEFER_VZ_GUI_TEST_RESIZE=<秒>:x` 測試鉤子(程式化 `setContentSize`,與拖拉同路徑),供 vz-smoke 自動驗證本鏈。macOS 13 無 `automaticallyReconfiguresDisplay` → 維持固定尺寸。實際打字點擊/關窗語意留人工檢核(`scripts/vz-smoke.sh --gui` 清單)。**已知問題(實機發現,均已補修)**:(a) helper 對 guest:55381 的剪貼簿連線偶發卡在 NWConnection 靜默 `.waiting`——已補一次性提示 + 3 秒棄連重連 + `CHEFER_CODESIGN_IDENTITY` 穩定簽章(見上「剪貼簿」段的 `.waiting` 可見性);(b) 對 runtime 行程單發 SIGINT/SIGTERM(非終端機 Ctrl+C 的整個 process group)時 helper/VM 會殘留(`vz-smoke.sh` 的 `kill -INT` 即觸發)——已補 stdin liveness 契約(helper 讀 stdin EOF 自我了結,見上「Helper 生命週期」;WHP 側另有 Job Object 雙保險,見 §6 whp 節)。 + - **分期**:① vz 後端先點亮(非 GUI)✅ → ② appliance 加 virtio-gpu + cage + Xwayland,先在 **QEMU** 驗 GUI app 能算繪 ✅ → ③ 真 Mac 接 `VZVirtualMachineView` + HID —— ✅ **實體 Apple Silicon 驗證通過(2026-07)**:視窗顯示、X11 client 連線算繪、compositor 8/8 次開機全部就緒(`WLR_LIBINPUT_NO_DEVICES` boot-race 修復在 vz 上成立);**鍵鼠實際輸入與關窗語意亦以 computer-use 驅動實機視窗驗過**:guest 內 `xev` 記到指標 motion、按鍵 press/release,鍵盤 keycode 忠實且各異(數字排 10/11/12/13 一對一到位)→ 證明 NSEvent→VZ USB HID→guest evdev 鏈完整;字母鍵在 macOS 端被當前輸入源(測試機為注音)轉換後才到 view,屬 host 端 artifact 非 chefer。關窗(點紅鈕)→ `windowWillClose → vm.stop → exit(0)`,runtime 回 `exit code = 0` 且無殘留 helper/VM → ④ 剪貼簿/縮放 —— ✅ **剪貼簿實機驗證**:文字雙向、PNG guest→host(NSPasteboard 位元組相符)、PNG host→guest(線上送達)皆通過;**縮放(動態解析度)實測結論(兩段式)**:`automaticallyReconfiguresDisplay` 確實把視窗**像素**尺寸推進 guest——**開機時** guest 模式=視窗 Retina backing(1280×800 點視窗 → guest 2560×1600;`CHEFER_VZ_GUI_SIZE=1024x600` → 2048×1200);但**執行中拖拉視窗不會 re-modeset、只有畫面縮放**(實機驗證:拖拉全程 guest 維持 2560×1600)——與 WHP 相同的 guest 端限制:`cage` kiosk compositor 啟動後不跟隨模式變更——**guest 側跟隨已補上(vz/WHP 共用)**:guest-agent 的 **resize watcher(`resize.rs`)**監聽 drm uevent → `GETCONNECTOR` re-probe 取新 preferred mode → `wlr-randr --custom-mode` 經 **wlr-output-management** 要求 cage re-modeset(overlay 需 **cage ≥0.2.0**=Alpine 3.22,`build-gui-overlay.sh` 已升版並附 wlr-randr;舊 overlay 下 watcher 自動停用)。實機 e2e(Apple Silicon):`CHEFER_VZ_DYNAMIC_RESOLUTION=1` 下視窗縮放 → console `gui: resize: Virtual-1 -> 2200x1400` → guest 內 xdpyinfo dimensions 2560×1600 → 2200×1400 跟隨。**預設取捨(實測後定案)**:guest 不 re-modeset 的情況下開 `automaticallyReconfiguresDisplay` 反而讓 view 停止縮放(等一個不會來的 re-modeset,拖拉時畫面尺寸不同步),且它推給 guest 的是 Retina **實體像素**尺寸、Linux guest 無 HiDPI 概念 → UI/游標縮半——故**當時**預設 scanout=視窗「點」數、關 `automaticallyReconfiguresDisplay`、由 view 等比縮放+鎖長寬比(Retina 上 2× 放大略軟但尺寸正確;此模式現保留為 `CHEFER_VZ_DYNAMIC_RESOLUTION=0`/macOS 13 的保底路徑——搭到舊 overlay(cage <0.2.0、guest 不 re-modeset)時仍可用);guest 端 re-modeset 由 resize watcher 補上(見上)後全鏈可用;翻預設原 gated on 的 **HiDPI output scale 已補上**:helper 於動態解析度模式把開機當下螢幕的 backingScaleFactor 以 **`chefer.gui_scale=`** 附進 kernel cmdline(同 clip_token 作法;視窗跨螢幕拖移的 scale 變化不追、恆用開機值),guest 的 resize watcher 啟動時即對現行輸出補設 output scale(開機初始模式已是 Retina 像素尺寸、之後未必再有 drm 事件),其後每次 `--custom-mode` 同一次 apply 一併重設(cage 0.2.0 的 wlr-output-management apply 走 `wlr_output_head_v1_state_apply`,支援 scale)——邏輯尺寸回到「點」數、游標/UI 尺寸正常,X11 surface 由 compositor 放大(預期視覺等同預設 view-scaling,但比例自由、無 letterbox)。**實機 e2e(Apple Silicon,2026-07-12)機器可驗項全過**:開機 console `gui: resize: Virtual-1 output scale -> 2` → guest 內 `xdpyinfo` 回報**邏輯**尺寸 1280×800(實體模式 2560×1600);`CHEFER_VZ_GUI_TEST_RESIZE` 縮放 1100×700pt → `gui: resize: Virtual-1 -> 2200x1400 (scale 2)` → `xdpyinfo` 跟隨 1100×700——cage 0.2.0 對 mode+scale 同次 apply 無誤。**人工目測項(游標/UI 尺寸、座標映射、Xwayland 放大畫質;拖拉中細黑邊確認為 debounce 前暫態)亦於 2026-07-12 實機通過 → 真動態解析度已翻為 macOS 14+ 預設**,`CHEFER_VZ_DYNAMIC_RESOLUTION=0` 退回 view 縮放保底(檢核清單見 `vz-smoke.sh --gui`)。helper 另提供 `CHEFER_VZ_GUI_TEST_RESIZE=<秒>:x` 測試鉤子(程式化 `setContentSize`,與拖拉同路徑),供 vz-smoke 自動驗證本鏈。macOS 13 無 `automaticallyReconfiguresDisplay` → 維持固定尺寸。**打字點擊/關窗語意已於 2026-07 以 computer-use 驅動實機視窗驗過**(xev 記到 motion/button/忠實 keycode;關窗乾淨 exit 0 無殘留——見上分期③)。**已知問題(實機發現,均已補修)**:(a) helper 對 guest:55381 的剪貼簿連線偶發卡在 NWConnection 靜默 `.waiting`——已補一次性提示 + 3 秒棄連重連 + `CHEFER_CODESIGN_IDENTITY` 穩定簽章(見上「剪貼簿」段的 `.waiting` 可見性);(b) 對 runtime 行程單發 SIGINT/SIGTERM(非終端機 Ctrl+C 的整個 process group)時 helper/VM 會殘留(`vz-smoke.sh` 的 `kill -INT` 即觸發)——已補 stdin liveness 契約(helper 讀 stdin EOF 自我了結,見上「Helper 生命週期」;WHP 側另有 Job Object 雙保險,見 §6 whp 節)。 - **替代(不採用)**:X11 → 要求使用者裝 **XQuartz**、X11 經 vsock 轉發——較快但破壞零安裝、過 VM 邊界延遲高、XQuartz 老舊,故不採。 - **GUI overlay 打包契約(vz 與 whp 共用)**:cage + Xwayland + Mesa(llvmpipe) 及其依賴閉包太肥(估數十 MB),**不放進基礎 initramfs**——做成獨立 kit 產物 **`chefer-gui-overlay-.sqfs`**(rootfs subtree 的 **squashfs**,zstd 壓縮;由 `scripts/build-gui-overlay.sh` 於容器內自 Alpine 套件以 `mksquashfs` 收集,release workflow 建置、`verify-release-kit` 檢查)。**squashfs 而非 tar.zst**:唯讀、可直接掛載、頁面按需載入 → guest 免每次開機解壓 200MB+。基底 **Alpine 3.22**:動態解析度要求 **cage ≥0.2.0**(wlr-output-management 協定;3.20 的 0.1.5 沒有),並附 **wlr-randr**(guest-agent resize watcher 對 cage 下 custom mode/HiDPI output scale 的工具)。 diff --git a/docs/HANDOFF-macos-vz.md b/docs/HANDOFF-macos-vz.md index 86a00f1..2acaf3b 100644 --- a/docs/HANDOFF-macos-vz.md +++ b/docs/HANDOFF-macos-vz.md @@ -22,7 +22,7 @@ bash scripts/vz-smoke.sh --kit-dir <...> --gui 已實作、CI(swiftc 編譯+entitlement 檢查)把關、**但端到端待實機**: -- **vz 開機路徑**(`crates/vmm-backend/src/vz.rs` + `vz-helper/main.swift`):以 bundle 內嵌 Linux appliance 開一台 micro-VM,virtiofs 共享 bundle/data,guest-agent 在裡面跑;解析 console 的 `CHEFER_GUEST_IP` / `CHEFER_GUEST_EXIT` 標記做埠轉發與 exit code 傳播。實機驗證前 `availability()` 預設不可用,需 `CHEFER_VZ_EXPERIMENTAL=1`。 +- **vz 開機路徑**(`crates/vmm-backend/src/vz.rs` + `vz-helper/main.swift`):以 bundle 內嵌 Linux appliance 開一台 micro-VM,virtiofs 共享 bundle/data,guest-agent 在裡面跑;解析 console 的 `CHEFER_GUEST_IP` / `CHEFER_GUEST_EXIT` 標記做埠轉發與 exit code 傳播。**已於 2026-07 實機驗證通過,`availability()` 依真實前提決定可用性、無 `CHEFER_VZ_EXPERIMENTAL` env 鎖**(本文件下方仍出現的 `CHEFER_VZ_EXPERIMENTAL=1` 為當時歷史紀錄,現已不需要)。 - **macOS GUI host 側**(`vz-helper --gui`):virtio-gpu scanout(1280×800 預設,`CHEFER_VZ_GUI_SIZE=WxH` 覆寫)+ USB 鍵盤/絕對座標指標 + AppKit 視窗承載 `VZVirtualMachineView`;**關窗=app 結束**(同 WHP 語意)。macOS 14+ 開 `.resizable` + `automaticallyReconfiguresDisplay`。 - **剪貼簿**(host↔guest,文字 + PNG):與 WHP 同一線協定 + cmdline token;host 端經 VZ NAT 直連 guest IP:55381,NSPasteboard 讀寫 `.png`/`.tiff`。 @@ -106,7 +106,7 @@ bash scripts/vz-smoke.sh --kit-dir kit --gui - `README.md` 平台表(第 ~29–39 行)macOS 欄的 🔧 → ✅、以及 GUI 欄「real-Mac VZ pending」字樣。 - `README.md` roadmap(第 ~266–267 行)「macOS VZ boot validated」「GUI apps on macOS」兩項 `[ ]`/`[~]` → `[x]`。 - `docs/DESIGN.md` §6 macOS 分期 ③④ 的「待實體 Mac 驗證」字樣、以及動態解析度那段(依實測是 re-modeset 還是縮放修正)。 -- 若一切成立,可考慮把 `vz.rs` 的 `CHEFER_VZ_EXPERIMENTAL` 閘門放寬(例如驗證過的 macOS 版本才預設可用)——這屬**改變預設對外行為**,動之前先確認。 +- ~~若一切成立,可考慮把 `vz.rs` 的 `CHEFER_VZ_EXPERIMENTAL` 閘門放寬~~ **已完成(2026-07)**:驗證通過後 env 鎖已整個移除,`availability()` 改依真實前提(arch/appliance/helper/macOS 13+)判定,macOS 上 vz 預設啟用。 --- diff --git a/scripts/vz-smoke.sh b/scripts/vz-smoke.sh index 8160663..71fddc9 100644 --- a/scripts/vz-smoke.sh +++ b/scripts/vz-smoke.sh @@ -123,7 +123,7 @@ export CHEFER_KIT_DIR="$smoke_kit" "$root/target/release/chefer-cli" build "$work/exitcode.yml" --out "$work/dist" exit_exe="$work/dist/vz-exit/vz-exit_${arch}-apple-darwin" set +e -CHEFER_VZ_EXPERIMENTAL=1 "$exit_exe" --extract-dir "$work/extract-exit" >"$work/exit.log" 2>&1 +"$exit_exe" --extract-dir "$work/extract-exit" >"$work/exit.log" 2>&1 rc=$? set -e grep -q "CHEFER_GUEST_EXIT=" "$work/exit.log" \ @@ -152,7 +152,6 @@ exe="$work/dist/vz-smoke/vz-smoke_${arch}-apple-darwin" [[ -x "$exe" ]] || die "build 沒產出 $exe" log="$work/run.log" -export CHEFER_VZ_EXPERIMENTAL=1 "$exe" --extract-dir "$work/extract" >"$log" 2>&1 & app_pid=$! trap 'kill "$app_pid" 2>/dev/null || true' EXIT @@ -204,7 +203,7 @@ YML [ ] 動態解析度 + HiDPI scale(macOS 14+ **預設開**;guest 側 re-modeset 與 output scale 由 guest-agent resize watcher 提供,需上面 3b/5 的新 overlay)——本清單 跑完後另跑: - CHEFER_VZ_EXPERIMENTAL=1 CHEFER_VZ_GUI_TEST_RESIZE=20:1100x700 <同一個單檔> + CHEFER_VZ_GUI_TEST_RESIZE=20:1100x700 <同一個單檔> 開機後 console 應出現「gui: resize: Virtual-1 output scale -> 2」(Retina 2x; helper 經 kernel cmdline chefer.gui_scale 傳入),20 秒後視窗自動縮放(與手動 拖拉同路徑),console 應出現「gui: resize: Virtual-1 -> 2200x1400 (scale 2)」 @@ -216,7 +215,7 @@ YML (CHEFER_VZ_DYNAMIC_RESOLUTION=0 或 macOS 13 退回 view 等比縮放+鎖長寬比—— 保底路徑;搭舊 overlay(cage <0.2.0、guest 不 re-modeset)時亦建議如此。) CHECK - CHEFER_VZ_EXPERIMENTAL=1 "$work/dist/vz-gui-smoke/vz-gui-smoke_${arch}-apple-darwin" --extract-dir "$work/extract-gui" + "$work/dist/vz-gui-smoke/vz-gui-smoke_${arch}-apple-darwin" --extract-dir "$work/extract-gui" note "GUI app 已結束(exit $?)——請依上方清單回填結果" else note "5/5 略過 GUI(加 --gui 執行 GUI 驗證)" From 91eb18cd376e727beeee10a6f32358359cc031de Mon Sep 17 00:00:00 2001 From: TimLai666 Date: Sun, 19 Jul 2026 13:40:33 +0800 Subject: [PATCH 2/2] chore(release): bump 0.4.0 -> 0.5.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit user-facing crates(chefer-cli/runtime/pack/assembler/bundle、vmm-backend、 guest-agent)與一起出貨的 whp-helper 皆 bump 至 0.5.0;inline path-dep 版本約束 同步更新以保持 workspace 可解析。appcipe-spec / appcipe-normalize 的**自身**版本 維持 0.1.0(跟隨 appcipe.yml spec 版本,非 release)。 本版重點:macOS vz 後端完整實機驗證並預設啟用(移除 CHEFER_VZ_EXPERIMENTAL)、 VM GUI 執行中動態解析度 + HiDPI、容器內 DNS 注入,以及實機揪出的一批修復 (arm64 initramfs busybox 交叉建置、tar mode 還原、埠代理讓位)。 Co-Authored-By: Claude Opus 4.8 --- Cargo.lock | 16 ++++++++-------- crates/appcipe-spec/Cargo.toml | 2 +- crates/chefer-assembler/Cargo.toml | 4 ++-- crates/chefer-bundle/Cargo.toml | 2 +- crates/chefer-cli/Cargo.toml | 8 ++++---- crates/chefer-pack/Cargo.toml | 4 ++-- crates/chefer-runtime/Cargo.toml | 6 +++--- crates/guest-agent/Cargo.toml | 4 ++-- crates/vmm-backend/Cargo.toml | 6 +++--- crates/whp-helper/Cargo.toml | 2 +- 10 files changed, 27 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7c733eb..107bdbd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -242,7 +242,7 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chefer-assembler" -version = "0.4.0" +version = "0.5.0" dependencies = [ "anyhow", "chefer-bundle", @@ -256,7 +256,7 @@ dependencies = [ [[package]] name = "chefer-bundle" -version = "0.4.0" +version = "0.5.0" dependencies = [ "anyhow", "serde", @@ -266,7 +266,7 @@ dependencies = [ [[package]] name = "chefer-cli" -version = "0.4.0" +version = "0.5.0" dependencies = [ "anyhow", "appcipe-normalize", @@ -295,7 +295,7 @@ dependencies = [ [[package]] name = "chefer-pack" -version = "0.4.0" +version = "0.5.0" dependencies = [ "anyhow", "appcipe-spec", @@ -317,7 +317,7 @@ dependencies = [ [[package]] name = "chefer-runtime" -version = "0.4.0" +version = "0.5.0" dependencies = [ "anyhow", "chefer-bundle", @@ -1088,7 +1088,7 @@ dependencies = [ [[package]] name = "guest-agent" -version = "0.4.0" +version = "0.5.0" dependencies = [ "anyhow", "chefer-bundle", @@ -3076,7 +3076,7 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "vmm-backend" -version = "0.4.0" +version = "0.5.0" dependencies = [ "anyhow", "chefer-bundle", @@ -3273,7 +3273,7 @@ dependencies = [ [[package]] name = "whp-helper" -version = "0.4.0" +version = "0.5.0" dependencies = [ "png", "smoltcp", diff --git a/crates/appcipe-spec/Cargo.toml b/crates/appcipe-spec/Cargo.toml index e5b2e32..74470c6 100644 --- a/crates/appcipe-spec/Cargo.toml +++ b/crates/appcipe-spec/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] anyhow = "1.0.98" -chefer-bundle = { version = "0.4.0", path = "../chefer-bundle" } +chefer-bundle = { version = "0.5.0", path = "../chefer-bundle" } serde = { version = "1.0.219", features = ["derive"] } serde_yaml = "0.9.34" diff --git a/crates/chefer-assembler/Cargo.toml b/crates/chefer-assembler/Cargo.toml index 1d2b956..a741bb5 100644 --- a/crates/chefer-assembler/Cargo.toml +++ b/crates/chefer-assembler/Cargo.toml @@ -1,11 +1,11 @@ [package] name = "chefer-assembler" -version = "0.4.0" +version = "0.5.0" edition = "2024" [dependencies] anyhow = "1.0.102" -chefer-bundle = { version = "0.4.0", path = "../chefer-bundle" } +chefer-bundle = { version = "0.5.0", path = "../chefer-bundle" } clap = { version = "4.6.1", features = ["derive"] } fs-err = "3.3.0" sha2 = "0.11.0" diff --git a/crates/chefer-bundle/Cargo.toml b/crates/chefer-bundle/Cargo.toml index 5e7ea98..b447ed6 100644 --- a/crates/chefer-bundle/Cargo.toml +++ b/crates/chefer-bundle/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "chefer-bundle" -version = "0.4.0" +version = "0.5.0" edition = "2024" [dependencies] diff --git a/crates/chefer-cli/Cargo.toml b/crates/chefer-cli/Cargo.toml index d15d767..e8d2085 100644 --- a/crates/chefer-cli/Cargo.toml +++ b/crates/chefer-cli/Cargo.toml @@ -1,14 +1,14 @@ [package] name = "chefer-cli" -version = "0.4.0" +version = "0.5.0" edition = "2024" [dependencies] anyhow = "1.0.98" appcipe-normalize = { version = "0.1.0", path = "../appcipe-normalize" } appcipe-spec = { path = "../appcipe-spec" } -chefer-assembler = { version = "0.4.0", path = "../chefer-assembler" } -chefer-bundle = { version = "0.4.0", path = "../chefer-bundle" } +chefer-assembler = { version = "0.5.0", path = "../chefer-assembler" } +chefer-bundle = { version = "0.5.0", path = "../chefer-bundle" } chefer-pack = { path = "../chefer-pack" } clap = { version = "4.5.43", features = ["derive"] } comfy-table = "7.1.4" @@ -23,7 +23,7 @@ serde_yaml = "0.9.34" sha2 = "0.11.0" tar = "0.4.46" tempfile = "3.27.0" -vmm-backend = { version = "0.4.0", path = "../vmm-backend" } +vmm-backend = { version = "0.5.0", path = "../vmm-backend" } zip = { version = "8.6.0", default-features = false, features = ["deflate-flate2"] } zstd = "0.13.3" diff --git a/crates/chefer-pack/Cargo.toml b/crates/chefer-pack/Cargo.toml index be9bd85..08b629d 100644 --- a/crates/chefer-pack/Cargo.toml +++ b/crates/chefer-pack/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "chefer-pack" -version = "0.4.0" +version = "0.5.0" edition = "2024" [dependencies] @@ -8,7 +8,7 @@ anyhow = "1.0.102" appcipe-spec = { version = "0.1.0", path = "../appcipe-spec" } # 解 docker config.json 的 auths.auth(base64 "user:pass");本就在依賴樹(oci-client 傳遞依賴)。 base64 = "0.22" -chefer-bundle = { version = "0.4.0", path = "../chefer-bundle" } +chefer-bundle = { version = "0.5.0", path = "../chefer-bundle" } flate2 = "1.1.9" fs-err = "3.3.0" # registry pull(source: image)。rustls 對齊 workspace(chefer-cli 也用 rustls)。 diff --git a/crates/chefer-runtime/Cargo.toml b/crates/chefer-runtime/Cargo.toml index 4dee8fe..bb63d3e 100644 --- a/crates/chefer-runtime/Cargo.toml +++ b/crates/chefer-runtime/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "chefer-runtime" -version = "0.4.0" +version = "0.5.0" edition = "2024" [dependencies] @@ -15,8 +15,8 @@ clap = { version = "4.0", features = ["derive"] } fs-err = "3.3.0" sha2 = "0.11.0" ctrlc = "3.5.2" -chefer-bundle = { version = "0.4.0", path = "../chefer-bundle" } -vmm-backend = { version = "0.4.0", path = "../vmm-backend" } +chefer-bundle = { version = "0.5.0", path = "../chefer-bundle" } +vmm-backend = { version = "0.5.0", path = "../vmm-backend" } [target.'cfg(windows)'.dependencies] windows-sys = { version = "0.61", features = [ diff --git a/crates/guest-agent/Cargo.toml b/crates/guest-agent/Cargo.toml index 190d6e8..7d0169f 100644 --- a/crates/guest-agent/Cargo.toml +++ b/crates/guest-agent/Cargo.toml @@ -1,11 +1,11 @@ [package] name = "guest-agent" -version = "0.4.0" +version = "0.5.0" edition = "2024" [dependencies] anyhow = "1.0.102" -chefer-bundle = { version = "0.4.0", path = "../chefer-bundle" } +chefer-bundle = { version = "0.5.0", path = "../chefer-bundle" } clap = { version = "4.6.1", features = ["derive"] } hex = "0.4.3" ruzstd = "0.8.3" diff --git a/crates/vmm-backend/Cargo.toml b/crates/vmm-backend/Cargo.toml index a17ac69..b654100 100644 --- a/crates/vmm-backend/Cargo.toml +++ b/crates/vmm-backend/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "vmm-backend" -version = "0.4.0" +version = "0.5.0" edition = "2024" [dependencies] anyhow = "1.0.102" -chefer-bundle = { version = "0.4.0", path = "../chefer-bundle" } -guest-agent = { version = "0.4.0", path = "../guest-agent" } +chefer-bundle = { version = "0.5.0", path = "../chefer-bundle" } +guest-agent = { version = "0.5.0", path = "../guest-agent" } sha2 = "0.11.0" tar = "0.4.46" diff --git a/crates/whp-helper/Cargo.toml b/crates/whp-helper/Cargo.toml index 1f6364a..99c809c 100644 --- a/crates/whp-helper/Cargo.toml +++ b/crates/whp-helper/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "whp-helper" -version = "0.4.0" +version = "0.5.0" edition = "2024" [[bin]]