1import * as Context from "effect/Context";
2import * as Effect from "effect/Effect";
3import * as Schema from "effect/Schema";
4
5export class PtySpawnError extends Schema.TaggedError<PtySpawnError>()("PtySpawnError", {
6 adapter: Schema.String,
7 shell: Schema.optional(Schema.String),
8 attemptedShells: Schema.optional(Schema.Array(Schema.String)),
9 cause: Schema.optional(Schema.Defect()),
10}) {
11 override get message(): string {
12 const shell = this.shell === undefined ? "" : ` '${this.shell}'`;
13 const attemptedShells =
14 this.attemptedShells === undefined || this.attemptedShells.length === 0
15 ? ""
16 : ` Tried shells: ${this.attemptedShells.join(", ")}.`;
17 return `Failed to spawn PTY process${shell} with ${this.adapter}.${attemptedShells}`;
18 }
19}
20
21export interface PtyExitEvent {
22 exitCode: number;
23 signal: number | null;
24}
25
26export interface PtyProcess {
27 readonly pid: number;
28 write(data: string): void;
29 resize(cols: number, rows: number): void;
30 kill(signal?: string): void;
31 onData(callback: (data: string) => void): () => void;
32 onExit(callback: (event: PtyExitEvent) => void): () => void;
33}
34
35export interface PtySpawnInput {
36 shell: string;
37 args?: string[];
38 cwd: string;
39 cols: number;
40 rows: number;
41 env: NodeJS.ProcessEnv;
42}
43
44export class PtyAdapter extends Context.Service<
45 PtyAdapter,
46 {
47 readonly spawn: (input: PtySpawnInput) => Effect.Effect<PtyProcess, PtySpawnError>;
48 }
49>()("@t3tools/shared/PtyAdapter") {}