terminal-pilot 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/keys.js +17 -12
- package/dist/mcp-tools.js +2 -2
- package/dist/terminal-session.js +16 -8
- package/package.json +1 -1
- package/dist/testing/menu-cli.d.ts +0 -2
- package/dist/testing/menu-cli.js +0 -63
- package/dist/testing/test-cli.d.ts +0 -2
- package/dist/testing/test-cli.js +0 -22
package/dist/keys.js
CHANGED
|
@@ -14,18 +14,24 @@ const NAMED_KEY_SEQUENCES = {
|
|
|
14
14
|
PageDown: "\x1b[6~",
|
|
15
15
|
Space: " "
|
|
16
16
|
};
|
|
17
|
+
const NAMED_KEY_LOWER = new Map(Object.entries(NAMED_KEY_SEQUENCES).map(([k, v]) => [k.toLowerCase(), v]));
|
|
18
|
+
const VALID_KEYS_HINT = `Valid keys: ${Object.keys(NAMED_KEY_SEQUENCES).join(", ")}, Control+<letter>, Alt+<key>`;
|
|
19
|
+
function unknownKeyError(key) {
|
|
20
|
+
return new Error(`Unknown terminal key: ${key}. ${VALID_KEYS_HINT}`);
|
|
21
|
+
}
|
|
17
22
|
export function keyToSequence(key) {
|
|
18
|
-
const
|
|
23
|
+
const lowerKey = key.toLowerCase();
|
|
24
|
+
const namedSequence = NAMED_KEY_LOWER.get(lowerKey);
|
|
19
25
|
if (namedSequence !== undefined) {
|
|
20
26
|
return namedSequence;
|
|
21
27
|
}
|
|
22
|
-
if (
|
|
23
|
-
return controlKeyToSequence(key);
|
|
28
|
+
if (lowerKey.startsWith("control+")) {
|
|
29
|
+
return controlKeyToSequence(key.slice("control+".length));
|
|
24
30
|
}
|
|
25
|
-
if (
|
|
26
|
-
const nestedKey = key.slice("
|
|
31
|
+
if (lowerKey.startsWith("alt+")) {
|
|
32
|
+
const nestedKey = key.slice("alt+".length);
|
|
27
33
|
if (nestedKey.length === 0) {
|
|
28
|
-
throw
|
|
34
|
+
throw unknownKeyError(key);
|
|
29
35
|
}
|
|
30
36
|
if (nestedKey.length === 1) {
|
|
31
37
|
return "\x1b" + nestedKey;
|
|
@@ -34,20 +40,19 @@ export function keyToSequence(key) {
|
|
|
34
40
|
return "\x1b" + keyToSequence(nestedKey);
|
|
35
41
|
}
|
|
36
42
|
catch {
|
|
37
|
-
throw
|
|
43
|
+
throw unknownKeyError(key);
|
|
38
44
|
}
|
|
39
45
|
}
|
|
40
|
-
throw
|
|
46
|
+
throw unknownKeyError(key);
|
|
41
47
|
}
|
|
42
|
-
function controlKeyToSequence(
|
|
43
|
-
const controlKey = key.slice("Control+".length);
|
|
48
|
+
function controlKeyToSequence(controlKey) {
|
|
44
49
|
if (controlKey.length !== 1) {
|
|
45
|
-
throw
|
|
50
|
+
throw unknownKeyError(`Control+${controlKey}`);
|
|
46
51
|
}
|
|
47
52
|
const uppercaseLetter = controlKey.toUpperCase();
|
|
48
53
|
const charCode = uppercaseLetter.charCodeAt(0);
|
|
49
54
|
if (charCode < 65 || charCode > 90) {
|
|
50
|
-
throw
|
|
55
|
+
throw unknownKeyError(`Control+${controlKey}`);
|
|
51
56
|
}
|
|
52
57
|
return String.fromCharCode(charCode - 64);
|
|
53
58
|
}
|
package/dist/mcp-tools.js
CHANGED
|
@@ -71,10 +71,10 @@ export function terminalWaitForTool(agent) {
|
|
|
71
71
|
async handler(input) {
|
|
72
72
|
const session = agent.getSession(input.sessionId);
|
|
73
73
|
const pattern = new RegExp(input.pattern);
|
|
74
|
-
const
|
|
74
|
+
const line = input.timeout === undefined
|
|
75
75
|
? await session.waitFor(pattern)
|
|
76
76
|
: await session.waitFor(pattern, { timeout: input.timeout });
|
|
77
|
-
return { matched: true,
|
|
77
|
+
return { matched: true, line };
|
|
78
78
|
}
|
|
79
79
|
};
|
|
80
80
|
}
|
package/dist/terminal-session.js
CHANGED
|
@@ -66,7 +66,7 @@ export class TerminalSession {
|
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
async fill(text) {
|
|
69
|
-
await this.send(text);
|
|
69
|
+
await this.send(text.replace(/\r?\n/g, "\r"));
|
|
70
70
|
}
|
|
71
71
|
async press(key) {
|
|
72
72
|
await this.send(keyToSequence(key));
|
|
@@ -140,7 +140,9 @@ export class TerminalSession {
|
|
|
140
140
|
async resize(cols, rows) {
|
|
141
141
|
this.currentCols = cols;
|
|
142
142
|
this.currentRows = rows;
|
|
143
|
-
this.
|
|
143
|
+
if (this.exitCode === null) {
|
|
144
|
+
this.pty.resize(cols, rows);
|
|
145
|
+
}
|
|
144
146
|
this.terminal.resize(cols, rows);
|
|
145
147
|
}
|
|
146
148
|
async close() {
|
|
@@ -252,13 +254,19 @@ function signalToExitCode(signal) {
|
|
|
252
254
|
return 128 + signalNumber;
|
|
253
255
|
}
|
|
254
256
|
function matchPattern(buffer, pattern) {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
+
const clean = normalizeHistoryBuffer(stripAnsi(buffer));
|
|
258
|
+
for (const line of clean.split("\n")) {
|
|
259
|
+
if (typeof pattern === "string") {
|
|
260
|
+
if (line.includes(pattern))
|
|
261
|
+
return line;
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
const flags = removeCharacter(pattern.flags, "g");
|
|
265
|
+
if (new RegExp(pattern.source, flags).test(line))
|
|
266
|
+
return line;
|
|
267
|
+
}
|
|
257
268
|
}
|
|
258
|
-
|
|
259
|
-
const localPattern = new RegExp(pattern.source, flags);
|
|
260
|
-
const match = localPattern.exec(buffer);
|
|
261
|
-
return match?.[0] ?? null;
|
|
269
|
+
return null;
|
|
262
270
|
}
|
|
263
271
|
function removeCharacter(input, charToRemove) {
|
|
264
272
|
let output = "";
|
package/package.json
CHANGED
package/dist/testing/menu-cli.js
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env tsx
|
|
2
|
-
import readline from "node:readline";
|
|
3
|
-
const options = ["Option 1", "Option 2", "Option 3"];
|
|
4
|
-
const { stdin, stdout } = process;
|
|
5
|
-
let selectedIndex = 0;
|
|
6
|
-
let renderedLineCount = 0;
|
|
7
|
-
let hasExited = false;
|
|
8
|
-
readline.emitKeypressEvents(stdin);
|
|
9
|
-
if (stdin.isTTY) {
|
|
10
|
-
stdin.setRawMode(true);
|
|
11
|
-
}
|
|
12
|
-
stdin.resume();
|
|
13
|
-
render();
|
|
14
|
-
stdin.on("keypress", (_, key) => {
|
|
15
|
-
if (key.ctrl && key.name === "c") {
|
|
16
|
-
exitWithCode(130);
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
19
|
-
if (key.name === "up") {
|
|
20
|
-
selectedIndex = (selectedIndex + options.length - 1) % options.length;
|
|
21
|
-
render();
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
if (key.name === "down") {
|
|
25
|
-
selectedIndex = (selectedIndex + 1) % options.length;
|
|
26
|
-
render();
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
if (key.name === "return" || key.name === "enter") {
|
|
30
|
-
cleanup();
|
|
31
|
-
stdout.write(`You selected: ${options[selectedIndex]}\n`);
|
|
32
|
-
process.exit(0);
|
|
33
|
-
}
|
|
34
|
-
});
|
|
35
|
-
process.on("SIGINT", () => {
|
|
36
|
-
exitWithCode(130);
|
|
37
|
-
});
|
|
38
|
-
function cleanup() {
|
|
39
|
-
if (stdin.isTTY) {
|
|
40
|
-
stdin.setRawMode(false);
|
|
41
|
-
}
|
|
42
|
-
stdin.pause();
|
|
43
|
-
}
|
|
44
|
-
function render() {
|
|
45
|
-
if (renderedLineCount > 0) {
|
|
46
|
-
readline.moveCursor(stdout, 0, -renderedLineCount);
|
|
47
|
-
readline.clearScreenDown(stdout);
|
|
48
|
-
}
|
|
49
|
-
const lines = [
|
|
50
|
-
"Select an option:",
|
|
51
|
-
...options.map((option, index) => `${index === selectedIndex ? ">" : " "} ${option}`)
|
|
52
|
-
];
|
|
53
|
-
stdout.write(`${lines.join("\n")}\n`);
|
|
54
|
-
renderedLineCount = lines.length;
|
|
55
|
-
}
|
|
56
|
-
function exitWithCode(code) {
|
|
57
|
-
if (hasExited) {
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
hasExited = true;
|
|
61
|
-
cleanup();
|
|
62
|
-
process.exit(code);
|
|
63
|
-
}
|
package/dist/testing/test-cli.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env tsx
|
|
2
|
-
import readline from "node:readline";
|
|
3
|
-
const terminal = readline.createInterface({
|
|
4
|
-
input: process.stdin,
|
|
5
|
-
output: process.stdout
|
|
6
|
-
});
|
|
7
|
-
let hasGreeted = false;
|
|
8
|
-
process.stdout.write("What is your name? ");
|
|
9
|
-
terminal.on("line", (name) => {
|
|
10
|
-
if (hasGreeted) {
|
|
11
|
-
return;
|
|
12
|
-
}
|
|
13
|
-
hasGreeted = true;
|
|
14
|
-
console.log(`Hello, ${name}!`);
|
|
15
|
-
terminal.close();
|
|
16
|
-
});
|
|
17
|
-
terminal.on("close", () => {
|
|
18
|
-
if (!hasGreeted) {
|
|
19
|
-
console.log(`Hello, ${terminal.line}!`);
|
|
20
|
-
}
|
|
21
|
-
process.exit(0);
|
|
22
|
-
});
|