File size: 2,731 Bytes
68d7816 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | import { z } from 'zod';
import { createDecorator, type ServiceIdentifier } from '#/_base/di/instantiation';
import type { Event } from '#/_base/event';
import { isoDateTimeSchema } from '#/_base/utils/isoDateTime';
const relativeCwdSchema = z
.string()
.min(1)
.refine((value) => !isAbsolutePath(value), 'cwd must be relative to the session workspace');
export const terminalStatusSchema = z.enum(['running', 'exited']);
export type TerminalStatus = z.infer<typeof terminalStatusSchema>;
export const terminalSchema = z.object({
id: z.string().min(1),
session_id: z.string().min(1),
cwd: z.string().min(1),
shell: z.string().min(1),
cols: z.number().int().positive(),
rows: z.number().int().positive(),
status: terminalStatusSchema,
created_at: isoDateTimeSchema,
exited_at: isoDateTimeSchema.optional(),
exit_code: z.number().int().nullable().optional(),
});
export type Terminal = z.infer<typeof terminalSchema>;
export const createTerminalRequestSchema = z.object({
runtime_id: z.string().min(1),
cwd: relativeCwdSchema.optional(),
shell: z.string().min(1).optional(),
cols: z.number().int().positive().optional(),
rows: z.number().int().positive().optional(),
});
export type CreateTerminalRequest = z.infer<typeof createTerminalRequestSchema>;
export interface TerminalOutputMessage {
type: 'terminal_output';
seq: number;
session_id: string;
terminal_id: string;
timestamp: string;
payload: { data: string };
}
export interface TerminalExitMessage {
type: 'terminal_exit';
session_id: string;
terminal_id: string;
timestamp: string;
payload: { exit_code?: number | null | undefined };
}
export type TerminalFrame = TerminalOutputMessage | TerminalExitMessage;
function isAbsolutePath(value: string): boolean {
return (
value.startsWith('/') ||
value.startsWith('\\') ||
/^[A-Za-z]:[\\/]/.test(value)
);
}
export interface TerminalAttachSink {
readonly id: string;
send(frame: TerminalFrame): void;
}
export interface TerminalAttachOptions {
readonly sinceSeq?: number;
}
export interface TerminalSpawnOptions {
readonly cwd: string;
readonly shell: string;
readonly cols: number;
readonly rows: number;
}
export interface TerminalProcess {
readonly onProcessData: Event<string>;
readonly onProcessExit: Event<{ exitCode: number | null }>;
write(data: string): void;
resize(cols: number, rows: number): void;
kill(): void;
}
export interface IHostTerminalService {
readonly _serviceBrand: undefined;
spawn(options: TerminalSpawnOptions): Promise<TerminalProcess>;
}
export const IHostTerminalService: ServiceIdentifier<IHostTerminalService> =
createDecorator<IHostTerminalService>('hostTerminalService');
|