Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Changelog

- **Fixed** `vp run` no longer fails while setting up task communication in default Codex CLI and Claude Code sandboxes that block Unix domain sockets ([#562](https://github.com/voidzero-dev/vite-task/issues/562), [#569](https://github.com/voidzero-dev/vite-task/pull/569)).
- **Fixed** Automatic file-access tracking now works inside coding-agent sandboxes, including the default Codex CLI and Claude Code sandboxes ([#563](https://github.com/voidzero-dev/vite-task/issues/563), [#576](https://github.com/voidzero-dev/vite-task/pull/576)).
- **Added** Tasks now run with `VP_RUN=1` set, so tools can tell they are running under `vp run` instead of being invoked directly ([#570](https://github.com/voidzero-dev/vite-task/pull/570)).
- **Fixed** The task cache now supports much larger automatically tracked input sets without hitting wincode's default 4 MiB sequence preallocation limit ([#554](https://github.com/voidzero-dev/vite-task/pull/554)).
Expand Down
17 changes: 14 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ uuid = "1.18.1"
vec1 = "1.12.1"
vite_glob = { path = "crates/vite_glob" }
vite_graph_ser = { path = "crates/vite_graph_ser" }
pipe_socket = { path = "crates/pipe_socket" }
vite_path = { path = "crates/vite_path" }
vite_powershell = { path = "crates/vite_powershell" }
vite_select = { path = "crates/vite_select" }
Expand Down
34 changes: 34 additions & 0 deletions crates/pipe_socket/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
[package]
name = "pipe_socket"
version = "0.0.0"
edition.workspace = true
license.workspace = true
publish = false
rust-version.workspace = true

[dependencies]
tokio = { workspace = true, features = ["io-util", "net"] }

[target.'cfg(unix)'.dependencies]
nix = { workspace = true, features = ["fs", "poll"] }
tempfile = { workspace = true }
tokio = { workspace = true, features = ["fs"] }
uuid = { workspace = true, features = ["v4"] }
vite_path = { workspace = true }

[target.'cfg(windows)'.dependencies]
uuid = { workspace = true, features = ["v4"] }
winapi = { workspace = true, features = ["namedpipeapi"] }

[dev-dependencies]
tokio = { workspace = true, features = ["macros", "rt", "time"] }

[target.'cfg(unix)'.dev-dependencies]
nix = { workspace = true, features = ["fs"] }

[lints]
workspace = true

[lib]
doctest = false
test = false
16 changes: 16 additions & 0 deletions crates/pipe_socket/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# `pipe_socket`

Socket-style server-client IPC, implemented on named pipes rather than Unix
domain sockets.

A server binds under an opaque name and hands that name to its clients, for
example through an environment variable. Clients connect synchronously and get
a byte stream; the server accepts each connection asynchronously with Tokio.
The API is four items: `Server::bind`, `Server::name`, `Server::accept`, and
`Client::connect`.

The transport is named pipes on every platform: FIFOs on Unix, named pipe
objects on Windows. Both are available in environments that block Unix domain
sockets, such as coding-agent sandboxes.

A client whose server is gone gets an error, not a hang, on both platforms.
118 changes: 118 additions & 0 deletions crates/pipe_socket/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
#![doc = include_str!("../README.md")]

use std::{
ffi::OsStr,
io::{self, Read, Write},
pin::Pin,
task::{Context, Poll},
};

use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};

#[cfg(unix)]
mod unix;
#[cfg(unix)]
use unix as imp;
#[cfg(windows)]
mod windows;
#[cfg(windows)]
use windows as imp;

#[cfg(not(any(unix, windows)))]
compile_error!("pipe_socket supports only Unix and Windows");

/// A named server that asynchronously accepts byte-stream connections.
pub struct Server {
inner: imp::Server,
}

impl Server {
/// Creates a server with a new unique name.
///
/// # Errors
///
/// Returns an error if the platform transport cannot be created.
pub fn bind() -> io::Result<Self> {
imp::Server::bind().map(|inner| Self { inner })
}

/// Returns the opaque name clients use to connect to this server.
#[must_use]
pub fn name(&self) -> &OsStr {
self.inner.name()
}

/// Waits for and accepts the next client connection.
///
/// # Errors
///
/// Returns an error if the connection cannot be accepted.
pub async fn accept(&mut self) -> io::Result<ServerConnection> {
self.inner.accept().await.map(|inner| ServerConnection { inner })
}
}

/// The server side of an accepted byte-stream connection.
pub struct ServerConnection {
inner: imp::ServerConnection,
}

impl AsyncRead for ServerConnection {
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
Pin::new(&mut self.inner).poll_read(cx, buf)
}
}

impl AsyncWrite for ServerConnection {
fn poll_write(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
Pin::new(&mut self.inner).poll_write(cx, buf)
}

fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Pin::new(&mut self.inner).poll_flush(cx)
}

fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Pin::new(&mut self.inner).poll_shutdown(cx)
}
}

/// A synchronous client byte stream connected by a server name.
pub struct Client {
inner: imp::Client,
}

impl Client {
/// Connects to the server identified by `name`.
///
/// # Errors
///
/// Returns an error if the name is invalid or the server cannot be reached.
pub fn connect(name: &OsStr) -> io::Result<Self> {
imp::Client::connect(name).map(|inner| Self { inner })
}
}

impl Read for Client {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.inner.read(buf)
}
}

impl Write for Client {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.inner.write(buf)
}

fn flush(&mut self) -> io::Result<()> {
self.inner.flush()
}
}
Loading
Loading