| author | |
| committer | |
| log | 2c351726ae21a73ac0549500783d5a4de6394214 |
| tree | b151bfec1f42230b0b9094f44acff9f16c582654 |
| parent | 9e891d2e97d65a6e211e3da8ee95807b9a091563 |
| signature | Signed by PGP key B5690EEEBB952194 |
5 files changed, 22 insertions(+), 6 deletions(-)
apps/server/src/terminal/Layers/Manager.test.ts+12-1| ... | @@ -6,6 +6,7 @@ import { | ... | @@ -6,6 +6,7 @@ import { |
| 6 | DEFAULT_TERMINAL_ID, | 6 | DEFAULT_TERMINAL_ID, |
| 7 | type TerminalEvent, | 7 | type TerminalEvent, |
| 8 | type TerminalOpenInput, | 8 | type TerminalOpenInput, |
| 9 | type TerminalRestartInput, | ||
| 9 | } from "@t3tools/contracts"; | 10 | } from "@t3tools/contracts"; |
| 10 | import { afterEach, describe, expect, it } from "vitest"; | 11 | import { afterEach, describe, expect, it } from "vitest"; |
| 11 | 12 | ||
| ... | @@ -134,6 +135,16 @@ function openInput(overrides: Partial<TerminalOpenInput> = {}): TerminalOpenInpu | ... | @@ -134,6 +135,16 @@ function openInput(overrides: Partial<TerminalOpenInput> = {}): TerminalOpenInpu |
| 134 | }; | 135 | }; |
| 135 | } | 136 | } |
| 136 | 137 | ||
| 138 | function restartInput(overrides: Partial<TerminalRestartInput> = {}): TerminalRestartInput { | ||
| 139 | return { | ||
| 140 | threadId: "thread-1", | ||
| 141 | cwd: process.cwd(), | ||
| 142 | cols: 100, | ||
| 143 | rows: 24, | ||
| 144 | ...overrides, | ||
| 145 | }; | ||
| 146 | } | ||
| 147 | |||
| 137 | function historyLogName(threadId: string): string { | 148 | function historyLogName(threadId: string): string { |
| 138 | return `terminal_${Encoding.encodeBase64Url(threadId)}.log`; | 149 | return `terminal_${Encoding.encodeBase64Url(threadId)}.log`; |
| 139 | } | 150 | } |
| ... | @@ -361,7 +372,7 @@ describe("TerminalManager", () => { | ... | @@ -361,7 +372,7 @@ describe("TerminalManager", () => { |
| 361 | firstProcess.emitData("before restart\n"); | 372 | firstProcess.emitData("before restart\n"); |
| 362 | await waitFor(() => fs.existsSync(historyLogPath(logsDir))); | 373 | await waitFor(() => fs.existsSync(historyLogPath(logsDir))); |
| 363 | 374 | ||
| 364 | const snapshot = await manager.restart(openInput()); | 375 | const snapshot = await manager.restart(restartInput()); |
| 365 | expect(snapshot.history).toBe(""); | 376 | expect(snapshot.history).toBe(""); |
| 366 | expect(snapshot.status).toBe("running"); | 377 | expect(snapshot.status).toBe("running"); |
| 367 | expect(ptyAdapter.spawnInputs).toHaveLength(2); | 378 | expect(ptyAdapter.spawnInputs).toHaveLength(2); |
apps/server/src/terminal/Layers/Manager.ts+4-2| ... | @@ -8,6 +8,7 @@ import { | ... | @@ -8,6 +8,7 @@ import { |
| 8 | TerminalCloseInput, | 8 | TerminalCloseInput, |
| 9 | TerminalOpenInput, | 9 | TerminalOpenInput, |
| 10 | TerminalResizeInput, | 10 | TerminalResizeInput, |
| 11 | TerminalRestartInput, | ||
| 11 | TerminalWriteInput, | 12 | TerminalWriteInput, |
| 12 | type TerminalEvent, | 13 | type TerminalEvent, |
| 13 | type TerminalSessionSnapshot, | 14 | type TerminalSessionSnapshot, |
| ... | @@ -37,6 +38,7 @@ const DEFAULT_OPEN_ROWS = 30; | ... | @@ -37,6 +38,7 @@ const DEFAULT_OPEN_ROWS = 30; |
| 37 | const TERMINAL_ENV_BLOCKLIST = new Set(["PORT", "ELECTRON_RENDERER_PORT", "ELECTRON_RUN_AS_NODE"]); | 38 | const TERMINAL_ENV_BLOCKLIST = new Set(["PORT", "ELECTRON_RENDERER_PORT", "ELECTRON_RUN_AS_NODE"]); |
| 38 | 39 | ||
| 39 | const decodeTerminalOpenInput = Schema.decodeUnknownSync(TerminalOpenInput); | 40 | const decodeTerminalOpenInput = Schema.decodeUnknownSync(TerminalOpenInput); |
| 41 | const decodeTerminalRestartInput = Schema.decodeUnknownSync(TerminalRestartInput); | ||
| 40 | const decodeTerminalWriteInput = Schema.decodeUnknownSync(TerminalWriteInput); | 42 | const decodeTerminalWriteInput = Schema.decodeUnknownSync(TerminalWriteInput); |
| 41 | const decodeTerminalResizeInput = Schema.decodeUnknownSync(TerminalResizeInput); | 43 | const decodeTerminalResizeInput = Schema.decodeUnknownSync(TerminalResizeInput); |
| 42 | const decodeTerminalClearInput = Schema.decodeUnknownSync(TerminalClearInput); | 44 | const decodeTerminalClearInput = Schema.decodeUnknownSync(TerminalClearInput); |
| ... | @@ -478,8 +480,8 @@ export class TerminalManagerRuntime extends EventEmitter<TerminalManagerEvents> | ... | @@ -478,8 +480,8 @@ export class TerminalManagerRuntime extends EventEmitter<TerminalManagerEvents> |
| 478 | }); | 480 | }); |
| 479 | } | 481 | } |
| 480 | 482 | ||
| 481 | async restart(raw: TerminalOpenInput): Promise<TerminalSessionSnapshot> { | 483 | async restart(raw: TerminalRestartInput): Promise<TerminalSessionSnapshot> { |
| 482 | const input = decodeTerminalOpenInput(raw); | 484 | const input = decodeTerminalRestartInput(raw); |
| 483 | return this.runWithThreadLock(input.threadId, async () => { | 485 | return this.runWithThreadLock(input.threadId, async () => { |
| 484 | await this.assertValidCwd(input.cwd); | 486 | await this.assertValidCwd(input.cwd); |
| 485 | 487 |
apps/server/src/terminal/Services/Manager.ts+2-1| ... | @@ -12,6 +12,7 @@ import { | ... | @@ -12,6 +12,7 @@ import { |
| 12 | TerminalEvent, | 12 | TerminalEvent, |
| 13 | TerminalOpenInput, | 13 | TerminalOpenInput, |
| 14 | TerminalResizeInput, | 14 | TerminalResizeInput, |
| 15 | TerminalRestartInput, | ||
| 15 | TerminalSessionSnapshot, | 16 | TerminalSessionSnapshot, |
| 16 | TerminalSessionStatus, | 17 | TerminalSessionStatus, |
| 17 | TerminalWriteInput, | 18 | TerminalWriteInput, |
| ... | @@ -88,7 +89,7 @@ export interface TerminalManagerShape { | ... | @@ -88,7 +89,7 @@ export interface TerminalManagerShape { |
| 88 | * Always resets history before spawning the new process. | 89 | * Always resets history before spawning the new process. |
| 89 | */ | 90 | */ |
| 90 | readonly restart: ( | 91 | readonly restart: ( |
| 91 | input: TerminalOpenInput, | 92 | input: TerminalRestartInput, |
| 92 | ) => Effect.Effect<TerminalSessionSnapshot, TerminalError>; | 93 | ) => Effect.Effect<TerminalSessionSnapshot, TerminalError>; |
| 93 | 94 | ||
| 94 | /** | 95 | /** |
packages/contracts/src/ipc.ts+2-1| ... | @@ -31,6 +31,7 @@ import type { | ... | @@ -31,6 +31,7 @@ import type { |
| 31 | TerminalEvent, | 31 | TerminalEvent, |
| 32 | TerminalOpenInput, | 32 | TerminalOpenInput, |
| 33 | TerminalResizeInput, | 33 | TerminalResizeInput, |
| 34 | TerminalRestartInput, | ||
| 34 | TerminalSessionSnapshot, | 35 | TerminalSessionSnapshot, |
| 35 | TerminalWriteInput, | 36 | TerminalWriteInput, |
| 36 | } from "./terminal"; | 37 | } from "./terminal"; |
| ... | @@ -118,7 +119,7 @@ export interface NativeApi { | ... | @@ -118,7 +119,7 @@ export interface NativeApi { |
| 118 | write: (input: TerminalWriteInput) => Promise<void>; | 119 | write: (input: TerminalWriteInput) => Promise<void>; |
| 119 | resize: (input: TerminalResizeInput) => Promise<void>; | 120 | resize: (input: TerminalResizeInput) => Promise<void>; |
| 120 | clear: (input: TerminalClearInput) => Promise<void>; | 121 | clear: (input: TerminalClearInput) => Promise<void>; |
| 121 | restart: (input: TerminalOpenInput) => Promise<TerminalSessionSnapshot>; | 122 | restart: (input: TerminalRestartInput) => Promise<TerminalSessionSnapshot>; |
| 122 | close: (input: TerminalCloseInput) => Promise<void>; | 123 | close: (input: TerminalCloseInput) => Promise<void>; |
| 123 | onEvent: (callback: (event: TerminalEvent) => void) => () => void; | 124 | onEvent: (callback: (event: TerminalEvent) => void) => () => void; |
| 124 | }; | 125 | }; |
packages/contracts/src/terminal.ts+2-1| ... | @@ -60,12 +60,13 @@ export const TerminalClearInput = TerminalSessionInput; | ... | @@ -60,12 +60,13 @@ export const TerminalClearInput = TerminalSessionInput; |
| 60 | export type TerminalClearInput = Schema.Codec.Encoded<typeof TerminalClearInput>; | 60 | export type TerminalClearInput = Schema.Codec.Encoded<typeof TerminalClearInput>; |
| 61 | 61 | ||
| 62 | export const TerminalRestartInput = Schema.Struct({ | 62 | export const TerminalRestartInput = Schema.Struct({ |
| 63 | ...TerminalThreadInput.fields, | 63 | ...TerminalSessionInput.fields, |
| 64 | cwd: TrimmedNonEmptyStringSchema, | 64 | cwd: TrimmedNonEmptyStringSchema, |
| 65 | cols: TerminalColsSchema, | 65 | cols: TerminalColsSchema, |
| 66 | rows: TerminalRowsSchema, | 66 | rows: TerminalRowsSchema, |
| 67 | env: Schema.optional(TerminalEnvSchema), | 67 | env: Schema.optional(TerminalEnvSchema), |
| 68 | }); | 68 | }); |
| 69 | export type TerminalRestartInput = Schema.Codec.Encoded<typeof TerminalRestartInput>; | ||
| 69 | 70 | ||
| 70 | export const TerminalCloseInput = Schema.Struct({ | 71 | export const TerminalCloseInput = Schema.Struct({ |
| 71 | ...TerminalThreadInput.fields, | 72 | ...TerminalThreadInput.fields, |