Generator (all four structural gaps):
- harness service pages now render public properties/accessors, not just
methods (ctx.codeRuntime.language/isolation were missing);
- the class page merges the same-named interface half, so ctx.root/baseUrl/
events/logger/reflect/registry appear on Context (vendor root JSDoc gains
prose alongside @experimental);
- Pick<…> heritage on a Context merge resolves to the picked class members,
giving ctx.effect a documented signature on the Fiber page;
- {@link} tags normalize to code spans; merge sections get their own h2 so
reflect members no longer nest under 'Static members'.
verify-website-yaml: reject the unloadable 'group:' pseudo-name (tree.import
only special-cases 'cordis:'; no builtin is registered here) and recurse into
@cordisjs/plugin-group nested entry lists instead.
Prose corrected against loader/cordis source: service.md isolation example
uses the real group plugin + group: true + the required isolate map;
config.md documents concurrent entry startup (Promise.all; order via inject)
and the real hmr defaults (root ['.'], base/ignored/debounce); events.md
fixes emit (synchronous, not parallel), bail (null/false also delegate), and
serial (stops at the first bail value).
6.1 KiB
ctx.bash
BashExecutor (abstract seam) — provided by @deepseek-ai/dsh-bash.
Abstract bash execution service. Subclass, implement the abstract methods, and load the subclass as a plugin — it registers as ctx.bash (one implementation per context; loading a second throws, which is cordis' standard duplicate-service behavior).
Semantics every implementation must honor:
- run REJECTS only for infrastructure failures (unusable workdir, missing shell, pre-aborted signal). Nonzero exits, timeout kills, and abort kills RESOLVE with a descriptive BashRunResult — reporting a failed command is the tool layer's job, not an exception.
- start returns immediately; no timeout applies to background tasks (callers stop them via kill or the spec's AbortSignal). Completion must fire the onTaskDone listeners exactly once per task, and must NOT fire after the service is disposed.
- readOutput is incremental: consecutive reads never re-deliver output. Implementations bound their buffers; reads that lost data flag
lossyand point at full-stream spill files when available. - Disposal kills every running task and awaits their exit (no orphan processes survive
fiber.dispose()).
ctx.bash.resolve(request)
abstract resolve(request: BashExecRequest): BashExecSpec
Resolve a caller's BashExecRequest into a fully-specified BashExecSpec, applying this implementation's config defaults and caps (working directory, default/max timeout). Consumers (tool layer) call this, then pass the result to run/start — keeping defaulting in the implementation that owns the config while the seam type stays explicit (no hidden ?? default inside run/start).
request— the caller's request; omitted fields get this implementation's defaults, capped fields are clamped.
Returns the fully-specified spec to hand to run/start.
ctx.bash.run(spec)
abstract run(spec: BashExecSpec): Promise<BashRunResult>
Run a command in the foreground; resolves when it finishes.
spec— a resolved spec fromresolve, never a raw request.
Returns the outcome; nonzero exits, timeout kills, and abort kills resolve with a descriptive result rather than reject.
ctx.bash.start(spec)
abstract start(spec: BashExecSpec): BashTask
Start a background task and return its handle immediately.
spec— a resolved spec fromresolve, never a raw request.
Returns the live task handle; completion fires onTaskDone.
ctx.bash.get(id)
abstract get(id: BashTaskId): BashTask | undefined
Look up a background task by id.
id— the task id to look up.
Returns the tracked task, or undefined for an id this executor never issued.
ctx.bash.ownerOf(id)
abstract ownerOf(id: BashTaskId): OwnerToken | undefined
The opaque OWNER token recorded for a background task at start (from the BashExecSpec's owner), or undefined for an unknown id OR a known-but-ownerless task. The executor stores and returns the token verbatim — it never interprets it; the access POLICY (who may read/kill a task) lives in the consumer (@deepseek-ai/dsh-tool-bash), which compares ownerOf(id) to the caller's token. Collapsing unknown-id and known-but-unowned into the same undefined is fine: the consumer's access gate treats undefined as "open", and a genuinely unknown id then fails loudly at the subsequent readOutput/kill ("unknown task"). Storing ownership in the executor (disposed with ITS fiber) — not in the tool plugin — is what makes ownership survive a tool-bash HMR reload.
id— the background task id to look up ownership for.
Returns the token recorded at start, verbatim; undefined for an unknown id or a known-but-ownerless task.
ctx.bash.list()
abstract list(): BashTask[]
All tracked background tasks (insertion order).
Returns every task this executor started, running or finished.
ctx.bash.readOutput(id)
abstract readOutput(id: BashTaskId): BashTaskRead
Read output produced since the previous read. Throws for unknown ids.
id— the task to read from.
Returns the incremental read; consecutive reads never re-deliver output.
ctx.bash.kill(id)
abstract kill(id: BashTaskId): boolean
Kill a running background task. Returns false when it had already finished (no-op). Throws for unknown ids.
id— the task to kill.
Returns true when this call killed it, false when it had already finished.
ctx.bash.onTaskDone(listener)
onTaskDone(listener: BashTaskListener): () => void
Register a background-task completion listener (disposed with the calling fiber). Listeners never fire after this service is disposed.
listener— called exactly once per task completion.
Returns the disposer that unregisters the listener.