fix: respond to PTY cursor queries
This commit is contained in:
@@ -24,6 +24,7 @@
|
||||
- Tools: default `exec` exit notifications and auto-migrate legacy `tools.bash` to `tools.exec`.
|
||||
- Tools: add tmux-style `process send-keys` and bracketed paste helpers for PTY sessions.
|
||||
- Tools: add `process submit` helper to send CR for PTY sessions.
|
||||
- Tools: respond to PTY cursor position queries to unblock interactive TUIs.
|
||||
- Status: trim `/status` to current-provider usage only and drop the OAuth/token block.
|
||||
- Directory: unify `clawdbot directory` across channels and plugin channels.
|
||||
- UI: allow deleting sessions from the Control UI.
|
||||
|
||||
@@ -29,6 +29,7 @@ import {
|
||||
truncateMiddle,
|
||||
} from "./bash-tools.shared.js";
|
||||
import { getShellConfig, sanitizeBinaryOutput } from "./shell-utils.js";
|
||||
import { buildCursorPositionResponse, stripDsrRequests } from "./pty-dsr.js";
|
||||
|
||||
const DEFAULT_MAX_OUTPUT = clampNumber(
|
||||
readEnvInt("PI_BASH_MAX_OUTPUT_CHARS"),
|
||||
@@ -451,7 +452,17 @@ export function createExecTool(
|
||||
};
|
||||
|
||||
if (pty) {
|
||||
pty.onData(handleStdout);
|
||||
const cursorResponse = buildCursorPositionResponse();
|
||||
pty.onData((data) => {
|
||||
const raw = data.toString();
|
||||
const { cleaned, requests } = stripDsrRequests(raw);
|
||||
if (requests > 0) {
|
||||
for (let i = 0; i < requests; i += 1) {
|
||||
pty.write(cursorResponse);
|
||||
}
|
||||
}
|
||||
handleStdout(cleaned);
|
||||
});
|
||||
} else if (child) {
|
||||
child.stdout.on("data", handleStdout);
|
||||
child.stderr.on("data", handleStderr);
|
||||
|
||||
15
src/agents/pty-dsr.test.ts
Normal file
15
src/agents/pty-dsr.test.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { expect, test } from "vitest";
|
||||
|
||||
import { buildCursorPositionResponse, stripDsrRequests } from "./pty-dsr.js";
|
||||
|
||||
test("stripDsrRequests removes cursor queries and counts them", () => {
|
||||
const input = "hi\x1b[6nthere\x1b[?6n";
|
||||
const { cleaned, requests } = stripDsrRequests(input);
|
||||
expect(cleaned).toBe("hithere");
|
||||
expect(requests).toBe(2);
|
||||
});
|
||||
|
||||
test("buildCursorPositionResponse returns CPR sequence", () => {
|
||||
expect(buildCursorPositionResponse()).toBe("\x1b[1;1R");
|
||||
expect(buildCursorPositionResponse(12, 34)).toBe("\x1b[12;34R");
|
||||
});
|
||||
14
src/agents/pty-dsr.ts
Normal file
14
src/agents/pty-dsr.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
const DSR_PATTERN = /\x1b\[\??6n/g;
|
||||
|
||||
export function stripDsrRequests(input: string): { cleaned: string; requests: number } {
|
||||
let requests = 0;
|
||||
const cleaned = input.replace(DSR_PATTERN, () => {
|
||||
requests += 1;
|
||||
return "";
|
||||
});
|
||||
return { cleaned, requests };
|
||||
}
|
||||
|
||||
export function buildCursorPositionResponse(row = 1, col = 1): string {
|
||||
return `\x1b[${row};${col}R`;
|
||||
}
|
||||
Reference in New Issue
Block a user