authorgravatar for 158713173+yassiEmp@users.noreply.github.comyassiEmp <158713173+yassiEmp@users.noreply.github.com> 2026-08-11 05:33:58+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-08-10 23:33:58-04:00
logc14bcca1035895edca5ebd8d65faea5fe788a825
tree1c3bd5856859c8637cc7b25189c9c27b06c0ee08
parent1fa315ea93f24e6f72455e234fa84a97de28492b
signature Signed by PGP key B5690EEEBB952194

fix(server): advertise 256-color TERM on Windows terminals (#5693)


2 files changed, 39 insertions(+), 4 deletions(-)

apps/server/src/terminal/NodePtyAdapter.test.ts+30-2
......@@ -33,6 +33,7 @@ const testLayer = NodePtyAdapter.layer.pipe(
3333
3434it.effect("spawns through the public adapter with the provided host references", () =>
3535 Effect.gen(function* () {
36 spawn.mockClear();
3637 const adapter = yield* PtyAdapter.PtyAdapter;
3738 const process = yield* adapter.spawn({
3839 shell: "powershell.exe",
......@@ -52,8 +53,35 @@ it.effect("spawns through the public adapter with the provided host references",
5253 cwd: "C:\\workspace",
5354 cols: 120,
5455 rows: 40,
55 env: {},
56 name: "xterm-color",
56 env: { TERM: "xterm-256color" },
57 name: "xterm-256color",
58 },
59 ]);
60 }).pipe(Effect.provide(testLayer)),
61);
62
63it.effect("preserves a caller-provided TERM in the spawn env on win32", () =>
64 Effect.gen(function* () {
65 spawn.mockClear();
66 const adapter = yield* PtyAdapter.PtyAdapter;
67 yield* adapter.spawn({
68 shell: "powershell.exe",
69 cwd: "C:\\workspace",
70 cols: 80,
71 rows: 24,
72 env: { TERM: "xterm-direct" },
73 });
74
75 assert.equal(spawn.mock.calls.length, 1);
76 assert.deepEqual(spawn.mock.calls[0], [
77 "powershell.exe",
78 [],
79 {
80 cwd: "C:\\workspace",
81 cols: 80,
82 rows: 24,
83 env: { TERM: "xterm-direct" },
84 name: "xterm-256color",
5785 },
5886 ]);
5987 }).pipe(Effect.provide(testLayer)),
apps/server/src/terminal/NodePtyAdapter.ts+9-2
......@@ -141,14 +141,21 @@ export const make = Effect.fn("NodePtyAdapter.make")(function* (
141141 return PtyAdapter.PtyAdapter.of({
142142 spawn: Effect.fn("NodePtyAdapter.spawn")(function* (input) {
143143 yield* ensureNodePtySpawnHelperExecutableCached;
144 // node-pty only writes `name` into the child's TERM on the Unix path;
145 // the ConPTY path leaves the environment untouched, so Windows children
146 // inherit a missing or 16-color TERM unless it is set here.
147 const env =
148 platform === "win32" && input.env["TERM"] === undefined
149 ? { ...input.env, TERM: "xterm-256color" }
150 : input.env;
144151 const ptyProcess = yield* Effect.try({
145152 try: () =>
146153 nodePty.spawn(input.shell, input.args ?? [], {
147154 cwd: input.cwd,
148155 cols: input.cols,
149156 rows: input.rows,
150 env: input.env,
151 name: platform === "win32" ? "xterm-color" : "xterm-256color",
157 env,
158 name: "xterm-256color",
152159 }),
153160 catch: (cause) =>
154161 new PtyAdapter.PtySpawnError({