authorgravatar for crazywolf132@gmail.com0x1f99d <crazywolf132@gmail.com> 2026-03-11 03:46:24+11:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-03-10 09:46:24-07:00
log2c351726ae21a73ac0549500783d5a4de6394214
treeb151bfec1f42230b0b9094f44acff9f16c582654
parent9e891d2e97d65a6e211e3da8ee95807b9a091563
signature Signed by PGP key B5690EEEBB952194

fix(contracts): align terminal restart input type across IPC, WS, and server (#597)


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";
10import { afterEach, describe, expect, it } from "vitest";11import { afterEach, describe, expect, it } from "vitest";
1112
...@@ -134,6 +135,16 @@ function openInput(overrides: Partial<TerminalOpenInput> = {}): TerminalOpenInpu...@@ -134,6 +135,16 @@ function openInput(overrides: Partial<TerminalOpenInput> = {}): TerminalOpenInpu
134 };135 };
135}136}
136137
138function 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
137function historyLogName(threadId: string): string {148function 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)));
363374
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;
37const TERMINAL_ENV_BLOCKLIST = new Set(["PORT", "ELECTRON_RENDERER_PORT", "ELECTRON_RUN_AS_NODE"]);38const TERMINAL_ENV_BLOCKLIST = new Set(["PORT", "ELECTRON_RENDERER_PORT", "ELECTRON_RUN_AS_NODE"]);
3839
39const decodeTerminalOpenInput = Schema.decodeUnknownSync(TerminalOpenInput);40const decodeTerminalOpenInput = Schema.decodeUnknownSync(TerminalOpenInput);
41const decodeTerminalRestartInput = Schema.decodeUnknownSync(TerminalRestartInput);
40const decodeTerminalWriteInput = Schema.decodeUnknownSync(TerminalWriteInput);42const decodeTerminalWriteInput = Schema.decodeUnknownSync(TerminalWriteInput);
41const decodeTerminalResizeInput = Schema.decodeUnknownSync(TerminalResizeInput);43const decodeTerminalResizeInput = Schema.decodeUnknownSync(TerminalResizeInput);
42const decodeTerminalClearInput = Schema.decodeUnknownSync(TerminalClearInput);44const 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 }
480482
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);
485487
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>;
9394
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;
60export type TerminalClearInput = Schema.Codec.Encoded<typeof TerminalClearInput>;60export type TerminalClearInput = Schema.Codec.Encoded<typeof TerminalClearInput>;
6161
62export const TerminalRestartInput = Schema.Struct({62export 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});
69export type TerminalRestartInput = Schema.Codec.Encoded<typeof TerminalRestartInput>;
6970
70export const TerminalCloseInput = Schema.Struct({71export const TerminalCloseInput = Schema.Struct({
71 ...TerminalThreadInput.fields,72 ...TerminalThreadInput.fields,