Repository navigation
v0.5.0
Improvements
- A foreground timeout keeps the command running as a background job. A foreground call that outlives
timeoutMsis promoted instead of killed, the result says so, and reading it names any dropped output together with its spill file — dsh's own shell tools behave the same way. A newpromoteOnTimeoutknob (default on, deliberately not on the Plugins page) turns promotion off; withenableRunInBackground: false, a timeout still kills the command exactly as before. Act only if you relied on timeouts killing the command. - Foreground commands are registered as jobs from the start. They appear in the task tools and can be killed with
job_kill; a kill from outside the call is reported as[stopped: …]rather than as a command failure, so the model does not retry it. - The sandbox result now says what actually happened. A run that was not wrapped by a sandbox reports
sandbox.unconfined: true; when the reason is a failedsandbox: autoprobe, every result carries a short note and the first one per row load carries the full explanation with both ways out. An explicitsandbox: nonecarries the fact without any note, anddanger-full-accesscarries neither. deniedis optional and never guessed. It istruewhen a failed confined run's output matches the backend's denial dialect,falseon success, and absent when nothing matched — with a note pointing at stderr instead of a claim nobody observed. Each row also learns its host's dialect once, from a deliberately-denied harmless write outside the workspace root (the backend's own signatures are kept as the union).- The Windows sandbox boundary is documented from measurement. The ACL backend's grant follows the object rather than the path, reads stay unconfined, and dsh rates that backend
partial. On a host whose runner cannot start MSYS bash at all,git_bashreports on every call that it ran unconfined;requireSandbox: trueis the fail-closed alternative there (it refuses every command rather than degrading). run_in_backgroundfollows the configuration, not the job registry. With no job registry composed the parameter is still advertised and the call fails loudly naming the missing packages; previously the parameter silently disappeared from the schema depending on load order.
Bug fixes
- A failed confinement runner no longer silently re-runs unconfined. It fails closed with actionable text (retry with
sandbox_permissions: "danger-full-access", or setsandbox: noneon the row). Act only if you relied on the silent fallback. wsl_bash's bubblewrap profile is complete and its probe is functional. The profile now carries--unshare-pidand the wrapper keeps the separatorbwrap <profile> -- <command>, so a confined command can no longer reach the host through procfs magic links; the probe builds the same profile and actually runs a command instead of checking thatbwrapexists.git_bash's confinement probe no longer blocks the event loop (it spawns asynchronously; default probe bound 4 s, WSL 8 s).wsl_bashaccepts MSYS-style paths andwslDistroconsistently, and passes the working directory explicitly so the starting directory is a property of the call rather than of the distro.- Promoted results are loss-aware: a promotion that dropped output names the dropped bytes and the spill file, matching the foreground path.
- The Plugins page saves are fenced: platform-driven saves no longer race the staged draft, style installation is guarded,
resetFieldvalidates its key, and the row key has a single source.
Compatibility
install.ps1anduninstall.ps1are gone; the tarball is 20 files (22 before). Install or upgrade withdsh plugin --profile web add dsh-win-multi-bash, or add the patch rows by hand — a profile that still lists afilesentry for the removed scripts is unaffected, since nothing loads them.sandbox.deniedmay be absent (see above). A consumer that reads it must tolerate a missing field;sandbox.unconfinedis new and equally optional.- Confinement runner failures now fail closed instead of degrading to an unwrapped run.
- The tool parameter table matches dsh's ordering (
descriptionbeforecommand) and results carrycwdplus presentation metadata; both are additive for existing callers. - YAML written by hand keeps working; the knobs that were already volatile are unchanged, and
promoteOnTimeoutis read from the row's config only.
Verification
Verified on dsh 0.2.1-alpha.2 and 0.2.0-rc.2.
smoke/audit.test.mjs: 293 checks, 0 failures (224 at v0.4.0), run three times per gate on rc.2 plus once against an isolated alpha.2 device; it covers the prompt dedup matrix, the volatile config projection, the browser half's form contract, the job/promotion behaviour, the sandbox facts and their once-per-load wording, and the repository records (bilingual hashes, LF, packagefiles).npm test(smoke/run.ps1): exit 0 —git_bash,wsl_bashand the base bundle's ownpwshseat each ran a real command in both the schema-default and pinned-bashPathvariants.npm pack --dry-run: 20 files, noinstall.ps1/uninstall.ps1.- Real-machine sandbox measurements behind the notes above: a WSL/bubblewrap write outside the workspace is refused and the row learns that host's refusal text (~0.3 s, once per row load); on the Windows host tested, the ACL runner cannot start MSYS bash, so
git_bashreportsunconfinedper call.