index.ts 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. /**
  2. * Service Definition for the `ctx.shell` capability seam, covering foreground commands and background process
  3. * handles. Job ids, ownership, polling, and notices belong to
  4. * `@deepseek-ai/dsh-jobs`, keeping executors independent of sessions.
  5. * @module @deepseek-ai/dsh-shell
  6. */
  7. import { Context, Service } from '@deepseek-ai/cordis'
  8. import type { SandboxMode } from '@deepseek-ai/dsh-sandbox'
  9. import type { ShellExecRequest, ShellExecSpec, ShellProcess, ShellRunResult } from './types.ts'
  10. /**
  11. * Settings namespace of this capability, owned here rather than by either
  12. * executor family because it names the capability, not an implementation: a
  13. * host composes exactly one provider of `ctx.shell` (the win32 layer swaps the
  14. * POSIX rows for the pwsh ones, and mounting both fails loud on a duplicate
  15. * service registration), so the providers share one namespace without ever
  16. * registering it twice, and a settings document carried between platforms
  17. * keeps resolving on both.
  18. */
  19. export const SHELL_SETTINGS_NAMESPACE = 'shell'
  20. export { DSH_ENV_PREFIX } from './types.ts'
  21. export type {
  22. ShellExecRequest,
  23. ShellExecSpec,
  24. ShellProcess,
  25. ShellProcessRead,
  26. ShellProcessStatus,
  27. ShellRunResult,
  28. ShellSandboxInfo,
  29. CollectedOutput,
  30. DshEnvironment,
  31. DshEnvironmentKey,
  32. } from './types.ts'
  33. export { parseExitStatus } from './render.ts'
  34. export type { ParsedExitStatus } from './render.ts'
  35. declare module '@deepseek-ai/cordis' {
  36. interface Context {
  37. shell: ShellExecutor
  38. }
  39. }
  40. /**
  41. * Abstract bash execution service. Subclass, implement the abstract methods,
  42. * and load the subclass as a plugin — it registers as `ctx.shell` (one
  43. * implementation per context; loading a second throws, which is cordis'
  44. * standard duplicate-service behavior).
  45. *
  46. * Implementations must honor these semantics:
  47. * - {@link run} rejects only for infrastructure failures. Nonzero exits,
  48. * timeout kills, and abort kills resolve with a {@link ShellRunResult}.
  49. * - {@link start} returns immediately; no timeout applies to background
  50. * processes. `done` settles at process close and never rejects; spawn
  51. * failures settle as `killed` with the error on stderr.
  52. * - {@link ShellProcess.readOutput} is incremental: consecutive reads never
  53. * repeat output. Lossy reads report truncation and available spill files.
  54. * - A still-running background process is stopped and awaited when its
  55. * owning composition tears down. With the subprocess seam that
  56. * boundary is `ctx.subprocess` disposal, so a background process survives
  57. * an executor-only reload.
  58. */
  59. export abstract class ShellExecutor extends Service {
  60. constructor(ctx: Context) {
  61. super(ctx, 'shell')
  62. }
  63. /**
  64. * The sandbox mode this executor applies by default, or `undefined` when it
  65. * does not sandbox commands.
  66. * @returns the configured default sandbox mode, when supported.
  67. */
  68. get sandboxMode(): SandboxMode | undefined {
  69. return undefined
  70. }
  71. /**
  72. * Apply implementation-owned defaults and caps to a request before execution.
  73. * @param request - the caller's request; omitted fields get this
  74. * implementation's defaults, capped fields are clamped.
  75. * @returns the fully-specified spec to hand to {@link run}/{@link start}.
  76. */
  77. abstract resolve(request: ShellExecRequest): ShellExecSpec
  78. /**
  79. * Run a command in the foreground; resolves when it finishes.
  80. * @param spec - a resolved spec from {@link resolve}, never a raw request.
  81. * @returns the outcome; nonzero exits, timeout kills, and abort kills
  82. * resolve with a descriptive result rather than reject.
  83. */
  84. abstract run(spec: ShellExecSpec): Promise<ShellRunResult>
  85. /**
  86. * Start a background process and return its handle immediately.
  87. * @param spec - a resolved spec from {@link resolve}, never a raw request.
  88. * @returns the live process handle (reads, kill, quiescence promise).
  89. */
  90. abstract start(spec: ShellExecSpec): ShellProcess
  91. }
  92. export default ShellExecutor