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.
Files changed (57) hide show
  1. package/dist/cli.js +430 -68
  2. package/dist/cli.js.map +3 -3
  3. package/dist/commands/close-session.js +73 -6
  4. package/dist/commands/close-session.js.map +2 -2
  5. package/dist/commands/create-session.js +80 -9
  6. package/dist/commands/create-session.js.map +2 -2
  7. package/dist/commands/fill.js +73 -6
  8. package/dist/commands/fill.js.map +2 -2
  9. package/dist/commands/get-session.js +73 -6
  10. package/dist/commands/get-session.js.map +2 -2
  11. package/dist/commands/index.js +371 -62
  12. package/dist/commands/index.js.map +3 -3
  13. package/dist/commands/install.js +10 -5
  14. package/dist/commands/install.js.map +2 -2
  15. package/dist/commands/installer.js +10 -5
  16. package/dist/commands/installer.js.map +2 -2
  17. package/dist/commands/list-sessions.js +66 -4
  18. package/dist/commands/list-sessions.js.map +2 -2
  19. package/dist/commands/press-key.js +146 -71
  20. package/dist/commands/press-key.js.map +3 -3
  21. package/dist/commands/read-history.js +81 -7
  22. package/dist/commands/read-history.js.map +2 -2
  23. package/dist/commands/read-screen.js +73 -6
  24. package/dist/commands/read-screen.js.map +2 -2
  25. package/dist/commands/resize.js +75 -8
  26. package/dist/commands/resize.js.map +2 -2
  27. package/dist/commands/runtime.js +66 -4
  28. package/dist/commands/runtime.js.map +2 -2
  29. package/dist/commands/screenshot.js +228 -23
  30. package/dist/commands/screenshot.js.map +3 -3
  31. package/dist/commands/send-signal.js +73 -6
  32. package/dist/commands/send-signal.js.map +2 -2
  33. package/dist/commands/type.js +73 -6
  34. package/dist/commands/type.js.map +2 -2
  35. package/dist/commands/uninstall.js +10 -5
  36. package/dist/commands/uninstall.js.map +2 -2
  37. package/dist/commands/wait-for-exit.js +79 -7
  38. package/dist/commands/wait-for-exit.js.map +2 -2
  39. package/dist/commands/wait-for.js +90 -8
  40. package/dist/commands/wait-for.js.map +3 -3
  41. package/dist/index.js +52 -1
  42. package/dist/index.js.map +2 -2
  43. package/dist/keys.d.ts +1 -0
  44. package/dist/keys.js +15 -0
  45. package/dist/keys.js.map +2 -2
  46. package/dist/terminal-buffer.d.ts +2 -0
  47. package/dist/terminal-buffer.js +38 -1
  48. package/dist/terminal-buffer.js.map +2 -2
  49. package/dist/terminal-pilot.js +52 -1
  50. package/dist/terminal-pilot.js.map +2 -2
  51. package/dist/terminal-session.js +52 -1
  52. package/dist/terminal-session.js.map +2 -2
  53. package/dist/testing/cli-repl.js +430 -68
  54. package/dist/testing/cli-repl.js.map +3 -3
  55. package/dist/testing/qa-cli.js +430 -68
  56. package/dist/testing/qa-cli.js.map +3 -3
  57. package/package.json +1 -1
@@ -431,6 +431,9 @@ import { rename, rm, writeFile } from "node:fs/promises";
431
431
 
432
432
  // ../terminal-png/src/ansi-parser.ts
433
433
  var ESC = "\x1B";
