Skip to content

feat(streaming): add live command I/O and async output sinks - #2741

Merged
chaliy merged 14 commits into
mainfrom
feat/streaming-command-io
Oct 16, 2026
Merged

chaliy merged 14 commits into
mainfrom
feat/streaming-command-io

Conversation

@chaliy

@chaliy chaliy commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

What changed

Custom commands can read stdin incrementally and await byte writes to stdout/stderr while a script runs. Rust, native Python, native JavaScript and browser/worker WASM expose live handlers, async output sinks and bounded capture. The CLI delivers output through the same execution driver; the C ABI adds synchronous output callbacks.

Capture and delivery are independent: callers can stream without retaining final output, capture a bounded copy, or do both. Pipes apply backpressure, shell redirections preserve descriptor order, and cancellation/deadlines cover pending handlers and sinks. Request-scoped I/O and filesystem capabilities expire when execution ends.

Native JS command I/O uses validated opaque handles and clones Rust capabilities before async work, avoiding generated class pointer conversions. Forged and cross-type handles fail at the boundary. Python/TypeScript runtime host functions use the same execution-local dispatcher with buffered calls, live byte sinks and explicit capture; host functions and sinks retain caller async context. C runtime host functions also work during streaming execution; both execution entry points share callback admission guards, and blocking calls from output callbacks fail before locking or nesting runtimes.

Public guides include runnable Rust/Python/JS/WASM examples, migration instructions, remaining buffering boundaries and a per-surface support matrix. The Rust script-analysis enum is now CommandPlacement; CommandContext names the running handler's capabilities.

Why

The buffered custom-command contract and synchronous output observers cannot support consumers that need incremental input, awaited output and bounded retention. This addresses that handler/execution boundary and documents the remaining whole-buffer roadmap.

Before / After

  • Before: a custom handler receives collected stdin and returns an output value. An output observer cannot await a slow destination and still retains a final result.
  • After: printf hello | copy passes bytes through an async reader/writer handler; Rust .stdout(writer).capture(1024), Python stdout=sink, capture=1024, and JS {stdout: sink, capture: 1024} select delivery and capture explicitly.
  • Gated tests observe a final filter's first output while its producer is still running, on native runtimes and real Chromium main-thread/worker WASM. Other tests cover binary data, bounded queues, redirects, early reader exit, sink failure, cancellation, deadlines, capability revocation and reuse.
  • Input process substitutions retain their producer bytes for /dev/fd instead of leaking them to the caller's sinks. Cancelled Python requests queued behind another handler cannot start filesystem side effects. Deep substitution tests exercise both buffered and live execution; pipeline futures have a separate heap boundary to preserve recursive stack limits.
  • The 96-case Bash comparison run matches GNU Bash output in every case. Benchmark reports are included; shared-host timings are exploratory.

Risk

  • High: this changes interpreter routing and execution lifetime across several bindings. CI and runtime-specific tests cover those boundaries.
  • Rust analysis imports and variant references must use CommandPlacement. Rust Bash::execute discards output unless capture is selected; buffered exec remains available. Python/JS ordinary execution keeps bounded capture.
  • Live execution rejects whole-output transformation hooks and incompatible output observers. Process substitutions, whole-input utilities, legacy tool queues and some other surfaces still buffer; the public support matrix states those limits.

Checklist

  • Tests added or updated
  • Backward compatibility considered; intentional Rust rename and execution policies documented

Validation includes Rust unit/integration/CLI and Rustdoc tests, native Python and Node/Bun/Deno tests, WASM package and real-browser tests, 32 C ABI tests and four compiled C11 examples plus a C++17 header check after rebasing onto the C host-function API, lint/type checks, documentation/site checks and saved performance runs. Final CI results are required before merge.

The final local pass includes 2,206 Rust integration tests (four existing ignores), 939 Python tests (16 optional skips), 20 repeated runs of eight queued-cancellation cases, 681 native JS AVA tests, 172 Node and Bun compatibility tests each, Deno compatibility tests (32 tests / 163 nested steps), queued live Python/TypeScript host-function context and cancellation regressions after rebasing onto current main, TypeScript checks, all-features Clippy with warnings denied, cargo-vet, Python lint and repository documentation/script checks. The workspace-wide just pre-pr test link exhausted this environment's 32 GB disk; scoped tests completed after clearing build caches, and the full CI matrix validates the PR before merge.

The post-fix parallel-execution benchmark also completed in the dev profile. Its timings are not compared with the saved release-profile results.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
bashkit 4af2b7a Commit Preview URL

Branch Preview URL
Oct 16 2026, 09:56 PM

Comment thread crates/bashkit-js/src/streaming.rs Fixed
Comment thread crates/bashkit-js/src/streaming.rs Fixed
@chaliy
chaliy force-pushed the feat/streaming-command-io branch from b880eac to 5f9bf33 Compare October 16, 2026 21:33
@chaliy
chaliy force-pushed the feat/streaming-command-io branch from 5f9bf33 to 4af2b7a Compare October 16, 2026 21:55
@chaliy
chaliy merged commit ca149cc into main Oct 16, 2026
55 checks passed
@chaliy
chaliy deleted the feat/streaming-command-io branch October 16, 2026 22:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants