Align terminal stream with snapshot-only attach

This commit is contained in:
Mohamed Boudra
2026-03-22 22:49:05 +07:00
parent 138c774297
commit babc724220
14 changed files with 262 additions and 550 deletions
+3 -4
View File
@@ -2104,15 +2104,15 @@ export const TerminalCellSchema = z.object({
bold: z.boolean().optional(),
italic: z.boolean().optional(),
underline: z.boolean().optional(),
});
}).strict();
export const TerminalStateSchema = z.object({
rows: z.number(),
cols: z.number(),
grid: z.array(z.array(TerminalCellSchema)),
scrollback: z.array(z.array(TerminalCellSchema)),
cursor: z.object({ row: z.number(), col: z.number() }),
});
cursor: z.object({ row: z.number(), col: z.number() }).strict(),
}).strict();
export const ListTerminalsResponseSchema = z.object({
type: z.literal("list_terminals_response"),
@@ -2144,7 +2144,6 @@ export const SubscribeTerminalResponseSchema = z.object({
type: z.literal("subscribe_terminal_response"),
payload: z.object({
terminalId: z.string(),
state: TerminalStateSchema.nullable(),
error: z.string().nullable(),
requestId: z.string(),
}),
@@ -56,4 +56,50 @@ describe("terminal stream protocol", () => {
it("rejects unknown opcodes", () => {
expect(decodeTerminalStreamFrame(new Uint8Array([0xff, 0x01]))).toBeNull();
});
it("rejects malformed JSON payloads", () => {
const malformed = new TextEncoder().encode("{");
expect(decodeTerminalResizePayload(malformed)).toBeNull();
expect(decodeTerminalSnapshotPayload(malformed)).toBeNull();
});
it("rejects invalid resize and snapshot shapes", () => {
expect(
decodeTerminalResizePayload(new TextEncoder().encode(JSON.stringify({ rows: "24", cols: 80 }))),
).toBeNull();
expect(
decodeTerminalSnapshotPayload(
new TextEncoder().encode(
JSON.stringify({
rows: 1,
cols: 1,
grid: [[{ char: "A" }]],
scrollback: [],
}),
),
),
).toBeNull();
});
it("rejects unknown fields in resize and snapshot payloads", () => {
expect(
decodeTerminalResizePayload(
new TextEncoder().encode(JSON.stringify({ rows: 24, cols: 80, extra: true })),
),
).toBeNull();
expect(
decodeTerminalSnapshotPayload(
new TextEncoder().encode(
JSON.stringify({
rows: 1,
cols: 1,
grid: [[{ char: "A", extra: true }]],
scrollback: [],
cursor: { row: 0, col: 1 },
}),
),
),
).toBeNull();
});
});
@@ -4,7 +4,7 @@ import { TerminalStateSchema } from "./messages.js";
export const TerminalStreamResizeSchema = z.object({
rows: z.number().int().positive(),
cols: z.number().int().positive(),
});
}).strict();
export const TerminalStreamOpcode = {
Output: 0x01,
@@ -115,6 +115,10 @@ function encodeJsonPayload(value: unknown): Uint8Array {
}
function decodeJsonPayload(bytes: Uint8Array): unknown {
const text = new TextDecoder().decode(bytes);
return JSON.parse(text);
try {
const text = new TextDecoder().decode(bytes);
return JSON.parse(text);
} catch {
return null;
}
}