From 2b01a7fb9f5adfb793f2be19722b972a62873ef2 Mon Sep 17 00:00:00 2001 From: Mike Hilgendorf Date: Wed, 20 May 2026 15:01:27 -0500 Subject: [PATCH 01/11] feat(sandbox/vm): add DAX support to the vfs --- Cargo.lock | 50 +++--- Cargo.toml | 9 +- Cargo.toml.1 | 219 +++++++++++++++++++++++ packages/cli/src/sandbox/vm/run.rs | 6 + packages/sandbox/src/lib.rs | 2 + packages/sandbox/src/vm/init.rs | 45 ++++- packages/sandbox/src/vm/run.rs | 46 +++-- packages/sandbox/src/vm/spawn.rs | 2 + packages/sandbox/src/vm/virtiofsd.rs | 4 +- packages/server/src/config.rs | 28 +++ packages/server/src/run/sandbox.rs | 18 +- packages/server/src/sandbox/isolation.rs | 2 + packages/server/src/vfs.rs | 4 +- packages/server/src/vfs/provider.rs | 8 +- packages/vfs/src/virtiofsd.rs | 174 ++++++++++++------ 15 files changed, 507 insertions(+), 110 deletions(-) create mode 100644 Cargo.toml.1 diff --git a/Cargo.lock b/Cargo.lock index 8f647b6253..d0a067598f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -148,7 +148,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -159,7 +159,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -1691,7 +1691,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccc2776f0c61eca1ca32528f85548abd1a4be8fb53d1b21c013e4f18da1e7090" dependencies = [ "data-encoding", - "syn 2.0.117", + "syn 1.0.109", ] [[package]] @@ -2100,7 +2100,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -4198,7 +4198,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -6450,7 +6450,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -6518,7 +6518,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -7194,7 +7194,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -8268,10 +8268,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.4.2", + "getrandom 0.3.4", "once_cell", "rustix 1.1.4", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -8324,7 +8324,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874" dependencies = [ "rustix 1.1.4", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -9421,9 +9421,9 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "vhost" -version = "0.13.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bce0aad4d8776cb64f1ac591e908a561c50ba6adac4416296efee590b155623f" +checksum = "ee90657203a8644e9a0860a0db6a7887d8ef0c7bc09fc22dfa4ae75df65bac86" dependencies = [ "bitflags 2.12.1", "libc", @@ -9434,9 +9434,9 @@ dependencies = [ [[package]] name = "vhost-user-backend" -version = "0.17.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daa03d476437d005abd2dec0970c468ed2a692e6a0604b834699680e171de942" +checksum = "d5925983d8fb537752ad3e26604c0a17abfa5de77cb6773a096c8a959c9eca0f" dependencies = [ "libc", "log", @@ -9455,10 +9455,11 @@ checksum = "091f1f09cfbf2a78563b562e7a949465cce1aef63b6065645188d995162f8868" [[package]] name = "virtio-queue" -version = "0.14.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "872e2f3fbd70a7e6f01689720cce3d5c2c5efe52b484dd07b674246ada0e9a8d" +checksum = "e358084f32ed165fddb41d98ff1b7ff3c08b9611d8d6114a1b422e2e85688baf" dependencies = [ + "libc", "log", "virtio-bindings", "vm-memory", @@ -9468,8 +9469,7 @@ dependencies = [ [[package]] name = "virtiofsd" version = "1.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162e60c45fbfeaf1d3f8407d788d4b5a0a718f49f469ccd3c7140480716d7677" +source = "git+https://github.com/tangramdotdev/virtiofsd?branch=feat%2Fdax#3d9835ba89fdcc99f0f435185b3207ae2735cec7" dependencies = [ "bitflags 1.3.2", "btree-range-map", @@ -9493,21 +9493,21 @@ dependencies = [ [[package]] name = "vm-memory" -version = "0.16.2" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd5e56d48353c5f54ef50bd158a0452fc82f5383da840f7b8efc31695dd3b9d" +checksum = "f39348a049689cabd3377cdd9182bf526ec76a6f823b79903896452e9d7a7380" dependencies = [ "arc-swap", "libc", - "thiserror 1.0.69", + "thiserror 2.0.18", "winapi", ] [[package]] name = "vmm-sys-util" -version = "0.12.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1435039746e20da4f8d507a72ee1b916f7b4b05af7a91c093d2c6561934ede" +checksum = "506c62fdf617a5176827c2f9afbcf1be155b03a9b4bf9617a60dbc07e3a1642f" dependencies = [ "bitflags 1.3.2", "libc", @@ -9956,7 +9956,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index e20ffb8345..ac5e67215f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -206,9 +206,9 @@ unicode-width = "0.2" uuid = { version = "1", features = ["v7"] } v8 = { git = "https://github.com/tangramdotdev/rusty_v8" } virtiofsd = "1" -vhost = "0.13" -vhost-user-backend = "0.17" -vm-memory = { version = "0.16", features = ["backend-atomic", "backend-mmap"] } +vhost = "0.16" +vhost-user-backend = "0.22" +vm-memory = { version = "0.17.1", features = ["backend-atomic", "backend-mmap"] } waitpid-any = "0.3" webbrowser = "1" winnow = "1" @@ -225,3 +225,6 @@ ignored = [ "octocrab", "webbrowser", ] + +[patch.crates-io] +virtiofsd = { git = "https://github.com/tangramdotdev/virtiofsd", branch = "feat/dax" } diff --git a/Cargo.toml.1 b/Cargo.toml.1 new file mode 100644 index 0000000000..04d2a2ec8a --- /dev/null +++ b/Cargo.toml.1 @@ -0,0 +1,219 @@ +[workspace] +default-members = ["packages/cli"] +members = [ + "packages/builtin", + "packages/cli", + "packages/clients/rust", + "packages/compiler", + "packages/database", + "packages/either", + "packages/fdb", + "packages/futures", + "packages/http", + "packages/ignore", + "packages/index", + "packages/js", + "packages/stores/log", + "packages/messenger", + "packages/stores/object", + "packages/sandbox", + "packages/serialize", + "packages/server", + "packages/uri", + "packages/util", + "packages/v8", + "packages/vfs", + "packages/zed", +] +resolver = "2" + +[workspace.package] +authors = ["Tangram , + /// The size of the artifacts DAX window in bytes, or `0` to disable DAX. Must be a power of + /// two because the window is exposed as a PCI BAR. + #[arg(long, default_value_t = 512 * 1024 * 1024, hide = true)] + pub dax: u64, + #[arg(action = clap::ArgAction::Append, long = "dns", num_args = 1)] pub dns: Vec, @@ -89,6 +94,7 @@ impl Cli { artifacts_path: args.artifacts_path, create_snapshot: args.create_snapshot, cpu: args.cpu, + dax: (args.dax != 0).then_some(args.dax), dns: args.dns, firewall: args.firewall, guest_ip: args.guest_ip, diff --git a/packages/sandbox/src/lib.rs b/packages/sandbox/src/lib.rs index 1bbe9d3465..1880ebac80 100644 --- a/packages/sandbox/src/lib.rs +++ b/packages/sandbox/src/lib.rs @@ -137,6 +137,8 @@ pub struct SeatbeltIsolation {} #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct VmIsolation { + /// The size of the artifacts DAX window in bytes, or `None` to disable DAX. + pub dax: Option, pub kernel_path: PathBuf, pub max_cpu: u64, pub max_memory: u64, diff --git a/packages/sandbox/src/vm/init.rs b/packages/sandbox/src/vm/init.rs index a267f4ca34..d7bdb0db1e 100644 --- a/packages/sandbox/src/vm/init.rs +++ b/packages/sandbox/src/vm/init.rs @@ -73,16 +73,31 @@ pub fn run() -> tg::Result { configure_memory_hotplug(); online_cpus(); - wait_for_virtiofs() - .map_err(|source| tg::error!(!source, "error waiting for virtiofs tags to connect"))?; + wait_for_virtiofs().map_err(|source| { + tg::error!(!source, "error waiting for virtiofs tags to connect") + })?; tracing::trace!("virtiofs tags ready"); online_cpus(); - mount_virtiofs(SANDBOX_FS_TAG, HOST_MOUNT_POINT)?; + mount_virtiofs(SANDBOX_FS_TAG, HOST_MOUNT_POINT, None)?; tracing::trace!("mounted {HOST_MOUNT_POINT}"); - mount_virtiofs(ARTIFACTS_FS_TAG, ARTIFACTS_MOUNT_POINT)?; + let artifacts_options = if dax_from_cmdline() { Some("dax") } else { None }; + mount_virtiofs(ARTIFACTS_FS_TAG, ARTIFACTS_MOUNT_POINT, artifacts_options)?; tracing::trace!("mounted {ARTIFACTS_MOUNT_POINT}"); + // Raise the artifacts BDI readahead window so a single FUSE_READ amortizes the virtio + // round-trip over the full virtiofsd buffer. The host caps each reply at 1 MiB. + let mut stat: libc::stat = unsafe { std::mem::zeroed() }; + let path = c"/mnt/host/opt/tangram/artifacts"; + if unsafe { libc::stat(path.as_ptr(), &mut stat) } == 0 { + let major = libc::major(stat.st_dev); + let minor = libc::minor(stat.st_dev); + let bdi = format!("/sys/class/bdi/{major}:{minor}/read_ahead_kb"); + if let Err(error) = std::fs::write(&bdi, "1024") { + tracing::warn!(%error, path = %bdi, "failed to raise the artifacts readahead"); + } + } + let bytes = std::fs::read(HOST_INIT_CONFIG_PATH) .map_err(|error| tg::error!(!error, "failed to read the init config"))?; tracing::trace!("read {} bytes from {HOST_INIT_CONFIG_PATH}", bytes.len()); @@ -156,7 +171,14 @@ pub fn run() -> tg::Result { } } -fn mount_virtiofs(tag: &str, target: &str) -> tg::Result<()> { +fn dax_from_cmdline() -> bool { + let Ok(cmdline) = std::fs::read_to_string("/proc/cmdline") else { + return false; + }; + cmdline.split_whitespace().any(|token| token == "tangram.dax=1") +} + +fn mount_virtiofs(tag: &str, target: &str, options: Option<&str>) -> tg::Result<()> { std::fs::create_dir_all(target) .map_err(|error| tg::error!(!error, %target, "failed to create the virtiofs target"))?; let source = CString::new(tag) @@ -164,18 +186,27 @@ fn mount_virtiofs(tag: &str, target: &str) -> tg::Result<()> { let target_c = CString::new(target) .map_err(|error| tg::error!(!error, "failed to encode the virtiofs target"))?; let fstype = CString::new("virtiofs").unwrap(); + let data = options + .map(|options| { + CString::new(options) + .map_err(|error| tg::error!(!error, "failed to encode the virtiofs options")) + }) + .transpose()?; + let data_ptr = data.as_ref().map_or(std::ptr::null(), |data| data.as_ptr().cast()); let result = unsafe { libc::mount( source.as_ptr(), target_c.as_ptr(), fstype.as_ptr(), 0, - std::ptr::null(), + data_ptr, ) }; if result != 0 { let error = std::io::Error::last_os_error(); - return Err(tg::error!(!error, %tag, %target, "failed to mount virtiofs")); + return Err( + tg::error!(!error, %tag, %target, "failed to mount virtiofs"), + ); } Ok(()) } diff --git a/packages/sandbox/src/vm/run.rs b/packages/sandbox/src/vm/run.rs index a3938f645d..6f2ad91dd2 100644 --- a/packages/sandbox/src/vm/run.rs +++ b/packages/sandbox/src/vm/run.rs @@ -22,6 +22,7 @@ pub const CLOUD_HYPERVISOR_VSOCK_SOCKET_NAME: &str = "cloud-hypervisor-vsock.soc const SANDBOX_FS_TAG: &str = "sandbox"; const ARTIFACTS_FS_TAG: &str = "artifacts"; + const HOST_MOUNT_POINT: &str = "/mnt/host"; const SERIAL_SOCKET_NAME: &str = "serial.sock"; const VIRTIOFSD_SANDBOX_SOCKET_NAME: &str = "virtiofsd-sandbox.sock"; @@ -49,6 +50,8 @@ pub struct Arg { pub artifacts_path: PathBuf, pub create_snapshot: Option, pub cpu: Option, + /// The size of the artifacts DAX window in bytes, or `None` to disable DAX. + pub dax: Option, pub dns: Vec, pub firewall: crate::Firewall, pub guest_ip: Option, @@ -355,13 +358,26 @@ pub fn run(arg: &Arg) -> tg::Result { cloud_hypervisor_args.push( format!("path={VMM_ROOTFS_IMAGE_PATH},readonly=on,sparse=off,image_type=raw").into(), ); + // Attach the artifacts share at boot, including when creating a snapshot, so its DAX + // cache window is allocated at boot and captured in the snapshot. The cache window is a + // PCI BAR that cannot be allocated when hotplugging the device on resume, so the device + // must be present in the snapshot. + cloud_hypervisor_args.push("--fs".into()); + let artifacts_fs = if let Some(window_size) = arg.dax { + format!( + "tag={ARTIFACTS_FS_TAG},socket={VMM_VFS_SOCKET},dax=true,shm_size={window_size}" + ) + } else { + format!("tag={ARTIFACTS_FS_TAG},socket={VMM_VFS_SOCKET}") + }; + cloud_hypervisor_args.push(artifacts_fs.into()); + // The sandbox share is per-sandbox, so attach it only on a cold boot. On resume it is + // hotplugged after restoring the snapshot. if arg.create_snapshot.is_none() { cloud_hypervisor_args.push("--fs".into()); - cloud_hypervisor_args - .push(format!("tag={SANDBOX_FS_TAG},socket={VMM_VIRTIOFSD_SANDBOX_SOCKET}").into()); - cloud_hypervisor_args.push("--fs".into()); - cloud_hypervisor_args - .push(format!("tag={ARTIFACTS_FS_TAG},socket={VMM_VFS_SOCKET}").into()); + cloud_hypervisor_args.push( + format!("tag={SANDBOX_FS_TAG},socket={VMM_VIRTIOFSD_SANDBOX_SOCKET}").into(), + ); } cloud_hypervisor_args.push("--vsock".into()); cloud_hypervisor_args.push(format!("cid={VMM_GUEST_CID},socket={VMM_VSOCK_SOCKET}").into()); @@ -527,15 +543,6 @@ pub fn run(arg: &Arg) -> tg::Result { &format!("tag={SANDBOX_FS_TAG},socket={VMM_VIRTIOFSD_SANDBOX_SOCKET}"), ], )?; - ch_remote_run( - &ch_remote_bin, - &api_socket, - &host_vm_path_from_root(&arg.path).join("ch-remote.log"), - &[ - "add-fs", - &format!("tag={ARTIFACTS_FS_TAG},socket={VMM_VFS_SOCKET}"), - ], - )?; } wait_for_cloud_hypervisor(&mut cloud_hypervisor, &mut helpers) @@ -736,6 +743,9 @@ fn kernel_cmdline(arg: &Arg) -> String { let tangram_path = Sandbox::guest_tangram_path_from_host_tangram_path(&arg.tangram_path); let mut cmdline = String::from("console=ttyS0 quiet loglevel=0 root=/dev/vda rootfstype=squashfs ro"); + if arg.dax.is_some() { + cmdline.push_str(" tangram.dax=1"); + } write!( &mut cmdline, " init={} -- sandbox vm init", @@ -1034,6 +1044,14 @@ fn build_cloud_hypervisor_mount_arg( target: VMM_VFS_SOCKET.into(), }); } + // Bind the server-provided artifacts vfs socket under the writable VMM root directory so + // cloud-hypervisor can reach it post-chroot. This is required on a cold boot, when creating + // a snapshot so the artifacts share is captured in the snapshot, and on resume so the + // restored device can reconnect to the backend. + binds.push(container::run::Bind { + source: arg.path.join(VFS_SOCKET_NAME), + target: VMM_VFS_SOCKET.into(), + }); let mut ro_binds = vec![ container::run::Bind { source: arg.rootfs_path.clone(), diff --git a/packages/sandbox/src/vm/spawn.rs b/packages/sandbox/src/vm/spawn.rs index f753181f7b..ce23d6b152 100644 --- a/packages/sandbox/src/vm/spawn.rs +++ b/packages/sandbox/src/vm/spawn.rs @@ -21,6 +21,8 @@ pub(crate) fn spawn( .arg(arg.id.to_string()) .arg("--artifacts-path") .arg(&arg.artifacts_path) + .arg("--dax") + .arg(vm.dax.unwrap_or(0).to_string()) .arg("--kernel-path") .arg(&vm.kernel_path) .arg("--max-cpu") diff --git a/packages/sandbox/src/vm/virtiofsd.rs b/packages/sandbox/src/vm/virtiofsd.rs index 467e96067b..44b476e186 100644 --- a/packages/sandbox/src/vm/virtiofsd.rs +++ b/packages/sandbox/src/vm/virtiofsd.rs @@ -146,9 +146,9 @@ fn child_main( vm_memory::GuestMemoryAtomic::new(vm_memory::GuestMemoryMmap::new()), ) .map_err(|error| tg::error!(%error, "failed to create the vhost-user daemon"))?; - let listener = unsafe { vhost::vhost_user::Listener::from_raw_fd(listener_fd) }; + let mut listener = unsafe { vhost::vhost_user::Listener::from_raw_fd(listener_fd) }; daemon - .start(listener) + .start(&mut listener) .map_err(|error| tg::error!(?error, "failed to start the vhost-user daemon"))?; if let Err(error) = daemon.wait() && !matches!( diff --git a/packages/server/src/config.rs b/packages/server/src/config.rs index 1de5ff1bcc..e2a0c2c759 100644 --- a/packages/server/src/config.rs +++ b/packages/server/src/config.rs @@ -645,9 +645,14 @@ pub enum SandboxIsolationDefault { #[serde(default, deny_unknown_fields)] pub struct SeatbeltSandboxIsolation {} +#[serde_as] #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[serde(deny_unknown_fields)] pub struct VmSandboxIsolation { + #[serde_as(as = "BoolOptionDefault")] + #[serde(default = "default_vm_dax")] + pub dax: Option, + pub kernel_path: PathBuf, #[serde(default, skip_serializing_if = "Option::is_none")] @@ -669,6 +674,16 @@ pub struct VmSandboxIsolation { pub snapshot_memory: u64, } +#[derive(Clone, Copy, Debug, serde::Deserialize, serde::Serialize)] +#[serde(default, deny_unknown_fields)] +pub struct Dax { + /// The size of the DAX window for the artifacts share, in kibibytes. Must be a power of two + /// because the window is exposed as a PCI BAR. The guest maps artifact files directly into + /// this window, bypassing the FUSE read path. + #[serde(rename = "window_size")] + pub window_size_kib: usize, +} + #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[serde(default, deny_unknown_fields)] pub struct SandboxNetwork { @@ -1238,6 +1253,14 @@ impl Default for SandboxIsolation { } } +impl Default for Dax { + fn default() -> Self { + Self { + window_size_kib: 512 * 1024, + } + } +} + impl Default for SandboxNetwork { fn default() -> Self { Self { @@ -1464,6 +1487,11 @@ fn default_dns() -> Vec { Vec::new() } +#[expect(clippy::unnecessary_wraps)] +fn default_vm_dax() -> Option { + Some(Dax::default()) +} + fn default_vm_snapshot_cpu() -> u64 { 1 } diff --git a/packages/server/src/run/sandbox.rs b/packages/server/src/run/sandbox.rs index 2a3f851f7c..6927facb9f 100644 --- a/packages/server/src/run/sandbox.rs +++ b/packages/server/src/run/sandbox.rs @@ -137,6 +137,7 @@ impl Session { .unwrap_or_else(|| self.server.vm_snapshot_path()), ); tangram_sandbox::Isolation::Vm(tangram_sandbox::VmIsolation { + dax: vm.dax.map(|dax| dax.window_size_kib as u64 * 1024), kernel_path, max_cpu: vm.max_cpu, max_memory: vm.max_memory, @@ -204,10 +205,10 @@ impl Session { // Start the per-sandbox virtiofsd serving artifacts. Kept as a local so the daemon // stops and the socket file is removed when the sandbox task ends. #[cfg(target_os = "linux")] - let _vfs = if matches!(isolation, tangram_sandbox::Isolation::Vm(_)) { + let _vfs = if let tangram_sandbox::Isolation::Vm(vm) = &isolation { let socket = temp.path().join("vfs.sock"); let options = self.server.config.vfs.unwrap_or_default(); - let vfs = crate::vfs::Server::start_virtiofsd(&self.server, options, &socket) + let vfs = crate::vfs::Server::start_virtiofsd(&self.server, options, &socket, vm.dax) .await .map_err(|error| tg::error!(!error, %id, "failed to start the artifacts vfs"))?; Some(vfs) @@ -569,6 +570,17 @@ impl Server { .map_err(|error| { tg::error!(!error, "failed to create the vm snapshot temp directory") })?; + // Start the artifacts virtiofsd so the snapshot process can attach the artifacts share at + // boot and capture it in the snapshot, rather than hotplugging it on resume, which does + // not support the DAX cache. Kept as a local so the daemon stops and the socket is removed + // when the snapshot process completes. + let _vfs = { + let socket = temp.path().join("vfs.sock"); + let options = self.config.vfs.unwrap_or_default(); + crate::vfs::Server::start_virtiofsd(self, options, &socket, vm.dax) + .await + .map_err(|error| tg::error!(!error, "failed to start the artifacts vfs"))? + }; let image_path = self.sandbox_vm_image.as_ref().ok_or_else(|| { tg::error!( "cannot create the vm snapshot without an image; ensure vm isolation is configured" @@ -594,6 +606,8 @@ impl Server { .arg(snapshot_id.to_string()) .arg("--artifacts-path") .arg(self.artifacts_path()) + .arg("--dax") + .arg(vm.dax.unwrap_or(0).to_string()) .arg("--firewall") .arg(firewall.to_string()) .arg("--kernel-path") diff --git a/packages/server/src/sandbox/isolation.rs b/packages/server/src/sandbox/isolation.rs index fa8e2f0956..245840a5a3 100644 --- a/packages/server/src/sandbox/isolation.rs +++ b/packages/server/src/sandbox/isolation.rs @@ -33,6 +33,7 @@ impl Server { ); Ok(tangram_sandbox::Isolation::Vm( tangram_sandbox::VmIsolation { + dax: vm.dax.map(|dax| dax.window_size_kib as u64 * 1024), kernel_path: vm.kernel_path.clone(), max_cpu: vm.max_cpu, max_memory: vm.max_memory, @@ -96,6 +97,7 @@ impl Server { ); return Some(tangram_sandbox::Isolation::Vm( tangram_sandbox::VmIsolation { + dax: vm.dax.map(|dax| dax.window_size_kib as u64 * 1024), kernel_path: vm.kernel_path.clone(), max_cpu: vm.max_cpu, max_memory: vm.max_memory, diff --git a/packages/server/src/vfs.rs b/packages/server/src/vfs.rs index 8148f37038..a35eafcda4 100644 --- a/packages/server/src/vfs.rs +++ b/packages/server/src/vfs.rs @@ -102,11 +102,13 @@ impl Server { server: &crate::Server, options: crate::config::Vfs, socket: &Path, + dax: Option, ) -> tg::Result { let provider = Provider::new(server, options) .await .map_err(|error| tg::error!(!error, "failed to create the vfs provider"))?; - let server = vfs::virtiofsd::Server::start(provider, socket) + let dax_window_size = dax.unwrap_or(0); + let server = vfs::virtiofsd::Server::start(provider, socket, dax_window_size) .await .map_err(|error| tg::error!(!error, "failed to start the virtiofsd server"))?; Ok(Self::Virtiofsd(server)) diff --git a/packages/server/src/vfs/provider.rs b/packages/server/src/vfs/provider.rs index 183db4c8c3..ba7b89dd05 100644 --- a/packages/server/src/vfs/provider.rs +++ b/packages/server/src/vfs/provider.rs @@ -1801,8 +1801,12 @@ impl Provider { path.push(path_); } match std::fs::File::open(&path) { - Ok(file) => Ok(Some(file.into())), - Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(None), + Ok(file) => { + Ok(Some(file.into())) + }, + Err(error) if error.kind() == std::io::ErrorKind::NotFound => { + Ok(None) + }, Err(error) => { tracing::error!(%error, path = %path.display(), "failed to open cache file"); Err(std::io::Error::from_raw_os_error(libc::EIO)) diff --git a/packages/vfs/src/virtiofsd.rs b/packages/vfs/src/virtiofsd.rs index 8935cbb564..f1be557b3e 100644 --- a/packages/vfs/src/virtiofsd.rs +++ b/packages/vfs/src/virtiofsd.rs @@ -1,14 +1,15 @@ use { crate::{Attrs, AttrsInner, EntryKind, Provider, Result}, std::{ + collections::HashMap, ffi::{CStr, CString}, io::{self, Write as _}, - os::fd::FromRawFd as _, + os::fd::{AsRawFd as _, FromRawFd as _, OwnedFd, RawFd}, path::{Path, PathBuf}, sync::{Arc, Mutex}, time::Duration, }, - virtiofsd::filesystem as fs, + virtiofsd as fs, }; const S_IFDIR: u32 = 0o040_000; @@ -30,7 +31,12 @@ impl Drop for State { } } -pub struct Adapter

(P); +pub struct Adapter

(P, Mutex); + +struct AdapterFds { + by_handle: HashMap>, + by_inode: HashMap>, +} pub struct DirIter { entries: Vec, @@ -44,15 +50,22 @@ struct Item { } impl Server { - pub async fn start

(provider: P, socket: &Path) -> Result + pub async fn start

(provider: P, socket: &Path, dax_window_size: u64) -> Result where P: Provider + Send + Sync + 'static, { let backend = Arc::new( - virtiofsd::vhost_user::VhostUserFsBackendBuilder::default() + fs::vhost_user::VhostUserFsBackendBuilder::default() .set_thread_pool_size(0) .set_tag(None) - .build(Adapter(provider)) + .set_dax_window_size(dax_window_size) + .build(Adapter( + provider, + Mutex::new(AdapterFds { + by_handle: HashMap::new(), + by_inode: HashMap::new(), + }), + )) .map_err(|error| { io::Error::other(format!("failed to build the vhost-user backend: {error}")) })?, @@ -65,11 +78,11 @@ impl Server { .map_err(|error| { io::Error::other(format!("failed to create the vhost-user daemon: {error}")) })?; - let listener = vhost::vhost_user::Listener::new(socket, true).map_err(|error| { + let mut listener = vhost::vhost_user::Listener::new(socket, true).map_err(|error| { io::Error::other(format!("failed to bind the vhost-user socket: {error:?}")) })?; let task = tangram_futures::task::Shared::spawn_blocking(move |_stop| { - if let Err(error) = daemon.start(listener) { + if let Err(error) = daemon.start(&mut listener) { tracing::error!(?error, "the vhost-user daemon failed to start"); return; } @@ -104,7 +117,7 @@ impl Server { } } -impl

fs::FileSystem for Adapter

+impl

fs::filesystem::FileSystem for Adapter

where P: Provider + Send + Sync, { @@ -112,14 +125,19 @@ where type Handle = u64; type DirIter = DirIter; - fn init(&self, capable: virtiofsd::fuse::FsOptions) -> Result { - let wanted = virtiofsd::fuse::FsOptions::ASYNC_READ - | virtiofsd::fuse::FsOptions::EXPORT_SUPPORT - | virtiofsd::fuse::FsOptions::DO_READDIRPLUS; + fn init(&self, capable: fs::fuse::FsOptions) -> Result { + let wanted = fs::fuse::FsOptions::ASYNC_READ + | fs::fuse::FsOptions::EXPORT_SUPPORT + | fs::fuse::FsOptions::DO_READDIRPLUS; Ok(wanted & capable) } - fn lookup(&self, _ctx: fs::Context, parent: u64, name: &CStr) -> Result { + fn lookup( + &self, + _ctx: fs::filesystem::Context, + parent: u64, + name: &CStr, + ) -> Result { let name = name .to_str() .map_err(|_| io::Error::from_raw_os_error(libc::EINVAL))?; @@ -129,7 +147,7 @@ where .ok_or_else(|| io::Error::from_raw_os_error(libc::ENOENT))?; let attrs = self.0.getattr_sync(id)?; self.0.remember_sync(id); - Ok(fs::Entry { + Ok(fs::filesystem::Entry { inode: id, generation: 0, attr: attr_from_attrs(id, attrs), @@ -138,49 +156,56 @@ where }) } - fn forget(&self, _ctx: fs::Context, inode: u64, count: u64) { + fn forget(&self, _ctx: fs::filesystem::Context, inode: u64, count: u64) { self.0.forget_sync(inode, count); } fn getattr( &self, - _ctx: fs::Context, + _ctx: fs::filesystem::Context, inode: u64, _handle: Option, - ) -> Result<(virtiofsd::fuse::Attr, Duration)> { + ) -> Result<(fs::fuse::Attr, Duration)> { let attrs = self.0.getattr_sync(inode)?; Ok((attr_from_attrs(inode, attrs), TIMEOUT)) } - fn readlink(&self, _ctx: fs::Context, inode: u64) -> Result> { + fn readlink(&self, _ctx: fs::filesystem::Context, inode: u64) -> Result> { let bytes = self.0.readlink_sync(inode)?; Ok(bytes.to_vec()) } fn open( &self, - _ctx: fs::Context, + _ctx: fs::filesystem::Context, inode: u64, _kill_priv: bool, _flags: u32, - ) -> Result<(Option, virtiofsd::fuse::OpenOptions)> { - let (handle, _backing_fd) = self.0.open_sync(inode)?; - Ok((Some(handle), virtiofsd::fuse::OpenOptions::empty())) + ) -> Result<(Option, fs::fuse::OpenOptions)> { + let (handle, backing_fd) = self.0.open_sync(inode)?; + if let Some(fd) = backing_fd { + let fd = Arc::new(fd); + let mut fds = self.1.lock().unwrap(); + fds.by_handle.insert(handle, Arc::clone(&fd)); + fds.by_inode.entry(inode).or_insert(fd); + } + // Artifact content is immutable, so keep the guest page cache across reopens. + Ok((Some(handle), fs::fuse::OpenOptions::KEEP_CACHE)) } fn opendir( &self, - _ctx: fs::Context, + _ctx: fs::filesystem::Context, inode: u64, _flags: u32, - ) -> Result<(Option, virtiofsd::fuse::OpenOptions)> { + ) -> Result<(Option, fs::fuse::OpenOptions)> { let handle = self.0.opendir_sync(inode)?; - Ok((Some(handle), virtiofsd::fuse::OpenOptions::empty())) + Ok((Some(handle), fs::fuse::OpenOptions::empty())) } - fn read( + fn read( &self, - _ctx: fs::Context, + _ctx: fs::filesystem::Context, _inode: u64, handle: u64, mut w: W, @@ -204,7 +229,7 @@ where fn release( &self, - _ctx: fs::Context, + _ctx: fs::filesystem::Context, _inode: u64, _flags: u32, handle: u64, @@ -212,18 +237,19 @@ where _flock_release: bool, _lock_owner: Option, ) -> Result<()> { + self.1.lock().unwrap().by_handle.remove(&handle); self.0.close_sync(handle); Ok(()) } - fn releasedir(&self, _ctx: fs::Context, _inode: u64, _flags: u32, handle: u64) -> Result<()> { + fn releasedir(&self, _ctx: fs::filesystem::Context, _inode: u64, _flags: u32, handle: u64) -> Result<()> { self.0.close_sync(handle); Ok(()) } fn readdir( &self, - _ctx: fs::Context, + _ctx: fs::filesystem::Context, _inode: u64, handle: u64, _size: u32, @@ -232,12 +258,12 @@ where let entries = self.0.readdir_sync(handle)?; let items = entries .into_iter() - .map(|(name, id, kind)| { - let name = - CString::new(name).map_err(|_| io::Error::from_raw_os_error(libc::EINVAL))?; + .map(|(name, id, typ)| { + let name = CString::new(name) + .map_err(|_| io::Error::from_raw_os_error(libc::EINVAL))?; Ok::<_, io::Error>(Item { ino: id, - type_: entry_kind(kind), + type_: dir_entry_type(typ), name, }) }) @@ -251,11 +277,11 @@ where fn getxattr( &self, - _ctx: fs::Context, + _ctx: fs::filesystem::Context, inode: u64, name: &CStr, size: u32, - ) -> Result { + ) -> Result { let name = name .to_str() .map_err(|_| io::Error::from_raw_os_error(libc::EINVAL))?; @@ -265,15 +291,20 @@ where .ok_or_else(|| io::Error::from_raw_os_error(libc::ENODATA))?; if size == 0 { let count = u32::try_from(value.len()).unwrap_or(u32::MAX); - return Ok(fs::GetxattrReply::Count(count)); + return Ok(fs::filesystem::GetxattrReply::Count(count)); } if value.len() > size as usize { return Err(io::Error::from_raw_os_error(libc::ERANGE)); } - Ok(fs::GetxattrReply::Value(value.to_vec())) + Ok(fs::filesystem::GetxattrReply::Value(value.to_vec())) } - fn listxattr(&self, _ctx: fs::Context, inode: u64, size: u32) -> Result { + fn listxattr( + &self, + _ctx: fs::filesystem::Context, + inode: u64, + size: u32, + ) -> Result { let names = self.0.listxattrs_sync(inode)?; let mut buf = Vec::new(); for name in &names { @@ -282,26 +313,60 @@ where } if size == 0 { let count = u32::try_from(buf.len()).unwrap_or(u32::MAX); - return Ok(fs::ListxattrReply::Count(count)); + return Ok(fs::filesystem::ListxattrReply::Count(count)); } if buf.len() > size as usize { return Err(io::Error::from_raw_os_error(libc::ERANGE)); } - Ok(fs::ListxattrReply::Names(buf)) + Ok(fs::filesystem::ListxattrReply::Names(buf)) } - fn access(&self, _ctx: fs::Context, _inode: u64, _mask: u32) -> Result<()> { + fn access(&self, _ctx: fs::filesystem::Context, _inode: u64, _mask: u32) -> Result<()> { Ok(()) } + + fn setupmapping( + &self, + _ctx: fs::filesystem::Context, + inode: u64, + _handle: u64, + foffset: u64, + _len: u64, + flags: fs::fuse::SetupmappingFlags, + ) -> Result<(RawFd, u64)> { + // The artifacts filesystem is read-only, so reject any writable mapping. + if flags.contains(fs::fuse::SetupmappingFlags::WRITE) { + return Err(io::Error::from_raw_os_error(libc::EROFS)); + } + let fds = self.1.lock().unwrap(); + let Some(fd) = fds.by_inode.get(&inode) else { + return Err(io::Error::from_raw_os_error(libc::EBADF)); + }; + let raw = fd.as_raw_fd(); + // The backing descriptor refers to the whole artifact file beginning at offset zero, so the + // offset within it is the requested file offset. + Ok((raw, foffset)) + } } -impl

fs::SerializableFileSystem for Adapter

where P: Provider + Send + Sync {} +impl

fs::filesystem::SerializableFileSystem for Adapter

+where + P: Provider + Send + Sync, +{ + fn serialize(&self, _state_pipe: std::fs::File) -> Result<()> { + Ok(()) + } + + fn deserialize_and_apply(&self, _state_pipe: std::fs::File) -> Result<()> { + Ok(()) + } +} -impl fs::DirectoryIterator for DirIter { - fn next(&mut self) -> Option> { +impl fs::filesystem::DirectoryIterator for DirIter { + fn next(&mut self) -> Option> { let entry = self.entries.get(self.cursor)?; self.cursor += 1; - Some(fs::DirEntry { + Some(fs::filesystem::DirEntry { ino: entry.ino as libc::ino64_t, offset: self.cursor as u64, type_: entry.type_, @@ -310,15 +375,16 @@ impl fs::DirectoryIterator for DirIter { } } -fn attr_from_attrs(inode: u64, attrs: Attrs) -> virtiofsd::fuse::Attr { +fn attr_from_attrs(inode: u64, attrs: Attrs) -> fs::fuse::Attr { let (size, mode) = match attrs.inner { AttrsInner::Directory => (0, S_IFDIR | 0o555), - AttrsInner::File { executable, size } => { - (size, S_IFREG | 0o444 | if executable { 0o111 } else { 0 }) - }, + AttrsInner::File { executable, size } => ( + size, + S_IFREG | 0o444 | if executable { 0o111 } else { 0 }, + ), AttrsInner::Symlink => (0, S_IFLNK | 0o444), }; - virtiofsd::fuse::Attr { + fs::fuse::Attr { ino: inode, size, blocks: 0, @@ -338,8 +404,8 @@ fn attr_from_attrs(inode: u64, attrs: Attrs) -> virtiofsd::fuse::Attr { } } -fn entry_kind(kind: EntryKind) -> u32 { - match kind { +fn dir_entry_type(typ: EntryKind) -> u32 { + match typ { EntryKind::Directory => u32::from(libc::DT_DIR), EntryKind::File => u32::from(libc::DT_REG), EntryKind::Symlink => u32::from(libc::DT_LNK), From caf399ffee24c9dfc7efd07cc6eaa89743607d21 Mon Sep 17 00:00:00 2001 From: Mike Hilgendorf Date: Tue, 2 Jun 2026 11:18:34 -0500 Subject: [PATCH 02/11] fix: resource leak --- packages/vfs/src/virtiofsd.rs | 56 +++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/packages/vfs/src/virtiofsd.rs b/packages/vfs/src/virtiofsd.rs index f1be557b3e..d636f9f755 100644 --- a/packages/vfs/src/virtiofsd.rs +++ b/packages/vfs/src/virtiofsd.rs @@ -1,10 +1,10 @@ use { crate::{Attrs, AttrsInner, EntryKind, Provider, Result}, + dashmap::DashMap, std::{ - collections::HashMap, ffi::{CStr, CString}, io::{self, Write as _}, - os::fd::{AsRawFd as _, FromRawFd as _, OwnedFd, RawFd}, + os::fd::{AsRawFd as _, FromRawFd as _, RawFd}, path::{Path, PathBuf}, sync::{Arc, Mutex}, time::Duration, @@ -31,11 +31,11 @@ impl Drop for State { } } -pub struct Adapter

(P, Mutex); +pub struct Adapter

(P, DashMap); -struct AdapterFds { - by_handle: HashMap>, - by_inode: HashMap>, +struct InodeFd { + fd: std::fs::File, + count: usize, } pub struct DirIter { @@ -59,13 +59,7 @@ impl Server { .set_thread_pool_size(0) .set_tag(None) .set_dax_window_size(dax_window_size) - .build(Adapter( - provider, - Mutex::new(AdapterFds { - by_handle: HashMap::new(), - by_inode: HashMap::new(), - }), - )) + .build(Adapter(provider, DashMap::new())) .map_err(|error| { io::Error::other(format!("failed to build the vhost-user backend: {error}")) })?, @@ -184,10 +178,13 @@ where ) -> Result<(Option, fs::fuse::OpenOptions)> { let (handle, backing_fd) = self.0.open_sync(inode)?; if let Some(fd) = backing_fd { - let fd = Arc::new(fd); - let mut fds = self.1.lock().unwrap(); - fds.by_handle.insert(handle, Arc::clone(&fd)); - fds.by_inode.entry(inode).or_insert(fd); + self.1 + .entry(inode) + .and_modify(|entry| entry.count += 1) + .or_insert(InodeFd { + fd: fd.into(), + count: 1, + }); } // Artifact content is immutable, so keep the guest page cache across reopens. Ok((Some(handle), fs::fuse::OpenOptions::KEEP_CACHE)) @@ -206,7 +203,7 @@ where fn read( &self, _ctx: fs::filesystem::Context, - _inode: u64, + inode: u64, handle: u64, mut w: W, size: u32, @@ -214,6 +211,15 @@ where _lock_owner: Option, _flags: u32, ) -> Result { + // Splice directly from the backing cache file when one is available. It begins at offset + // zero, so the requested offset is the file offset, and the read is positional, so sharing + // the descriptor across concurrent reads is safe. + if let Some(entry) = self.1.get(&inode) { + return w.read_from_file_at(&entry.fd, size as usize, offset, None); + } + + // Fall back for blobs with no contiguous backing file: materialize the content and splice + // it through a memfd, since the transport only accepts a file. let bytes = self.0.read_sync(handle, offset, u64::from(size))?; if bytes.is_empty() { return Ok(0); @@ -230,14 +236,19 @@ where fn release( &self, _ctx: fs::filesystem::Context, - _inode: u64, + inode: u64, _flags: u32, handle: u64, _flush: bool, _flock_release: bool, _lock_owner: Option, ) -> Result<()> { - self.1.lock().unwrap().by_handle.remove(&handle); + // Drop the backing descriptor once the last handle for the inode is released. An active + // DAX mapping keeps the file alive on its own, so this is safe even while one exists. + self.1.remove_if_mut(&inode, |_, entry| { + entry.count -= 1; + entry.count == 0 + }); self.0.close_sync(handle); Ok(()) } @@ -338,11 +349,10 @@ where if flags.contains(fs::fuse::SetupmappingFlags::WRITE) { return Err(io::Error::from_raw_os_error(libc::EROFS)); } - let fds = self.1.lock().unwrap(); - let Some(fd) = fds.by_inode.get(&inode) else { + let Some(entry) = self.1.get(&inode) else { return Err(io::Error::from_raw_os_error(libc::EBADF)); }; - let raw = fd.as_raw_fd(); + let raw = entry.fd.as_raw_fd(); // The backing descriptor refers to the whole artifact file beginning at offset zero, so the // offset within it is the requested file offset. Ok((raw, foffset)) From a40af1d1d6ee4f62247719e20ec5e51d3571df16 Mon Sep 17 00:00:00 2001 From: Mike Hilgendorf Date: Tue, 2 Jun 2026 15:09:44 -0500 Subject: [PATCH 03/11] add tests --- packages/cli/tests/run/sandboxed_mount_cat.nu | 30 +++++++++++++ .../cli/tests/run/sandboxed_mount_cat_dax.nu | 45 +++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 packages/cli/tests/run/sandboxed_mount_cat.nu create mode 100644 packages/cli/tests/run/sandboxed_mount_cat_dax.nu diff --git a/packages/cli/tests/run/sandboxed_mount_cat.nu b/packages/cli/tests/run/sandboxed_mount_cat.nu new file mode 100644 index 0000000000..824fd10b8c --- /dev/null +++ b/packages/cli/tests/run/sandboxed_mount_cat.nu @@ -0,0 +1,30 @@ +use ../../test.nu * + +if $nu.os-info.name != 'linux' { + return +} + +let server = spawn --busybox + +let mount = mktemp -d | str trim +"hello from the mount\n" | save -f ($mount | path join "file") + +let path = artifact { + tangram.ts: ' + import busybox from "busybox"; + + export default async function (mount) { + const host = tg.host.current; + tg.assert(typeof host === "string"); + return await tg.run({ + args: ["-c", "cat /target/file"], + env: tg.build(busybox), + executable: "sh", + host, + }).sandbox().mount({ source: mount, target: "/target" }); + } + ', +} + +let output = tg run $path --arg-string $mount | str trim +assert equal $output "hello from the mount" diff --git a/packages/cli/tests/run/sandboxed_mount_cat_dax.nu b/packages/cli/tests/run/sandboxed_mount_cat_dax.nu new file mode 100644 index 0000000000..df9aed92ff --- /dev/null +++ b/packages/cli/tests/run/sandboxed_mount_cat_dax.nu @@ -0,0 +1,45 @@ +use ../../test.nu * + +if $nu.os-info.name != 'linux' { + return +} + +# This test only applies when the vm isolation feature is enabled. +if (($env.TANGRAM_TEST_VM? | default "") | str length) == 0 { + return +} + +for dax in [true false] { + let server = spawn --busybox --config { + sandbox: { + isolation: { + vm: { + dax: $dax, + }, + }, + }, + } + + let mount = mktemp -d | str trim + "hello from the mount\n" | save -f ($mount | path join "file") + + let path = artifact { + tangram.ts: ' + import busybox from "busybox"; + + export default async function (mount) { + const host = tg.host.current; + tg.assert(typeof host === "string"); + return await tg.run({ + args: ["-c", "cat /target/file"], + env: tg.build(busybox), + executable: "sh", + host, + }).sandbox().mount({ source: mount, target: "/target" }); + } + ', + } + + let output = tg run $path --arg-string $mount | str trim + assert equal $output "hello from the mount" +} From 950e9aa08de8168322214568832b78d87d2cbade Mon Sep 17 00:00:00 2001 From: Mike Hilgendorf Date: Tue, 2 Jun 2026 15:48:28 -0500 Subject: [PATCH 04/11] default DAX window size to 8GB, fix race condition in startup, remove hotplugged virtiofs for non-user mounts --- packages/sandbox/src/vm/init.rs | 20 +++++++++++-- packages/sandbox/src/vm/run.rs | 52 ++++++++++++++++----------------- packages/server/src/config.rs | 2 +- 3 files changed, 45 insertions(+), 29 deletions(-) diff --git a/packages/sandbox/src/vm/init.rs b/packages/sandbox/src/vm/init.rs index d7bdb0db1e..34e43bdcc5 100644 --- a/packages/sandbox/src/vm/init.rs +++ b/packages/sandbox/src/vm/init.rs @@ -77,6 +77,24 @@ pub fn run() -> tg::Result { tg::error!(!source, "error waiting for virtiofs tags to connect") })?; tracing::trace!("virtiofs tags ready"); + + // Both shares are attached at boot and captured in the snapshot. Signal that the guest is ready + // to be snapshotted, then block on a byte from the serial console. The snapshot freezes the + // guest here, before either share is mounted; on resume the host writes the byte and the shares + // mount fresh against their reconnected backends. + signal_ready(); + let mut resume = [0u8; 1]; + loop { + let n = unsafe { libc::read(libc::STDIN_FILENO, resume.as_mut_ptr().cast(), resume.len()) }; + if n >= 0 { + break; + } + let error = std::io::Error::last_os_error(); + if error.raw_os_error() != Some(libc::EINTR) { + return Err(tg::error!(!error, "failed to read the resume signal")); + } + } + tracing::trace!("resumed"); online_cpus(); mount_virtiofs(SANDBOX_FS_TAG, HOST_MOUNT_POINT, None)?; @@ -536,8 +554,6 @@ fn wait_for_virtiofs() -> tg::Result<()> { return Ok(()); } - signal_ready(); - let mut buf = [0u8; 8192]; while !pending.is_empty() { let n = unsafe { libc::recv(socket, buf.as_mut_ptr().cast(), buf.len(), 0) }; diff --git a/packages/sandbox/src/vm/run.rs b/packages/sandbox/src/vm/run.rs index 6f2ad91dd2..24ab996a21 100644 --- a/packages/sandbox/src/vm/run.rs +++ b/packages/sandbox/src/vm/run.rs @@ -5,6 +5,7 @@ use { ffi::{CStr, CString, OsStr}, fmt::Write as _, hash::{Hash as _, Hasher as _}, + io::Write as _, net::Ipv4Addr, os::{ fd::{AsRawFd as _, FromRawFd as _, OwnedFd}, @@ -261,8 +262,11 @@ pub fn run(arg: &Arg) -> tg::Result { } let mut helpers: Vec = Vec::new(); - if arg.create_snapshot.is_none() { - // Spawn the sandbox virtiofsd, serving the host share dir. + { + // Spawn the sandbox virtiofsd, serving the host share dir. Spawn it even when creating a + // snapshot, with an empty share, so the sandbox share device is attached at boot and + // captured in the snapshot rather than hotplugged on resume. On resume the device + // reconnects to a fresh sandbox virtiofsd serving the real per-sandbox content. let sandbox_socket_path = host_vm_path_from_root(&arg.path).join(VIRTIOFSD_SANDBOX_SOCKET_NAME); tracing::trace!( @@ -303,8 +307,6 @@ pub fn run(arg: &Arg) -> tg::Result { )?; tracing::trace!("sandbox virtiofsd helper pid={sandbox_pid}"); helpers.push(ChildProcess::new(sandbox_pid)); - } else { - tracing::trace!("snapshot mode: skipping virtiofsd helpers"); } let cloud_hypervisor_bin = find_in_path("cloud-hypervisor") @@ -358,10 +360,6 @@ pub fn run(arg: &Arg) -> tg::Result { cloud_hypervisor_args.push( format!("path={VMM_ROOTFS_IMAGE_PATH},readonly=on,sparse=off,image_type=raw").into(), ); - // Attach the artifacts share at boot, including when creating a snapshot, so its DAX - // cache window is allocated at boot and captured in the snapshot. The cache window is a - // PCI BAR that cannot be allocated when hotplugging the device on resume, so the device - // must be present in the snapshot. cloud_hypervisor_args.push("--fs".into()); let artifacts_fs = if let Some(window_size) = arg.dax { format!( @@ -371,14 +369,9 @@ pub fn run(arg: &Arg) -> tg::Result { format!("tag={ARTIFACTS_FS_TAG},socket={VMM_VFS_SOCKET}") }; cloud_hypervisor_args.push(artifacts_fs.into()); - // The sandbox share is per-sandbox, so attach it only on a cold boot. On resume it is - // hotplugged after restoring the snapshot. - if arg.create_snapshot.is_none() { - cloud_hypervisor_args.push("--fs".into()); - cloud_hypervisor_args.push( - format!("tag={SANDBOX_FS_TAG},socket={VMM_VIRTIOFSD_SANDBOX_SOCKET}").into(), - ); - } + cloud_hypervisor_args.push("--fs".into()); + cloud_hypervisor_args + .push(format!("tag={SANDBOX_FS_TAG},socket={VMM_VIRTIOFSD_SANDBOX_SOCKET}").into()); cloud_hypervisor_args.push("--vsock".into()); cloud_hypervisor_args.push(format!("cid={VMM_GUEST_CID},socket={VMM_VSOCK_SOCKET}").into()); cloud_hypervisor_args.push("--console".into()); @@ -404,7 +397,7 @@ pub fn run(arg: &Arg) -> tg::Result { let mut cloud_hypervisor = ChildProcess::new(cloud_hypervisor_pid); let serial_socket_path = host_vm_path_from_root(&arg.path).join(SERIAL_SOCKET_NAME); - let serial_stream = connect_serial_socket(&serial_socket_path, Duration::from_secs(10))?; + let mut serial_stream = connect_serial_socket(&serial_socket_path, Duration::from_secs(10))?; tracing::trace!("connected to guest serial socket; tailing to stderr"); let serial_reader = serial_stream .try_clone() @@ -534,15 +527,12 @@ pub fn run(arg: &Arg) -> tg::Result { &["add-net", net], )?; } - ch_remote_run( - &ch_remote_bin, - &api_socket, - &host_vm_path_from_root(&arg.path).join("ch-remote.log"), - &[ - "add-fs", - &format!("tag={SANDBOX_FS_TAG},socket={VMM_VIRTIOFSD_SANDBOX_SOCKET}"), - ], - )?; + serial_stream + .write_all(b"\n") + .map_err(|error| tg::error!(!error, "failed to write the resume signal"))?; + serial_stream + .flush() + .map_err(|error| tg::error!(!error, "failed to flush the resume signal"))?; } wait_for_cloud_hypervisor(&mut cloud_hypervisor, &mut helpers) @@ -570,6 +560,16 @@ fn connect_serial_socket( ) })?; }, + Err(error) if error.kind() == std::io::ErrorKind::ConnectionRefused => { + if Instant::now() >= deadline { + return Err(tg::error!( + !error, + path = %path.display(), + "timed out connecting to the guest serial socket", + )); + } + std::thread::sleep(Duration::from_millis(10)); + }, Err(error) => { return Err(tg::error!( !error, diff --git a/packages/server/src/config.rs b/packages/server/src/config.rs index e2a0c2c759..295344b75c 100644 --- a/packages/server/src/config.rs +++ b/packages/server/src/config.rs @@ -1256,7 +1256,7 @@ impl Default for SandboxIsolation { impl Default for Dax { fn default() -> Self { Self { - window_size_kib: 512 * 1024, + window_size_kib: 8 * 1024 * 1024, } } } From f4365028c538140de50675b70d47320d62442fde Mon Sep 17 00:00:00 2001 From: Mike Hilgendorf Date: Thu, 4 Jun 2026 13:03:03 -0500 Subject: [PATCH 05/11] bad file --- Cargo.toml.1 | 219 --------------------------------------------------- 1 file changed, 219 deletions(-) delete mode 100644 Cargo.toml.1 diff --git a/Cargo.toml.1 b/Cargo.toml.1 deleted file mode 100644 index 04d2a2ec8a..0000000000 --- a/Cargo.toml.1 +++ /dev/null @@ -1,219 +0,0 @@ -[workspace] -default-members = ["packages/cli"] -members = [ - "packages/builtin", - "packages/cli", - "packages/clients/rust", - "packages/compiler", - "packages/database", - "packages/either", - "packages/fdb", - "packages/futures", - "packages/http", - "packages/ignore", - "packages/index", - "packages/js", - "packages/stores/log", - "packages/messenger", - "packages/stores/object", - "packages/sandbox", - "packages/serialize", - "packages/server", - "packages/uri", - "packages/util", - "packages/v8", - "packages/vfs", - "packages/zed", -] -resolver = "2" - -[workspace.package] -authors = ["Tangram Date: Sun, 7 Jun 2026 17:29:57 -0400 Subject: [PATCH 06/11] nits --- packages/cli/src/sandbox/vm/run.rs | 8 +- .../cli/tests/run/sandboxed_mount_cat_dax.nu | 1 - packages/sandbox/src/lib.rs | 1 - packages/sandbox/src/vm.rs | 2 +- packages/sandbox/src/vm/init.rs | 29 +++-- packages/sandbox/src/vm/run.rs | 19 +-- packages/sandbox/src/vm/spawn.rs | 5 +- .../src/vm/{virtiofsd.rs => virtiofs.rs} | 0 packages/server/src/config.rs | 3 - packages/server/src/run/sandbox.rs | 19 ++- packages/server/src/vfs.rs | 12 +- packages/server/src/vfs/provider.rs | 8 +- packages/vfs/src/lib.rs | 2 +- .../vfs/src/{virtiofsd.rs => virtiofs.rs} | 112 +++++++++--------- 14 files changed, 95 insertions(+), 126 deletions(-) rename packages/sandbox/src/vm/{virtiofsd.rs => virtiofs.rs} (100%) rename packages/vfs/src/{virtiofsd.rs => virtiofs.rs} (71%) diff --git a/packages/cli/src/sandbox/vm/run.rs b/packages/cli/src/sandbox/vm/run.rs index b0c17bca19..1bb4ff5ad4 100644 --- a/packages/cli/src/sandbox/vm/run.rs +++ b/packages/cli/src/sandbox/vm/run.rs @@ -16,10 +16,8 @@ pub struct Args { #[arg(long)] pub cpu: Option, - /// The size of the artifacts DAX window in bytes, or `0` to disable DAX. Must be a power of - /// two because the window is exposed as a PCI BAR. - #[arg(long, default_value_t = 512 * 1024 * 1024, hide = true)] - pub dax: u64, + #[arg(long, hide = true)] + pub dax: Option, #[arg(action = clap::ArgAction::Append, long = "dns", num_args = 1)] pub dns: Vec, @@ -94,7 +92,7 @@ impl Cli { artifacts_path: args.artifacts_path, create_snapshot: args.create_snapshot, cpu: args.cpu, - dax: (args.dax != 0).then_some(args.dax), + dax: args.dax, dns: args.dns, firewall: args.firewall, guest_ip: args.guest_ip, diff --git a/packages/cli/tests/run/sandboxed_mount_cat_dax.nu b/packages/cli/tests/run/sandboxed_mount_cat_dax.nu index df9aed92ff..48718685f0 100644 --- a/packages/cli/tests/run/sandboxed_mount_cat_dax.nu +++ b/packages/cli/tests/run/sandboxed_mount_cat_dax.nu @@ -4,7 +4,6 @@ if $nu.os-info.name != 'linux' { return } -# This test only applies when the vm isolation feature is enabled. if (($env.TANGRAM_TEST_VM? | default "") | str length) == 0 { return } diff --git a/packages/sandbox/src/lib.rs b/packages/sandbox/src/lib.rs index 1880ebac80..a4e768545e 100644 --- a/packages/sandbox/src/lib.rs +++ b/packages/sandbox/src/lib.rs @@ -137,7 +137,6 @@ pub struct SeatbeltIsolation {} #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct VmIsolation { - /// The size of the artifacts DAX window in bytes, or `None` to disable DAX. pub dax: Option, pub kernel_path: PathBuf, pub max_cpu: u64, diff --git a/packages/sandbox/src/vm.rs b/packages/sandbox/src/vm.rs index 12870f73fd..fd2931362f 100644 --- a/packages/sandbox/src/vm.rs +++ b/packages/sandbox/src/vm.rs @@ -9,7 +9,7 @@ pub(crate) mod network; pub mod image; pub mod init; pub mod run; -pub mod virtiofsd; +pub mod virtiofs; #[derive(Clone, Debug)] pub struct Network { diff --git a/packages/sandbox/src/vm/init.rs b/packages/sandbox/src/vm/init.rs index 34e43bdcc5..1868b34390 100644 --- a/packages/sandbox/src/vm/init.rs +++ b/packages/sandbox/src/vm/init.rs @@ -73,15 +73,10 @@ pub fn run() -> tg::Result { configure_memory_hotplug(); online_cpus(); - wait_for_virtiofs().map_err(|source| { - tg::error!(!source, "error waiting for virtiofs tags to connect") - })?; + wait_for_virtiofs() + .map_err(|source| tg::error!(!source, "error waiting for virtiofs tags to connect"))?; tracing::trace!("virtiofs tags ready"); - // Both shares are attached at boot and captured in the snapshot. Signal that the guest is ready - // to be snapshotted, then block on a byte from the serial console. The snapshot freezes the - // guest here, before either share is mounted; on resume the host writes the byte and the shares - // mount fresh against their reconnected backends. signal_ready(); let mut resume = [0u8; 1]; loop { @@ -99,12 +94,14 @@ pub fn run() -> tg::Result { mount_virtiofs(SANDBOX_FS_TAG, HOST_MOUNT_POINT, None)?; tracing::trace!("mounted {HOST_MOUNT_POINT}"); - let artifacts_options = if dax_from_cmdline() { Some("dax") } else { None }; + let artifacts_options = if dax_from_cmdline() { + Some("dax") + } else { + None + }; mount_virtiofs(ARTIFACTS_FS_TAG, ARTIFACTS_MOUNT_POINT, artifacts_options)?; tracing::trace!("mounted {ARTIFACTS_MOUNT_POINT}"); - // Raise the artifacts BDI readahead window so a single FUSE_READ amortizes the virtio - // round-trip over the full virtiofsd buffer. The host caps each reply at 1 MiB. let mut stat: libc::stat = unsafe { std::mem::zeroed() }; let path = c"/mnt/host/opt/tangram/artifacts"; if unsafe { libc::stat(path.as_ptr(), &mut stat) } == 0 { @@ -193,7 +190,9 @@ fn dax_from_cmdline() -> bool { let Ok(cmdline) = std::fs::read_to_string("/proc/cmdline") else { return false; }; - cmdline.split_whitespace().any(|token| token == "tangram.dax=1") + cmdline + .split_whitespace() + .any(|token| token == "tangram.dax=1") } fn mount_virtiofs(tag: &str, target: &str, options: Option<&str>) -> tg::Result<()> { @@ -210,7 +209,9 @@ fn mount_virtiofs(tag: &str, target: &str, options: Option<&str>) -> tg::Result< .map_err(|error| tg::error!(!error, "failed to encode the virtiofs options")) }) .transpose()?; - let data_ptr = data.as_ref().map_or(std::ptr::null(), |data| data.as_ptr().cast()); + let data_ptr = data + .as_ref() + .map_or(std::ptr::null(), |data| data.as_ptr().cast()); let result = unsafe { libc::mount( source.as_ptr(), @@ -222,9 +223,7 @@ fn mount_virtiofs(tag: &str, target: &str, options: Option<&str>) -> tg::Result< }; if result != 0 { let error = std::io::Error::last_os_error(); - return Err( - tg::error!(!error, %tag, %target, "failed to mount virtiofs"), - ); + return Err(tg::error!(!error, %tag, %target, "failed to mount virtiofs")); } Ok(()) } diff --git a/packages/sandbox/src/vm/run.rs b/packages/sandbox/src/vm/run.rs index 24ab996a21..1ea844ce92 100644 --- a/packages/sandbox/src/vm/run.rs +++ b/packages/sandbox/src/vm/run.rs @@ -1,5 +1,5 @@ use { - crate::{Sandbox, container, vm::virtiofsd}, + crate::{Sandbox, container, vm::virtiofs}, std::{ collections::hash_map::DefaultHasher, ffi::{CStr, CString, OsStr}, @@ -51,7 +51,6 @@ pub struct Arg { pub artifacts_path: PathBuf, pub create_snapshot: Option, pub cpu: Option, - /// The size of the artifacts DAX window in bytes, or `None` to disable DAX. pub dax: Option, pub dns: Vec, pub firewall: crate::Firewall, @@ -263,10 +262,6 @@ pub fn run(arg: &Arg) -> tg::Result { let mut helpers: Vec = Vec::new(); { - // Spawn the sandbox virtiofsd, serving the host share dir. Spawn it even when creating a - // snapshot, with an empty share, so the sandbox share device is attached at boot and - // captured in the snapshot rather than hotplugged on resume. On resume the device - // reconnects to a fresh sandbox virtiofsd serving the real per-sandbox content. let sandbox_socket_path = host_vm_path_from_root(&arg.path).join(VIRTIOFSD_SANDBOX_SOCKET_NAME); tracing::trace!( @@ -275,8 +270,6 @@ pub fn run(arg: &Arg) -> tg::Result { ); let sandbox_shared_dir = host_share_path_from_root(&arg.path); - // Create the user mount targets in the share dir. The helper binds them in its own - // namespace so virtiofsd serves them to the guest. let mut sandbox_binds = Vec::new(); for (index, mount) in arg.mounts.iter().enumerate() { let target = host_share_mounts_path_from_root(&arg.path).join(index.to_string()); @@ -289,7 +282,7 @@ pub fn run(arg: &Arg) -> tg::Result { tg::error!(!error, path = %target.display(), "failed to create the user mount share target file") })?; } - sandbox_binds.push(virtiofsd::Bind { + sandbox_binds.push(virtiofs::Bind { source: mount.source.clone(), target, }); @@ -297,7 +290,7 @@ pub fn run(arg: &Arg) -> tg::Result { let sandbox_log_path = host_vm_path_from_root(&arg.path).join("virtiofsd-sandbox.log"); tracing::trace!("spawning sandbox virtiofsd helper"); - let sandbox_pid = virtiofsd::spawn( + let sandbox_pid = virtiofs::spawn( user.uid, user.gid, &sandbox_shared_dir, @@ -1037,17 +1030,11 @@ fn build_cloud_hypervisor_mount_arg( target: VMM_SNAPSHOT_OUTPUT_DIR.into(), }); } else { - // Bind the server-provided artifacts vfs socket under the writable VMM root - // directory so cloud-hypervisor can reach it post-chroot. binds.push(container::run::Bind { source: arg.path.join(VFS_SOCKET_NAME), target: VMM_VFS_SOCKET.into(), }); } - // Bind the server-provided artifacts vfs socket under the writable VMM root directory so - // cloud-hypervisor can reach it post-chroot. This is required on a cold boot, when creating - // a snapshot so the artifacts share is captured in the snapshot, and on resume so the - // restored device can reconnect to the backend. binds.push(container::run::Bind { source: arg.path.join(VFS_SOCKET_NAME), target: VMM_VFS_SOCKET.into(), diff --git a/packages/sandbox/src/vm/spawn.rs b/packages/sandbox/src/vm/spawn.rs index ce23d6b152..d30b630393 100644 --- a/packages/sandbox/src/vm/spawn.rs +++ b/packages/sandbox/src/vm/spawn.rs @@ -21,8 +21,6 @@ pub(crate) fn spawn( .arg(arg.id.to_string()) .arg("--artifacts-path") .arg(&arg.artifacts_path) - .arg("--dax") - .arg(vm.dax.unwrap_or(0).to_string()) .arg("--kernel-path") .arg(&vm.kernel_path) .arg("--max-cpu") @@ -45,6 +43,9 @@ pub(crate) fn spawn( .arg(&arg.tangram_path) .arg("--url") .arg(serve_arg.url.to_string()); + if let Some(dax) = vm.dax { + command.arg("--dax").arg(dax.to_string()); + } if let Some(snapshot) = &vm.snapshot { command.arg("--snapshot").arg(snapshot); } diff --git a/packages/sandbox/src/vm/virtiofsd.rs b/packages/sandbox/src/vm/virtiofs.rs similarity index 100% rename from packages/sandbox/src/vm/virtiofsd.rs rename to packages/sandbox/src/vm/virtiofs.rs diff --git a/packages/server/src/config.rs b/packages/server/src/config.rs index 295344b75c..80b18a4753 100644 --- a/packages/server/src/config.rs +++ b/packages/server/src/config.rs @@ -677,9 +677,6 @@ pub struct VmSandboxIsolation { #[derive(Clone, Copy, Debug, serde::Deserialize, serde::Serialize)] #[serde(default, deny_unknown_fields)] pub struct Dax { - /// The size of the DAX window for the artifacts share, in kibibytes. Must be a power of two - /// because the window is exposed as a PCI BAR. The guest maps artifact files directly into - /// this window, bypassing the FUSE read path. #[serde(rename = "window_size")] pub window_size_kib: usize, } diff --git a/packages/server/src/run/sandbox.rs b/packages/server/src/run/sandbox.rs index 6927facb9f..7512b7fdf0 100644 --- a/packages/server/src/run/sandbox.rs +++ b/packages/server/src/run/sandbox.rs @@ -208,7 +208,7 @@ impl Session { let _vfs = if let tangram_sandbox::Isolation::Vm(vm) = &isolation { let socket = temp.path().join("vfs.sock"); let options = self.server.config.vfs.unwrap_or_default(); - let vfs = crate::vfs::Server::start_virtiofsd(&self.server, options, &socket, vm.dax) + let vfs = crate::vfs::Server::start_virtiofs(&self.server, options, &socket, vm.dax) .await .map_err(|error| tg::error!(!error, %id, "failed to start the artifacts vfs"))?; Some(vfs) @@ -570,14 +570,10 @@ impl Server { .map_err(|error| { tg::error!(!error, "failed to create the vm snapshot temp directory") })?; - // Start the artifacts virtiofsd so the snapshot process can attach the artifacts share at - // boot and capture it in the snapshot, rather than hotplugging it on resume, which does - // not support the DAX cache. Kept as a local so the daemon stops and the socket is removed - // when the snapshot process completes. let _vfs = { let socket = temp.path().join("vfs.sock"); let options = self.config.vfs.unwrap_or_default(); - crate::vfs::Server::start_virtiofsd(self, options, &socket, vm.dax) + crate::vfs::Server::start_virtiofs(self, options, &socket, vm.dax) .await .map_err(|error| tg::error!(!error, "failed to start the artifacts vfs"))? }; @@ -596,7 +592,8 @@ impl Server { crate::config::SandboxNetworkFirewall::Iptables => tangram_sandbox::Firewall::Iptables, crate::config::SandboxNetworkFirewall::Nft => tangram_sandbox::Firewall::Nft, }; - let status = tokio::process::Command::new(&self.tangram_path) + let mut command = tokio::process::Command::new(&self.tangram_path); + command .arg("sandbox") .arg("vm") .arg("run") @@ -606,8 +603,6 @@ impl Server { .arg(snapshot_id.to_string()) .arg("--artifacts-path") .arg(self.artifacts_path()) - .arg("--dax") - .arg(vm.dax.unwrap_or(0).to_string()) .arg("--firewall") .arg(firewall.to_string()) .arg("--kernel-path") @@ -629,7 +624,11 @@ impl Server { .arg("--path") .arg(temp.path()) .arg("--url") - .arg("http+vsock://2:6748") + .arg("http+vsock://2:6748"); + if let Some(dax) = vm.dax { + command.arg("--dax").arg(dax.to_string()); + } + let status = command .status() .await .map_err(|error| tg::error!(!error, "failed to spawn the snapshot process"))?; diff --git a/packages/server/src/vfs.rs b/packages/server/src/vfs.rs index a35eafcda4..ff9d01dcbe 100644 --- a/packages/server/src/vfs.rs +++ b/packages/server/src/vfs.rs @@ -14,7 +14,7 @@ pub enum Server { Fuse(vfs::fuse::Server), Nfs(vfs::nfs::Server), #[cfg(target_os = "linux")] - Virtiofsd(vfs::virtiofsd::Server), + Virtiofs(vfs::virtiofs::Server), } impl Server { @@ -98,7 +98,7 @@ impl Server { } #[cfg(target_os = "linux")] - pub async fn start_virtiofsd( + pub async fn start_virtiofs( server: &crate::Server, options: crate::config::Vfs, socket: &Path, @@ -108,10 +108,10 @@ impl Server { .await .map_err(|error| tg::error!(!error, "failed to create the vfs provider"))?; let dax_window_size = dax.unwrap_or(0); - let server = vfs::virtiofsd::Server::start(provider, socket, dax_window_size) + let server = vfs::virtiofs::Server::start(provider, socket, dax_window_size) .await .map_err(|error| tg::error!(!error, "failed to start the virtiofsd server"))?; - Ok(Self::Virtiofsd(server)) + Ok(Self::Virtiofs(server)) } pub async fn unmount(kind: Kind, path: &Path) -> tg::Result<()> { @@ -142,7 +142,7 @@ impl Server { Server::Fuse(server) => server.stop(), Server::Nfs(server) => server.stop(), #[cfg(target_os = "linux")] - Server::Virtiofsd(server) => server.stop(), + Server::Virtiofs(server) => server.stop(), } } @@ -156,7 +156,7 @@ impl Server { server.wait().await; }, #[cfg(target_os = "linux")] - Server::Virtiofsd(server) => { + Server::Virtiofs(server) => { server.wait().await; }, } diff --git a/packages/server/src/vfs/provider.rs b/packages/server/src/vfs/provider.rs index ba7b89dd05..183db4c8c3 100644 --- a/packages/server/src/vfs/provider.rs +++ b/packages/server/src/vfs/provider.rs @@ -1801,12 +1801,8 @@ impl Provider { path.push(path_); } match std::fs::File::open(&path) { - Ok(file) => { - Ok(Some(file.into())) - }, - Err(error) if error.kind() == std::io::ErrorKind::NotFound => { - Ok(None) - }, + Ok(file) => Ok(Some(file.into())), + Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(None), Err(error) => { tracing::error!(%error, path = %path.display(), "failed to open cache file"); Err(std::io::Error::from_raw_os_error(libc::EIO)) diff --git a/packages/vfs/src/lib.rs b/packages/vfs/src/lib.rs index 20fb969dbc..b8f402da24 100644 --- a/packages/vfs/src/lib.rs +++ b/packages/vfs/src/lib.rs @@ -4,7 +4,7 @@ use {bytes::Bytes, futures::Future, std::io::Error, std::os::fd::OwnedFd}; pub mod fuse; pub mod nfs; #[cfg(target_os = "linux")] -pub mod virtiofsd; +pub mod virtiofs; pub const ROOT_NODE_ID: u64 = 1; diff --git a/packages/vfs/src/virtiofsd.rs b/packages/vfs/src/virtiofs.rs similarity index 71% rename from packages/vfs/src/virtiofsd.rs rename to packages/vfs/src/virtiofs.rs index d636f9f755..ab1c4f0679 100644 --- a/packages/vfs/src/virtiofsd.rs +++ b/packages/vfs/src/virtiofs.rs @@ -9,7 +9,6 @@ use { sync::{Arc, Mutex}, time::Duration, }, - virtiofsd as fs, }; const S_IFDIR: u32 = 0o040_000; @@ -55,7 +54,7 @@ impl Server { P: Provider + Send + Sync + 'static, { let backend = Arc::new( - fs::vhost_user::VhostUserFsBackendBuilder::default() + virtiofsd::vhost_user::VhostUserFsBackendBuilder::default() .set_thread_pool_size(0) .set_tag(None) .set_dax_window_size(dax_window_size) @@ -111,7 +110,7 @@ impl Server { } } -impl

fs::filesystem::FileSystem for Adapter

+impl

virtiofsd::filesystem::FileSystem for Adapter

where P: Provider + Send + Sync, { @@ -119,19 +118,19 @@ where type Handle = u64; type DirIter = DirIter; - fn init(&self, capable: fs::fuse::FsOptions) -> Result { - let wanted = fs::fuse::FsOptions::ASYNC_READ - | fs::fuse::FsOptions::EXPORT_SUPPORT - | fs::fuse::FsOptions::DO_READDIRPLUS; + fn init(&self, capable: virtiofsd::fuse::FsOptions) -> Result { + let wanted = virtiofsd::fuse::FsOptions::ASYNC_READ + | virtiofsd::fuse::FsOptions::EXPORT_SUPPORT + | virtiofsd::fuse::FsOptions::DO_READDIRPLUS; Ok(wanted & capable) } fn lookup( &self, - _ctx: fs::filesystem::Context, + _ctx: virtiofsd::filesystem::Context, parent: u64, name: &CStr, - ) -> Result { + ) -> Result { let name = name .to_str() .map_err(|_| io::Error::from_raw_os_error(libc::EINVAL))?; @@ -141,7 +140,7 @@ where .ok_or_else(|| io::Error::from_raw_os_error(libc::ENOENT))?; let attrs = self.0.getattr_sync(id)?; self.0.remember_sync(id); - Ok(fs::filesystem::Entry { + Ok(virtiofsd::filesystem::Entry { inode: id, generation: 0, attr: attr_from_attrs(id, attrs), @@ -150,32 +149,32 @@ where }) } - fn forget(&self, _ctx: fs::filesystem::Context, inode: u64, count: u64) { + fn forget(&self, _ctx: virtiofsd::filesystem::Context, inode: u64, count: u64) { self.0.forget_sync(inode, count); } fn getattr( &self, - _ctx: fs::filesystem::Context, + _ctx: virtiofsd::filesystem::Context, inode: u64, _handle: Option, - ) -> Result<(fs::fuse::Attr, Duration)> { + ) -> Result<(virtiofsd::fuse::Attr, Duration)> { let attrs = self.0.getattr_sync(inode)?; Ok((attr_from_attrs(inode, attrs), TIMEOUT)) } - fn readlink(&self, _ctx: fs::filesystem::Context, inode: u64) -> Result> { + fn readlink(&self, _ctx: virtiofsd::filesystem::Context, inode: u64) -> Result> { let bytes = self.0.readlink_sync(inode)?; Ok(bytes.to_vec()) } fn open( &self, - _ctx: fs::filesystem::Context, + _ctx: virtiofsd::filesystem::Context, inode: u64, _kill_priv: bool, _flags: u32, - ) -> Result<(Option, fs::fuse::OpenOptions)> { + ) -> Result<(Option, virtiofsd::fuse::OpenOptions)> { let (handle, backing_fd) = self.0.open_sync(inode)?; if let Some(fd) = backing_fd { self.1 @@ -187,22 +186,22 @@ where }); } // Artifact content is immutable, so keep the guest page cache across reopens. - Ok((Some(handle), fs::fuse::OpenOptions::KEEP_CACHE)) + Ok((Some(handle), virtiofsd::fuse::OpenOptions::KEEP_CACHE)) } fn opendir( &self, - _ctx: fs::filesystem::Context, + _ctx: virtiofsd::filesystem::Context, inode: u64, _flags: u32, - ) -> Result<(Option, fs::fuse::OpenOptions)> { + ) -> Result<(Option, virtiofsd::fuse::OpenOptions)> { let handle = self.0.opendir_sync(inode)?; - Ok((Some(handle), fs::fuse::OpenOptions::empty())) + Ok((Some(handle), virtiofsd::fuse::OpenOptions::empty())) } - fn read( + fn read( &self, - _ctx: fs::filesystem::Context, + _ctx: virtiofsd::filesystem::Context, inode: u64, handle: u64, mut w: W, @@ -211,15 +210,10 @@ where _lock_owner: Option, _flags: u32, ) -> Result { - // Splice directly from the backing cache file when one is available. It begins at offset - // zero, so the requested offset is the file offset, and the read is positional, so sharing - // the descriptor across concurrent reads is safe. if let Some(entry) = self.1.get(&inode) { return w.read_from_file_at(&entry.fd, size as usize, offset, None); } - // Fall back for blobs with no contiguous backing file: materialize the content and splice - // it through a memfd, since the transport only accepts a file. let bytes = self.0.read_sync(handle, offset, u64::from(size))?; if bytes.is_empty() { return Ok(0); @@ -235,7 +229,7 @@ where fn release( &self, - _ctx: fs::filesystem::Context, + _ctx: virtiofsd::filesystem::Context, inode: u64, _flags: u32, handle: u64, @@ -243,8 +237,6 @@ where _flock_release: bool, _lock_owner: Option, ) -> Result<()> { - // Drop the backing descriptor once the last handle for the inode is released. An active - // DAX mapping keeps the file alive on its own, so this is safe even while one exists. self.1.remove_if_mut(&inode, |_, entry| { entry.count -= 1; entry.count == 0 @@ -253,14 +245,20 @@ where Ok(()) } - fn releasedir(&self, _ctx: fs::filesystem::Context, _inode: u64, _flags: u32, handle: u64) -> Result<()> { + fn releasedir( + &self, + _ctx: virtiofsd::filesystem::Context, + _inode: u64, + _flags: u32, + handle: u64, + ) -> Result<()> { self.0.close_sync(handle); Ok(()) } fn readdir( &self, - _ctx: fs::filesystem::Context, + _ctx: virtiofsd::filesystem::Context, _inode: u64, handle: u64, _size: u32, @@ -270,8 +268,8 @@ where let items = entries .into_iter() .map(|(name, id, typ)| { - let name = CString::new(name) - .map_err(|_| io::Error::from_raw_os_error(libc::EINVAL))?; + let name = + CString::new(name).map_err(|_| io::Error::from_raw_os_error(libc::EINVAL))?; Ok::<_, io::Error>(Item { ino: id, type_: dir_entry_type(typ), @@ -288,11 +286,11 @@ where fn getxattr( &self, - _ctx: fs::filesystem::Context, + _ctx: virtiofsd::filesystem::Context, inode: u64, name: &CStr, size: u32, - ) -> Result { + ) -> Result { let name = name .to_str() .map_err(|_| io::Error::from_raw_os_error(libc::EINVAL))?; @@ -302,20 +300,20 @@ where .ok_or_else(|| io::Error::from_raw_os_error(libc::ENODATA))?; if size == 0 { let count = u32::try_from(value.len()).unwrap_or(u32::MAX); - return Ok(fs::filesystem::GetxattrReply::Count(count)); + return Ok(virtiofsd::filesystem::GetxattrReply::Count(count)); } if value.len() > size as usize { return Err(io::Error::from_raw_os_error(libc::ERANGE)); } - Ok(fs::filesystem::GetxattrReply::Value(value.to_vec())) + Ok(virtiofsd::filesystem::GetxattrReply::Value(value.to_vec())) } fn listxattr( &self, - _ctx: fs::filesystem::Context, + _ctx: virtiofsd::filesystem::Context, inode: u64, size: u32, - ) -> Result { + ) -> Result { let names = self.0.listxattrs_sync(inode)?; let mut buf = Vec::new(); for name in &names { @@ -324,42 +322,39 @@ where } if size == 0 { let count = u32::try_from(buf.len()).unwrap_or(u32::MAX); - return Ok(fs::filesystem::ListxattrReply::Count(count)); + return Ok(virtiofsd::filesystem::ListxattrReply::Count(count)); } if buf.len() > size as usize { return Err(io::Error::from_raw_os_error(libc::ERANGE)); } - Ok(fs::filesystem::ListxattrReply::Names(buf)) + Ok(virtiofsd::filesystem::ListxattrReply::Names(buf)) } - fn access(&self, _ctx: fs::filesystem::Context, _inode: u64, _mask: u32) -> Result<()> { + fn access(&self, _ctx: virtiofsd::filesystem::Context, _inode: u64, _mask: u32) -> Result<()> { Ok(()) } fn setupmapping( &self, - _ctx: fs::filesystem::Context, + _ctx: virtiofsd::filesystem::Context, inode: u64, _handle: u64, foffset: u64, _len: u64, - flags: fs::fuse::SetupmappingFlags, + flags: virtiofsd::fuse::SetupmappingFlags, ) -> Result<(RawFd, u64)> { - // The artifacts filesystem is read-only, so reject any writable mapping. - if flags.contains(fs::fuse::SetupmappingFlags::WRITE) { + if flags.contains(virtiofsd::fuse::SetupmappingFlags::WRITE) { return Err(io::Error::from_raw_os_error(libc::EROFS)); } let Some(entry) = self.1.get(&inode) else { return Err(io::Error::from_raw_os_error(libc::EBADF)); }; let raw = entry.fd.as_raw_fd(); - // The backing descriptor refers to the whole artifact file beginning at offset zero, so the - // offset within it is the requested file offset. Ok((raw, foffset)) } } -impl

fs::filesystem::SerializableFileSystem for Adapter

+impl

virtiofsd::filesystem::SerializableFileSystem for Adapter

where P: Provider + Send + Sync, { @@ -372,11 +367,11 @@ where } } -impl fs::filesystem::DirectoryIterator for DirIter { - fn next(&mut self) -> Option> { +impl virtiofsd::filesystem::DirectoryIterator for DirIter { + fn next(&mut self) -> Option> { let entry = self.entries.get(self.cursor)?; self.cursor += 1; - Some(fs::filesystem::DirEntry { + Some(virtiofsd::filesystem::DirEntry { ino: entry.ino as libc::ino64_t, offset: self.cursor as u64, type_: entry.type_, @@ -385,16 +380,15 @@ impl fs::filesystem::DirectoryIterator for DirIter { } } -fn attr_from_attrs(inode: u64, attrs: Attrs) -> fs::fuse::Attr { +fn attr_from_attrs(inode: u64, attrs: Attrs) -> virtiofsd::fuse::Attr { let (size, mode) = match attrs.inner { AttrsInner::Directory => (0, S_IFDIR | 0o555), - AttrsInner::File { executable, size } => ( - size, - S_IFREG | 0o444 | if executable { 0o111 } else { 0 }, - ), + AttrsInner::File { executable, size } => { + (size, S_IFREG | 0o444 | if executable { 0o111 } else { 0 }) + }, AttrsInner::Symlink => (0, S_IFLNK | 0o444), }; - fs::fuse::Attr { + virtiofsd::fuse::Attr { ino: inode, size, blocks: 0, From 47ae15947d3f925aadc0b35289159cb63d959112 Mon Sep 17 00:00:00 2001 From: David Yamnitsky Date: Mon, 8 Jun 2026 14:44:17 +0000 Subject: [PATCH 07/11] update deps --- Cargo.lock | 582 +++++++++++++++++++++++++++++++++++++-------------- Cargo.toml | 8 +- bun.lock | 102 ++++----- package.json | 6 +- 4 files changed, 486 insertions(+), 212 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d0a067598f..cfa6cb9a99 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -148,7 +148,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -159,7 +159,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -184,6 +184,15 @@ version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + [[package]] name = "arc-swap" version = "1.9.1" @@ -277,9 +286,9 @@ dependencies = [ [[package]] name = "async-nats" -version = "0.49.0" +version = "0.49.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "407486109ea5cfdf53fde05f46996dadf0547518a4d49f050d25f405ae31ed2d" +checksum = "fad3cd6df81292728e2a8cb1f1dcb4d7e7a1ab59b80c14fbbcba2baf9d5cf86a" dependencies = [ "base64 0.22.1", "bytes", @@ -587,7 +596,7 @@ version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.13.0", "cexpr", "clang-sys", "itertools 0.12.1", @@ -610,7 +619,7 @@ version = "0.72.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.13.0", "cexpr", "clang-sys", "itertools 0.13.0", @@ -647,9 +656,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.12.1" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84d7ced0ae9557296835c32bf1b1e02b44c746701f898460fb000d7eaa84f00a" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" dependencies = [ "serde_core", ] @@ -843,6 +852,12 @@ version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" +[[package]] +name = "by_address" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06" + [[package]] name = "byte-unit" version = "5.2.0" @@ -1050,9 +1065,9 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.44" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" dependencies = [ "iana-time-zone", "js-sys", @@ -1469,7 +1484,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.13.0", "crossterm_winapi", "derive_more", "document-features", @@ -1691,7 +1706,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccc2776f0c61eca1ca32528f85548abd1a4be8fb53d1b21c013e4f18da1e7090" dependencies = [ "data-encoding", - "syn 1.0.109", + "syn 2.0.117", ] [[package]] @@ -2100,7 +2115,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -2179,6 +2194,12 @@ dependencies = [ "arrayvec", ] +[[package]] +name = "fast-srgb8" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd2e7510819d6fbf51a5545c8f922716ecfb14df168a3242f7d33e0239efe6a1" + [[package]] name = "fastbloom" version = "0.14.1" @@ -2322,6 +2343,159 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" +[[package]] +name = "forked_react_compiler" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34495a9926779b57310fd72e031fafdabc90f7c62e188aa5317ca08a115cce21" +dependencies = [ + "forked_react_compiler_ast", + "forked_react_compiler_diagnostics", + "forked_react_compiler_hir", + "forked_react_compiler_inference", + "forked_react_compiler_lowering", + "forked_react_compiler_optimization", + "forked_react_compiler_reactive_scopes", + "forked_react_compiler_ssa", + "forked_react_compiler_typeinference", + "forked_react_compiler_validation", + "indexmap 2.14.0", + "regex-lite", + "serde", + "serde_json", +] + +[[package]] +name = "forked_react_compiler_ast" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8ed273c27b68afe5b5f3bec2cd0215d6507f93921bf93a63b77f4362699c67e" +dependencies = [ + "indexmap 2.14.0", + "serde", + "serde_json", +] + +[[package]] +name = "forked_react_compiler_diagnostics" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0c5c845634ca8381a4dba94fc53f757c85de2e1e29711ef8fd7b5a4ca1fe9f" +dependencies = [ + "serde", +] + +[[package]] +name = "forked_react_compiler_hir" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26bbec4ac046d8eac451b16c75d51d7709b10cbfdc8a52e7c580e1e45239491b" +dependencies = [ + "forked_react_compiler_diagnostics", + "indexmap 2.14.0", + "serde", + "serde_json", +] + +[[package]] +name = "forked_react_compiler_inference" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c910dd22822ab23c29d2e969f3e3ca9302fb10c065fcf48b83344dab715e798" +dependencies = [ + "forked_react_compiler_diagnostics", + "forked_react_compiler_hir", + "forked_react_compiler_lowering", + "forked_react_compiler_optimization", + "forked_react_compiler_ssa", + "forked_react_compiler_utils", + "indexmap 2.14.0", +] + +[[package]] +name = "forked_react_compiler_lowering" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d891d2ea5d43aa948c56a036960059f405489c60089e57c261be4f35f05d815" +dependencies = [ + "forked_react_compiler_ast", + "forked_react_compiler_diagnostics", + "forked_react_compiler_hir", + "indexmap 2.14.0", + "serde_json", +] + +[[package]] +name = "forked_react_compiler_optimization" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8611f21629cf6333c1b634e1bca816c863d4cba48d4ac1f42d61203835797de9" +dependencies = [ + "forked_react_compiler_diagnostics", + "forked_react_compiler_hir", + "forked_react_compiler_lowering", + "forked_react_compiler_ssa", + "indexmap 2.14.0", +] + +[[package]] +name = "forked_react_compiler_reactive_scopes" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "294a00391f2051dec90669fdff54397a464b16bb568117b48a959bed56d2798b" +dependencies = [ + "forked_react_compiler_ast", + "forked_react_compiler_diagnostics", + "forked_react_compiler_hir", + "hmac 0.12.1", + "indexmap 2.14.0", + "serde_json", + "sha2 0.10.9", +] + +[[package]] +name = "forked_react_compiler_ssa" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4782ac57df264c71abaabf68e90b9e3a96f3d607e772bc9ee6e9d26654614aa" +dependencies = [ + "forked_react_compiler_diagnostics", + "forked_react_compiler_hir", + "forked_react_compiler_lowering", + "indexmap 2.14.0", +] + +[[package]] +name = "forked_react_compiler_typeinference" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15d9379f0f48a565d82ed03de02946571af209057cfdc81fe09355518e587a54" +dependencies = [ + "forked_react_compiler_diagnostics", + "forked_react_compiler_hir", + "forked_react_compiler_ssa", +] + +[[package]] +name = "forked_react_compiler_utils" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "531d2adbbfa861f9669f70e49eb1cc27e8817a7f1d000bee3e2c71a2d82217ec" +dependencies = [ + "indexmap 2.14.0", +] + +[[package]] +name = "forked_react_compiler_validation" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f8b2c12e00b41dc87bc2c63656b473047dcdf75b1f5dc545ca93a308d53a89" +dependencies = [ + "forked_react_compiler_diagnostics", + "forked_react_compiler_hir", + "indexmap 2.14.0", +] + [[package]] name = "form_urlencoded" version = "1.2.2" @@ -2756,6 +2930,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" dependencies = [ "allocator-api2", + "equivalent", + "foldhash 0.2.0", ] [[package]] @@ -2797,7 +2973,7 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad82d6598ccf1dac15c8b758a1bd282b755b6776be600429176757190a1b0202" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.13.0", "byteorder", "heed-traits", "heed-types", @@ -2914,9 +3090,9 @@ checksum = "e9025058dae765dee5070ec375f591e2ba14638c63feff74f13805a72e523163" [[package]] name = "http" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8be7462df143984c4598a256ef469b251d7d7f9e271135073e78fc535414f3d0" +checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" dependencies = [ "bytes", "itoa", @@ -2945,6 +3121,16 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "http-serde" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f056c8559e3757392c8d091e796416e4649d8e49e88b8d76df6c002f05027fd" +dependencies = [ + "http", + "serde", +] + [[package]] name = "httparse" version = "1.10.1" @@ -3312,7 +3498,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "533e68a5842e734946fe159fb03fc9bbbb254f590dd0d8ad321ae5ff7beca2c1" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.13.0", "inotify-sys", "libc", ] @@ -3388,7 +3574,7 @@ version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d09b98f7eace8982db770e4408e7470b028ce513ac28fecdc6bf4c30fe92b62" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.13.0", "cfg-if", "libc", ] @@ -3602,7 +3788,7 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07293a4e297ac234359b510362495713f75ea345d5307140414f20c69ffeb087" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.13.0", "libc", ] @@ -3714,9 +3900,9 @@ checksum = "9a7cbbd4ad467251987c6e5b47d53b11a5a05add08f2447a9e2d70aef1e0d138" [[package]] name = "libsqlite3-sys" -version = "0.38.0" +version = "0.38.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a76001fb4daed01e5f2b518aac0b4dc592e7c734da63dbffcf0c64fa612a8d0c" +checksum = "f6c19a05435c21ac299d71b6a9c13db3e3f47c520517d58990a462a1397a61db" dependencies = [ "cc", "pkg-config", @@ -3729,7 +3915,7 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f50e8f47623268b5407192d26876c4d7f89d686ca130fdc53bced4814cd29f8" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.13.0", ] [[package]] @@ -3804,9 +3990,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.31" +version = "0.4.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "113b30b4cd05f7c06868fdb2854f66a7b9fece9a48425351cd532e810d74024f" +checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" [[package]] name = "loom" @@ -3830,6 +4016,15 @@ dependencies = [ "hashbrown 0.16.1", ] +[[package]] +name = "lru" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a860605968fce16869fd239cf4237a82f3ac470723415db603b0e8b6c8d4fb9" +dependencies = [ + "hashbrown 0.17.1", +] + [[package]] name = "lru-slab" version = "0.1.2" @@ -4070,7 +4265,7 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22f9786d56d972959e1408b6a93be6af13b9c1392036c5c1fafa08a1b0c6ee87" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.13.0", "byteorder", "derive_builder", "getset", @@ -4108,7 +4303,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.13.0", "cfg-if", "cfg_aliases", "libc", @@ -4121,7 +4316,7 @@ version = "0.31.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.13.0", "cfg-if", "cfg_aliases", "libc", @@ -4171,7 +4366,7 @@ version = "8.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.13.0", "fsevent-sys", "inotify", "kqueue", @@ -4189,7 +4384,7 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42b8cfee0e339a0337359f3c88165702ac6e600dc01c0cc9579a92d62b08477a" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.13.0", ] [[package]] @@ -4198,7 +4393,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -4371,7 +4566,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.13.0", ] [[package]] @@ -4386,7 +4581,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.13.0", "objc2", ] @@ -4410,9 +4605,9 @@ dependencies = [ [[package]] name = "octocrab" -version = "0.51.0" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb2ad8abffe4e2b05f9cdc7e061de63d305a6dca0af81ca1064a7d98e0b78267" +checksum = "46a4184473c1d00a892dad5259e1d19421ab1f8e4b7d714113a6cfdb213d9980" dependencies = [ "arc-swap", "async-trait", @@ -4427,6 +4622,7 @@ dependencies = [ "http", "http-body", "http-body-util", + "http-serde", "hyper", "hyper-rustls", "hyper-timeout", @@ -4473,7 +4669,7 @@ version = "6.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc3cbf698f9438986c11a880c90a6d04b9de27575afd28bbf45b154b6c709e2" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.13.0", "libc", "once_cell", "onig_sys", @@ -4637,8 +4833,8 @@ checksum = "d211803b9b6b570f68772237e415a029d5a50c65d382910b879fb19d3271f94d" [[package]] name = "oxc" -version = "0.134.0" -source = "git+https://github.com/oxc-project/oxc#0f7c31905bc25a032d932399fae865ee4d8de7e8" +version = "0.135.0" +source = "git+https://github.com/oxc-project/oxc#e9b8fd595cf6c997e2bb14fc4cbb9ccf91739f87" dependencies = [ "oxc_allocator", "oxc_ast", @@ -4649,6 +4845,7 @@ dependencies = [ "oxc_mangler", "oxc_minifier", "oxc_parser", + "oxc_react_compiler", "oxc_regular_expression", "oxc_semantic", "oxc_span", @@ -4698,8 +4895,8 @@ dependencies = [ [[package]] name = "oxc_allocator" -version = "0.134.0" -source = "git+https://github.com/oxc-project/oxc#0f7c31905bc25a032d932399fae865ee4d8de7e8" +version = "0.135.0" +source = "git+https://github.com/oxc-project/oxc#e9b8fd595cf6c997e2bb14fc4cbb9ccf91739f87" dependencies = [ "allocator-api2", "hashbrown 0.17.1", @@ -4709,10 +4906,10 @@ dependencies = [ [[package]] name = "oxc_ast" -version = "0.134.0" -source = "git+https://github.com/oxc-project/oxc#0f7c31905bc25a032d932399fae865ee4d8de7e8" +version = "0.135.0" +source = "git+https://github.com/oxc-project/oxc#e9b8fd595cf6c997e2bb14fc4cbb9ccf91739f87" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.13.0", "oxc_allocator", "oxc_ast_macros", "oxc_data_structures", @@ -4726,8 +4923,8 @@ dependencies = [ [[package]] name = "oxc_ast_macros" -version = "0.134.0" -source = "git+https://github.com/oxc-project/oxc#0f7c31905bc25a032d932399fae865ee4d8de7e8" +version = "0.135.0" +source = "git+https://github.com/oxc-project/oxc#e9b8fd595cf6c997e2bb14fc4cbb9ccf91739f87" dependencies = [ "phf 0.13.1", "proc-macro2", @@ -4737,8 +4934,8 @@ dependencies = [ [[package]] name = "oxc_ast_visit" -version = "0.134.0" -source = "git+https://github.com/oxc-project/oxc#0f7c31905bc25a032d932399fae865ee4d8de7e8" +version = "0.135.0" +source = "git+https://github.com/oxc-project/oxc#e9b8fd595cf6c997e2bb14fc4cbb9ccf91739f87" dependencies = [ "oxc_allocator", "oxc_ast", @@ -4748,10 +4945,10 @@ dependencies = [ [[package]] name = "oxc_codegen" -version = "0.134.0" -source = "git+https://github.com/oxc-project/oxc#0f7c31905bc25a032d932399fae865ee4d8de7e8" +version = "0.135.0" +source = "git+https://github.com/oxc-project/oxc#e9b8fd595cf6c997e2bb14fc4cbb9ccf91739f87" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.13.0", "cow-utils", "dragonbox_ecma", "itoa", @@ -4769,8 +4966,8 @@ dependencies = [ [[package]] name = "oxc_compat" -version = "0.134.0" -source = "git+https://github.com/oxc-project/oxc#0f7c31905bc25a032d932399fae865ee4d8de7e8" +version = "0.135.0" +source = "git+https://github.com/oxc-project/oxc#e9b8fd595cf6c997e2bb14fc4cbb9ccf91739f87" dependencies = [ "cow-utils", "oxc-browserslist", @@ -4781,16 +4978,16 @@ dependencies = [ [[package]] name = "oxc_data_structures" -version = "0.134.0" -source = "git+https://github.com/oxc-project/oxc#0f7c31905bc25a032d932399fae865ee4d8de7e8" +version = "0.135.0" +source = "git+https://github.com/oxc-project/oxc#e9b8fd595cf6c997e2bb14fc4cbb9ccf91739f87" dependencies = [ "ropey", ] [[package]] name = "oxc_diagnostics" -version = "0.134.0" -source = "git+https://github.com/oxc-project/oxc#0f7c31905bc25a032d932399fae865ee4d8de7e8" +version = "0.135.0" +source = "git+https://github.com/oxc-project/oxc#e9b8fd595cf6c997e2bb14fc4cbb9ccf91739f87" dependencies = [ "cow-utils", "oxc-miette", @@ -4799,8 +4996,8 @@ dependencies = [ [[package]] name = "oxc_ecmascript" -version = "0.134.0" -source = "git+https://github.com/oxc-project/oxc#0f7c31905bc25a032d932399fae865ee4d8de7e8" +version = "0.135.0" +source = "git+https://github.com/oxc-project/oxc#e9b8fd595cf6c997e2bb14fc4cbb9ccf91739f87" dependencies = [ "cow-utils", "num-bigint", @@ -4814,13 +5011,13 @@ dependencies = [ [[package]] name = "oxc_estree" -version = "0.134.0" -source = "git+https://github.com/oxc-project/oxc#0f7c31905bc25a032d932399fae865ee4d8de7e8" +version = "0.135.0" +source = "git+https://github.com/oxc-project/oxc#e9b8fd595cf6c997e2bb14fc4cbb9ccf91739f87" [[package]] name = "oxc_formatter" -version = "0.53.0" -source = "git+https://github.com/oxc-project/oxc#0f7c31905bc25a032d932399fae865ee4d8de7e8" +version = "0.54.0" +source = "git+https://github.com/oxc-project/oxc#e9b8fd595cf6c997e2bb14fc4cbb9ccf91739f87" dependencies = [ "cow-utils", "fast-glob", @@ -4845,14 +5042,13 @@ dependencies = [ [[package]] name = "oxc_formatter_core" -version = "0.53.0" -source = "git+https://github.com/oxc-project/oxc#0f7c31905bc25a032d932399fae865ee4d8de7e8" +version = "0.54.0" +source = "git+https://github.com/oxc-project/oxc#e9b8fd595cf6c997e2bb14fc4cbb9ccf91739f87" dependencies = [ "cow-utils", "oxc_allocator", "oxc_data_structures", "oxc_span", - "oxc_syntax", "rustc-hash 2.1.2", "serde_json", "unicode-width 0.2.2", @@ -4860,9 +5056,9 @@ dependencies = [ [[package]] name = "oxc_index" -version = "4.1.0" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3e6120999627ec9703025eab7c9f410ebb7e95557632a8902ca48210416c2b" +checksum = "191884bee6c3744909a51acc7d78d4ae370d817b25875b10642f632327b6296e" dependencies = [ "nonmax", "serde", @@ -4870,10 +5066,10 @@ dependencies = [ [[package]] name = "oxc_isolated_declarations" -version = "0.134.0" -source = "git+https://github.com/oxc-project/oxc#0f7c31905bc25a032d932399fae865ee4d8de7e8" +version = "0.135.0" +source = "git+https://github.com/oxc-project/oxc#e9b8fd595cf6c997e2bb14fc4cbb9ccf91739f87" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.13.0", "oxc_allocator", "oxc_ast", "oxc_ast_visit", @@ -4887,8 +5083,8 @@ dependencies = [ [[package]] name = "oxc_jsdoc" -version = "0.134.0" -source = "git+https://github.com/oxc-project/oxc#0f7c31905bc25a032d932399fae865ee4d8de7e8" +version = "0.135.0" +source = "git+https://github.com/oxc-project/oxc#e9b8fd595cf6c997e2bb14fc4cbb9ccf91739f87" dependencies = [ "oxc_ast", "oxc_span", @@ -4897,8 +5093,8 @@ dependencies = [ [[package]] name = "oxc_mangler" -version = "0.134.0" -source = "git+https://github.com/oxc-project/oxc#0f7c31905bc25a032d932399fae865ee4d8de7e8" +version = "0.135.0" +source = "git+https://github.com/oxc-project/oxc#e9b8fd595cf6c997e2bb14fc4cbb9ccf91739f87" dependencies = [ "itertools 0.14.0", "oxc_allocator", @@ -4914,8 +5110,8 @@ dependencies = [ [[package]] name = "oxc_minifier" -version = "0.134.0" -source = "git+https://github.com/oxc-project/oxc#0f7c31905bc25a032d932399fae865ee4d8de7e8" +version = "0.135.0" +source = "git+https://github.com/oxc-project/oxc#e9b8fd595cf6c997e2bb14fc4cbb9ccf91739f87" dependencies = [ "cow-utils", "itoa", @@ -4939,10 +5135,10 @@ dependencies = [ [[package]] name = "oxc_parser" -version = "0.134.0" -source = "git+https://github.com/oxc-project/oxc#0f7c31905bc25a032d932399fae865ee4d8de7e8" +version = "0.135.0" +source = "git+https://github.com/oxc-project/oxc#e9b8fd595cf6c997e2bb14fc4cbb9ccf91739f87" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.13.0", "cow-utils", "memchr", "num-bigint", @@ -4960,12 +5156,34 @@ dependencies = [ "seq-macro", ] +[[package]] +name = "oxc_react_compiler" +version = "0.135.0" +source = "git+https://github.com/oxc-project/oxc#e9b8fd595cf6c997e2bb14fc4cbb9ccf91739f87" +dependencies = [ + "forked_react_compiler", + "forked_react_compiler_ast", + "forked_react_compiler_hir", + "indexmap 2.14.0", + "oxc_allocator", + "oxc_ast", + "oxc_ast_visit", + "oxc_codegen", + "oxc_diagnostics", + "oxc_parser", + "oxc_semantic", + "oxc_span", + "oxc_syntax", + "rustc-hash 2.1.2", + "serde_json", +] + [[package]] name = "oxc_regular_expression" -version = "0.134.0" -source = "git+https://github.com/oxc-project/oxc#0f7c31905bc25a032d932399fae865ee4d8de7e8" +version = "0.135.0" +source = "git+https://github.com/oxc-project/oxc#e9b8fd595cf6c997e2bb14fc4cbb9ccf91739f87" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.13.0", "oxc_allocator", "oxc_ast_macros", "oxc_diagnostics", @@ -4978,8 +5196,8 @@ dependencies = [ [[package]] name = "oxc_semantic" -version = "0.134.0" -source = "git+https://github.com/oxc-project/oxc#0f7c31905bc25a032d932399fae865ee4d8de7e8" +version = "0.135.0" +source = "git+https://github.com/oxc-project/oxc#e9b8fd595cf6c997e2bb14fc4cbb9ccf91739f87" dependencies = [ "itertools 0.14.0", "memchr", @@ -5012,8 +5230,8 @@ dependencies = [ [[package]] name = "oxc_span" -version = "0.134.0" -source = "git+https://github.com/oxc-project/oxc#0f7c31905bc25a032d932399fae865ee4d8de7e8" +version = "0.135.0" +source = "git+https://github.com/oxc-project/oxc#e9b8fd595cf6c997e2bb14fc4cbb9ccf91739f87" dependencies = [ "compact_str", "oxc-miette", @@ -5025,8 +5243,8 @@ dependencies = [ [[package]] name = "oxc_str" -version = "0.134.0" -source = "git+https://github.com/oxc-project/oxc#0f7c31905bc25a032d932399fae865ee4d8de7e8" +version = "0.135.0" +source = "git+https://github.com/oxc-project/oxc#e9b8fd595cf6c997e2bb14fc4cbb9ccf91739f87" dependencies = [ "compact_str", "hashbrown 0.17.1", @@ -5036,10 +5254,10 @@ dependencies = [ [[package]] name = "oxc_syntax" -version = "0.134.0" -source = "git+https://github.com/oxc-project/oxc#0f7c31905bc25a032d932399fae865ee4d8de7e8" +version = "0.135.0" +source = "git+https://github.com/oxc-project/oxc#e9b8fd595cf6c997e2bb14fc4cbb9ccf91739f87" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.13.0", "cow-utils", "dragonbox_ecma", "nonmax", @@ -5055,8 +5273,8 @@ dependencies = [ [[package]] name = "oxc_transformer" -version = "0.134.0" -source = "git+https://github.com/oxc-project/oxc#0f7c31905bc25a032d932399fae865ee4d8de7e8" +version = "0.135.0" +source = "git+https://github.com/oxc-project/oxc#e9b8fd595cf6c997e2bb14fc4cbb9ccf91739f87" dependencies = [ "base64 0.22.1", "compact_str", @@ -5084,8 +5302,8 @@ dependencies = [ [[package]] name = "oxc_transformer_plugins" -version = "0.134.0" -source = "git+https://github.com/oxc-project/oxc#0f7c31905bc25a032d932399fae865ee4d8de7e8" +version = "0.135.0" +source = "git+https://github.com/oxc-project/oxc#e9b8fd595cf6c997e2bb14fc4cbb9ccf91739f87" dependencies = [ "cow-utils", "itoa", @@ -5106,8 +5324,8 @@ dependencies = [ [[package]] name = "oxc_traverse" -version = "0.134.0" -source = "git+https://github.com/oxc-project/oxc#0f7c31905bc25a032d932399fae865ee4d8de7e8" +version = "0.135.0" +source = "git+https://github.com/oxc-project/oxc#e9b8fd595cf6c997e2bb14fc4cbb9ccf91739f87" dependencies = [ "itoa", "oxc_allocator", @@ -5165,6 +5383,30 @@ dependencies = [ "winapi", ] +[[package]] +name = "palette" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cbf71184cc5ecc2e4e1baccdb21026c20e5fc3dcf63028a086131b3ab00b6e6" +dependencies = [ + "approx", + "fast-srgb8", + "libm", + "palette_derive", +] + +[[package]] +name = "palette_derive" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5030daf005bface118c096f510ffb781fc28f9ab6a32ab224d8631be6851d30" +dependencies = [ + "by_address", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "parking" version = "2.2.1" @@ -5629,9 +5871,9 @@ dependencies = [ [[package]] name = "prost" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ea70524a2f82d518bce41317d0fae74151505651af45faf1ffbd6fd33f0568" +checksum = "528ac67416ff8646872a3c02cad9cc4ee5dc9f9540c9b10771855c95cb2e5ae1" dependencies = [ "bytes", "prost-derive", @@ -5639,9 +5881,9 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b" +checksum = "b570b25f7617e43d59005d0990ccb79e950a423952cea19671b7a876da390adf" dependencies = [ "anyhow", "itertools 0.14.0", @@ -5652,9 +5894,9 @@ dependencies = [ [[package]] name = "prost-types" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8991c4cbdb8bc5b11f0b074ffe286c30e523de90fee5ba8132f1399f23cb3dd7" +checksum = "f94967dc7688f3054c7fac87473ffae4cc4c3904800e2d9f5b857246d8963b0a" dependencies = [ "prost", ] @@ -5901,9 +6143,9 @@ dependencies = [ [[package]] name = "ratatui" -version = "0.30.0" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1ce67fb8ba4446454d1c8dbaeda0557ff5e94d39d5e5ed7f10a65eb4c8266bc" +checksum = "1695748e3a735b34968c887ceea5a380b43545903868ae8f5b666593100f6b68" dependencies = [ "instability", "ratatui-core", @@ -5911,22 +6153,26 @@ dependencies = [ "ratatui-macros", "ratatui-termwiz", "ratatui-widgets", + "serde", ] [[package]] name = "ratatui-core" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef8dea09a92caaf73bff7adb70b76162e5937524058a7e5bff37869cbbec293" +checksum = "42d3603f354bba8c595fa47860e60142d7372b7210c27044c6a7d0e1a4336b44" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.13.0", "compact_str", - "hashbrown 0.16.1", + "critical-section", + "hashbrown 0.17.1", "indoc", "itertools 0.14.0", "kasuari", - "lru", - "strum 0.27.2", + "lru 0.18.0", + "palette", + "serde", + "strum 0.28.0", "thiserror 2.0.18", "unicode-segmentation", "unicode-truncate", @@ -5935,9 +6181,9 @@ dependencies = [ [[package]] name = "ratatui-crossterm" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "577c9b9f652b4c121fb25c6a391dd06406d3b092ba68827e6d2f09550edc54b3" +checksum = "2b2867bedcbd6a690ca4f8672a687b730ec07660c79844517b084311b529980c" dependencies = [ "cfg-if", "crossterm", @@ -5947,9 +6193,9 @@ dependencies = [ [[package]] name = "ratatui-macros" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7f1342a13e83e4bb9d0b793d0ea762be633f9582048c892ae9041ef39c936f4" +checksum = "80fac59720679490d89d200df411faa249be728681adcabed3d047ae72c48f1d" dependencies = [ "ratatui-core", "ratatui-widgets", @@ -5957,9 +6203,9 @@ dependencies = [ [[package]] name = "ratatui-termwiz" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f76fe0bd0ed4295f0321b1676732e2454024c15a35d01904ddb315afd3d545c" +checksum = "386b8ff8f74ed749509391c56d549761a2fcdb408e1f42e467286bcb7dac8967" dependencies = [ "ratatui-core", "termwiz", @@ -5967,18 +6213,19 @@ dependencies = [ [[package]] name = "ratatui-widgets" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7dbfa023cd4e604c2553483820c5fe8aa9d71a42eea5aa77c6e7f35756612db" +checksum = "7ef4f17dd7ac3abf5adc2b920a03c61eee4bfe6a88fa5191936895525371d79c" dependencies = [ - "bitflags 2.12.1", - "hashbrown 0.16.1", + "bitflags 2.13.0", + "hashbrown 0.17.1", "indoc", "instability", "itertools 0.14.0", "line-clipping", "ratatui-core", - "strum 0.27.2", + "serde", + "strum 0.28.0", "time", "unicode-segmentation", "unicode-width 0.2.2", @@ -6010,7 +6257,7 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.13.0", ] [[package]] @@ -6088,6 +6335,12 @@ dependencies = [ "regex-syntax", ] +[[package]] +name = "regex-lite" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab834c73d247e67f4fae452806d17d3c7501756d98c8808d7c9c7aa7d18f973" + [[package]] name = "regex-syntax" version = "0.8.10" @@ -6352,11 +6605,11 @@ dependencies = [ [[package]] name = "rusqlite" -version = "0.40.0" +version = "0.40.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b3492ea85308705c3a5cc24fb9b9cf77273d30590349070db42991202b214c4" +checksum = "11438310b19e3109b6446c33d1ed5e889428cf2e278407bc7896bc4aaea43323" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.13.0", "fallible-iterator 0.3.0", "fallible-streaming-iterator", "libsqlite3-sys", @@ -6433,7 +6686,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.13.0", "errno", "libc", "linux-raw-sys 0.4.15", @@ -6446,11 +6699,11 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.13.0", "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -6518,7 +6771,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -6698,7 +6951,7 @@ version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.13.0", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -6868,9 +7121,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.20.0" +version = "3.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e72c1c2cb7b223fafb600a619537a871c2818583d619401b785e7c0b746ccde2" +checksum = "76a5c54c7310e7b8b9577c286d7e399ddd876c3e12b3ed917a8aabc4b96e9e8c" dependencies = [ "base64 0.22.1", "bs58", @@ -6888,9 +7141,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.20.0" +version = "3.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b90c488738ecb4fb0262f41f43bc40efc5868d9fb744319ddf5f5317f417bfac" +checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660" dependencies = [ "darling 0.23.0", "proc-macro2", @@ -7194,7 +7447,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -7320,6 +7573,15 @@ dependencies = [ "strum_macros 0.27.2", ] +[[package]] +name = "strum" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9628de9b8791db39ceda2b119bbe13134770b56c138ec1d3af810d045c04f9bd" +dependencies = [ + "strum_macros 0.28.0", +] + [[package]] name = "strum_macros" version = "0.26.4" @@ -7345,6 +7607,18 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "strum_macros" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab85eea0270ee17587ed4156089e10b9e6880ee688791d45a905f5b1ca36f664" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "subtle" version = "2.6.1" @@ -7469,7 +7743,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.13.0", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -8131,7 +8405,7 @@ dependencies = [ "itertools 0.14.0", "levenshtein_automata", "log", - "lru", + "lru 0.16.4", "lz4_flex 0.13.1", "measure_time", "memmap2", @@ -8268,10 +8542,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.3.4", + "getrandom 0.4.2", "once_cell", "rustix 1.1.4", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -8324,7 +8598,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874" dependencies = [ "rustix 1.1.4", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -8356,7 +8630,7 @@ checksum = "4676b37242ccbd1aabf56edb093a4827dc49086c0ffd764a5705899e0f35f8f7" dependencies = [ "anyhow", "base64 0.22.1", - "bitflags 2.12.1", + "bitflags 2.13.0", "fancy-regex", "filedescriptor", "finl_unicode", @@ -8796,7 +9070,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" dependencies = [ "async-compression", - "bitflags 2.12.1", + "bitflags 2.13.0", "bytes", "futures-core", "futures-util", @@ -9006,7 +9280,7 @@ dependencies = [ "antithesis_sdk", "arc-swap", "bigdecimal", - "bitflags 2.12.1", + "bitflags 2.13.0", "branches", "bumpalo", "bytemuck", @@ -9088,7 +9362,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54b94707e5605411cddbd5a1bd6cc2d6b72181b02223b36b37ba350ed6b71877" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.13.0", "memchr", "miette", "strum 0.26.3", @@ -9391,7 +9665,7 @@ version = "147.1.0" source = "git+https://github.com/tangramdotdev/rusty_v8#2bbef876b9b2128736e28f90f5002c1a1289aaba" dependencies = [ "bindgen 0.72.1", - "bitflags 2.12.1", + "bitflags 2.13.0", "fslock", "gzip-header", "home", @@ -9425,7 +9699,7 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee90657203a8644e9a0860a0db6a7887d8ef0c7bc09fc22dfa4ae75df65bac86" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.13.0", "libc", "uuid", "vm-memory", @@ -9743,7 +10017,7 @@ version = "0.227.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f51cad774fb3c9461ab9bccc9c62dfb7388397b5deda31bf40e8108ccd678b2" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.13.0", "hashbrown 0.15.5", "indexmap 2.14.0", "semver", @@ -9755,7 +10029,7 @@ version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.13.0", "hashbrown 0.15.5", "indexmap 2.14.0", "semver", @@ -9956,7 +10230,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -10319,7 +10593,7 @@ version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4db52a11d4dfb0a59f194c064055794ee6564eb1ced88c25da2cf76e50c5621" dependencies = [ - "bitflags 2.12.1", + "bitflags 2.13.0", "futures", "once_cell", ] @@ -10393,7 +10667,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "635c3adc595422cbf2341a17fb73a319669cc8d33deed3a48368a841df86b676" dependencies = [ "anyhow", - "bitflags 2.12.1", + "bitflags 2.13.0", "indexmap 2.14.0", "log", "serde", @@ -10412,7 +10686,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", - "bitflags 2.12.1", + "bitflags 2.13.0", "indexmap 2.14.0", "log", "serde", @@ -10517,9 +10791,9 @@ checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" [[package]] name = "yoke" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" dependencies = [ "stable_deref_trait", "yoke-derive", diff --git a/Cargo.toml b/Cargo.toml index ac5e67215f..3c2ae60c98 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -121,7 +121,7 @@ num-derive = "0.4" num-traits = "0.2" nu-ansi-term = "0.50" oauth2 = "5.0.0" -octocrab = "0.51" +octocrab = "0.53" opentelemetry = "0.32" opentelemetry-appender-tracing = "0.32" opentelemetry-otlp = { version = "0.32", features = ["grpc-tonic", "zstd-tonic", "logs", "metrics"] } @@ -198,8 +198,8 @@ tower = { version = "0.5", features = ["timeout", "util"] } tower-http = { version = "0.6", features = ["add-extension", "compression-zstd", "decompression-zstd", "map-request-body", "map-response-body", "set-header", "timeout", "util"] } tracing = "0.1.44" tracing-opentelemetry = "0.33" -tracing-subscriber = { version = "0.3.22", features = ["env-filter", "json"] } -tracing-tree = "0.4.1" +tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] } +tracing-tree = "0.4" turso = "0.6.0" unicode-segmentation = "1" unicode-width = "0.2" @@ -208,7 +208,7 @@ v8 = { git = "https://github.com/tangramdotdev/rusty_v8" } virtiofsd = "1" vhost = "0.16" vhost-user-backend = "0.22" -vm-memory = { version = "0.17.1", features = ["backend-atomic", "backend-mmap"] } +vm-memory = { version = "0.17", features = ["backend-atomic", "backend-mmap"] } waitpid-any = "0.3" webbrowser = "1" winnow = "1" diff --git a/bun.lock b/bun.lock index 70c20d85b3..730449250e 100644 --- a/bun.lock +++ b/bun.lock @@ -5,10 +5,10 @@ "": { "dependencies": { "@types/bun": "^1.3.14", - "@typescript/native-preview": "^7.0.0-dev.20260602.1", + "@typescript/native-preview": "^7.0.0-dev.20260608.1", "esbuild": "^0.28.0", - "oxfmt": "^0.53.0", - "oxlint": "^1.68.0", + "oxfmt": "^0.54.0", + "oxlint": "^1.69.0", "typescript": "^6.0.3", }, }, @@ -135,81 +135,81 @@ "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], - "@oxfmt/binding-android-arm-eabi": ["@oxfmt/binding-android-arm-eabi@0.53.0", "", { "os": "android", "cpu": "arm" }, "sha512-XfVM8AmIovBTKXCt14Op5wbfcoM8418nttd+nhMgM3RAVaJg1MtJc73FyWfUt0oxLyBGVwfniNVUsbV/b3VmPg=="], + "@oxfmt/binding-android-arm-eabi": ["@oxfmt/binding-android-arm-eabi@0.54.0", "", { "os": "android", "cpu": "arm" }, "sha512-NAtpl/SiaeU103e7/OmZw0MvUnsUUopW7hEm/ecegJg7YM0skQaA0IXEZoyTV6NUdiNPupdIUreRqUZTShbn/g=="], - "@oxfmt/binding-android-arm64": ["@oxfmt/binding-android-arm64@0.53.0", "", { "os": "android", "cpu": "arm64" }, "sha512-btHDfXckwdf9zgyAVznfZkf+GVyB0I1m1hlvaOMRx2xoyz3hphfPX97s89J3wfCN8QBETLtk4lQUaeOkrMuQOg=="], + "@oxfmt/binding-android-arm64": ["@oxfmt/binding-android-arm64@0.54.0", "", { "os": "android", "cpu": "arm64" }, "sha512-B4VZfBUlKK1rmMChsssNZbkZjE8+FzG3avMjGgMDwbGxXRoXkoeXiAZ+78Oa+eyDPHvDCiUb4zH/vmCOUSafLQ=="], - "@oxfmt/binding-darwin-arm64": ["@oxfmt/binding-darwin-arm64@0.53.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-k2RjMcSTkHjoOlsVGbL35JVzXL+oQco3GHPl/5kjebVF4oHNfE24In8F5isqBh9LBJucycWHKDXdGrCchdWcHQ=="], + "@oxfmt/binding-darwin-arm64": ["@oxfmt/binding-darwin-arm64@0.54.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-i02vF75b+ePsQP3tHqSxVYI5S6b8X/xqdPu7/mDHXtpgXLTYXi3jJmfHU0j+dnZZDKaYTx/ioCK7QYJmtiJR2g=="], - "@oxfmt/binding-darwin-x64": ["@oxfmt/binding-darwin-x64@0.53.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-65jIBE2H1l5SSs16fmv6/7b6sAx/WpvnsgDhVWK9qSjNFDUro7MPQ6q5UhpY7kl46yltfR046iAnxy/Bzqbiew=="], + "@oxfmt/binding-darwin-x64": ["@oxfmt/binding-darwin-x64@0.54.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-8VMFvGvooXj7mswkbrhdVZ2/sgiDaBzWpkkbtO+qGDLV4EfJd67nQadHkQC0ZNbaWA9ajXfqI6i7PZLIeDzxEQ=="], - "@oxfmt/binding-freebsd-x64": ["@oxfmt/binding-freebsd-x64@0.53.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-oYe1gkz7U49PCYrS9147d2fJZj8mDI4Di6AvlsU5fu9p+Tq8S7qqOMSZjUiVTLX8bXuSA9Lk/tIxuegVjkNYRA=="], + "@oxfmt/binding-freebsd-x64": ["@oxfmt/binding-freebsd-x64@0.54.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-0cRHnp43WN1Jrc5s0BdbdKgR1XirdvHy7TAFi3JEsoEVQVJxTXMbpVd76sxXlgRswNMDhVFSJw+y7Eb8mEavFQ=="], - "@oxfmt/binding-linux-arm-gnueabihf": ["@oxfmt/binding-linux-arm-gnueabihf@0.53.0", "", { "os": "linux", "cpu": "arm" }, "sha512-ailB2vLzGi629tymdAb2VYJyEHref7oqGxP+tRBrtRBxQrb6NV55JMT7xtGZ8uTeG2+Y9zojqW4LhJYxQnz9Pg=="], + "@oxfmt/binding-linux-arm-gnueabihf": ["@oxfmt/binding-linux-arm-gnueabihf@0.54.0", "", { "os": "linux", "cpu": "arm" }, "sha512-JyQAk3hK/OEtup7Rw6kZwfdzbKqTVD5jXXb8Xpfay29suwZyfBDMVW/bj4RqEPySYWc6zCp198pOluf8n5uYzg=="], - "@oxfmt/binding-linux-arm-musleabihf": ["@oxfmt/binding-linux-arm-musleabihf@0.53.0", "", { "os": "linux", "cpu": "arm" }, "sha512-abh4mWBvOvD966sobqF7r103y2yYx7Rb4WGHLOS4+5igGqLbbPxS9aK5+45D6iUY7dWMsk3Muz9a8gUtufvqJA=="], + "@oxfmt/binding-linux-arm-musleabihf": ["@oxfmt/binding-linux-arm-musleabihf@0.54.0", "", { "os": "linux", "cpu": "arm" }, "sha512-qnvLatTpM8vtvjOfcckBOzJjk+n6ce/wwpP8OFeUrD5aNLYcKyWAitwj+Rk3PK9jGanbZvKsJnv14JGQ6XqFdw=="], - "@oxfmt/binding-linux-arm64-gnu": ["@oxfmt/binding-linux-arm64-gnu@0.53.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-z73PvuhJ8qA+cDbaiqbtopHglA91U4+y5wn2sTJJrnpB957d5P33FEuyP3DQIFd7ofljmDmfVT4G0CVGHZaJWg=="], + "@oxfmt/binding-linux-arm64-gnu": ["@oxfmt/binding-linux-arm64-gnu@0.54.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-SMkhnCzIYZYDk9vw3W/80eeYKmrMpGF0Giuxt4HruFlCH7jEtnPeb3SdQKMfgYi/dgtaf+hZAb5XWPYnxqCQ3w=="], - "@oxfmt/binding-linux-arm64-musl": ["@oxfmt/binding-linux-arm64-musl@0.53.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-I6bhOTroqc3ThrwZ89l2k3ivKuELhdPLbAcJhRNyjWvlgwb0vjRgEnVL1XLx5Jud04/ypNRZBykAWrSk6l/D+g=="], + "@oxfmt/binding-linux-arm64-musl": ["@oxfmt/binding-linux-arm64-musl@0.54.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-QrwJlBFFKnxOd95TAaszpMbZBLzMoYMpGaQTZF8oibacnF5rv8l12IhILhQRPmksWiBqg0YSe2Mnl7ayeJAHSA=="], - "@oxfmt/binding-linux-ppc64-gnu": ["@oxfmt/binding-linux-ppc64-gnu@0.53.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-w0p3JzB/PkkQjXALMJMqP9YfP3yq4w6zGsu5kezQmUnxRkN3b/Theg2l/nDgBsOcczxS3gL6Gam5XNAVrO6QJQ=="], + "@oxfmt/binding-linux-ppc64-gnu": ["@oxfmt/binding-linux-ppc64-gnu@0.54.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-WILatiol/TUHTlhod7R09+7Az/XlhKwmY1MHfLZNmewltPWNN/EwxP2rQSHahibZ/cB8gmckEBjBOByD+5bYsQ=="], - "@oxfmt/binding-linux-riscv64-gnu": ["@oxfmt/binding-linux-riscv64-gnu@0.53.0", "", { "os": "linux", "cpu": "none" }, "sha512-mzBhF6k1Yq1K/dqDmVe/AAafnlJfEpx7yfUiksyeWXJk5iSzZqBSxcsa02zIytYgQFRZ7h6WPZfwHg/DoOE1Kw=="], + "@oxfmt/binding-linux-riscv64-gnu": ["@oxfmt/binding-linux-riscv64-gnu@0.54.0", "", { "os": "linux", "cpu": "none" }, "sha512-f05YMG4BH4G8S4ME6UM6fi1MnJ9094mrnvO5Pa4SJlMfWlUM+1/ZWMEF4NnjM7shZAvbHsHRuVYpUo0PHC4P9Q=="], - "@oxfmt/binding-linux-riscv64-musl": ["@oxfmt/binding-linux-riscv64-musl@0.53.0", "", { "os": "linux", "cpu": "none" }, "sha512-AlFCpnRQhogQFzZXWbO6xB6/Udy745L+eQNmDPGg7G/OeWsYmJc4jZYfUN5pQg0reOPWSED2mOQqKZOJM1U8cA=="], + "@oxfmt/binding-linux-riscv64-musl": ["@oxfmt/binding-linux-riscv64-musl@0.54.0", "", { "os": "linux", "cpu": "none" }, "sha512-UfL+2hj1ClNqcCRT9s8vBU4axDpjxgVxX96G+9DYAYjoc5b0u15CJtn2jgsi9iM+EbGNc5CW1HVRgwVu76UsSA=="], - "@oxfmt/binding-linux-s390x-gnu": ["@oxfmt/binding-linux-s390x-gnu@0.53.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-XD4ulY4f1DWbuuZXAqxhVn+gdPmrhnmojWtFN78ctVoupmS845fGhsUrk1HZXKQI+iymbaiz9vAjPsghHNQ7Ag=="], + "@oxfmt/binding-linux-s390x-gnu": ["@oxfmt/binding-linux-s390x-gnu@0.54.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-3/XZe931Hka+J6NjnaqJzYpsWWxDTuRdUdwSQHnOuJEgbC+SehIMFJS8hsEjV7LBhVSL2OCnRLvbVW8O97XIyw=="], - "@oxfmt/binding-linux-x64-gnu": ["@oxfmt/binding-linux-x64-gnu@0.53.0", "", { "os": "linux", "cpu": "x64" }, "sha512-xg8KWX0QnxmYWRe60CgHYWXI0ZOtBbqTsXvWiWrcl2XUHJ3fht2QerOk2iWvylzX3zNT2GpvBRxGoR4d3sxPRQ=="], + "@oxfmt/binding-linux-x64-gnu": ["@oxfmt/binding-linux-x64-gnu@0.54.0", "", { "os": "linux", "cpu": "x64" }, "sha512-Ik93RlObtu43GbxApafayFjwYE06L6Xr08cSwpBPYbDrLp2ReZx0Jm1DqwRyYRnukUJy+rK2WaEvUQOxdytU9Q=="], - "@oxfmt/binding-linux-x64-musl": ["@oxfmt/binding-linux-x64-musl@0.53.0", "", { "os": "linux", "cpu": "x64" }, "sha512-MWExpYBGvl+pIvVB/gj/CcWlN2al8AizT7rUbtaYaWNoQkhWARM6W3qpgoCr72CYSN9PborzPmM5MIRe2BrNdA=="], + "@oxfmt/binding-linux-x64-musl": ["@oxfmt/binding-linux-x64-musl@0.54.0", "", { "os": "linux", "cpu": "x64" }, "sha512-yZcakmPlD86CNymknd7KfW+FH+qfbqJH+i0h69CYfV1+KMoVeM9UED+8+TDVoU4haxI0NxY7RPCvRLy3Sqd2Qg=="], - "@oxfmt/binding-openharmony-arm64": ["@oxfmt/binding-openharmony-arm64@0.53.0", "", { "os": "none", "cpu": "arm64" }, "sha512-u4sajgO4nxgmJIgc/y2AqPhkdbOkQH8WugXpA1+pW0ESQhvGZ1oGq61Q4xMbJHJU1hFgtO18QNrcFYDPYH0gwQ=="], + "@oxfmt/binding-openharmony-arm64": ["@oxfmt/binding-openharmony-arm64@0.54.0", "", { "os": "none", "cpu": "arm64" }, "sha512-GiVBZNnEZnKu00f1jTg49nomv187d0GQX+O+ocykoLeiaALuEO+swoTehHn9TehTfi7V8H0i0e/yvUjCqnwk1w=="], - "@oxfmt/binding-win32-arm64-msvc": ["@oxfmt/binding-win32-arm64-msvc@0.53.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-Yq9sOZoIOJ5xPjO0qOyHJS4CiPuTkB2en9auxZz7Ar2p5RaC7BzLyVVmAA7zz9/L9YnjjY1DwNxN+ivKXimN/A=="], + "@oxfmt/binding-win32-arm64-msvc": ["@oxfmt/binding-win32-arm64-msvc@0.54.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-J0SSB8Z1Fre2sxRolYcW6Rl1RQmKdQ2hnHyq4YJrfBRiXTObLw4DXnIVraM/UyqGqwOi7yTrQA4VT7DPxlHVKA=="], - "@oxfmt/binding-win32-ia32-msvc": ["@oxfmt/binding-win32-ia32-msvc@0.53.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-es1fVNZEkBqEcQtBpn19SYFgZF7FawlkCjkT/iImfEAus4gun8fBwB1E9hpV5LcR9B0DBNvRIXhW8BQk3JaE+Q=="], + "@oxfmt/binding-win32-ia32-msvc": ["@oxfmt/binding-win32-ia32-msvc@0.54.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-O61UDVj8zz6yXJjkHPf05VaMLOXmEF8P5kf/N0W7AQMmd6bcQogl+KJc7rMutKTL524oE9iH32JXZClBFmEQIg=="], - "@oxfmt/binding-win32-x64-msvc": ["@oxfmt/binding-win32-x64-msvc@0.53.0", "", { "os": "win32", "cpu": "x64" }, "sha512-QFmJs2bEu9AO4O6qsmEaZNGi6dFq8N+rT8EHAAnZIq/B9SeJDUbc4DzVxQ48MfDsL7D3sCZzo37zuTuspcURgg=="], + "@oxfmt/binding-win32-x64-msvc": ["@oxfmt/binding-win32-x64-msvc@0.54.0", "", { "os": "win32", "cpu": "x64" }, "sha512-1MDpqJPiFqxWtIHas8vkb1VZ7f7eKyTffAwmO8isxQYMaG1OFKsH666BWLeXQLO+IWNfiMssLD55hbR1lIPTqg=="], - "@oxlint/binding-android-arm-eabi": ["@oxlint/binding-android-arm-eabi@1.68.0", "", { "os": "android", "cpu": "arm" }, "sha512-wEdsIspexXLLMCPAEOcCuFLMt6aE3AzTuA/nQKLPRnoJ+EQTturmGheDkhHuuVHx0GbutjQ3JKmEn+Gz6Ag28Q=="], + "@oxlint/binding-android-arm-eabi": ["@oxlint/binding-android-arm-eabi@1.69.0", "", { "os": "android", "cpu": "arm" }, "sha512-DKQQbD5cZ/MYfDgDI7YGyGD9FSxABlsBsYFo5p26lloob543tP9+4N3guwdXIYJN+7HSZxLe8YJuwcOWw5qnHg=="], - "@oxlint/binding-android-arm64": ["@oxlint/binding-android-arm64@1.68.0", "", { "os": "android", "cpu": "arm64" }, "sha512-6aZRNNXQTsYtgaus8HTb9nuCcsrQTlKXGnktwvwW0n/SooRWNxNb3925grDkC63aEYZuCIyOVLV16IdYIoC2aQ=="], + "@oxlint/binding-android-arm64": ["@oxlint/binding-android-arm64@1.69.0", "", { "os": "android", "cpu": "arm64" }, "sha512-lEhb+I5pr4inux+JFwfCa1HRq3Os7NirEFQ0H1I35SVEHPm6byX0Ah47xmRha3qi6LAkxUcxViL8o/9PivjzBg=="], - "@oxlint/binding-darwin-arm64": ["@oxlint/binding-darwin-arm64@1.68.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-lVTbsE3kO4bLpZELgjRZuAJc8kP98wb83yMXWH8gaPaFZ+cM2IDeZto4ByoUAYj0Mxv2rvw+A1ssZequSepVSg=="], + "@oxlint/binding-darwin-arm64": ["@oxlint/binding-darwin-arm64@1.69.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-GY2YE8lOZW59BW1Ia1y+1gR0XyjrZRvVWHAr8LGeGhYHE0OQJ/7cRKXTkx1P+E9/6awEc3SX8a68SFTjh/E//A=="], - "@oxlint/binding-darwin-x64": ["@oxlint/binding-darwin-x64@1.68.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-nCmw2XrmQskjBUh/sfP5yKs93V68LijQgjd1cuuZ/q4SCARngLYs60/qqyzuMsg8QQ9KArDI98hxs/RDGE4KRQ=="], + "@oxlint/binding-darwin-x64": ["@oxlint/binding-darwin-x64@1.69.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-ax1oZnOjHX3LB7myQyHEaQkDwfLb6str3/nSP6O7EVUviQGNkEGzGV0EqcBJWK+Ufwx0l4xPgyYayurvhAdl2Q=="], - "@oxlint/binding-freebsd-x64": ["@oxlint/binding-freebsd-x64@1.68.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TI4ovQJliYE9V6e06cEv+qEI9uj7Ao65fmif4er4HD+aouyYyh0P31q2jh3KtqsOHHcQqv2PZ61TjJFLpBDGWQ=="], + "@oxlint/binding-freebsd-x64": ["@oxlint/binding-freebsd-x64@1.69.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-kHWeHv4g2h8NY+mpCxzCtY4uerMJWTN/TSnNj1CPbakFpHEJ6cTya2wWV0pDSYWOJ2+0UiEbhn3AtXxHtsnKjg=="], - "@oxlint/binding-linux-arm-gnueabihf": ["@oxlint/binding-linux-arm-gnueabihf@1.68.0", "", { "os": "linux", "cpu": "arm" }, "sha512-LcNnEi9g71Cmry5ZpLbKT+oVv+/zYG3hYVAbBBB5X85nOQZSk8l92CnDkxJMcxUg0NCnMCOFZuaVDlMyv4tYJw=="], + "@oxlint/binding-linux-arm-gnueabihf": ["@oxlint/binding-linux-arm-gnueabihf@1.69.0", "", { "os": "linux", "cpu": "arm" }, "sha512-gq84vM1a1oEehXo27YCDzGVcxPsZDI1yswZwz2Da1/cbnWtrL16XZZnz0G/+gIU8edtHpfjxq5c+vWEHqJfWoQ=="], - "@oxlint/binding-linux-arm-musleabihf": ["@oxlint/binding-linux-arm-musleabihf@1.68.0", "", { "os": "linux", "cpu": "arm" }, "sha512-OovHahL3FX4UaK+hgSf11llUx2vszqjSdQQ61Ck9InOEI/ptZoC4XSQJurITqItVvd53JSlmkLMeaNjM1PoQew=="], + "@oxlint/binding-linux-arm-musleabihf": ["@oxlint/binding-linux-arm-musleabihf@1.69.0", "", { "os": "linux", "cpu": "arm" }, "sha512-kIqEa98JQ0VRyrcncxA417m2AzasqTlD+FyVT1AksjvjkqQcvm7pBWYvoW3/mpyOP2XYvi5nSCCTIe6De1yu5g=="], - "@oxlint/binding-linux-arm64-gnu": ["@oxlint/binding-linux-arm64-gnu@1.68.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-YbzTglnHLzzi9zv5or8Ztz5fykAoZE8W9iM42/bOrF4HBSB6rJTqdLQWuoP76EHQw9DuKl76K1QmFlG29sPJXQ=="], + "@oxlint/binding-linux-arm64-gnu": ["@oxlint/binding-linux-arm64-gnu@1.69.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-j+xYiXozxGWx2cpjCrwwGR4awTxPFsRv3JZrv23RCogEPMc4R7UqjHW47p/RG0aRlbWiROCJ8coUfCwy0dvzHA=="], - "@oxlint/binding-linux-arm64-musl": ["@oxlint/binding-linux-arm64-musl@1.68.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-qVKtCZNic+OoNnOr/hCQAu22HSQzflI7Fsq/Blzkw02SnLuv163k3kfmrVpZjSBlUHgsRKj6WgQiw30d3SX02Q=="], + "@oxlint/binding-linux-arm64-musl": ["@oxlint/binding-linux-arm64-musl@1.69.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-xEPpNppTfN1l/nM7gYSf9iocscu/as+p/7vxkLeLEKnYU+09Dm+5V6IhDYDh+Uz6FajEupWwCLt5SOG0y1PCKg=="], - "@oxlint/binding-linux-ppc64-gnu": ["@oxlint/binding-linux-ppc64-gnu@1.68.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-zExyZ8ZOUuAyQ0y9jpTcyjKUz62YY9JhKPyVxzvjTpXzZ3ujdqiVwfPWDdnA1SsIOrxdtxHn7KErDHLWskFjXg=="], + "@oxlint/binding-linux-ppc64-gnu": ["@oxlint/binding-linux-ppc64-gnu@1.69.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-Ug0+eU7HJBlek+SjklYH62IlOMirEJsdxpihH0kSqX0XdrDD4NdHpQc10fK1JC35yn6KrrcN+uYzlHD38XAf8Q=="], - "@oxlint/binding-linux-riscv64-gnu": ["@oxlint/binding-linux-riscv64-gnu@1.68.0", "", { "os": "linux", "cpu": "none" }, "sha512-6C4MPuwewyDavA7sxM14wzgRi5GGL68HPIxRCdVyS75U4MDbpFVYzKO9WNR6KLKTMPq2pcz3THwo1sK2uiqngw=="], + "@oxlint/binding-linux-riscv64-gnu": ["@oxlint/binding-linux-riscv64-gnu@1.69.0", "", { "os": "linux", "cpu": "none" }, "sha512-iEyI3GIg0l/s3G4qy2TlaaWKdzj4PJJStwtlocpDTC00PY9hZueotf6OKUj9+yfQh0lrpBW/pLMgTztbAHKJEg=="], - "@oxlint/binding-linux-riscv64-musl": ["@oxlint/binding-linux-riscv64-musl@1.68.0", "", { "os": "linux", "cpu": "none" }, "sha512-bnZooVeHAcvA+dH0EDLgx+7HY/DRi6e0hFszg3P+OBatuUjV6EvfIyNIzWOusmqAVh4L6r21GGTZtiKE4iqM4Q=="], + "@oxlint/binding-linux-riscv64-musl": ["@oxlint/binding-linux-riscv64-musl@1.69.0", "", { "os": "linux", "cpu": "none" }, "sha512-NjHjpiI4WIKSMwuoJSZi5VToPeoYOS1FR52HLIDG6lidMdqquusgtODb4iLk0+lb1q3Z0nv2/aPRcC/olmpQGg=="], - "@oxlint/binding-linux-s390x-gnu": ["@oxlint/binding-linux-s390x-gnu@1.68.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-dIqnZnJSmHCMOUpUcWQOiV14o3DDPVx1DSsMaSzvdhNjC1tB1iEPZbdiMSCIEYbkgbsYznHXWqFdKL8WUB3F8g=="], + "@oxlint/binding-linux-s390x-gnu": ["@oxlint/binding-linux-s390x-gnu@1.69.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-Ai/prDewoItkDXbp38gwGZi41DycZbUTZJ3UidwoHgQC0/DaqC2TGdtBTQLJ6hSD+SAxASzh8+/eSBPmxfOacA=="], - "@oxlint/binding-linux-x64-gnu": ["@oxlint/binding-linux-x64-gnu@1.68.0", "", { "os": "linux", "cpu": "x64" }, "sha512-zc9lEnfV/HreDTY6gdMlZe+irkwHSxQ4/B1pS9GyK7RVaA5LxhoZY/w6/o2vIwLLEYiXQ5ujGxOM1ZazeFAAIA=="], + "@oxlint/binding-linux-x64-gnu": ["@oxlint/binding-linux-x64-gnu@1.69.0", "", { "os": "linux", "cpu": "x64" }, "sha512-Gt3KHgp46mRKz4sJeaASmKvD8ayXookRw07RMf+NowhEztGGDZ7VrXpoW96XuKJLjFukWizOFVNjmYb/u7caNQ=="], - "@oxlint/binding-linux-x64-musl": ["@oxlint/binding-linux-x64-musl@1.68.0", "", { "os": "linux", "cpu": "x64" }, "sha512-Dl5QEX0TCo/40Cdh1o1JdPS//+YiWqjC+Hrrya5OQmStZZr4svAFtdlqcpCrU9yq2Mo3vRVyO9B3h0dzD8s36Q=="], + "@oxlint/binding-linux-x64-musl": ["@oxlint/binding-linux-x64-musl@1.69.0", "", { "os": "linux", "cpu": "x64" }, "sha512-7tQhJ2+p/oHv1zcfnjYI7YVzC/7iBaVOfIvFYtxdJ5F45mWgEdrCyXZXZGfiLey5t/5JhOhsaMnnv1kAzckd7g=="], - "@oxlint/binding-openharmony-arm64": ["@oxlint/binding-openharmony-arm64@1.68.0", "", { "os": "none", "cpu": "arm64" }, "sha512-/qy6dOvi4S3/LeXq0l5BT5pRKPYA7oj3uKwJOAZOr5HRLL+HK6jdBynvWuXIA2wwfE01RzNYmbBdM7vwYx00sA=="], + "@oxlint/binding-openharmony-arm64": ["@oxlint/binding-openharmony-arm64@1.69.0", "", { "os": "none", "cpu": "arm64" }, "sha512-vmWz6TKp/3hfA4lksR0zHBv/6xuX1jhym6eqOjdH2DXsDDHZWcp2f0KG0VCAnlVbIrjk29G4wAWMXb/Hn1YobA=="], - "@oxlint/binding-win32-arm64-msvc": ["@oxlint/binding-win32-arm64-msvc@1.68.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-fHNtVqPHSYE7UFDSLVFUjxQjnSVXxseNJmRW+XuP4pXXDwePdPda43NL7/BBCFTxHjycOc44JNDaOPtFDNui9A=="], + "@oxlint/binding-win32-arm64-msvc": ["@oxlint/binding-win32-arm64-msvc@1.69.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-9RExaLgmaw6IoIkU9cTpT71mLfI0xZ86iZH8x518LVsOkjquJMYqb9P7KpC8lgd1t0Dxs41p2pxynq4XR3Ttzw=="], - "@oxlint/binding-win32-ia32-msvc": ["@oxlint/binding-win32-ia32-msvc@1.68.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-NnKXr4Wgo4nps3erhrE0f8shBvBPZMHg72nDsvX0JyrRvsNiP3f1JNvbCKh+A6VFvpF7ZoJxu904P3cKMhvZnA=="], + "@oxlint/binding-win32-ia32-msvc": ["@oxlint/binding-win32-ia32-msvc@1.69.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-1907kRPF8/PrcIw1E7LMs9JbVrpgnt/MvFdss3an8oDkYNAACXzTntV3t3869ZZhMZxb2AzRGbz1pA/jdFatXA=="], - "@oxlint/binding-win32-x64-msvc": ["@oxlint/binding-win32-x64-msvc@1.68.0", "", { "os": "win32", "cpu": "x64" }, "sha512-zg5pA+84AlU6XHJ3ruiRxziO71QTrz8nLsk6u01JGS5+tL9/bnlakFiklFrcy4R1/V7ktWtaNitN3JZWmKnf6g=="], + "@oxlint/binding-win32-x64-msvc": ["@oxlint/binding-win32-x64-msvc@1.69.0", "", { "os": "win32", "cpu": "x64" }, "sha512-w8SOXv3mT9Fi6jY8OXdXCfnvX/3KNLXGNr4HEz2TA7S4Mv/PYAOmpB8y/ge40mxvBMgGNaSaaDwZpAsQn7HtWA=="], "@secretlint/config-creator": ["@secretlint/config-creator@10.2.2", "", { "dependencies": { "@secretlint/types": "^10.2.2" } }, "sha512-BynOBe7Hn3LJjb3CqCHZjeNB09s/vgf0baBaHVw67w7gHF0d25c3ZsZ5+vv8TgwSchRdUCRrbbcq5i2B1fJ2QQ=="], @@ -263,21 +263,21 @@ "@types/vscode": ["@types/vscode@1.107.0", "", {}, "sha512-XS8YE1jlyTIowP64+HoN30OlC1H9xqSlq1eoLZUgFEC8oUTO6euYZxti1xRiLSfZocs4qytTzR6xCBYtioQTCg=="], - "@typescript/native-preview": ["@typescript/native-preview@7.0.0-dev.20260602.1", "", { "optionalDependencies": { "@typescript/native-preview-darwin-arm64": "7.0.0-dev.20260602.1", "@typescript/native-preview-darwin-x64": "7.0.0-dev.20260602.1", "@typescript/native-preview-linux-arm": "7.0.0-dev.20260602.1", "@typescript/native-preview-linux-arm64": "7.0.0-dev.20260602.1", "@typescript/native-preview-linux-x64": "7.0.0-dev.20260602.1", "@typescript/native-preview-win32-arm64": "7.0.0-dev.20260602.1", "@typescript/native-preview-win32-x64": "7.0.0-dev.20260602.1" }, "bin": { "tsgo": "bin/tsgo.js" } }, "sha512-Gfnju5EahMkyN5Mn9EIFTXabj/yfixdxtLcb1u8fSDWLD9AOflOLO95GuQcyZHPjCNukTE+9uAvjpMKcRNmbXQ=="], + "@typescript/native-preview": ["@typescript/native-preview@7.0.0-dev.20260608.1", "", { "optionalDependencies": { "@typescript/native-preview-darwin-arm64": "7.0.0-dev.20260608.1", "@typescript/native-preview-darwin-x64": "7.0.0-dev.20260608.1", "@typescript/native-preview-linux-arm": "7.0.0-dev.20260608.1", "@typescript/native-preview-linux-arm64": "7.0.0-dev.20260608.1", "@typescript/native-preview-linux-x64": "7.0.0-dev.20260608.1", "@typescript/native-preview-win32-arm64": "7.0.0-dev.20260608.1", "@typescript/native-preview-win32-x64": "7.0.0-dev.20260608.1" }, "bin": { "tsgo": "bin/tsgo.js" } }, "sha512-nJyuPQwFn/VlP7JvFA0jPHJMMHaDy5vWM5xRYUwjZZd3dcY7LldfZ1EkdPOLc+GW0trXcmvRemQ4XCsBkOGOUQ=="], - "@typescript/native-preview-darwin-arm64": ["@typescript/native-preview-darwin-arm64@7.0.0-dev.20260602.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-eSPHtC/iz2jp6H4qVXg2f8C03/dbtZCBVTNg6v8iajGRgD/V23kcOArVk4IQAR0VyqKRCkzQK+TLqKGwrQxP1Q=="], + "@typescript/native-preview-darwin-arm64": ["@typescript/native-preview-darwin-arm64@7.0.0-dev.20260608.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ZGs+yhsPWFDkSHydJyF8I3d4witpXiD69auWZtSotNWG3gBR5Ne291m38rvxoLFDRlFHwAQpPT7q3yStp+cQSQ=="], - "@typescript/native-preview-darwin-x64": ["@typescript/native-preview-darwin-x64@7.0.0-dev.20260602.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-tw/714Iw8hsleZowmqxaKrBr1TSAuJU99vEXBjVcBQTW9mChWSt7HDSDNaM2Y3XFLiCj8MAJvzf83uP3wIqBtQ=="], + "@typescript/native-preview-darwin-x64": ["@typescript/native-preview-darwin-x64@7.0.0-dev.20260608.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-cxmBCl1+fvGgom2vwPTbZ43bW68rfjDIU1ZDQGtjXvj1bpW9fEozskJqgZdNNHu8WQIl17zj4Ke+noENwgGu9w=="], - "@typescript/native-preview-linux-arm": ["@typescript/native-preview-linux-arm@7.0.0-dev.20260602.1", "", { "os": "linux", "cpu": "arm" }, "sha512-rtGXfkDyUCjASRw/NXhVwOUfDzqSZrfTGBievr71XBcehVlMBZF9DxWVaIfRMKzXfFpVsmD9C+2JnFgDUBrBaQ=="], + "@typescript/native-preview-linux-arm": ["@typescript/native-preview-linux-arm@7.0.0-dev.20260608.1", "", { "os": "linux", "cpu": "arm" }, "sha512-tWFLuzAWg8XnOkN7f94MAW8qLTMG7Q74IXHSG18cVmftuBxwvn1Lov+0Rnd9rsH7VAuSIu00Co6G3/PbGvGrxA=="], - "@typescript/native-preview-linux-arm64": ["@typescript/native-preview-linux-arm64@7.0.0-dev.20260602.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-uh5XGYggWJWf8QVNI4oylUK04pe74WCTq0DqpZx3y/CnnHzVrgWPlEFe+wn2MhknwVAhOtCIYu2F0UKUqHucTA=="], + "@typescript/native-preview-linux-arm64": ["@typescript/native-preview-linux-arm64@7.0.0-dev.20260608.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-/JPrQqa36CSMdXeEXhZ0mYUmufehIL8ujagXvU8z/3/b/CeWjgoZLJCuy76k9NLZCG7wGvcFWFK5kgOEKyDQdw=="], - "@typescript/native-preview-linux-x64": ["@typescript/native-preview-linux-x64@7.0.0-dev.20260602.1", "", { "os": "linux", "cpu": "x64" }, "sha512-ZKNmZkJCh39uLzHSWC6jeH0d7lTO8ddj1hLm+Gu+4O0maaInZfWLC3xGxME183adMp54tCasCJcZ8XhDgO2NEw=="], + "@typescript/native-preview-linux-x64": ["@typescript/native-preview-linux-x64@7.0.0-dev.20260608.1", "", { "os": "linux", "cpu": "x64" }, "sha512-QfVLSH1NMAOa+N6Ey9cFHWppLFISOeCG3c0nJER3LXrDwkE4WtsVGHQ4IRDpIYT3Q2iC7QWUFWwrHayNKlrGYA=="], - "@typescript/native-preview-win32-arm64": ["@typescript/native-preview-win32-arm64@7.0.0-dev.20260602.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-YZ3wcQ/nN0C71CII0P8Sx6jAvh7WX6zwa3wwGXQOOO4SvxQgUu1ijznsOz+Gz3KpjyyXAloGppkA+0JT5BhbmQ=="], + "@typescript/native-preview-win32-arm64": ["@typescript/native-preview-win32-arm64@7.0.0-dev.20260608.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-DvpbX0aPCWCe4bJEP0cOky4cCwF+5QJZHsLoGV4gYd+0EFEkarTOxZG0CezP30E/iX6u1RUOrtTeMZOQthqDpQ=="], - "@typescript/native-preview-win32-x64": ["@typescript/native-preview-win32-x64@7.0.0-dev.20260602.1", "", { "os": "win32", "cpu": "x64" }, "sha512-ENQpKND75VD7WgtGwj1xXpOQKBM0bmjtzC4W9rzxJjInYfbu22VOXnfzEZlb7fmo/EnyZL6QF6P/zx4ojRMX9Q=="], + "@typescript/native-preview-win32-x64": ["@typescript/native-preview-win32-x64@7.0.0-dev.20260608.1", "", { "os": "win32", "cpu": "x64" }, "sha512-J9nQB87sU+YJE3fq3XKwQvgLNWVXiEWyOP1L/FuyOlHb77UkFXmpaUbFTJ1w7rfd91nlKb2EECfsdTxbELUT0Q=="], "@typespec/ts-http-runtime": ["@typespec/ts-http-runtime@0.3.2", "", { "dependencies": { "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.0", "tslib": "^2.6.2" } }, "sha512-IlqQ/Gv22xUC1r/WQm4StLkYQmaaTsXAhUVsNE0+xiyf0yRFiH5++q78U3bw6bLKDCTmh0uqKB9eG9+Bt75Dkg=="], @@ -611,9 +611,9 @@ "open": ["open@10.2.0", "", { "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "wsl-utils": "^0.1.0" } }, "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA=="], - "oxfmt": ["oxfmt@0.53.0", "", { "dependencies": { "tinypool": "2.1.0" }, "optionalDependencies": { "@oxfmt/binding-android-arm-eabi": "0.53.0", "@oxfmt/binding-android-arm64": "0.53.0", "@oxfmt/binding-darwin-arm64": "0.53.0", "@oxfmt/binding-darwin-x64": "0.53.0", "@oxfmt/binding-freebsd-x64": "0.53.0", "@oxfmt/binding-linux-arm-gnueabihf": "0.53.0", "@oxfmt/binding-linux-arm-musleabihf": "0.53.0", "@oxfmt/binding-linux-arm64-gnu": "0.53.0", "@oxfmt/binding-linux-arm64-musl": "0.53.0", "@oxfmt/binding-linux-ppc64-gnu": "0.53.0", "@oxfmt/binding-linux-riscv64-gnu": "0.53.0", "@oxfmt/binding-linux-riscv64-musl": "0.53.0", "@oxfmt/binding-linux-s390x-gnu": "0.53.0", "@oxfmt/binding-linux-x64-gnu": "0.53.0", "@oxfmt/binding-linux-x64-musl": "0.53.0", "@oxfmt/binding-openharmony-arm64": "0.53.0", "@oxfmt/binding-win32-arm64-msvc": "0.53.0", "@oxfmt/binding-win32-ia32-msvc": "0.53.0", "@oxfmt/binding-win32-x64-msvc": "0.53.0" }, "peerDependencies": { "svelte": "^5.0.0", "vite-plus": "*" }, "optionalPeers": ["svelte", "vite-plus"], "bin": { "oxfmt": "bin/oxfmt" } }, "sha512-9cB5glS3Ip6NMuZ+6NYTao9FCWkDhRtPYCtR3QBu/NxHoFbgzzTvi41N4jxz/GqGfuLKspui1qb/LlSu2IbMcw=="], + "oxfmt": ["oxfmt@0.54.0", "", { "dependencies": { "tinypool": "2.1.0" }, "optionalDependencies": { "@oxfmt/binding-android-arm-eabi": "0.54.0", "@oxfmt/binding-android-arm64": "0.54.0", "@oxfmt/binding-darwin-arm64": "0.54.0", "@oxfmt/binding-darwin-x64": "0.54.0", "@oxfmt/binding-freebsd-x64": "0.54.0", "@oxfmt/binding-linux-arm-gnueabihf": "0.54.0", "@oxfmt/binding-linux-arm-musleabihf": "0.54.0", "@oxfmt/binding-linux-arm64-gnu": "0.54.0", "@oxfmt/binding-linux-arm64-musl": "0.54.0", "@oxfmt/binding-linux-ppc64-gnu": "0.54.0", "@oxfmt/binding-linux-riscv64-gnu": "0.54.0", "@oxfmt/binding-linux-riscv64-musl": "0.54.0", "@oxfmt/binding-linux-s390x-gnu": "0.54.0", "@oxfmt/binding-linux-x64-gnu": "0.54.0", "@oxfmt/binding-linux-x64-musl": "0.54.0", "@oxfmt/binding-openharmony-arm64": "0.54.0", "@oxfmt/binding-win32-arm64-msvc": "0.54.0", "@oxfmt/binding-win32-ia32-msvc": "0.54.0", "@oxfmt/binding-win32-x64-msvc": "0.54.0" }, "peerDependencies": { "svelte": "^5.0.0", "vite-plus": "*" }, "optionalPeers": ["svelte", "vite-plus"], "bin": { "oxfmt": "bin/oxfmt" } }, "sha512-DjnMwn7smSLF+Mc2+pRItnuPftm/dkUFpY/d4+33y9TfKrsHZo8GLhmUg9BrOIUEy94Rlom1Q11N6vuhE+e0oQ=="], - "oxlint": ["oxlint@1.68.0", "", { "optionalDependencies": { "@oxlint/binding-android-arm-eabi": "1.68.0", "@oxlint/binding-android-arm64": "1.68.0", "@oxlint/binding-darwin-arm64": "1.68.0", "@oxlint/binding-darwin-x64": "1.68.0", "@oxlint/binding-freebsd-x64": "1.68.0", "@oxlint/binding-linux-arm-gnueabihf": "1.68.0", "@oxlint/binding-linux-arm-musleabihf": "1.68.0", "@oxlint/binding-linux-arm64-gnu": "1.68.0", "@oxlint/binding-linux-arm64-musl": "1.68.0", "@oxlint/binding-linux-ppc64-gnu": "1.68.0", "@oxlint/binding-linux-riscv64-gnu": "1.68.0", "@oxlint/binding-linux-riscv64-musl": "1.68.0", "@oxlint/binding-linux-s390x-gnu": "1.68.0", "@oxlint/binding-linux-x64-gnu": "1.68.0", "@oxlint/binding-linux-x64-musl": "1.68.0", "@oxlint/binding-openharmony-arm64": "1.68.0", "@oxlint/binding-win32-arm64-msvc": "1.68.0", "@oxlint/binding-win32-ia32-msvc": "1.68.0", "@oxlint/binding-win32-x64-msvc": "1.68.0" }, "peerDependencies": { "oxlint-tsgolint": ">=0.22.1", "vite-plus": "*" }, "optionalPeers": ["oxlint-tsgolint", "vite-plus"], "bin": { "oxlint": "bin/oxlint" } }, "sha512-dXcbq+xsmLrMy6T8d0euf3IYUfLmjHIE11pOxiUSi5LHkFZaYPv568R6sEjcavVpUxoaQe66UBuK4HEi74NxpA=="], + "oxlint": ["oxlint@1.69.0", "", { "optionalDependencies": { "@oxlint/binding-android-arm-eabi": "1.69.0", "@oxlint/binding-android-arm64": "1.69.0", "@oxlint/binding-darwin-arm64": "1.69.0", "@oxlint/binding-darwin-x64": "1.69.0", "@oxlint/binding-freebsd-x64": "1.69.0", "@oxlint/binding-linux-arm-gnueabihf": "1.69.0", "@oxlint/binding-linux-arm-musleabihf": "1.69.0", "@oxlint/binding-linux-arm64-gnu": "1.69.0", "@oxlint/binding-linux-arm64-musl": "1.69.0", "@oxlint/binding-linux-ppc64-gnu": "1.69.0", "@oxlint/binding-linux-riscv64-gnu": "1.69.0", "@oxlint/binding-linux-riscv64-musl": "1.69.0", "@oxlint/binding-linux-s390x-gnu": "1.69.0", "@oxlint/binding-linux-x64-gnu": "1.69.0", "@oxlint/binding-linux-x64-musl": "1.69.0", "@oxlint/binding-openharmony-arm64": "1.69.0", "@oxlint/binding-win32-arm64-msvc": "1.69.0", "@oxlint/binding-win32-ia32-msvc": "1.69.0", "@oxlint/binding-win32-x64-msvc": "1.69.0" }, "peerDependencies": { "oxlint-tsgolint": ">=0.22.1", "vite-plus": "*" }, "optionalPeers": ["oxlint-tsgolint", "vite-plus"], "bin": { "oxlint": "bin/oxlint" } }, "sha512-ypZkK/aDc5NQV8zIR6s2H2Tl3aNW8FmJ1m9+2qsaYuRenl8vgnHNCGwTHviWJdUQzglOlHFchgopdtGhSy17Rw=="], "p-map": ["p-map@7.0.4", "", {}, "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ=="], diff --git a/package.json b/package.json index 22a01973c0..7a9a610155 100644 --- a/package.json +++ b/package.json @@ -26,10 +26,10 @@ }, "dependencies": { "@types/bun": "^1.3.14", - "@typescript/native-preview": "^7.0.0-dev.20260602.1", + "@typescript/native-preview": "^7.0.0-dev.20260608.1", "esbuild": "^0.28.0", - "oxfmt": "^0.53.0", - "oxlint": "^1.68.0", + "oxfmt": "^0.54.0", + "oxlint": "^1.69.0", "typescript": "^6.0.3" } } From a057cb098f1329bfa25ac2c058eff0eee1fafc3e Mon Sep 17 00:00:00 2001 From: David Yamnitsky Date: Mon, 8 Jun 2026 14:57:42 +0000 Subject: [PATCH 08/11] clear warnings --- packages/sandbox/src/vm/init.rs | 2 +- packages/server/src/watch.rs | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/sandbox/src/vm/init.rs b/packages/sandbox/src/vm/init.rs index 1868b34390..ab3025fa08 100644 --- a/packages/sandbox/src/vm/init.rs +++ b/packages/sandbox/src/vm/init.rs @@ -104,7 +104,7 @@ pub fn run() -> tg::Result { let mut stat: libc::stat = unsafe { std::mem::zeroed() }; let path = c"/mnt/host/opt/tangram/artifacts"; - if unsafe { libc::stat(path.as_ptr(), &mut stat) } == 0 { + if unsafe { libc::stat(path.as_ptr(), &raw mut stat) } == 0 { let major = libc::major(stat.st_dev); let minor = libc::minor(stat.st_dev); let bdi = format!("/sys/class/bdi/{major}:{minor}/read_ahead_kb"); diff --git a/packages/server/src/watch.rs b/packages/server/src/watch.rs index d4cae3988b..66b0ef46e4 100644 --- a/packages/server/src/watch.rs +++ b/packages/server/src/watch.rs @@ -227,7 +227,7 @@ impl Watch { lock, solutions, version, - next: _next, + next, } = arg; let mut state = self.state.lock().unwrap(); @@ -249,7 +249,9 @@ impl Watch { // On Linux, add the new paths. #[cfg(target_os = "linux")] - state.add_paths_linux(_next); + state.add_paths_linux(next); + #[cfg(not(target_os = "linux"))] + let _ = next; true } From 11bd4cbb5b866e428f2441ae4fb71523cf4f3f3d Mon Sep 17 00:00:00 2001 From: David Yamnitsky Date: Mon, 8 Jun 2026 16:01:45 +0000 Subject: [PATCH 09/11] add cloud_hypervisor_path to config and make window_size in bytes --- packages/cli/src/sandbox/vm/run.rs | 4 ++++ packages/sandbox/src/lib.rs | 1 + packages/sandbox/src/vm/run.rs | 9 +++++++-- packages/sandbox/src/vm/spawn.rs | 3 +++ packages/server/src/config.rs | 8 +++++--- packages/server/src/run/sandbox.rs | 6 +++++- packages/server/src/sandbox/isolation.rs | 6 ++++-- 7 files changed, 29 insertions(+), 8 deletions(-) diff --git a/packages/cli/src/sandbox/vm/run.rs b/packages/cli/src/sandbox/vm/run.rs index 1bb4ff5ad4..73ecf39041 100644 --- a/packages/cli/src/sandbox/vm/run.rs +++ b/packages/cli/src/sandbox/vm/run.rs @@ -10,6 +10,9 @@ pub struct Args { #[arg(long)] pub artifacts_path: PathBuf, + #[arg(long)] + pub cloud_hypervisor_path: Option, + #[arg(long, hide = true, value_name = "DIR")] pub create_snapshot: Option, @@ -90,6 +93,7 @@ impl Cli { pub fn command_sandbox_vm_run(args: Args) -> tg::Result { let arg = tangram_sandbox::vm::run::Arg { artifacts_path: args.artifacts_path, + cloud_hypervisor_path: args.cloud_hypervisor_path, create_snapshot: args.create_snapshot, cpu: args.cpu, dax: args.dax, diff --git a/packages/sandbox/src/lib.rs b/packages/sandbox/src/lib.rs index a4e768545e..5aa94bf484 100644 --- a/packages/sandbox/src/lib.rs +++ b/packages/sandbox/src/lib.rs @@ -137,6 +137,7 @@ pub struct SeatbeltIsolation {} #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct VmIsolation { + pub cloud_hypervisor_path: Option, pub dax: Option, pub kernel_path: PathBuf, pub max_cpu: u64, diff --git a/packages/sandbox/src/vm/run.rs b/packages/sandbox/src/vm/run.rs index 1ea844ce92..a9a51ef502 100644 --- a/packages/sandbox/src/vm/run.rs +++ b/packages/sandbox/src/vm/run.rs @@ -49,6 +49,7 @@ const VMM_GUEST_CID: u32 = 3; #[derive(Clone, Debug)] pub struct Arg { pub artifacts_path: PathBuf, + pub cloud_hypervisor_path: Option, pub create_snapshot: Option, pub cpu: Option, pub dax: Option, @@ -302,8 +303,12 @@ pub fn run(arg: &Arg) -> tg::Result { helpers.push(ChildProcess::new(sandbox_pid)); } - let cloud_hypervisor_bin = find_in_path("cloud-hypervisor") - .map_err(|error| tg::error!(!error, "failed to locate cloud-hypervisor on PATH"))?; + let cloud_hypervisor_bin = if let Some(path) = &arg.cloud_hypervisor_path { + path.clone() + } else { + find_in_path("cloud-hypervisor") + .map_err(|error| tg::error!(!error, "failed to locate cloud-hypervisor on PATH"))? + }; tracing::trace!( "cloud-hypervisor binary: {}", cloud_hypervisor_bin.display() diff --git a/packages/sandbox/src/vm/spawn.rs b/packages/sandbox/src/vm/spawn.rs index d30b630393..4e4b813066 100644 --- a/packages/sandbox/src/vm/spawn.rs +++ b/packages/sandbox/src/vm/spawn.rs @@ -46,6 +46,9 @@ pub(crate) fn spawn( if let Some(dax) = vm.dax { command.arg("--dax").arg(dax.to_string()); } + if let Some(path) = &vm.cloud_hypervisor_path { + command.arg("--cloud-hypervisor-path").arg(path); + } if let Some(snapshot) = &vm.snapshot { command.arg("--snapshot").arg(snapshot); } diff --git a/packages/server/src/config.rs b/packages/server/src/config.rs index 80b18a4753..52eb107e6c 100644 --- a/packages/server/src/config.rs +++ b/packages/server/src/config.rs @@ -649,6 +649,9 @@ pub struct SeatbeltSandboxIsolation {} #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[serde(deny_unknown_fields)] pub struct VmSandboxIsolation { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub cloud_hypervisor_path: Option, + #[serde_as(as = "BoolOptionDefault")] #[serde(default = "default_vm_dax")] pub dax: Option, @@ -677,8 +680,7 @@ pub struct VmSandboxIsolation { #[derive(Clone, Copy, Debug, serde::Deserialize, serde::Serialize)] #[serde(default, deny_unknown_fields)] pub struct Dax { - #[serde(rename = "window_size")] - pub window_size_kib: usize, + pub window_size: usize, } #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] @@ -1253,7 +1255,7 @@ impl Default for SandboxIsolation { impl Default for Dax { fn default() -> Self { Self { - window_size_kib: 8 * 1024 * 1024, + window_size: 8 * 1024 * 1024 * 1024, } } } diff --git a/packages/server/src/run/sandbox.rs b/packages/server/src/run/sandbox.rs index 7512b7fdf0..5213ac6268 100644 --- a/packages/server/src/run/sandbox.rs +++ b/packages/server/src/run/sandbox.rs @@ -137,7 +137,8 @@ impl Session { .unwrap_or_else(|| self.server.vm_snapshot_path()), ); tangram_sandbox::Isolation::Vm(tangram_sandbox::VmIsolation { - dax: vm.dax.map(|dax| dax.window_size_kib as u64 * 1024), + cloud_hypervisor_path: vm.cloud_hypervisor_path.clone(), + dax: vm.dax.map(|dax| dax.window_size as u64), kernel_path, max_cpu: vm.max_cpu, max_memory: vm.max_memory, @@ -628,6 +629,9 @@ impl Server { if let Some(dax) = vm.dax { command.arg("--dax").arg(dax.to_string()); } + if let Some(path) = &vm.cloud_hypervisor_path { + command.arg("--cloud-hypervisor-path").arg(path); + } let status = command .status() .await diff --git a/packages/server/src/sandbox/isolation.rs b/packages/server/src/sandbox/isolation.rs index 245840a5a3..0b9ac9fe46 100644 --- a/packages/server/src/sandbox/isolation.rs +++ b/packages/server/src/sandbox/isolation.rs @@ -33,7 +33,8 @@ impl Server { ); Ok(tangram_sandbox::Isolation::Vm( tangram_sandbox::VmIsolation { - dax: vm.dax.map(|dax| dax.window_size_kib as u64 * 1024), + cloud_hypervisor_path: vm.cloud_hypervisor_path.clone(), + dax: vm.dax.map(|dax| dax.window_size as u64), kernel_path: vm.kernel_path.clone(), max_cpu: vm.max_cpu, max_memory: vm.max_memory, @@ -97,7 +98,8 @@ impl Server { ); return Some(tangram_sandbox::Isolation::Vm( tangram_sandbox::VmIsolation { - dax: vm.dax.map(|dax| dax.window_size_kib as u64 * 1024), + cloud_hypervisor_path: vm.cloud_hypervisor_path.clone(), + dax: vm.dax.map(|dax| dax.window_size as u64), kernel_path: vm.kernel_path.clone(), max_cpu: vm.max_cpu, max_memory: vm.max_memory, From 851965c9f7d6374279758bd98968894a4d28e818 Mon Sep 17 00:00:00 2001 From: David Yamnitsky Date: Mon, 8 Jun 2026 16:22:52 +0000 Subject: [PATCH 10/11] write nul instead of newline as ready byte --- packages/sandbox/src/vm/run.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/sandbox/src/vm/run.rs b/packages/sandbox/src/vm/run.rs index a9a51ef502..6c8038ddec 100644 --- a/packages/sandbox/src/vm/run.rs +++ b/packages/sandbox/src/vm/run.rs @@ -526,7 +526,7 @@ pub fn run(arg: &Arg) -> tg::Result { )?; } serial_stream - .write_all(b"\n") + .write_all(&[0]) .map_err(|error| tg::error!(!error, "failed to write the resume signal"))?; serial_stream .flush() From 8bf139804cb4b8a7cf3b97661f6ecdcb84903ae8 Mon Sep 17 00:00:00 2001 From: David Yamnitsky Date: Mon, 8 Jun 2026 16:35:54 +0000 Subject: [PATCH 11/11] fix --- packages/sandbox/src/vm/run.rs | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/packages/sandbox/src/vm/run.rs b/packages/sandbox/src/vm/run.rs index 6c8038ddec..0691cc83fe 100644 --- a/packages/sandbox/src/vm/run.rs +++ b/packages/sandbox/src/vm/run.rs @@ -13,6 +13,10 @@ use { }, path::{Path, PathBuf}, process::ExitCode, + sync::{ + Arc, + atomic::{AtomicBool, Ordering}, + }, time::{Duration, Instant}, }, tangram_client::prelude::*, @@ -401,11 +405,14 @@ pub fn run(arg: &Arg) -> tg::Result { .try_clone() .map_err(|error| tg::error!(!error, "failed to clone the serial socket"))?; let (ready_tx, ready_rx) = std::sync::mpsc::sync_channel::<()>(1); + let suppress_resume_echo = Arc::new(AtomicBool::new(false)); + let suppress_resume_echo_ = suppress_resume_echo.clone(); std::thread::spawn(move || { let mut stream = serial_reader; let mut buf = [0u8; 4096]; let mut signaled = false; let stderr = std::io::stderr(); + let suppress_resume_echo = suppress_resume_echo_; loop { match std::io::Read::read(&mut stream, &mut buf) { Ok(0) | Err(_) => return, @@ -416,7 +423,21 @@ pub fn run(arg: &Arg) -> tg::Result { let output = chunk .iter() .copied() - .filter(|byte| *byte != 0) + .filter(|byte| { + if *byte == 0 { + return false; + } + if suppress_resume_echo.load(Ordering::Relaxed) { + if matches!(*byte, b'\r' | b'\n') { + if *byte == b'\n' { + suppress_resume_echo.store(false, Ordering::Relaxed); + } + return false; + } + suppress_resume_echo.store(false, Ordering::Relaxed); + } + true + }) .collect::>(); if !output.is_empty() { let _ = std::io::Write::write_all(&mut handle, &output); @@ -525,8 +546,9 @@ pub fn run(arg: &Arg) -> tg::Result { &["add-net", net], )?; } + suppress_resume_echo.store(true, Ordering::Relaxed); serial_stream - .write_all(&[0]) + .write_all(b"\n") .map_err(|error| tg::error!(!error, "failed to write the resume signal"))?; serial_stream .flush()