import { Schema } from "effect";import { TrimmedNonEmptyString } from "./baseSchemas"; export const DEFAULT_TERMINAL_ID = "default"; const TrimmedNonEmptyStringSchema = TrimmedNonEmptyString;const TerminalColsSchema = Schema.Int.check(Schema.isGreaterThanOrEqualTo(20)).check( Schema.isLessThanOrEqualTo(400),);const TerminalRowsSchema = Schema.Int.check(Schema.isGreaterThanOrEqualTo(5)).check( Schema.isLessThanOrEqualTo(200),);const TerminalIdSchema = TrimmedNonEmptyStringSchema.check(Schema.isMaxLength(128));const TerminalEnvKeySchema = Schema.String.check( Schema.isPattern(/^[A-Za-z_][A-Za-z0-9_]*$/),).check(Schema.isMaxLength(128));const TerminalEnvValueSchema = Schema.String.check(Schema.isMaxLength(8_192));const TerminalEnvSchema = Schema.Record(TerminalEnvKeySchema, TerminalEnvValueSchema).check( Schema.isMaxProperties(128),); const TerminalIdWithDefaultSchema = TerminalIdSchema.pipe( Schema.withDecodingDefault(() => DEFAULT_TERMINAL_ID),); export const TerminalThreadInput = Schema.Struct({ threadId: TrimmedNonEmptyStringSchema,});export type TerminalThreadInput = typeof TerminalThreadInput.Type; const TerminalSessionInput = Schema.Struct({ ...TerminalThreadInput.fields, terminalId: TerminalIdWithDefaultSchema,});export type TerminalSessionInput = Schema.Codec.Encoded<typeof TerminalSessionInput>; export const TerminalOpenInput = Schema.Struct({ ...TerminalSessionInput.fields, cwd: TrimmedNonEmptyStringSchema, cols: Schema.optional(TerminalColsSchema), rows: Schema.optional(TerminalRowsSchema), env: Schema.optional(TerminalEnvSchema),});export type TerminalOpenInput = Schema.Codec.Encoded<typeof TerminalOpenInput>; export const TerminalWriteInput = Schema.Struct({ ...TerminalSessionInput.fields, data: Schema.String.check(Schema.isNonEmpty()).check(Schema.isMaxLength(65_536)),});export type TerminalWriteInput = Schema.Codec.Encoded<typeof TerminalWriteInput>; export const TerminalResizeInput = Schema.Struct({ ...TerminalSessionInput.fields, cols: TerminalColsSchema, rows: TerminalRowsSchema,});export type TerminalResizeInput = Schema.Codec.Encoded<typeof TerminalResizeInput>; export const TerminalClearInput = TerminalSessionInput;export type TerminalClearInput = Schema.Codec.Encoded<typeof TerminalClearInput>; export const TerminalRestartInput = Schema.Struct({ ...TerminalSessionInput.fields, cwd: TrimmedNonEmptyStringSchema, cols: TerminalColsSchema, rows: TerminalRowsSchema, env: Schema.optional(TerminalEnvSchema),});export type TerminalRestartInput = Schema.Codec.Encoded<typeof TerminalRestartInput>; export const TerminalCloseInput = Schema.Struct({ ...TerminalThreadInput.fields, terminalId: Schema.optional(TerminalIdSchema), deleteHistory: Schema.optional(Schema.Boolean),});export type TerminalCloseInput = typeof TerminalCloseInput.Type; export const TerminalSessionStatus = Schema.Literals(["starting", "running", "exited", "error"]);export type TerminalSessionStatus = typeof TerminalSessionStatus.Type; export const TerminalSessionSnapshot = Schema.Struct({ threadId: Schema.String.check(Schema.isNonEmpty()), terminalId: Schema.String.check(Schema.isNonEmpty()), cwd: Schema.String.check(Schema.isNonEmpty()), status: TerminalSessionStatus, pid: Schema.NullOr(Schema.Int.check(Schema.isGreaterThan(0))), history: Schema.String, exitCode: Schema.NullOr(Schema.Int), exitSignal: Schema.NullOr(Schema.Int), updatedAt: Schema.String,});export type TerminalSessionSnapshot = typeof TerminalSessionSnapshot.Type; const TerminalEventBaseSchema = Schema.Struct({ threadId: Schema.String.check(Schema.isNonEmpty()), terminalId: Schema.String.check(Schema.isNonEmpty()), createdAt: Schema.String,}); const TerminalStartedEvent = Schema.Struct({ ...TerminalEventBaseSchema.fields, type: Schema.Literal("started"), snapshot: TerminalSessionSnapshot,}); const TerminalOutputEvent = Schema.Struct({ ...TerminalEventBaseSchema.fields, type: Schema.Literal("output"), data: Schema.String,}); const TerminalExitedEvent = Schema.Struct({ ...TerminalEventBaseSchema.fields, type: Schema.Literal("exited"), exitCode: Schema.NullOr(Schema.Int), exitSignal: Schema.NullOr(Schema.Int),}); const TerminalErrorEvent = Schema.Struct({ ...TerminalEventBaseSchema.fields, type: Schema.Literal("error"), message: Schema.String.check(Schema.isNonEmpty()),}); const TerminalClearedEvent = Schema.Struct({ ...TerminalEventBaseSchema.fields, type: Schema.Literal("cleared"),}); const TerminalRestartedEvent = Schema.Struct({ ...TerminalEventBaseSchema.fields, type: Schema.Literal("restarted"), snapshot: TerminalSessionSnapshot,}); const TerminalActivityEvent = Schema.Struct({ ...TerminalEventBaseSchema.fields, type: Schema.Literal("activity"), hasRunningSubprocess: Schema.Boolean,}); export const TerminalEvent = Schema.Union([ TerminalStartedEvent, TerminalOutputEvent, TerminalExitedEvent, TerminalErrorEvent, TerminalClearedEvent, TerminalRestartedEvent, TerminalActivityEvent,]);export type TerminalEvent = typeof TerminalEvent.Type; export class TerminalCwdError extends Schema.TaggedErrorClass<TerminalCwdError>()( "TerminalCwdError", { cwd: Schema.String, reason: Schema.Literals(["notFound", "notDirectory", "statFailed"]), cause: Schema.optional(Schema.Defect), },) { override get message() { if (this.reason === "notDirectory") { return `Terminal cwd is not a directory: ${this.cwd}`; } if (this.reason === "notFound") { return `Terminal cwd does not exist: ${this.cwd}`; } const causeMessage = this.cause && typeof this.cause === "object" && "message" in this.cause ? this.cause.message : undefined; return causeMessage ? `Failed to access terminal cwd: ${this.cwd} (${causeMessage})` : `Failed to access terminal cwd: ${this.cwd}`; }} export class TerminalHistoryError extends Schema.TaggedErrorClass<TerminalHistoryError>()( "TerminalHistoryError", { operation: Schema.Literals(["read", "truncate", "migrate"]), threadId: Schema.String, terminalId: Schema.String, cause: Schema.optional(Schema.Defect), },) { override get message() { return `Failed to ${this.operation} terminal history for thread: ${this.threadId}, terminal: ${this.terminalId}`; }} export class TerminalSessionLookupError extends Schema.TaggedErrorClass<TerminalSessionLookupError>()( "TerminalSessionLookupError", { threadId: Schema.String, terminalId: Schema.String, },) { override get message() { return `Unknown terminal thread: ${this.threadId}, terminal: ${this.terminalId}`; }} export class TerminalNotRunningError extends Schema.TaggedErrorClass<TerminalNotRunningError>()( "TerminalNotRunningError", { threadId: Schema.String, terminalId: Schema.String, },) { override get message() { return `Terminal is not running for thread: ${this.threadId}, terminal: ${this.terminalId}`; }} export const TerminalError = Schema.Union([ TerminalCwdError, TerminalHistoryError, TerminalSessionLookupError, TerminalNotRunningError,]);export type TerminalError = typeof TerminalError.Type;
import { Schema } from "effect";
import { TrimmedNonEmptyString } from "./baseSchemas";
export const DEFAULT_TERMINAL_ID = "default";
const TrimmedNonEmptyStringSchema = TrimmedNonEmptyString;
const TerminalColsSchema = Schema.Int.check(Schema.isGreaterThanOrEqualTo(20)).check(
Schema.isLessThanOrEqualTo(400),
);
const TerminalRowsSchema = Schema.Int.check(Schema.isGreaterThanOrEqualTo(5)).check(
Schema.isLessThanOrEqualTo(200),
);
const TerminalIdSchema = TrimmedNonEmptyStringSchema.check(Schema.isMaxLength(128));
const TerminalEnvKeySchema = Schema.String.check(
Schema.isPattern(/^[A-Za-z_][A-Za-z0-9_]*$/),
).check(Schema.isMaxLength(128));
const TerminalEnvValueSchema = Schema.String.check(Schema.isMaxLength(8_192));
const TerminalEnvSchema = Schema.Record(TerminalEnvKeySchema, TerminalEnvValueSchema).check(
Schema.isMaxProperties(128),
);
const TerminalIdWithDefaultSchema = TerminalIdSchema.pipe(
Schema.withDecodingDefault(() => DEFAULT_TERMINAL_ID),
);
export const TerminalThreadInput = Schema.Struct({
threadId: TrimmedNonEmptyStringSchema,
});
export type TerminalThreadInput = typeof TerminalThreadInput.Type;
const TerminalSessionInput = Schema.Struct({
...TerminalThreadInput.fields,
terminalId: TerminalIdWithDefaultSchema,
});
export type TerminalSessionInput = Schema.Codec.Encoded<typeof TerminalSessionInput>;
export const TerminalOpenInput = Schema.Struct({
...TerminalSessionInput.fields,
cwd: TrimmedNonEmptyStringSchema,
cols: Schema.optional(TerminalColsSchema),
rows: Schema.optional(TerminalRowsSchema),
env: Schema.optional(TerminalEnvSchema),
});
export type TerminalOpenInput = Schema.Codec.Encoded<typeof TerminalOpenInput>;
export const TerminalWriteInput = Schema.Struct({
...TerminalSessionInput.fields,
data: Schema.String.check(Schema.isNonEmpty()).check(Schema.isMaxLength(65_536)),
});
export type TerminalWriteInput = Schema.Codec.Encoded<typeof TerminalWriteInput>;
export const TerminalResizeInput = Schema.Struct({
...TerminalSessionInput.fields,
cols: TerminalColsSchema,
rows: TerminalRowsSchema,
});
export type TerminalResizeInput = Schema.Codec.Encoded<typeof TerminalResizeInput>;
export const TerminalClearInput = TerminalSessionInput;
export type TerminalClearInput = Schema.Codec.Encoded<typeof TerminalClearInput>;
export const TerminalRestartInput = Schema.Struct({
...TerminalSessionInput.fields,
cwd: TrimmedNonEmptyStringSchema,
cols: TerminalColsSchema,
rows: TerminalRowsSchema,
env: Schema.optional(TerminalEnvSchema),
});
export type TerminalRestartInput = Schema.Codec.Encoded<typeof TerminalRestartInput>;
export const TerminalCloseInput = Schema.Struct({
...TerminalThreadInput.fields,
terminalId: Schema.optional(TerminalIdSchema),
deleteHistory: Schema.optional(Schema.Boolean),
});
export type TerminalCloseInput = typeof TerminalCloseInput.Type;
export const TerminalSessionStatus = Schema.Literals(["starting", "running", "exited", "error"]);
export type TerminalSessionStatus = typeof TerminalSessionStatus.Type;
export const TerminalSessionSnapshot = Schema.Struct({
threadId: Schema.String.check(Schema.isNonEmpty()),
terminalId: Schema.String.check(Schema.isNonEmpty()),
cwd: Schema.String.check(Schema.isNonEmpty()),
status: TerminalSessionStatus,
pid: Schema.NullOr(Schema.Int.check(Schema.isGreaterThan(0))),
history: Schema.String,
exitCode: Schema.NullOr(Schema.Int),
exitSignal: Schema.NullOr(Schema.Int),
updatedAt: Schema.String,
});
export type TerminalSessionSnapshot = typeof TerminalSessionSnapshot.Type;
const TerminalEventBaseSchema = Schema.Struct({
threadId: Schema.String.check(Schema.isNonEmpty()),
terminalId: Schema.String.check(Schema.isNonEmpty()),
createdAt: Schema.String,
});
const TerminalStartedEvent = Schema.Struct({
...TerminalEventBaseSchema.fields,
type: Schema.Literal("started"),
snapshot: TerminalSessionSnapshot,
});
const TerminalOutputEvent = Schema.Struct({
...TerminalEventBaseSchema.fields,
type: Schema.Literal("output"),
data: Schema.String,
});
const TerminalExitedEvent = Schema.Struct({
...TerminalEventBaseSchema.fields,
type: Schema.Literal("exited"),
exitCode: Schema.NullOr(Schema.Int),
exitSignal: Schema.NullOr(Schema.Int),
});
const TerminalErrorEvent = Schema.Struct({
...TerminalEventBaseSchema.fields,
type: Schema.Literal("error"),
message: Schema.String.check(Schema.isNonEmpty()),
});
const TerminalClearedEvent = Schema.Struct({
...TerminalEventBaseSchema.fields,
type: Schema.Literal("cleared"),
});
const TerminalRestartedEvent = Schema.Struct({
...TerminalEventBaseSchema.fields,
type: Schema.Literal("restarted"),
snapshot: TerminalSessionSnapshot,
});
const TerminalActivityEvent = Schema.Struct({
...TerminalEventBaseSchema.fields,
type: Schema.Literal("activity"),
hasRunningSubprocess: Schema.Boolean,
});
export const TerminalEvent = Schema.Union([
TerminalStartedEvent,
TerminalOutputEvent,
TerminalExitedEvent,
TerminalErrorEvent,
TerminalClearedEvent,
TerminalRestartedEvent,
TerminalActivityEvent,
]);
export type TerminalEvent = typeof TerminalEvent.Type;
export class TerminalCwdError extends Schema.TaggedErrorClass<TerminalCwdError>()(
"TerminalCwdError",
{
cwd: Schema.String,
reason: Schema.Literals(["notFound", "notDirectory", "statFailed"]),
cause: Schema.optional(Schema.Defect),
},
) {
override get message() {
if (this.reason === "notDirectory") {
return `Terminal cwd is not a directory: ${this.cwd}`;
}
if (this.reason === "notFound") {
return `Terminal cwd does not exist: ${this.cwd}`;
}
const causeMessage =
this.cause && typeof this.cause === "object" && "message" in this.cause
? this.cause.message
: undefined;
return causeMessage
? `Failed to access terminal cwd: ${this.cwd} (${causeMessage})`
: `Failed to access terminal cwd: ${this.cwd}`;
}
}
export class TerminalHistoryError extends Schema.TaggedErrorClass<TerminalHistoryError>()(
"TerminalHistoryError",
{
operation: Schema.Literals(["read", "truncate", "migrate"]),
threadId: Schema.String,
terminalId: Schema.String,
cause: Schema.optional(Schema.Defect),
},
) {
override get message() {
return `Failed to ${this.operation} terminal history for thread: ${this.threadId}, terminal: ${this.terminalId}`;
}
}
export class TerminalSessionLookupError extends Schema.TaggedErrorClass<TerminalSessionLookupError>()(
"TerminalSessionLookupError",
{
threadId: Schema.String,
terminalId: Schema.String,
},
) {
override get message() {
return `Unknown terminal thread: ${this.threadId}, terminal: ${this.terminalId}`;
}
}
export class TerminalNotRunningError extends Schema.TaggedErrorClass<TerminalNotRunningError>()(
"TerminalNotRunningError",
{
threadId: Schema.String,
terminalId: Schema.String,
},
) {
override get message() {
return `Terminal is not running for thread: ${this.threadId}, terminal: ${this.terminalId}`;
}
}
export const TerminalError = Schema.Union([
TerminalCwdError,
TerminalHistoryError,
TerminalSessionLookupError,
TerminalNotRunningError,
]);
export type TerminalError = typeof TerminalError.Type;