* feat(release): publish package source, declaration maps, and documentation Every release member publishes its TypeScript source and the declaration maps that resolve into it, and @deepseek-ai/dsh publishes the repository Markdown documentation staged from docs/ at prepack time. The ./src/* export most members already declared now resolves for an installed consumer, and a consumer editor follows a declaration to the implementation. Bundled JavaScript maps stay unpublished; the vendored family and the Desktop runtime file policy are unchanged. Rationale: .agents/notes/implemented/process/2026-10-08-published-source-and-docs.md * feat(release): publish JavaScript maps and compress bundles Every emitted JavaScript map ships beside the bundle it describes, and the workspace build and the client preset compress bundles without mangling identifiers. The documentation payload now mirrors the website publication manifest and is removed again by `postpack`; Desktop and the Python executable exclude the published source, documentation, and maps; `clean` removes a staged copy. Typert's publication contract gains the Remote declaration map. * fix(release): keep node bundles readable for the worker image The browser worker image lowers `yield*` only as a whole statement and rewrites `for await` loops by their source layout, so compressed node-side entries either fail that lowering or lower to unparseable code. Only the browser bundles keep compression, which removes about a third of their text. The bundle-purity assertion now matches the module-loader call regardless of the quote style compression picks, and the release-pack fixture exercises the installed package's new documentation requirement. * feat(release): publish the whole documentation tree `@deepseek-ai/dsh` copies `docs/` in full instead of the website's page set: the maintainer documents, the generated migration records, the translation guidelines, and the `*.i18n.yaml` consistency records ship with the installed package, so the documentation's own links resolve without a checkout. * feat(release): publish the upgrade guides with the documentation The staged documentation keeps the website's page set and adds every `docs/upgrade-guide/` document, which is what an installed reader consults when a released change breaks something. The rest of the maintainer tree stays out. * test(release): expect source and maps in master's new payload cases * feat(release): keep installed docs and source readable in Desktop Desktop keeps the published src/ and docs/ trees inside app.asar, and every model shell call receives DSH_INSTALL_DIR and DSH_NODE_EXECUTABLE so an agent can read them through Electron's Node mode. Staging also generates docs/upgrade-guide/README.md, an index of the upgrade guides grouped by the release they upgrade from. Minified client bundles print string literals with backticks: the worker image packer now recognizes those chunk requests, client license banners are prepended after minification so their text survives, and the affected artifact tests accept either quote. * test(desktop): read archived docs through the Windows PowerShell form Regenerate the config catalog for the shifted shell-env source line. * test(desktop): wait for the GUI-subsystem Electron process from PowerShell * build(release): publish source and maps from the message-quotes package * build(release): publish source and maps from packages new on master * build(release): publish source and maps from the tool-office package
description, kind
| description | kind |
|---|---|
| The local PowerShell executor for deployments and maintainers choosing, configuring, or debugging unconfined PowerShell command execution over the shell seam. | package-reference |
@deepseek-ai/dsh-pwsh-local
English | 中文
Summary
dsh-pwsh-local is the PowerShell executor: every command runs as a fresh, non-interactive pwsh -Command process with no profile files, so no shell state survives between calls. It mirrors dsh-bash-local's semantics call-for-call and adds PowerShell-shaped concerns: executable resolution, UTF-8 output pinning, and the model-friendly terminal environment. Commands run with the harness process's own authority — this executor confines nothing; compose dsh-pwsh-sandbox when commands need the sandbox capability. The model-facing pwsh tool talks to it once it is mounted.
Table of Contents
- Use this package
- Understand the implementation
- Further Exploration
- Model Experience
- Known Limitations and Deferred Work
- Dev Note
Use this package
Mount this executor when a composition needs PowerShell command execution — typically on Windows — without confinement. It registers as ctx.shell, and the model-facing pwsh tool works over it immediately: an agent calls the tool, and the command runs as a fresh pwsh -Command process with the budgets below.
When to choose it
It is the Windows counterpart of dsh-bash-local: choose it where pwsh is the platform shell, so a composition can swap the POSIX rows for the pwsh rows and keep the same semantics. The executor resolves the pwsh executable from an explicit pwshPath, well-known Windows install locations, PATH entries, or Windows PowerShell 5.1 as a last resort. For unconfined execution it is the default; compose dsh-pwsh-sandbox when commands need the sandbox capability.
Minimal configuration
Load the executor with the budgets you want; every field has a default, so the smallest composition is the plugin entry alone. The settings provider (when composed) layers a user section over this entry, so budgets can change at runtime without a reload (see Adjusting budgets at runtime).
- id: bash
name: '@deepseek-ai/dsh-pwsh-local'
config:
cwd: C:\path\to\workspace
timeoutMs: 120000
| Field | Default | Meaning |
|---|---|---|
cwd |
process.cwd() |
Default working directory for commands |
timeoutMs |
120,000 |
Default foreground timeout, in milliseconds |
maxTimeoutMs |
600,000 |
Cap for per-call timeout overrides |
maxOutputBytes |
64,000 |
Per-stream in-memory output cap; overflow spills to a temp file |
maxSpillBytes |
67,108,864 |
Per-stream full-output spill cap |
graceMs |
3,000 |
Grace period for kill escalation and post-exit pipe draining |
pwshPath |
resolved | Explicit pwsh executable; else well-known locations, then PATH |
The generated configuration catalog is the exhaustive source for every accepted field and its JSDoc.
Running commands
Run a command by awaiting the execution's result() projection; a nonzero exit, a timeout, or a cancellation resolves descriptively, and only infrastructure failures reject. The command string rides as one argument to -Command: PowerShell parses the text itself and no intermediate shell exists, so there is no shell-quoting layer to escape and native Win32 paths pass through unchanged. Every command pins UTF-8 output first, so non-ASCII output is not garbled even on the Windows PowerShell 5.1 fallback. The environment is model-friendly: NO_COLOR=1 PAGER=cat GIT_PAGER=cat (no TERM=dumb — a POSIX concept), with explicit caller-provided entries still winning.
const execution = await ctx.shell.execute(ctx.shell.resolve({ command: 'Get-ChildItem' }))
const result = await execution.result()
if (result.timedOut) console.log('timed out after', result.timeoutMs)
Background processes
Resolve with onExpiry: 'none' and await execute to run a command in the background; no deadline is armed. Cancellation or preparation failure rejects before a handle is published. readOutput() merges the stream deltas into one consuming read, marking stderr under a [stderr] section; kill() terminates the provider-managed range; done settles when the direct command closes and never rejects. Job ids, ownership, polling, and notices belong to the generic ctx.jobs runtime, which the tool layer registers the handle with.
Adjusting budgets at runtime
Execution budgets are volatile Config fields sampled when resolving each command. The Plugins page edits the active executor’s profile entry. Complete Config validation rejects invalid numbers and timer limits before a form write reaches disk.
Understand the implementation
Implementation internals — click to expand
This section explains the design of the executor and points at the code that realizes it; the observable behavior is fully covered in Use this package.
Design concept
The executor is the PowerShell Service Provider for the ctx.shell seam built on the subprocess capability: it owns everything pwsh-shaped — executable resolution, command defaulting and caps, deadline fusion and cause classification, UTF-8 output pinning, the model-friendly terminal environment, and the background read merge — while managed-range mechanics (bounded spill-backed output, credential scrub, termination escalation, quiescence, and disposal) belong to the subprocess service. Every call spawns a fresh non-interactive pwsh -Command with -NoLogo -NoProfile -NonInteractive, so commands are deterministic and profile state never leaks between calls.
Source map
| File | Role |
|---|---|
src/index.ts |
Plugin entry: PwshLocalExecutor, Config, settings wiring, argv seam |
src/resolve.ts |
Pure resolvePwshPath/candidatePwshPaths executable resolution |
tests/ |
Exercised behavior: budgets, classification, resolution, background handles |
Main flow
A call runs through three steps: resolve() fills workdir/timeoutMs/stdoutMaxBytes from config (capping the per-call timeoutMs override); the executor builds the pwsh argv — pwsh -NoLogo -NoProfile -NonInteractive -Command <encoding preamble + command> — fuses the config-clamped timeout with the caller's abort signal into one deadline, and spawns through ctx.subprocess with explicit byte caps and the graceMs; the settled outcome is classified and projected into a ShellRunResult. Windows reports forced termination as exit 1 without a signal, so signal-stamped facts are POSIX-only there; the timeout/abort classification is platform-independent.
The foreground deadline starts before argv preparation and retains the same signal and remaining budget through execution. Preparation timeout returns empty output, timedOut: true, and null exitCode and signal; caller cancellation before process publication still rejects. Late preparation success or failure cannot trigger a spawn.
Invariants and ownership
- The
graceMsbudget must be positive, finite, and no greater thanMAX_TIMER_DELAY_MSso Node can represent it with one timer; invalid values are refused where they are written. - Environment layering is fixed: terminal overrides first, then the caller's
env, then the trusteddshEnvsnapshot last; the subprocess service scrubs ambient credentials and inheritedDSH_*names independently. - Executable resolution is a pure function of
(configured, env, platform)and re-probes the filesystem only when the storedpwshPathdiffers from the one the current executable was resolved from. - A background process belongs to the subprocess service: it survives an executor-only reload and is killed and joined when the service disposes.
Further Exploration
Read these pages when the executor contract is not enough. They move from the seam to the confining sibling and the PowerShell tool.
- shell seam — the executor contract this provider implements, including the request/spec split.
- bash-local — the POSIX counterpart this executor mirrors call-for-call.
- pwsh-sandbox — the confining executor to compose instead when commands need the sandbox capability.
- tool-pwsh — the model-facing
pwshtool over this executor. - Bash executor subsystem — request/spec vocabulary, results, and the service contract in full.
Model Experience
Indirectly, through dsh-tool-pwsh, which renders this executor's bounded stdout/stderr tails, background-process deltas (through the generic job runtime), spill-file paths, and infrastructure failures.
KV Cache effect
No direct invalidation; the named consumer owns any request-prefix changes.
Known Limitations and Deferred Work
These limits define when this executor is a poor fit. They are current package constraints, not a roadmap.
- Unconfined by itself — commands run with the harness process's authority; deployments needing confinement compose a sandboxing executor or policy instead.
- No persistent shell or PTY — every call starts a fresh
pwsh -Command. - The command string is PowerShell text — the
-Commanddomain has no shell-quoting layer, but a model-facing command is parsed by PowerShell itself, so PowerShell syntax errors are command failures, not launch failures. - A background provider-failure note is the whole stderr stream —
SubprocessHandle.donecan reject before or after target execution begins, and the subprocess service buffers no output for a target that never reported, so the executor serves the stage-neutralsubprocess failed before reporting an outcome: …as the observed stderr stream (offset readers re-read it at their own offsets) and folds it into exactly onereadOutput()delta; a consuming reader that discards that delta recovers it only throughobserved.stderr. - Windows termination reports no signal — a force-killed process settles as exit 1 with
signal: null, so signal-based status classification does not apply on Windows;kill()-initiated stops still stampkilleddirectly. - The encoding preamble precedes the command — PowerShell requires
param(...),#requires, andusingstatements at the very top of a script, so a command whose first statement is one of those cannot run under the UTF-8 output preamble; wrap aparam(...)script in& { … }, and runusing/#requiresscripts from a file instead. - Non-ASCII stdin under Windows PowerShell 5.1 may be mis-decoded — the preamble pins output encoding only;
[Console]::InputEncodingstays at the host default because setting it under redirected stdin throws; pwsh 7 defaults to UTF-8 and is unaffected.
Dev Note
Working context for maintainers — click to expand
None.