import type { Readable, Writable } from 'node:stream'; import { registerErrorDomain, type ErrorDomain } from '#/_base/errors/codes'; import { Error2, type Error2Options } from '#/_base/errors/errors'; import { createDecorator, type ServiceIdentifier } from '#/_base/di/instantiation'; export interface HostProcessOptions { readonly cwd?: string; readonly env?: Record; readonly shell?: boolean | string; readonly detached?: boolean; readonly windowsHide?: boolean; readonly mergeStderr?: boolean; readonly timeout?: number; } export interface IHostProcess { readonly _serviceBrand: undefined; readonly pid: number; readonly exitCode: number | null; readonly stdin: Writable; readonly stdout: Readable; readonly stderr: Readable; wait(): Promise; kill(signal?: NodeJS.Signals): Promise; dispose(): void | Promise; } export interface IHostProcessService { readonly _serviceBrand: undefined; spawn( command: string, args?: readonly string[], options?: HostProcessOptions, ): Promise; } export const IHostProcessService: ServiceIdentifier = createDecorator('hostProcessService'); export const OsProcessErrors = { codes: { OS_PROCESS_SPAWN_FAILED: 'os.process.spawn_failed', OS_PROCESS_KILL_FAILED: 'os.process.kill_failed', SHELL_GIT_BASH_NOT_FOUND: 'shell.git_bash_not_found', }, info: { 'os.process.spawn_failed': { title: 'Failed to spawn process', retryable: false, public: true, action: 'Check that the command exists and is executable.', }, 'os.process.kill_failed': { title: 'Failed to kill process', retryable: false, public: true, }, 'shell.git_bash_not_found': { title: 'Git Bash not found', retryable: false, public: true, action: 'Install Git for Windows so shell commands can run under Git Bash.', }, }, } as const satisfies ErrorDomain; registerErrorDomain(OsProcessErrors); export const HostProcessErrorCode = { SpawnFailed: OsProcessErrors.codes.OS_PROCESS_SPAWN_FAILED, KillFailed: OsProcessErrors.codes.OS_PROCESS_KILL_FAILED, ShellGitBashNotFound: OsProcessErrors.codes.SHELL_GIT_BASH_NOT_FOUND, } as const; export type HostProcessErrorCode = (typeof HostProcessErrorCode)[keyof typeof HostProcessErrorCode]; export class HostProcessError extends Error2 { constructor(code: HostProcessErrorCode, message: string, options?: Error2Options) { super(code, message, options); this.name = 'HostProcessError'; } }