434
+ var TAB_WIDTH = 8;
435
+ var MAX_TERMINAL_ROWS = 1e3;
436
+ var MAX_TERMINAL_COLUMNS = 1e3;
434
437
  function createDefaultStyle() {
435
438
  return {
436
439
  fg: null,
@@ -715,10 +718,34 @@ function positionParameter(params2, index, fallback) {
715
718
  const value = toInteger(params2.split(";")[index]);
716
719
  return value === null || value < 1 ? fallback : value;
717
720
  }
721
+ function modeParameter(params2, fallback) {
722
+ const value = toInteger(params2.split(";")[0]);
723
+ return value === null ? fallback : value;
724
+ }
725
+ function displayWidth(text) {
726
+ if (text === " ") {
727
+ return TAB_WIDTH;
728
+ }
729
+ const codePoint = text.codePointAt(0);
730
+ if (codePoint === void 0) {
731
+ return 0;
732
+ }
733
+ if (codePoint >= 4352 && codePoint <= 4447 || codePoint === 9001 || codePoint === 9002 || codePoint >= 11904 && codePoint <= 42191 && codePoint !== 12351 || codePoint >= 44032 && codePoint <= 55203 || codePoint >= 63744 && codePoint <= 64255 || codePoint >= 65040 && codePoint <= 65049 || codePoint >= 65072 && codePoint <= 65135 || codePoint >= 65280 && codePoint <= 65376 || codePoint >= 65504 && codePoint <= 65510 || codePoint >= 127744 && codePoint <= 128591 || codePoint >= 129280 && codePoint <= 129535 || codePoint >= 131072 && codePoint <= 262141) {
734
+ return 2;
735
+ }
736
+ return 1;
737
+ }
738
+ function hasRenderableCell(line) {
739
+ return (line ?? []).some((cell) => cell !== void 0 && cell.text.length > 0);
740
+ }
718
741
  function buildRuns(lines, lineBreakStyles) {
719
742
  const runs = [];
720
743
  const defaultStyle = createDefaultStyle();
721
- for (let row = 0; row < lines.length; row += 1) {
744
+ let lastRow = lines.length - 1;
745
+ while (lastRow > 0 && !hasRenderableCell(lines[lastRow]) && lineBreakStyles[lastRow - 1] === void 0) {
746
+ lastRow -= 1;
747
+ }
748
+ for (let row = 0; row <= lastRow; row += 1) {
722
749
  const line = lines[row] ?? [];
723
750
  let lastCell = line.length - 1;
724
751
  while (lastCell >= 0 && line[lastCell] === void 0) {
@@ -728,7 +755,7 @@ function buildRuns(lines, lineBreakStyles) {
728
755
  const cell = line[column] ?? { text: " ", style: defaultStyle };
729
756
  pushRun(runs, cell.style, cell.text);
730
757
  }
731
- if (row < lines.length - 1) {
758
+ if (row < lastRow) {
732
759
  pushRun(runs, lineBreakStyles[row] ?? defaultStyle, "\n");
733
760
  }
734
761
  }
@@ -740,7 +767,21 @@ function parseAnsi(input) {
740
767
  let style = createDefaultStyle();
741
768
  let row = 0;
742
769
  let column = 0;
770
+ let savedRow = 0;
771
+ let savedColumn = 0;
743
772
  let index = 0;
773
+ const clampRow = (nextRow) => {
774
+ if (nextRow < 0) {
775
+ return 0;
776
+ }
777
+ return Math.min(nextRow, MAX_TERMINAL_ROWS - 1);
778
+ };
779
+ const clampColumn = (nextColumn) => {
780
+ if (nextColumn < 0) {
781
+ return 0;
782
+ }
783
+ return Math.min(nextColumn, MAX_TERMINAL_COLUMNS - 1);
784
+ };
744
785
  const ensureLine = () => {
745
786
  while (lines.length <= row) {
746
787
  lines.push([]);
@@ -748,12 +789,64 @@ function parseAnsi(input) {
748
789
  };
749
790
  const moveDown = (keepColumn) => {
750
791
  lineBreakStyles[row] = cloneStyle(style);
751
- row += 1;
792
+ row = clampRow(row + 1);
752
793
  if (!keepColumn) {
753
794
  column = 0;
754
795
  }
755
796
  ensureLine();
756
797
  };
798
+ const eraseLine = (mode) => {
799
+ const line = lines[row] ?? [];
800
+ if (mode === 1) {
801
+ for (let cell = 0; cell <= column; cell += 1) {
802
+ delete line[cell];
803
+ }
804
+ return;
805
+ }
806
+ if (mode === 2) {
807
+ lines[row] = [];
808
+ return;
809
+ }
810
+ line.splice(column);
811
+ };
812
+ const eraseDisplay = (mode) => {
813
+ if (mode === 1) {
814
+ for (let lineRow = 0; lineRow < row; lineRow += 1) {
815
+ lines[lineRow] = [];
816
+ }
817
+ eraseLine(1);
818
+ return;
819
+ }
820
+ if (mode === 2 || mode === 3) {
821
+ lines.length = 0;
822
+ lineBreakStyles.length = 0;
823
+ ensureLine();
824
+ return;
825
+ }
826
+ eraseLine(0);
827
+ lines.splice(row + 1);
828
+ lineBreakStyles.splice(row);
829
+ };
830
+ const saveCursor = () => {
831
+ savedRow = row;
832
+ savedColumn = column;
833
+ };
834
+ const restoreCursor = () => {
835
+ row = clampRow(savedRow);
836
+ column = clampColumn(savedColumn);
837
+ ensureLine();
838
+ };
839
+ const writeText = (text) => {
840
+ const width = displayWidth(text);
841
+ if (width === 0) {
842
+ return;
843
+ }
844
+ lines[row][column] = { text, style: cloneStyle(style) };
845
+ for (let offset = 1; offset < width && column + offset < MAX_TERMINAL_COLUMNS; offset += 1) {
846
+ lines[row][column + offset] = { text: "", style: cloneStyle(style) };
847
+ }
848
+ column = clampColumn(column + width);
849
+ };
757
850
  while (index < input.length) {
758
851
  const char = input[index];
759
852
  if (char === "\n") {
@@ -776,38 +869,73 @@ function parseAnsi(input) {
776
869
  index += 1;
777
870
  continue;
778
871
  }
872
+ if (char === " ") {
873
+ const nextTabStop = Math.min(
874
+ Math.floor(column / TAB_WIDTH) * TAB_WIDTH + TAB_WIDTH,
875
+ MAX_TERMINAL_COLUMNS
876
+ );
877
+ while (column < nextTabStop) {
878
+ writeText(" ");
879
+ }
880
+ index += 1;
881
+ continue;
882
+ }
779
883
  if (char === ESC && input[index + 1] === "]") {
780
884
  index = parseOscEnd(input, index);
781
885
  continue;
782
886
  }
887
+ if (char === ESC && input[index + 1] === "7") {
888
+ saveCursor();
889
+ index += 2;
890
+ continue;
891
+ }
892
+ if (char === ESC && input[index + 1] === "8") {
893
+ restoreCursor();
894
+ index += 2;
895
+ continue;
896
+ }
783
897
  const csiPrefixLength = char === "\x9B" ? 1 : char === ESC && input[index + 1] === "[" ? 2 : null;
784
898
  if (csiPrefixLength !== null) {
785
899
  const sequence = parseCsi(input, index, csiPrefixLength);
786
900
  if (sequence.final === "m") {
787
901
  style = applySgr(style, sequence.params);
788
902
  } else if (sequence.final === "G") {
789
- column = positionParameter(sequence.params, 0, 1) - 1;
903
+ column = clampColumn(positionParameter(sequence.params, 0, 1) - 1);
790
904
  } else if (sequence.final === "C") {
791
- column += positionParameter(sequence.params, 0, 1);
905
+ column = clampColumn(column + positionParameter(sequence.params, 0, 1));
792
906
  } else if (sequence.final === "D") {
793
907
  column = Math.max(0, column - positionParameter(sequence.params, 0, 1));
794
908
  } else if (sequence.final === "A") {
795
- row = Math.max(0, row - positionParameter(sequence.params, 0, 1));
909
+ row = clampRow(row - positionParameter(sequence.params, 0, 1));
796
910
  } else if (sequence.final === "B") {
797
- row += positionParameter(sequence.params, 0, 1);
911
+ row = clampRow(row + positionParameter(sequence.params, 0, 1));
798
912
  ensureLine();
913
+ } else if (sequence.final === "E") {
914
+ row = clampRow(row + positionParameter(sequence.params, 0, 1));
915
+ column = 0;
916
+ ensureLine();
917
+ } else if (sequence.final === "F") {
918
+ row = clampRow(row - positionParameter(sequence.params, 0, 1));
919
+ column = 0;
799
920
  } else if (sequence.final === "H" || sequence.final === "f") {
800
- row = positionParameter(sequence.params, 0, 1) - 1;
801
- column = positionParameter(sequence.params, 1, 1) - 1;
921
+ row = clampRow(positionParameter(sequence.params, 0, 1) - 1);
922
+ column = clampColumn(positionParameter(sequence.params, 1, 1) - 1);
802
923
  ensureLine();
924
+ } else if (sequence.final === "K") {
925
+ eraseLine(modeParameter(sequence.params, 0));
926
+ } else if (sequence.final === "J") {
927
+ eraseDisplay(modeParameter(sequence.params, 0));
928
+ } else if (sequence.final === "s") {
929
+ saveCursor();
930
+ } else if (sequence.final === "u") {
931
+ restoreCursor();
803
932
  }
804
933
  index = sequence.end;
805
934
  continue;
806
935
  }
807
936
  const codePoint = input.codePointAt(index);
808
937
  const text = codePoint === void 0 ? "" : String.fromCodePoint(codePoint);
809
- lines[row][column] = { text, style: cloneStyle(style) };
810
- column += 1;
938
+ writeText(text);
811
939
  index += text.length;
812
940
  }
813
941
  return buildRuns(lines, lineBreakStyles);
@@ -1215,11 +1343,11 @@ function splitIntoLines(runs) {
1215
1343
  }
1216
1344
  function measureLines(lines) {
1217
1345
  return Math.max(
1218
- ...lines.map((line) => displayWidth(line.map((run) => run.text).join("")) * CHARACTER_WIDTH),
1346
+ ...lines.map((line) => displayWidth2(line.map((run) => run.text).join("")) * CHARACTER_WIDTH),
1219
1347
  0
1220
1348
  );
1221
1349
  }
1222
- function displayWidth(text, startColumn = 0) {
1350
+ function displayWidth2(text, startColumn = 0) {
1223
1351
  const segmenter = new Intl.Segmenter();
1224
1352
  let column = startColumn;
1225
1353
  for (const { segment } of segmenter.segment(text)) {
@@ -1281,7 +1409,7 @@ function renderBackgrounds(line, startX, y, height) {
1281
1409
  let column = 0;
1282
1410
  const rectangles = [];
1283
1411
  for (const run of line) {
1284
- const width = displayWidth(run.text, column);
1412
+ const width = displayWidth2(run.text, column);
1285
1413
  const background = resolveBackgroundColor(run);
1286
1414
  if (background !== BACKGROUND && width > 0) {
1287
1415
  rectangles.push(`<rect x="${formatNumber(startX + column * CHARACTER_WIDTH)}" y="${formatNumber(y)}" width="${formatNumber(width * CHARACTER_WIDTH)}" height="${formatNumber(height)}" fill="${escapeXmlAttribute(background)}" />`);
@@ -1315,7 +1443,7 @@ function renderRun(run) {
1315
1443
  if (run.dim) {
1316
1444
  attributes.push('opacity="0.7"');
1317
1445
  }
1318
- const text = run.conceal ? " ".repeat(displayWidth(run.text)) : run.text;
1446
+ const text = run.conceal ? " ".repeat(displayWidth2(run.text)) : run.text;
1319
1447
  return `<tspan ${attributes.join(" ")}>${escapeXmlText(text)}</tspan>`;
1320
1448
  }
1321
1449
  function renderWindowControls() {
@@ -1364,13 +1492,16 @@ async function renderTerminalPng(ansiText, options = {}) {
1364
1492
  if (options.padding !== void 0 && (!Number.isInteger(options.padding) || options.padding < 0)) {
1365
1493
  throw new Error("Padding must be a non-negative integer.");
1366
1494
  }
1495
+ if (options.output !== void 0 && options.output.length === 0) {
1496
+ throw new Error("Output path must not be empty.");
1497
+ }
1367
1498
  const runs = parseAnsi(ansiText);
1368
1499
  const svg = renderSvg(runs, {
1369
1500
  padding: options.padding,
1370
1501
  window: options.window
1371
1502
  });
1372
1503
  const png = renderPng(svg);
1373
- if (options.output) {
1504
+ if (options.output !== void 0) {
1374
1505
  const temporaryPath = `${options.output}.${randomUUID()}.tmp`;
1375
1506
  let temporaryCreated = false;
1376
1507
  try {
@@ -1647,6 +1778,10 @@ var TerminalBuffer = class {
1647
1778
  }
1648
1779
  _writePrintable(ch) {
1649
1780
  const width = this._cellWidth(ch);
1781
+ if (width === 0) {
1782
+ this._appendCombiningMark(ch);
1783
+ return;
1784
+ }
1650
1785
  if (this._autoWrap && this._pendingWrap) {
1651
1786
  this._cursorX = 0;
1652
1787
  this._newline();
@@ -1681,11 +1816,40 @@ var TerminalBuffer = class {
1681
1816
  _cellWidth(ch) {
1682
1817
  const codePoint = ch.codePointAt(0);
1683
1818
  if (codePoint === void 0) return 0;
1819
+ if (isCombiningCodePoint(codePoint)) {
1820
+ return 0;
1821
+ }
1684
1822
  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) {
1685
1823
  return Math.min(2, this._cols);
1686
1824
  }
1687
1825
  return 1;
1688
1826
  }
1827
+ _appendCombiningMark(ch) {
1828
+ const target = this._findCombiningTarget();
1829
+ if (target === null) {
1830
+ return;
1831
+ }
1832
+ const cell = this._screen[target.y]?.[target.x];
1833
+ if (cell === null || cell === void 0) {
1834
+ return;
1835
+ }
1836
+ cell[1] += ch;
1837
+ }
1838
+ _findCombiningTarget() {
1839
+ const startX = this._pendingWrap ? this._cursorX : this._cursorX - 1;
1840
+ for (let y = this._cursorY; y >= 0; y -= 1) {
1841
+ const row = this._screen[y];
1842
+ if (row === void 0) {
1843
+ continue;
1844
+ }
1845
+ for (let x = y === this._cursorY ? startX : this._cols - 1; x >= 0; x -= 1) {
1846
+ if (row[x] !== null) {
1847
+ return { x, y };
1848
+ }
1849
+ }
1850
+ }
1851
+ return null;
1852
+ }
1689
1853
  _setChar(y, x, ch) {
1690
1854
  const row = this._screen[y];
1691
1855
  if (row && x >= 0 && x < this._cols) {
@@ -1826,7 +1990,8 @@ var TerminalBuffer = class {
1826
1990
  case "J":
1827
1991
  if (p0 === 0) {
1828
1992
  this._eraseLine(this._cursorY, this._cursorX, this._cols - 1);
1829
- for (let y = this._cursorY + 1; y < this._rows; y++) this._eraseLine(y, 0, this._cols - 1);
1993
+ for (let y = this._cursorY + 1; y < this._rows; y++)
1994
+ this._eraseLine(y, 0, this._cols - 1);
1830
1995
  } else if (p0 === 1) {
1831
1996
  for (let y = 0; y < this._cursorY; y++) this._eraseLine(y, 0, this._cols - 1);
1832
1997
  this._eraseLine(this._cursorY, 0, this._cursorX);
@@ -2191,6 +2356,9 @@ function createDefaultStyleState() {
2191
2356
  strikethrough: false
2192
2357
  };
2193
2358
  }
2359
+ function isCombiningCodePoint(codePoint) {
2360
+ return codePoint >= 768 && codePoint <= 879 || codePoint >= 6832 && codePoint <= 6911 || codePoint >= 7616 && codePoint <= 7679 || codePoint >= 8400 && codePoint <= 8447 || codePoint >= 65056 && codePoint <= 65071;
2361
+ }
2194
2362
  function serializeStyleState(state) {
2195
2363
  const codes = [];
2196
2364
  if (state.bold) {
@@ -2244,6 +2412,8 @@ var NAMED_KEY_LOWER = new Map(
2244
2412
  Object.entries(NAMED_KEY_SEQUENCES).map(([k, v]) => [k.toLowerCase(), v])
2245
2413
  );
2246
2414
  var VALID_KEYS_HINT = `Valid keys: ${Object.keys(NAMED_KEY_SEQUENCES).join(", ")}, Control+<letter>, Alt+<key>`;
2415
+ var NAMED_KEY_PATTERN = Object.keys(NAMED_KEY_SEQUENCES).map(caseInsensitivePattern).join("|");
2416
+ var TERMINAL_KEY_PATTERN = `^(?:${NAMED_KEY_PATTERN}|.|[Cc][Oo][Nn][Tt][Rr][Oo][Ll]\\+[A-Za-z]|[Aa][Ll][Tt]\\+.+)$`;
2247
2417
  function unknownKeyError(key) {
2248
2418
  return new Error(`Unknown terminal key: ${key}. ${VALID_KEYS_HINT}`);
2249
2419
  }
@@ -2286,6 +2456,18 @@ function controlKeyToSequence(controlKey) {
2286
2456
  }
2287
2457
  return String.fromCharCode(charCode - 64);
2288
2458
  }
2459
+ function caseInsensitivePattern(value) {
2460
+ let pattern = "";
2461
+ for (const character of value) {
2462
+ const lower = character.toLowerCase();
2463
+ const upper = character.toUpperCase();
2464
+ pattern += lower === upper ? escapePatternCharacter(character) : `[${upper}${lower}]`;
2465
+ }
2466
+ return pattern;
2467
+ }
2468
+ function escapePatternCharacter(character) {
2469
+ return character.replace(/[\\^$.*+?()[\]{}|]/g, "\\$&");
2470
+ }
2289
2471
 
2290
2472
  // src/terminal-screen.ts
2291
2473
  var TerminalScreen = class {
@@ -2725,7 +2907,11 @@ function createTerminalPilotRuntime(options = {}) {
2725
2907
  const pendingNames = /* @__PURE__ */ new Set();
2726
2908
  let pilotPromise;
2727
2909
  function getRequestedName(name, env) {
2728
- return name ?? env?.get(SESSION_ENV_VAR);
2910
+ const requestedName = name ?? env?.get(SESSION_ENV_VAR);
2911
+ if (requestedName !== void 0 && requestedName.trim().length === 0) {
2912
+ throw new UserError("Session name must not be empty.");
2913
+ }
2914
+ return requestedName;
2729
2915
  }
2730
2916
  async function getPilot() {
2731
2917
  pilotPromise ??= launchPilot();
@@ -2761,7 +2947,9 @@ function createTerminalPilotRuntime(options = {}) {
2761
2947
  const sessionId = nameToId.get(name);
2762
2948
  if (sessionId === void 0) {
2763
2949
  const active = await listSessions();
2764
- throw new UserError(`Session "${name}" was not found. ${formatAvailableSessions(active.map((entry) => entry.name))}`);
2950
+ throw new UserError(
2951
+ `Session "${name}" was not found. ${formatAvailableSessions(active.map((entry) => entry.name))}`
2952
+ );
2765
2953
  }
2766
2954
  const pilot = await getPilot();
2767
2955
  try {
@@ -2769,7 +2957,9 @@ function createTerminalPilotRuntime(options = {}) {
2769
2957
  } catch {
2770
2958
  forgetSession(name, sessionId);
2771
2959
  const active = await listSessions();
2772
- throw new UserError(`Session "${name}" was not found. ${formatAvailableSessions(active.map((entry) => entry.name))}`);
2960
+ throw new UserError(
2961
+ `Session "${name}" was not found. ${formatAvailableSessions(active.map((entry) => entry.name))}`
2962
+ );
2773
2963
  }
2774
2964
  }
2775
2965
  async function listSessions() {
@@ -2800,6 +2990,9 @@ function createTerminalPilotRuntime(options = {}) {
2800
2990
  }
2801
2991
  return {
2802
2992
  async createSession(params2, env) {
2993
+ if (params2.command.trim().length === 0) {
2994
+ throw new UserError("Command must not be empty.");
2995
+ }
2803
2996
  const requestedName = getRequestedName(params2.session, env) ?? nextSessionName();
2804
2997
  await discardExitedSessionName(requestedName);
2805
2998
  if (nameToId.has(requestedName) || pendingNames.has(requestedName)) {
@@ -2865,10 +3058,19 @@ function createTerminalPilotRuntime(options = {}) {
2865
3058
 
2866
3059
  // src/commands/screenshot.ts
2867
3060
  var params = S.Object({
2868
- session: S.Optional(S.String({ short: "s", description: "Session name" })),
3061
+ session: S.Optional(
3062
+ S.String({ short: "s", description: "Session name", minLength: 1, pattern: "\\S" })
3063
+ ),
2869
3064
  output: S.String({ short: "o", description: "Path to the output PNG file" }),
2870
3065
  window: S.Optional(S.Boolean({ description: "Include terminal window chrome", default: true })),
2871
- padding: S.Optional(S.Number({ short: "p", description: "Padding around terminal content" }))
3066
+ padding: S.Optional(
3067
+ S.Number({
3068
+ short: "p",
3069
+ description: "Padding around terminal content",
3070
+ jsonType: "integer",
3071
+ minimum: 0
3072
+ })
3073
+ )
2872
3074
  });
2873
3075
  var screenshot = defineCommand({
2874
3076
  name: "screenshot",
@@ -2876,7 +3078,10 @@ var screenshot = defineCommand({
2876
3078
  scope: ["cli"],
2877
3079
  params,
2878
3080
  handler: async ({ params: params2, env, terminalPilotRuntime }) => {
2879
- const namedSession = await getTerminalPilotRuntime(terminalPilotRuntime).resolveSession(params2.session, env);
3081
+ const namedSession = await getTerminalPilotRuntime(terminalPilotRuntime).resolveSession(
3082
+ params2.session,
3083
+ env
3084
+ );
2880
3085
  const screen = await namedSession.session.screen();
2881
3086
  await renderTerminalPng(screen.rawLines.join("\n"), {
2882
3087
  output: params2.output,