terminal-pilot 0.0.28 → 0.0.30
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/cli.js +430 -68
- package/dist/cli.js.map +3 -3
- package/dist/commands/close-session.js +73 -6
- package/dist/commands/close-session.js.map +2 -2
- package/dist/commands/create-session.js +80 -9
- package/dist/commands/create-session.js.map +2 -2
- package/dist/commands/fill.js +73 -6
- package/dist/commands/fill.js.map +2 -2
- package/dist/commands/get-session.js +73 -6
- package/dist/commands/get-session.js.map +2 -2
- package/dist/commands/index.js +371 -62
- package/dist/commands/index.js.map +3 -3
- package/dist/commands/install.js +10 -5
- package/dist/commands/install.js.map +2 -2
- package/dist/commands/installer.js +10 -5
- package/dist/commands/installer.js.map +2 -2
- package/dist/commands/list-sessions.js +66 -4
- package/dist/commands/list-sessions.js.map +2 -2
- package/dist/commands/press-key.js +146 -71
- package/dist/commands/press-key.js.map +3 -3
- package/dist/commands/read-history.js +81 -7
- package/dist/commands/read-history.js.map +2 -2
- package/dist/commands/read-screen.js +73 -6
- package/dist/commands/read-screen.js.map +2 -2
- package/dist/commands/resize.js +75 -8
- package/dist/commands/resize.js.map +2 -2
- package/dist/commands/runtime.js +66 -4
- package/dist/commands/runtime.js.map +2 -2
- package/dist/commands/screenshot.js +228 -23
- package/dist/commands/screenshot.js.map +3 -3
- package/dist/commands/send-signal.js +73 -6
- package/dist/commands/send-signal.js.map +2 -2
- package/dist/commands/type.js +73 -6
- package/dist/commands/type.js.map +2 -2
- package/dist/commands/uninstall.js +10 -5
- package/dist/commands/uninstall.js.map +2 -2
- package/dist/commands/wait-for-exit.js +79 -7
- package/dist/commands/wait-for-exit.js.map +2 -2
- package/dist/commands/wait-for.js +90 -8
- package/dist/commands/wait-for.js.map +3 -3
- package/dist/index.js +52 -1
- package/dist/index.js.map +2 -2
- package/dist/keys.d.ts +1 -0
- package/dist/keys.js +15 -0
- package/dist/keys.js.map +2 -2
- package/dist/terminal-buffer.d.ts +2 -0
- package/dist/terminal-buffer.js +38 -1
- package/dist/terminal-buffer.js.map +2 -2
- package/dist/terminal-pilot.js +52 -1
- package/dist/terminal-pilot.js.map +2 -2
- package/dist/terminal-session.js +52 -1
- package/dist/terminal-session.js.map +2 -2
- package/dist/testing/cli-repl.js +430 -68
- package/dist/testing/cli-repl.js.map +3 -3
- package/dist/testing/qa-cli.js +430 -68
- package/dist/testing/qa-cli.js.map +3 -3
- package/package.json +1 -1
|
@@ -425,6 +425,84 @@ function defineCommand(config) {
|
|
|
425
425
|
);
|
|
426
426
|
}
|
|
427
427
|
|
|
428
|
+
// src/keys.ts
|
|
429
|
+
var NAMED_KEY_SEQUENCES = {
|
|
430
|
+
Enter: "\r",
|
|
431
|
+
Tab: " ",
|
|
432
|
+
Escape: "\x1B",
|
|
433
|
+
Backspace: "\x7F",
|
|
434
|
+
Delete: "\x1B[3~",
|
|
435
|
+
ArrowUp: "\x1B[A",
|
|
436
|
+
ArrowDown: "\x1B[B",
|
|
437
|
+
ArrowRight: "\x1B[C",
|
|
438
|
+
ArrowLeft: "\x1B[D",
|
|
439
|
+
Home: "\x1B[H",
|
|
440
|
+
End: "\x1B[F",
|
|
441
|
+
PageUp: "\x1B[5~",
|
|
442
|
+
PageDown: "\x1B[6~",
|
|
443
|
+
Space: " "
|
|
444
|
+
};
|
|
445
|
+
var NAMED_KEY_LOWER = new Map(
|
|
446
|
+
Object.entries(NAMED_KEY_SEQUENCES).map(([k, v]) => [k.toLowerCase(), v])
|
|
447
|
+
);
|
|
448
|
+
var VALID_KEYS_HINT = `Valid keys: ${Object.keys(NAMED_KEY_SEQUENCES).join(", ")}, Control+<letter>, Alt+<key>`;
|
|
449
|
+
var NAMED_KEY_PATTERN = Object.keys(NAMED_KEY_SEQUENCES).map(caseInsensitivePattern).join("|");
|
|
450
|
+
var TERMINAL_KEY_PATTERN = `^(?:${NAMED_KEY_PATTERN}|.|[Cc][Oo][Nn][Tt][Rr][Oo][Ll]\\+[A-Za-z]|[Aa][Ll][Tt]\\+.+)$`;
|
|
451
|
+
function unknownKeyError(key) {
|
|
452
|
+
return new Error(`Unknown terminal key: ${key}. ${VALID_KEYS_HINT}`);
|
|
453
|
+
}
|
|
454
|
+
function keyToSequence(key) {
|
|
455
|
+
const lowerKey = key.toLowerCase();
|
|
456
|
+
const namedSequence = NAMED_KEY_LOWER.get(lowerKey);
|
|
457
|
+
if (namedSequence !== void 0) {
|
|
458
|
+
return namedSequence;
|
|
459
|
+
}
|
|
460
|
+
if (lowerKey.startsWith("control+")) {
|
|
461
|
+
return controlKeyToSequence(key.slice("control+".length));
|
|
462
|
+
}
|
|
463
|
+
if (lowerKey.startsWith("alt+")) {
|
|
464
|
+
const nestedKey = key.slice("alt+".length);
|
|
465
|
+
if (nestedKey.length === 0) {
|
|
466
|
+
throw unknownKeyError(key);
|
|
467
|
+
}
|
|
468
|
+
if (nestedKey.length === 1) {
|
|
469
|
+
return "\x1B" + nestedKey;
|
|
470
|
+
}
|
|
471
|
+
try {
|
|
472
|
+
return "\x1B" + keyToSequence(nestedKey);
|
|
473
|
+
} catch {
|
|
474
|
+
throw unknownKeyError(key);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
if (key.length === 1) {
|
|
478
|
+
return key;
|
|
479
|
+
}
|
|
480
|
+
throw unknownKeyError(key);
|
|
481
|
+
}
|
|
482
|
+
function controlKeyToSequence(controlKey) {
|
|
483
|
+
if (controlKey.length !== 1) {
|
|
484
|
+
throw unknownKeyError(`Control+${controlKey}`);
|
|
485
|
+
}
|
|
486
|
+
const uppercaseLetter = controlKey.toUpperCase();
|
|
487
|
+
const charCode = uppercaseLetter.charCodeAt(0);
|
|
488
|
+
if (charCode < 65 || charCode > 90) {
|
|
489
|
+
throw unknownKeyError(`Control+${controlKey}`);
|
|
490
|
+
}
|
|
491
|
+
return String.fromCharCode(charCode - 64);
|
|
492
|
+
}
|
|
493
|
+
function caseInsensitivePattern(value) {
|
|
494
|
+
let pattern = "";
|
|
495
|
+
for (const character of value) {
|
|
496
|
+
const lower = character.toLowerCase();
|
|
497
|
+
const upper = character.toUpperCase();
|
|
498
|
+
pattern += lower === upper ? escapePatternCharacter(character) : `[${upper}${lower}]`;
|
|
499
|
+
}
|
|
500
|
+
return pattern;
|
|
501
|
+
}
|
|
502
|
+
function escapePatternCharacter(character) {
|
|
503
|
+
return character.replace(/[\\^$.*+?()[\]{}|]/g, "\\$&");
|
|
504
|
+
}
|
|
505
|
+
|
|
428
506
|
// src/terminal-pilot.ts
|
|
429
507
|
import { randomUUID } from "node:crypto";
|
|
430
508
|
|
|
@@ -678,6 +756,10 @@ var TerminalBuffer = class {
|
|
|
678
756
|
}
|
|
679
757
|
_writePrintable(ch) {
|
|
680
758
|
const width = this._cellWidth(ch);
|
|
759
|
+
if (width === 0) {
|
|
760
|
+
this._appendCombiningMark(ch);
|
|
761
|
+
return;
|
|
762
|
+
}
|
|
681
763
|
if (this._autoWrap && this._pendingWrap) {
|
|
682
764
|
this._cursorX = 0;
|
|
683
765
|
this._newline();
|
|
@@ -712,11 +794,40 @@ var TerminalBuffer = class {
|
|
|
712
794
|
_cellWidth(ch) {
|
|
713
795
|
const codePoint = ch.codePointAt(0);
|
|
714
796
|
if (codePoint === void 0) return 0;
|
|
797
|
+
if (isCombiningCodePoint(codePoint)) {
|
|
798
|
+
return 0;
|
|
799
|
+
}
|
|
715
800
|
if (codePoint >= 4352 && codePoint <= 4447 || codePoint === 9001 || codePoint === 9002 || codePoint >= 11904 && codePoint <= 12350 || codePoint >= 12353 && codePoint <= 13247 || codePoint >= 13312 && codePoint <= 19903 || codePoint >= 19968 && codePoint <= 42191 || codePoint >= 44032 && codePoint <= 55215 || codePoint >= 63744 && codePoint <= 64255 || codePoint >= 65040 && codePoint <= 65049 || codePoint >= 65072 && codePoint <= 65135 || codePoint >= 65280 && codePoint <= 65376 || codePoint >= 65504 && codePoint <= 65510 || codePoint >= 127488 && codePoint <= 131069 || codePoint >= 131072 && codePoint <= 262141) {
|
|
716
801
|
return Math.min(2, this._cols);
|
|
717
802
|
}
|
|
718
803
|
return 1;
|
|
719
804
|
}
|
|
805
|
+
_appendCombiningMark(ch) {
|
|
806
|
+
const target = this._findCombiningTarget();
|
|
807
|
+
if (target === null) {
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
810
|
+
const cell = this._screen[target.y]?.[target.x];
|
|
811
|
+
if (cell === null || cell === void 0) {
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
814
|
+
cell[1] += ch;
|
|
815
|
+
}
|
|
816
|
+
_findCombiningTarget() {
|
|
817
|
+
const startX = this._pendingWrap ? this._cursorX : this._cursorX - 1;
|
|
818
|
+
for (let y = this._cursorY; y >= 0; y -= 1) {
|
|
819
|
+
const row = this._screen[y];
|
|
820
|
+
if (row === void 0) {
|
|
821
|
+
continue;
|
|
822
|
+
}
|
|
823
|
+
for (let x = y === this._cursorY ? startX : this._cols - 1; x >= 0; x -= 1) {
|
|
824
|
+
if (row[x] !== null) {
|
|
825
|
+
return { x, y };
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
return null;
|
|
830
|
+
}
|
|
720
831
|
_setChar(y, x, ch) {
|
|
721
832
|
const row = this._screen[y];
|
|
722
833
|
if (row && x >= 0 && x < this._cols) {
|
|
@@ -857,7 +968,8 @@ var TerminalBuffer = class {
|
|
|
857
968
|
case "J":
|
|
858
969
|
if (p0 === 0) {
|
|
859
970
|
this._eraseLine(this._cursorY, this._cursorX, this._cols - 1);
|
|
860
|
-
for (let y = this._cursorY + 1; y < this._rows; y++)
|
|
971
|
+
for (let y = this._cursorY + 1; y < this._rows; y++)
|
|
972
|
+
this._eraseLine(y, 0, this._cols - 1);
|
|
861
973
|
} else if (p0 === 1) {
|
|
862
974
|
for (let y = 0; y < this._cursorY; y++) this._eraseLine(y, 0, this._cols - 1);
|
|
863
975
|
this._eraseLine(this._cursorY, 0, this._cursorX);
|
|
@@ -1222,6 +1334,9 @@ function createDefaultStyleState() {
|
|
|
1222
1334
|
strikethrough: false
|
|
1223
1335
|
};
|
|
1224
1336
|
}
|
|
1337
|
+
function isCombiningCodePoint(codePoint) {
|
|
1338
|
+
return codePoint >= 768 && codePoint <= 879 || codePoint >= 6832 && codePoint <= 6911 || codePoint >= 7616 && codePoint <= 7679 || codePoint >= 8400 && codePoint <= 8447 || codePoint >= 65056 && codePoint <= 65071;
|
|
1339
|
+
}
|
|
1225
1340
|
function serializeStyleState(state) {
|
|
1226
1341
|
const codes = [];
|
|
1227
1342
|
if (state.bold) {
|
|
@@ -1254,70 +1369,6 @@ function serializeStyleState(state) {
|
|
|
1254
1369
|
return codes.length === 0 ? "" : `\x1B[${codes.join(";")}m`;
|
|
1255
1370
|
}
|
|
1256
1371
|
|
|
1257
|
-
// src/keys.ts
|
|
1258
|
-
var NAMED_KEY_SEQUENCES = {
|
|
1259
|
-
Enter: "\r",
|
|
1260
|
-
Tab: " ",
|
|
1261
|
-
Escape: "\x1B",
|
|
1262
|
-
Backspace: "\x7F",
|
|
1263
|
-
Delete: "\x1B[3~",
|
|
1264
|
-
ArrowUp: "\x1B[A",
|
|
1265
|
-
ArrowDown: "\x1B[B",
|
|
1266
|
-
ArrowRight: "\x1B[C",
|
|
1267
|
-
ArrowLeft: "\x1B[D",
|
|
1268
|
-
Home: "\x1B[H",
|
|
1269
|
-
End: "\x1B[F",
|
|
1270
|
-
PageUp: "\x1B[5~",
|
|
1271
|
-
PageDown: "\x1B[6~",
|
|
1272
|
-
Space: " "
|
|
1273
|
-
};
|
|
1274
|
-
var NAMED_KEY_LOWER = new Map(
|
|
1275
|
-
Object.entries(NAMED_KEY_SEQUENCES).map(([k, v]) => [k.toLowerCase(), v])
|
|
1276
|
-
);
|
|
1277
|
-
var VALID_KEYS_HINT = `Valid keys: ${Object.keys(NAMED_KEY_SEQUENCES).join(", ")}, Control+<letter>, Alt+<key>`;
|
|
1278
|
-
function unknownKeyError(key) {
|
|
1279
|
-
return new Error(`Unknown terminal key: ${key}. ${VALID_KEYS_HINT}`);
|
|
1280
|
-
}
|
|
1281
|
-
function keyToSequence(key) {
|
|
1282
|
-
const lowerKey = key.toLowerCase();
|
|
1283
|
-
const namedSequence = NAMED_KEY_LOWER.get(lowerKey);
|
|
1284
|
-
if (namedSequence !== void 0) {
|
|
1285
|
-
return namedSequence;
|
|
1286
|
-
}
|
|
1287
|
-
if (lowerKey.startsWith("control+")) {
|
|
1288
|
-
return controlKeyToSequence(key.slice("control+".length));
|
|
1289
|
-
}
|
|
1290
|
-
if (lowerKey.startsWith("alt+")) {
|
|
1291
|
-
const nestedKey = key.slice("alt+".length);
|
|
1292
|
-
if (nestedKey.length === 0) {
|
|
1293
|
-
throw unknownKeyError(key);
|
|
1294
|
-
}
|
|
1295
|
-
if (nestedKey.length === 1) {
|
|
1296
|
-
return "\x1B" + nestedKey;
|
|
1297
|
-
}
|
|
1298
|
-
try {
|
|
1299
|
-
return "\x1B" + keyToSequence(nestedKey);
|
|
1300
|
-
} catch {
|
|
1301
|
-
throw unknownKeyError(key);
|
|
1302
|
-
}
|
|
1303
|
-
}
|
|
1304
|
-
if (key.length === 1) {
|
|
1305
|
-
return key;
|
|
1306
|
-
}
|
|
1307
|
-
throw unknownKeyError(key);
|
|
1308
|
-
}
|
|
1309
|
-
function controlKeyToSequence(controlKey) {
|
|
1310
|
-
if (controlKey.length !== 1) {
|
|
1311
|
-
throw unknownKeyError(`Control+${controlKey}`);
|
|
1312
|
-
}
|
|
1313
|
-
const uppercaseLetter = controlKey.toUpperCase();
|
|
1314
|
-
const charCode = uppercaseLetter.charCodeAt(0);
|
|
1315
|
-
if (charCode < 65 || charCode > 90) {
|
|
1316
|
-
throw unknownKeyError(`Control+${controlKey}`);
|
|
1317
|
-
}
|
|
1318
|
-
return String.fromCharCode(charCode - 64);
|
|
1319
|
-
}
|
|
1320
|
-
|
|
1321
1372
|
// src/terminal-screen.ts
|
|
1322
1373
|
var TerminalScreen = class {
|
|
1323
1374
|
lines;
|
|
@@ -1756,7 +1807,11 @@ function createTerminalPilotRuntime(options = {}) {
|
|
|
1756
1807
|
const pendingNames = /* @__PURE__ */ new Set();
|
|
1757
1808
|
let pilotPromise;
|
|
1758
1809
|
function getRequestedName(name, env) {
|
|
1759
|
-
|
|
1810
|
+
const requestedName = name ?? env?.get(SESSION_ENV_VAR);
|
|
1811
|
+
if (requestedName !== void 0 && requestedName.trim().length === 0) {
|
|
1812
|
+
throw new UserError("Session name must not be empty.");
|
|
1813
|
+
}
|
|
1814
|
+
return requestedName;
|
|
1760
1815
|
}
|
|
1761
1816
|
async function getPilot() {
|
|
1762
1817
|
pilotPromise ??= launchPilot();
|
|
@@ -1792,7 +1847,9 @@ function createTerminalPilotRuntime(options = {}) {
|
|
|
1792
1847
|
const sessionId = nameToId.get(name);
|
|
1793
1848
|
if (sessionId === void 0) {
|
|
1794
1849
|
const active = await listSessions();
|
|
1795
|
-
throw new UserError(
|
|
1850
|
+
throw new UserError(
|
|
1851
|
+
`Session "${name}" was not found. ${formatAvailableSessions(active.map((entry) => entry.name))}`
|
|
1852
|
+
);
|
|
1796
1853
|
}
|
|
1797
1854
|
const pilot = await getPilot();
|
|
1798
1855
|
try {
|
|
@@ -1800,7 +1857,9 @@ function createTerminalPilotRuntime(options = {}) {
|
|
|
1800
1857
|
} catch {
|
|
1801
1858
|
forgetSession(name, sessionId);
|
|
1802
1859
|
const active = await listSessions();
|
|
1803
|
-
throw new UserError(
|
|
1860
|
+
throw new UserError(
|
|
1861
|
+
`Session "${name}" was not found. ${formatAvailableSessions(active.map((entry) => entry.name))}`
|
|
1862
|
+
);
|
|
1804
1863
|
}
|
|
1805
1864
|
}
|
|
1806
1865
|
async function listSessions() {
|
|
@@ -1831,6 +1890,9 @@ function createTerminalPilotRuntime(options = {}) {
|
|
|
1831
1890
|
}
|
|
1832
1891
|
return {
|
|
1833
1892
|
async createSession(params2, env) {
|
|
1893
|
+
if (params2.command.trim().length === 0) {
|
|
1894
|
+
throw new UserError("Command must not be empty.");
|
|
1895
|
+
}
|
|
1834
1896
|
const requestedName = getRequestedName(params2.session, env) ?? nextSessionName();
|
|
1835
1897
|
await discardExitedSessionName(requestedName);
|
|
1836
1898
|
if (nameToId.has(requestedName) || pendingNames.has(requestedName)) {
|
|
@@ -1896,8 +1958,10 @@ function createTerminalPilotRuntime(options = {}) {
|
|
|
1896
1958
|
|
|
1897
1959
|
// src/commands/press-key.ts
|
|
1898
1960
|
var params = S.Object({
|
|
1899
|
-
key: S.String({ description: "Named key to press" }),
|
|
1900
|
-
session: S.Optional(
|
|
1961
|
+
key: S.String({ description: "Named key to press", pattern: TERMINAL_KEY_PATTERN }),
|
|
1962
|
+
session: S.Optional(
|
|
1963
|
+
S.String({ short: "s", description: "Session name", minLength: 1, pattern: "\\S" })
|
|
1964
|
+
)
|
|
1901
1965
|
});
|
|
1902
1966
|
var pressKey = defineCommand({
|
|
1903
1967
|
name: "press-key",
|
|
@@ -1906,11 +1970,22 @@ var pressKey = defineCommand({
|
|
|
1906
1970
|
positional: ["key"],
|
|
1907
1971
|
params,
|
|
1908
1972
|
handler: async ({ params: params2, env, terminalPilotRuntime }) => {
|
|
1909
|
-
|
|
1973
|
+
assertTerminalKey(params2.key);
|
|
1974
|
+
const namedSession = await getTerminalPilotRuntime(terminalPilotRuntime).resolveSession(
|
|
1975
|
+
params2.session,
|
|
1976
|
+
env
|
|
1977
|
+
);
|
|
1910
1978
|
await namedSession.session.press(params2.key);
|
|
1911
1979
|
return void 0;
|
|
1912
1980
|
}
|
|
1913
1981
|
});
|
|
1982
|
+
function assertTerminalKey(key) {
|
|
1983
|
+
try {
|
|
1984
|
+
keyToSequence(key);
|
|
1985
|
+
} catch (error) {
|
|
1986
|
+
throw new UserError(error instanceof Error ? error.message : String(error));
|
|
1987
|
+
}
|
|
1988
|
+
}
|
|
1914
1989
|
export {
|
|
1915
1990
|
pressKey
|
|
1916
1991
|
};
|