# Conflicts: # .agents/notes/implemented/feature/2026-08-08-web-background-job-display.i18n.yaml # docs/config-catalog.i18n.yaml # docs/event-producer-consumer.i18n.yaml # docs/module-graph.i18n.yaml # docs/subsystems/jobs.i18n.yaml # docs/subsystems/shell.i18n.yaml # docs/subsystems/shell.md # docs/subsystems/shell.zh.md # docs/tool-catalog.i18n.yaml # packages/api/session-controller/README.i18n.yaml # packages/context/tmux-context/tests/tmux-context.spec.ts # packages/e2b/e2b/tests/fixtures/composition/bin.ts # packages/extensions/tool-cordis/src/api-catalog.ts # packages/shell/bash-local/README.i18n.yaml # packages/shell/bash-local/README.md # packages/shell/bash-local/README.zh.md # packages/shell/bash-local/src/index.ts # packages/shell/bash-local/tests/executor.spec.ts # packages/shell/bash-sandbox/README.i18n.yaml # packages/shell/bash-sandbox/README.md # packages/shell/bash-sandbox/README.zh.md # packages/shell/bash-sandbox/src/index.ts # packages/shell/bash-sandbox/tests/bwrap.e2e.ts # packages/shell/bash-sandbox/tests/landlock.e2e.ts # packages/shell/bash-sandbox/tests/partial-landlock.spec.ts # packages/shell/bash-sandbox/tests/sandbox.spec.ts # packages/shell/bash-sandbox/tests/seatbelt.e2e.ts # packages/shell/pwsh-local/README.i18n.yaml # packages/shell/pwsh-local/README.md # packages/shell/pwsh-local/README.zh.md # packages/shell/pwsh-local/src/index.ts # packages/shell/pwsh-local/tests/executor.spec.ts # packages/shell/pwsh-sandbox/README.i18n.yaml # packages/shell/pwsh-sandbox/README.md # packages/shell/pwsh-sandbox/README.zh.md # packages/shell/pwsh-sandbox/src/index.ts # packages/shell/pwsh-sandbox/tests/sandbox.spec.ts # packages/shell/shell/README.i18n.yaml # packages/shell/shell/README.md # packages/shell/shell/README.zh.md # packages/shell/shell/src/index.ts # packages/shell/shell/tests/service.spec.ts # packages/shell/tool-bash/README.i18n.yaml # packages/shell/tool-bash/src/index.ts # packages/shell/tool-bash/tests/tools.spec.ts # packages/shell/tool-pwsh/README.i18n.yaml # packages/shell/tool-pwsh/src/index.ts # packages/shell/tool-pwsh/tests/background.spec.ts # packages/shell/tool-pwsh/tests/tools.spec.ts # packages/workflow/tool-workflow/README.i18n.yaml # packages/workflow/tool-workflow/package.json # packages/workflow/tool-workflow/tests/tool-workflow.spec.ts # snapshots/session/ptc-python-turn/system-prompt.expected.md
110 lines
4.5 KiB
TypeScript
110 lines
4.5 KiB
TypeScript
/**
|
|
* Service Definition for the `ctx.shell` capability seam, covering foreground commands and background process
|
|
* handles. Job ids, ownership, polling, and notices belong to
|
|
* `@deepseek-ai/dsh-jobs`, keeping executors independent of sessions.
|
|
* @module @deepseek-ai/dsh-shell
|
|
*/
|
|
|
|
import { Context, Service } from '@deepseek-ai/cordis'
|
|
import type { SandboxMode } from '@deepseek-ai/dsh-sandbox'
|
|
import type { ShellExecRequest, ShellExecSpec, ShellExecution } from './types.ts'
|
|
|
|
/**
|
|
* Settings namespace of this capability, owned here rather than by either
|
|
* executor family because it names the capability, not an implementation: a
|
|
* host composes exactly one provider of `ctx.shell` (the win32 layer swaps the
|
|
* POSIX rows for the pwsh ones, and mounting both fails loud on a duplicate
|
|
* service registration), so the providers share one namespace without ever
|
|
* registering it twice, and a settings document carried between platforms
|
|
* keeps resolving on both.
|
|
*/
|
|
export const SHELL_SETTINGS_NAMESPACE = 'shell'
|
|
|
|
export { DSH_ENV_PREFIX } from './types.ts'
|
|
export type {
|
|
ShellExecRequest,
|
|
ShellExecSpec,
|
|
ShellExecution,
|
|
ShellExpiryPolicy,
|
|
ShellProcess,
|
|
ShellProcessRead,
|
|
ShellProcessStatus,
|
|
ShellPromotionOffer,
|
|
ShellRunResult,
|
|
ShellSandboxInfo,
|
|
CollectedOutput,
|
|
DshEnvironment,
|
|
DshEnvironmentKey,
|
|
} from './types.ts'
|
|
export { parseExitStatus } from './render.ts'
|
|
export type { ParsedExitStatus } from './render.ts'
|
|
|
|
declare module '@deepseek-ai/cordis' {
|
|
interface Context {
|
|
shell: ShellExecutor
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Abstract bash execution service. Subclass, implement the abstract methods,
|
|
* and load the subclass as a plugin — it registers as `ctx.shell` (one
|
|
* implementation per context; loading a second throws, which is cordis'
|
|
* standard duplicate-service behavior).
|
|
*
|
|
* {@link execute} resolves with the process handle after preparation. "Foreground" is a property of what the caller awaits, not
|
|
* of the spawn — a caller that awaits {@link ShellExecution.result} ran the
|
|
* command in the foreground; one that keeps the handle ran it in the
|
|
* background; one that awaits {@link ShellExecution.promotion} under
|
|
* `onExpiry: 'offer'` decides at the deadline.
|
|
*
|
|
* Implementations must honor these semantics:
|
|
* - {@link ShellExecution.result} rejects only for infrastructure failures.
|
|
* Nonzero exits, timeout kills, and abort kills resolve with a descriptive
|
|
* result: first-cause `timedOut`/`aborted`, the spec's `timeoutMs` echoed.
|
|
* - The handle is published after preparation. `done` settles at process close
|
|
* and never rejects; spawn failures settle as `killed` with the error on the read
|
|
* path, while `result()` carries the same failure as its rejection.
|
|
* - `onExpiry: 'none'` arms no deadline; `'kill'` kills at expiry; `'offer'`
|
|
* resolves {@link ShellExecution.promotion} instead of killing, and an
|
|
* unanswered offer is treated as declined. Expiry during preparation
|
|
* returns a settled timed-out handle without output or a promotion offer.
|
|
* - {@link ShellProcess.readOutput} is incremental: consecutive reads never
|
|
* repeat output. Lossy reads report truncation and available spill files.
|
|
* - A still-running process is stopped and awaited when its owning
|
|
* composition tears down. With the subprocess seam that boundary is
|
|
* `ctx.subprocess` disposal, so a process survives an executor-only reload.
|
|
*/
|
|
export abstract class ShellExecutor extends Service {
|
|
constructor(ctx: Context) {
|
|
super(ctx, 'shell')
|
|
}
|
|
|
|
/**
|
|
* The sandbox mode this executor applies by default, or `undefined` when it
|
|
* does not sandbox commands.
|
|
* @returns the configured default sandbox mode, when supported.
|
|
*/
|
|
get sandboxMode(): SandboxMode | undefined {
|
|
return undefined
|
|
}
|
|
|
|
/**
|
|
* Apply implementation-owned defaults and caps to a request before execution.
|
|
* @param request - the caller's request; omitted fields get this
|
|
* implementation's defaults, capped fields are clamped.
|
|
* @returns the fully-specified spec to hand to {@link execute}.
|
|
*/
|
|
abstract resolve(request: ShellExecRequest): ShellExecSpec
|
|
|
|
/**
|
|
* Prepare and spawn the command under its resolved deadline.
|
|
* @param spec - a resolved spec from {@link resolve}, never a raw request.
|
|
* @returns the prepared handle, including its result and promotion signal;
|
|
* preparation timeout yields an already-settled handle with no output.
|
|
* @throws on preparation failure or caller cancellation before process publication.
|
|
*/
|
|
abstract execute(spec: ShellExecSpec): Promise<ShellExecution>
|
|
}
|
|
|
|
export default ShellExecutor
|