Align terminal stream with snapshot-only attach
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user