This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

Respond to DA1 queries to fix fish shell 10s startup delay

Fish 4.x sends a DA1 (Primary Device Attribute) query at startup
and blocks for up to 10s waiting for a response. Add a CSI handler
in PtyServer that intercepts the query and writes back a VT220
response. Add shell integration tests for bash, zsh, and fish.

Closes #5
Based on the approach by @schickling-assistant in #5

Nathan Herald (Apr 6, 2026, 9:12 PM +0200) 4f7d628b a5f09b5d

+83
+14
src/server.ts
··· 151 151 } 152 152 ); 153 153 154 + // Respond to DA1 (Primary Device Attribute) queries from the child process. 155 + // Shells like fish 4.x send ESC[c at startup and block for up to 10s waiting 156 + // for a response. Since xterm-headless doesn't reply, we intercept the query 157 + // in the output stream and write a VT220 response back to the pty process. 158 + this.terminal.parser.registerCsiHandler( 159 + { final: "c" }, 160 + (params) => { 161 + if (params.length === 0 || params[0] === 0) { 162 + this.ptyProcess.write("\x1b[?62;22c"); 163 + } 164 + return false; 165 + } 166 + ); 167 + 154 168 // ── Event detection ── 155 169 156 170 this.terminal.onBell(() => {
+69
tests/shells.test.ts
··· 1 + import { describe, it, expect } from "vitest"; 2 + import { Session } from "../src/testing/index.ts"; 3 + 4 + // Shell integration tests — verify that common shells start up, 5 + // accept input, and produce output when run inside a pty session. 6 + // 7 + // bash and zsh use Session.spawn() (direct PTY, no server). 8 + // fish uses Session.server() (PtyServer) to exercise the DA1 response 9 + // handler — fish 4.x sends DA1 at startup and blocks 10s without it. 10 + 11 + describe("shell integration", () => { 12 + describe("bash", () => { 13 + it("starts up and accepts commands", async () => { 14 + const session = Session.spawn("bash", ["--norc", "--noprofile"], { 15 + rows: 24, 16 + cols: 80, 17 + }); 18 + 19 + await session.waitForText("$", 5000); 20 + 21 + session.type("echo hello-bash\r"); 22 + await session.waitForText("hello-bash", 5000); 23 + 24 + session.press("ctrl+d"); 25 + await session.close(); 26 + }, 15000); 27 + }); 28 + 29 + describe("zsh", () => { 30 + it("starts up and accepts commands", async () => { 31 + const session = Session.spawn("zsh", ["--no-rcs"], { 32 + rows: 24, 33 + cols: 80, 34 + env: { PROMPT: "zsh> " }, 35 + }); 36 + 37 + await session.waitForText("zsh>", 5000); 38 + 39 + session.type("echo hello-zsh\r"); 40 + await session.waitForText("hello-zsh", 5000); 41 + 42 + session.press("ctrl+d"); 43 + await session.close(); 44 + }, 15000); 45 + }); 46 + 47 + describe("fish", () => { 48 + it("starts up within 3 seconds (requires DA1 response)", async () => { 49 + // fish 4.x sends a DA1 query (ESC[c) at startup and blocks for up to 50 + // 10s waiting for a terminal response. PtyServer must respond to DA1 51 + // for fish to start promptly. This test uses Session.server() to go 52 + // through PtyServer. 53 + const session = await Session.server("fish", ["--no-config"], { 54 + rows: 24, 55 + cols: 80, 56 + }); 57 + await session.attach(); 58 + 59 + // If DA1 is not handled, this will timeout at 3s 60 + await session.waitForText(">", 3000); 61 + 62 + session.type("echo hello-fish\r"); 63 + await session.waitForText("hello-fish", 5000); 64 + 65 + session.type("exit\r"); 66 + await session.close(); 67 + }, 15000); 68 + }); 69 + });