terminal-pilot 0.0.18 → 0.0.19

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 (58) hide show
  1. package/dist/cli.js +3467 -1463
  2. package/dist/cli.js.map +4 -4
  3. package/dist/commands/close-session.js +413 -98
  4. package/dist/commands/close-session.js.map +2 -2
  5. package/dist/commands/create-session.js +413 -98
  6. package/dist/commands/create-session.js.map +2 -2
  7. package/dist/commands/fill.js +413 -98
  8. package/dist/commands/fill.js.map +2 -2
  9. package/dist/commands/get-session.js +413 -98
  10. package/dist/commands/get-session.js.map +2 -2
  11. package/dist/commands/index.d.ts +224 -1
  12. package/dist/commands/index.js +1109 -264
  13. package/dist/commands/index.js.map +4 -4
  14. package/dist/commands/install.js +457 -107
  15. package/dist/commands/install.js.map +4 -4
  16. package/dist/commands/installer.d.ts +5 -0
  17. package/dist/commands/installer.js +127 -36
  18. package/dist/commands/installer.js.map +4 -4
  19. package/dist/commands/list-sessions.js +413 -98
  20. package/dist/commands/list-sessions.js.map +2 -2
  21. package/dist/commands/press-key.js +413 -98
  22. package/dist/commands/press-key.js.map +2 -2
  23. package/dist/commands/read-history.js +413 -98
  24. package/dist/commands/read-history.js.map +2 -2
  25. package/dist/commands/read-screen.js +413 -98
  26. package/dist/commands/read-screen.js.map +2 -2
  27. package/dist/commands/resize.js +413 -98
  28. package/dist/commands/resize.js.map +2 -2
  29. package/dist/commands/runtime.js +354 -87
  30. package/dist/commands/runtime.js.map +2 -2
  31. package/dist/commands/screenshot.js +648 -138
  32. package/dist/commands/screenshot.js.map +3 -3
  33. package/dist/commands/send-signal.js +413 -98
  34. package/dist/commands/send-signal.js.map +2 -2
  35. package/dist/commands/type.js +413 -98
  36. package/dist/commands/type.js.map +2 -2
  37. package/dist/commands/uninstall.js +213 -61
  38. package/dist/commands/uninstall.js.map +4 -4
  39. package/dist/commands/wait-for-exit.js +413 -98
  40. package/dist/commands/wait-for-exit.js.map +2 -2
  41. package/dist/commands/wait-for.js +413 -98
  42. package/dist/commands/wait-for.js.map +2 -2
  43. package/dist/index.d.ts +1 -0
  44. package/dist/index.js +318 -80
  45. package/dist/index.js.map +3 -3
  46. package/dist/terminal-buffer.d.ts +18 -0
  47. package/dist/terminal-buffer.js +234 -43
  48. package/dist/terminal-buffer.js.map +2 -2
  49. package/dist/terminal-pilot.js +312 -73
  50. package/dist/terminal-pilot.js.map +2 -2
  51. package/dist/terminal-session.d.ts +2 -2
  52. package/dist/terminal-session.js +306 -68
  53. package/dist/terminal-session.js.map +2 -2
  54. package/dist/testing/cli-repl.js +3463 -1459
  55. package/dist/testing/cli-repl.js.map +4 -4
  56. package/dist/testing/qa-cli.js +3486 -1482
  57. package/dist/testing/qa-cli.js.map +4 -4
  58. package/package.json +6 -2
@@ -40,13 +40,20 @@ function Json() {
40
40
  }
41
41
 
42
42
  // ../toolcraft-schema/src/oneof.ts
43
- function assertValidBranches(branches) {
43
+ function assertValidBranches(branches, discriminator) {
44
44
  if (Object.keys(branches).length === 0) {
45
45
  throw new Error("OneOf schema requires at least one branch");
46
46
  }
47
+ for (const [branchName, branch] of Object.entries(branches)) {
48
+ if (Object.prototype.hasOwnProperty.call(branch.shape, discriminator)) {
49
+ throw new Error(
50
+ `OneOf branch "${branchName}" must not declare discriminator field "${discriminator}".`
51
+ );
52
+ }
53
+ }
47
54
  }
48
55
  function OneOf(config) {
49
- assertValidBranches(config.branches);
56
+ assertValidBranches(config.branches, config.discriminator);
50
57
  return {
51
58
  kind: "oneOf",
52
59
  discriminator: config.discriminator,
@@ -69,21 +76,25 @@ function isOptionalSchema(schema) {
69
76
  function getRequiredKeys(schema) {
70
77
  return Object.keys(schema.shape).filter((key) => !isOptionalSchema(schema.shape[key])).sort();
71
78
  }
72
- function getRequiredKeyFingerprint(schema) {
73
- return getRequiredKeys(schema).join("+");
74
- }
75
79
  function assertUniqueRequiredKeyFingerprints(branches) {
76
80
  const fingerprints = /* @__PURE__ */ new Map();
77
81
  branches.forEach((branch, index) => {
78
- const fingerprint = getRequiredKeyFingerprint(branch);
79
- const indices = fingerprints.get(fingerprint) ?? [];
80
- indices.push(index);
81
- fingerprints.set(fingerprint, indices);
82
+ const requiredKeys = getRequiredKeys(branch);
83
+ const fingerprint = JSON.stringify(requiredKeys);
84
+ const existing = fingerprints.get(fingerprint);
85
+ if (existing === void 0) {
86
+ fingerprints.set(fingerprint, {
87
+ display: requiredKeys.join("+"),
88
+ indices: [index]
89
+ });
90
+ return;
91
+ }
92
+ existing.indices.push(index);
82
93
  });
83
- for (const [fingerprint, indices] of fingerprints) {
94
+ for (const { display, indices } of fingerprints.values()) {
84
95
  if (indices.length > 1) {
85
96
  throw new Error(
86
- `Union branches [${indices.join(", ")}] share required-key fingerprint "${fingerprint}". Each branch must require a distinct set of keys.`
97
+ `Union branches [${indices.join(", ")}] share required-key fingerprint "${display}". Each branch must require a distinct set of keys.`
87
98
  );
88
99
  }
89
100
  }
@@ -111,15 +122,47 @@ function assertValidEnumValues(values) {
111
122
  if (uniqueValues.size !== values.length) {
112
123
  throw new Error("Enum schema values must be unique");
113
124
  }
125
+ if (values.some((value) => typeof value === "number" && !Number.isFinite(value))) {
126
+ throw new Error("Enum schema numeric values must be finite");
127
+ }
128
+ }
129
+ function assertNonNegativeInteger(value, name) {
130
+ if (value !== void 0 && (!Number.isInteger(value) || value < 0)) {
131
+ throw new Error(`${name} must be a non-negative integer`);
132
+ }
133
+ }
134
+ function assertFiniteNumber(value, name) {
135
+ if (value !== void 0 && !Number.isFinite(value)) {
136
+ throw new Error(`${name} must be finite`);
137
+ }
138
+ }
139
+ function assertPattern(pattern) {
140
+ if (pattern === void 0) {
141
+ return;
142
+ }
143
+ try {
144
+ new RegExp(pattern);
145
+ } catch {
146
+ throw new Error("pattern must be a valid regular expression");
147
+ }
114
148
  }
115
149
  var S = {
116
150
  String(options = {}) {
151
+ assertNonNegativeInteger(options.minLength, "minLength");
152
+ assertNonNegativeInteger(options.maxLength, "maxLength");
153
+ assertPattern(options.pattern);
117
154
  return {
118
155
  kind: "string",
119
156
  ...options
120
157
  };
121
158
  },
122
159
  Number(options = {}) {
160
+ assertFiniteNumber(options.minimum, "minimum");
161
+ assertFiniteNumber(options.maximum, "maximum");
162
+ assertFiniteNumber(options.default, "default");
163
+ if (options.jsonType === "integer" && options.default !== void 0 && !Number.isInteger(options.default)) {
164
+ throw new Error("default must be an integer");
165
+ }
123
166
  return {
124
167
  kind: "number",
125
168
  ...options
@@ -133,6 +176,9 @@ var S = {
133
176
  },
134
177
  Enum(values, options = {}) {
135
178
  assertValidEnumValues(values);
179
+ if (options.jsonType === "integer" && values.some((value) => typeof value !== "number" || !Number.isInteger(value))) {
180
+ throw new Error("Integer enum values must be integers");
181
+ }
136
182
  return {
137
183
  kind: "enum",
138
184
  values,
@@ -140,6 +186,8 @@ var S = {
140
186
  };
141
187
  },
142
188
  Array(item, options = {}) {
189
+ assertNonNegativeInteger(options.minItems, "minItems");
190
+ assertNonNegativeInteger(options.maxItems, "maxItems");
143
191
  return {
144
192
  kind: "array",
145
193
  item,
@@ -448,27 +496,53 @@ function consumeTerminatedString(input, index, allowBellTerminator) {
448
496
 
449
497
  // src/terminal-buffer.ts
450
498
  var RESET_SGR = "\x1B[0m";
499
+ var DEC_SPECIAL_GRAPHICS = {
500
+ j: "\u2518",
501
+ k: "\u2510",
502
+ l: "\u250C",
503
+ m: "\u2514",
504
+ n: "\u253C",
505
+ q: "\u2500",
506
+ t: "\u251C",
507
+ u: "\u2524",
508
+ v: "\u2534",
509
+ w: "\u252C",
510
+ x: "\u2502"
511
+ };
451
512
  var TerminalBuffer = class {
452
513
  _cols;
453
514
  _rows;
454
515
  _screen;
516
+ _primaryScreen = null;
455
517
  _cursorX = 0;
456
518
  _cursorY = 0;
457
- _savedCursor = { x: 0, y: 0 };
519
+ _savedCursor;
458
520
  _scrollTop = 0;
459
521
  _scrollBottom;
460
522
  _state = 0 /* Normal */;
461
523
  _csiParams = "";
462
524
  _csiPrivate = "";
463
525
  _autoWrap = true;
526
+ _pendingWrap = false;
527
+ _originMode = false;
528
+ _insertMode = false;
529
+ _tabStops;
530
+ _lastPrintedChar = null;
464
531
  _style = createDefaultStyleState();
465
532
  _styleSequence = "";
533
+ _stringState = 0 /* Normal */;
534
+ _charsetTarget = null;
535
+ _g0Charset = "ascii";
536
+ _g1Charset = "ascii";
537
+ _shiftOut = false;
466
538
  displayBuffer;
467
539
  constructor(cols, rows) {
468
540
  this._cols = cols;
469
541
  this._rows = rows;
470
542
  this._scrollBottom = rows - 1;
471
543
  this._screen = this._makeScreen(cols, rows);
544
+ this._tabStops = this._createDefaultTabStops(cols);
545
+ this._savedCursor = this._createSavedCursorState();
472
546
  this.displayBuffer = Object.defineProperties(
473
547
  {},
474
548
  {
@@ -524,8 +598,12 @@ var TerminalBuffer = class {
524
598
  }
525
599
  this._cols = cols;
526
600
  this._rows = rows;
527
- this._scrollTop = 0;
528
- this._scrollBottom = rows - 1;
601
+ this._scrollTop = this._clamp(this._scrollTop, 0, rows - 1);
602
+ this._scrollBottom = this._clamp(this._scrollBottom, 0, rows - 1);
603
+ if (this._scrollTop >= this._scrollBottom) {
604
+ this._scrollTop = 0;
605
+ this._scrollBottom = rows - 1;
606
+ }
529
607
  this._cursorX = this._clamp(this._cursorX, 0, cols - 1);
530
608
  this._cursorY = this._clamp(this._cursorY, 0, rows - 1);
531
609
  }
@@ -535,13 +613,96 @@ var TerminalBuffer = class {
535
613
  _makeRow(cols) {
536
614
  return Array(cols).fill(null);
537
615
  }
616
+ _createDefaultTabStops(cols) {
617
+ const tabStops = /* @__PURE__ */ new Set();
618
+ for (let column = 8; column < cols; column += 8) {
619
+ tabStops.add(column);
620
+ }
621
+ return tabStops;
622
+ }
538
623
  _clamp(value, min, max) {
539
624
  return Math.max(min, Math.min(max, value));
540
625
  }
626
+ _createSavedCursorState() {
627
+ return {
628
+ x: this._cursorX,
629
+ y: this._cursorY,
630
+ autoWrap: this._autoWrap,
631
+ style: { ...this._style, fg: this._style.fg?.slice(), bg: this._style.bg?.slice() },
632
+ styleSequence: this._styleSequence
633
+ };
634
+ }
635
+ _saveCursor() {
636
+ this._savedCursor = this._createSavedCursorState();
637
+ }
638
+ _restoreCursor() {
639
+ this._cursorX = this._clamp(this._savedCursor.x, 0, this._cols - 1);
640
+ this._cursorY = this._clamp(this._savedCursor.y, 0, this._rows - 1);
641
+ this._autoWrap = this._savedCursor.autoWrap;
642
+ this._style = {
643
+ ...this._savedCursor.style,
644
+ fg: this._savedCursor.style.fg?.slice(),
645
+ bg: this._savedCursor.style.bg?.slice()
646
+ };
647
+ this._styleSequence = this._savedCursor.styleSequence;
648
+ }
649
+ _resetModes() {
650
+ this._autoWrap = true;
651
+ this._originMode = false;
652
+ this._insertMode = false;
653
+ this._pendingWrap = false;
654
+ this._tabStops = this._createDefaultTabStops(this._cols);
655
+ this._g0Charset = "ascii";
656
+ this._g1Charset = "ascii";
657
+ this._shiftOut = false;
658
+ this._resetStyle();
659
+ }
660
+ _writePrintable(ch) {
661
+ const width = this._cellWidth(ch);
662
+ if (this._autoWrap && this._pendingWrap) {
663
+ this._cursorX = 0;
664
+ this._newline();
665
+ } else if (this._autoWrap && this._cursorX + width > this._cols) {
666
+ this._cursorX = 0;
667
+ this._newline();
668
+ }
669
+ if (this._insertMode) {
670
+ const row = this._screen[this._cursorY];
671
+ if (row) {
672
+ row.splice(this._cursorX, 0, ...Array(width).fill(null));
673
+ row.splice(this._cols);
674
+ }
675
+ }
676
+ this._setChar(this._cursorY, this._cursorX, ch);
677
+ for (let column = 1; column < width && this._cursorX + column < this._cols; column += 1) {
678
+ const row = this._screen[this._cursorY];
679
+ if (row) row[this._cursorX + column] = null;
680
+ }
681
+ this._lastPrintedChar = ch;
682
+ if (!this._autoWrap) {
683
+ this._cursorX = Math.min(this._cursorX + width, this._cols - 1);
684
+ this._pendingWrap = false;
685
+ } else if (this._cursorX + width >= this._cols) {
686
+ this._cursorX = this._cols - 1;
687
+ this._pendingWrap = true;
688
+ } else {
689
+ this._cursorX += width;
690
+ this._pendingWrap = false;
691
+ }
692
+ }
693
+ _cellWidth(ch) {
694
+ const codePoint = ch.codePointAt(0);
695
+ if (codePoint === void 0) return 0;
696
+ 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) {
697
+ return Math.min(2, this._cols);
698
+ }
699
+ return 1;
700
+ }
541
701
  _setChar(y, x, ch) {
542
702
  const row = this._screen[y];
543
703
  if (row && x >= 0 && x < this._cols) {
544
- const cell = [ch.charCodeAt(0), ch];
704
+ const renderedChar = this._style.conceal ? " " : ch;
705
+ const cell = [renderedChar.charCodeAt(0), renderedChar];
545
706
  if (this._styleSequence.length > 0) {
546
707
  Object.defineProperty(cell, "style", {
547
708
  value: this._styleSequence,
@@ -572,6 +733,7 @@ var TerminalBuffer = class {
572
733
  }
573
734
  }
574
735
  _newline() {
736
+ this._pendingWrap = false;
575
737
  if (this._cursorY === this._scrollBottom) {
576
738
  this._scrollUp(1);
577
739
  } else {
@@ -580,7 +742,11 @@ var TerminalBuffer = class {
580
742
  }
581
743
  _parseCsiParams() {
582
744
  if (!this._csiParams) return [];
583
- return this._csiParams.split(";").map((s) => s === "" ? 0 : parseInt(s, 10));
745
+ const values = this._csiParams.split(";").flatMap((segment) => segment.split(":")).map((segment) => segment === "" ? 0 : parseInt(segment, 10));
746
+ if ((values[0] === 38 || values[0] === 48) && values[1] === 2 && values[2] === 0) {
747
+ values.splice(2, 1);
748
+ }
749
+ return values;
584
750
  }
585
751
  _execCsi(final) {
586
752
  const params2 = this._parseCsiParams();
@@ -591,21 +757,45 @@ var TerminalBuffer = class {
591
757
  if (params2.includes(7)) {
592
758
  this._autoWrap = final === "h";
593
759
  }
760
+ if (params2.includes(6)) {
761
+ this._originMode = final === "h";
762
+ this._cursorX = 0;
763
+ this._cursorY = this._originMode ? this._scrollTop : 0;
764
+ }
594
765
  if (params2.includes(1049)) {
595
766
  if (final === "h") {
767
+ this._primaryScreen = {
768
+ screen: this._screen,
769
+ cursorX: this._cursorX,
770
+ cursorY: this._cursorY,
771
+ pendingWrap: this._pendingWrap,
772
+ style: { ...this._style, fg: this._style.fg?.slice(), bg: this._style.bg?.slice() },
773
+ styleSequence: this._styleSequence
774
+ };
596
775
  this._screen = this._makeScreen(this._cols, this._rows);
597
776
  this._cursorX = 0;
598
777
  this._cursorY = 0;
778
+ this._pendingWrap = false;
599
779
  } else {
600
- this._screen = this._makeScreen(this._cols, this._rows);
601
- this._cursorX = 0;
602
- this._cursorY = 0;
780
+ if (this._primaryScreen !== null) {
781
+ this._screen = this._primaryScreen.screen;
782
+ this._cursorX = this._primaryScreen.cursorX;
783
+ this._cursorY = this._primaryScreen.cursorY;
784
+ this._pendingWrap = this._primaryScreen.pendingWrap;
785
+ this._style = this._primaryScreen.style;
786
+ this._styleSequence = this._primaryScreen.styleSequence;
787
+ this._primaryScreen = null;
788
+ }
603
789
  }
604
- this._resetStyle();
790
+ if (final === "h") this._resetStyle();
605
791
  }
606
792
  }
607
793
  return;
608
794
  }
795
+ if ((final === "h" || final === "l") && params2.includes(4)) {
796
+ this._insertMode = final === "h";
797
+ return;
798
+ }
609
799
  switch (final) {
610
800
  case "A":
611
801
  this._cursorY = this._clamp(this._cursorY - Math.max(1, p0), 0, this._rows - 1);
@@ -637,7 +827,7 @@ var TerminalBuffer = class {
637
827
  case "H":
638
828
  // cursor position
639
829
  case "f":
640
- this._cursorY = this._clamp(Math.max(1, p0) - 1, 0, this._rows - 1);
830
+ this._cursorY = this._originMode ? this._clamp(this._scrollTop + Math.max(1, p0) - 1, this._scrollTop, this._scrollBottom) : this._clamp(Math.max(1, p0) - 1, 0, this._rows - 1);
641
831
  this._cursorX = this._clamp(Math.max(1, p1) - 1, 0, this._cols - 1);
642
832
  break;
643
833
  case "I":
@@ -652,7 +842,7 @@ var TerminalBuffer = class {
652
842
  } else if (p0 === 1) {
653
843
  for (let y = 0; y < this._cursorY; y++) this._eraseLine(y, 0, this._cols - 1);
654
844
  this._eraseLine(this._cursorY, 0, this._cursorX);
655
- } else if (p0 === 2 || p0 === 3) {
845
+ } else if (p0 === 2) {
656
846
  for (let y = 0; y < this._rows; y++) this._eraseLine(y, 0, this._cols - 1);
657
847
  }
658
848
  break;
@@ -665,6 +855,9 @@ var TerminalBuffer = class {
665
855
  this._eraseLine(this._cursorY, this._cursorX, this._cursorX + Math.max(1, p0) - 1);
666
856
  break;
667
857
  case "L": {
858
+ if (this._cursorY < this._scrollTop || this._cursorY > this._scrollBottom) {
859
+ break;
860
+ }
668
861
  const n = Math.max(1, p0);
669
862
  for (let i = 0; i < n; i++) {
670
863
  this._screen.splice(this._scrollBottom, 1);
@@ -673,6 +866,9 @@ var TerminalBuffer = class {
673
866
  break;
674
867
  }
675
868
  case "M": {
869
+ if (this._cursorY < this._scrollTop || this._cursorY > this._scrollBottom) {
870
+ break;
871
+ }
676
872
  const n = Math.max(1, p0);
677
873
  for (let i = 0; i < n; i++) {
678
874
  this._screen.splice(this._cursorY, 1);
@@ -711,6 +907,13 @@ var TerminalBuffer = class {
711
907
  }
712
908
  break;
713
909
  }
910
+ case "b":
911
+ if (this._lastPrintedChar !== null) {
912
+ for (let index = 0; index < Math.max(1, p0); index += 1) {
913
+ this._writePrintable(this._lastPrintedChar);
914
+ }
915
+ }
916
+ break;
714
917
  case "d":
715
918
  this._cursorY = this._clamp(Math.max(1, p0) - 1, 0, this._rows - 1);
716
919
  break;
@@ -725,15 +928,19 @@ var TerminalBuffer = class {
725
928
  this._scrollBottom = bottom;
726
929
  }
727
930
  this._cursorX = 0;
728
- this._cursorY = 0;
931
+ this._cursorY = this._originMode ? this._scrollTop : 0;
729
932
  break;
730
933
  }
731
934
  case "s":
732
- this._savedCursor = { x: this._cursorX, y: this._cursorY };
935
+ this._saveCursor();
733
936
  break;
734
937
  case "u":
735
- this._cursorX = this._clamp(this._savedCursor.x, 0, this._cols - 1);
736
- this._cursorY = this._clamp(this._savedCursor.y, 0, this._rows - 1);
938
+ this._restoreCursor();
939
+ break;
940
+ case "p":
941
+ if (this._csiPrivate === "!") {
942
+ this._resetModes();
943
+ }
737
944
  break;
738
945
  case "m":
739
946
  this._applySgr(params2);
@@ -744,6 +951,14 @@ var TerminalBuffer = class {
744
951
  }
745
952
  _feed(ch) {
746
953
  const code = ch.charCodeAt(0);
954
+ if (this._state !== 0 /* Normal */ && (code === 24 || code === 26)) {
955
+ this._state = 0 /* Normal */;
956
+ return;
957
+ }
958
+ if (this._state === 2 /* Csi */ && code === 27) {
959
+ this._state = 1 /* Escape */;
960
+ return;
961
+ }
747
962
  switch (this._state) {
748
963
  case 0 /* Normal */:
749
964
  this._feedNormal(ch, code);
@@ -758,20 +973,37 @@ var TerminalBuffer = class {
758
973
  if (code === 7 || code === 156) {
759
974
  this._state = 0 /* Normal */;
760
975
  } else if (code === 27) {
761
- this._state = 0 /* Normal */;
976
+ this._stringState = 3 /* Osc */;
977
+ this._state = 5 /* StringEscape */;
762
978
  }
763
979
  break;
764
980
  case 4 /* Str */:
765
- if (code === 156 || code === 7) {
981
+ if (code === 156) {
766
982
  this._state = 0 /* Normal */;
767
983
  } else if (code === 27) {
768
- this._state = 0 /* Normal */;
984
+ this._stringState = 4 /* Str */;
985
+ this._state = 5 /* StringEscape */;
769
986
  }
770
987
  break;
771
- case 5 /* EscCharset */:
988
+ case 5 /* StringEscape */:
989
+ this._state = ch === "\\" ? 0 /* Normal */ : this._stringState;
990
+ break;
991
+ case 6 /* EscCharset */:
992
+ if (this._charsetTarget === "g0") this._g0Charset = ch === "0" ? "graphics" : "ascii";
993
+ if (this._charsetTarget === "g1") this._g1Charset = ch === "0" ? "graphics" : "ascii";
994
+ this._charsetTarget = null;
772
995
  this._state = 0 /* Normal */;
773
996
  break;
774
- case 6 /* EscHash */:
997
+ case 7 /* EscHash */:
998
+ if (ch === "8") {
999
+ for (let row = 0; row < this._rows; row += 1) {
1000
+ for (let column = 0; column < this._cols; column += 1) {
1001
+ this._setChar(row, column, "E");
1002
+ }
1003
+ }
1004
+ this._cursorX = 0;
1005
+ this._cursorY = 0;
1006
+ }
775
1007
  this._state = 0 /* Normal */;
776
1008
  break;
777
1009
  }
@@ -787,32 +1019,28 @@ var TerminalBuffer = class {
787
1019
  this._state = 3 /* Osc */;
788
1020
  } else if (code === 144 || code === 152 || code === 158 || code === 159) {
789
1021
  this._state = 4 /* Str */;
1022
+ } else if (code === 133) {
1023
+ this._cursorX = 0;
1024
+ this._newline();
1025
+ } else if (code === 156) {
790
1026
  } else if (code === 7 || code === 5 || code === 6) {
791
1027
  } else if (code === 8) {
1028
+ this._pendingWrap = false;
792
1029
  if (this._cursorX > 0) this._cursorX--;
793
1030
  } else if (code === 127) {
794
- if (this._cursorX > 0) {
795
- this._cursorX--;
796
- this._setChar(this._cursorY, this._cursorX, " ");
797
- }
798
1031
  } else if (code === 9) {
799
- this._cursorX = Math.min(this._cols - 1, (Math.floor(this._cursorX / 8) + 1) * 8);
1032
+ const nextTabStop = [...this._tabStops].sort((left, right) => left - right).find((tabStop) => tabStop > this._cursorX);
1033
+ this._cursorX = nextTabStop ?? this._cols - 1;
800
1034
  } else if (code === 10 || code === 11 || code === 12) {
801
1035
  this._newline();
802
1036
  } else if (code === 13) {
1037
+ this._pendingWrap = false;
803
1038
  this._cursorX = 0;
804
1039
  } else if (code === 14 || code === 15) {
1040
+ this._shiftOut = code === 14;
805
1041
  } else if (code >= 32 && code !== 127) {
806
- this._setChar(this._cursorY, this._cursorX, ch);
807
- this._cursorX++;
808
- if (!this._autoWrap) {
809
- this._cursorX = Math.min(this._cursorX, this._cols - 1);
810
- return;
811
- }
812
- if (this._cursorX >= this._cols) {
813
- this._cursorX = 0;
814
- this._newline();
815
- }
1042
+ const charset = this._shiftOut ? this._g1Charset : this._g0Charset;
1043
+ this._writePrintable(charset === "graphics" ? DEC_SPECIAL_GRAPHICS[ch] ?? ch : ch);
816
1044
  }
817
1045
  }
818
1046
  _feedEscape(ch, code) {
@@ -826,14 +1054,14 @@ var TerminalBuffer = class {
826
1054
  } else if (code === 80 || code === 88 || code === 94 || code === 95) {
827
1055
  this._state = 4 /* Str */;
828
1056
  } else if (code === 40 || code === 41 || code === 42 || code === 43 || code === 45 || code === 46) {
829
- this._state = 5 /* EscCharset */;
1057
+ this._charsetTarget = code === 40 ? "g0" : code === 41 ? "g1" : null;
1058
+ this._state = 6 /* EscCharset */;
830
1059
  } else if (code === 35) {
831
- this._state = 6 /* EscHash */;
1060
+ this._state = 7 /* EscHash */;
832
1061
  } else if (code === 55) {
833
- this._savedCursor = { x: this._cursorX, y: this._cursorY };
1062
+ this._saveCursor();
834
1063
  } else if (code === 56) {
835
- this._cursorX = this._clamp(this._savedCursor.x, 0, this._cols - 1);
836
- this._cursorY = this._clamp(this._savedCursor.y, 0, this._rows - 1);
1064
+ this._restoreCursor();
837
1065
  } else if (code === 68) {
838
1066
  this._newline();
839
1067
  } else if (code === 69) {
@@ -846,14 +1074,15 @@ var TerminalBuffer = class {
846
1074
  this._cursorY = Math.max(0, this._cursorY - 1);
847
1075
  }
848
1076
  } else if (code === 72) {
1077
+ this._tabStops.add(this._cursorX);
849
1078
  } else if (code === 99) {
850
1079
  this._screen = this._makeScreen(this._cols, this._rows);
851
1080
  this._cursorX = 0;
852
1081
  this._cursorY = 0;
853
- this._savedCursor = { x: 0, y: 0 };
854
1082
  this._scrollTop = 0;
855
1083
  this._scrollBottom = this._rows - 1;
856
- this._resetStyle();
1084
+ this._resetModes();
1085
+ this._savedCursor = this._createSavedCursorState();
857
1086
  }
858
1087
  }
859
1088
  _feedCsi(ch, code) {
@@ -862,7 +1091,7 @@ var TerminalBuffer = class {
862
1091
  this._state = 0 /* Normal */;
863
1092
  } else if (code === 63 || code === 33 || code === 62 || code === 32) {
864
1093
  this._csiPrivate = ch;
865
- } else if (code >= 48 && code <= 57 || code === 59) {
1094
+ } else if (code >= 48 && code <= 57 || code === 59 || code === 58) {
866
1095
  this._csiParams += ch;
867
1096
  }
868
1097
  }
@@ -893,6 +1122,9 @@ var TerminalBuffer = class {
893
1122
  case 7:
894
1123
  this._style.inverse = true;
895
1124
  break;
1125
+ case 8:
1126
+ this._style.conceal = true;
1127
+ break;
896
1128
  case 9:
897
1129
  this._style.strikethrough = true;
898
1130
  break;
@@ -910,6 +1142,9 @@ var TerminalBuffer = class {
910
1142
  case 27:
911
1143
  this._style.inverse = false;
912
1144
  break;
1145
+ case 28:
1146
+ this._style.conceal = false;
1147
+ break;
913
1148
  case 29:
914
1149
  this._style.strikethrough = false;
915
1150
  break;
@@ -964,6 +1199,7 @@ function createDefaultStyleState() {
964
1199
  italic: false,
965
1200
  underline: false,
966
1201
  inverse: false,
1202
+ conceal: false,
967
1203
  strikethrough: false
968
1204
  };
969
1205
  }
@@ -984,6 +1220,9 @@ function serializeStyleState(state) {
984
1220
  if (state.inverse) {
985
1221
  codes.push(7);
986
1222
  }
1223
+ if (state.conceal) {
1224
+ codes.push(8);
1225
+ }
987
1226
  if (state.strikethrough) {
988
1227
  codes.push(9);
989
1228
  }
@@ -1102,6 +1341,7 @@ var WAIT_FOR_POLL_MS = 10;
1102
1341
  var TYPE_DELAY_MS = 15;
1103
1342
  var CLOSE_AFTER_SIGNAL_GRACE_MS = 250;
1104
1343
  var CLOSE_AFTER_SIGTERM_MS = 1e3;
1344
+ var CLOSE_AFTER_SIGKILL_MS = 1e3;
1105
1345
  var TerminalSession = class {
1106
1346
  id;
1107
1347
  command;
@@ -1115,8 +1355,7 @@ var TerminalSession = class {
1115
1355
  lastDataAt = Date.now();
1116
1356
  currentCols;
1117
1357
  currentRows;
1118
- closeRequested = false;
1119
- signalRequested = false;
1358
+ closePromise = null;
1120
1359
  constructor({
1121
1360
  id,
1122
1361
  command,
@@ -1127,6 +1366,7 @@ var TerminalSession = class {
1127
1366
  rows = DEFAULT_ROWS,
1128
1367
  observe = false
1129
1368
  }) {
1369
+ assertTerminalGeometry(cols, rows);
1130
1370
  this.id = id;
1131
1371
  this.command = command;
1132
1372
  this.currentCols = cols;
@@ -1171,7 +1411,7 @@ var TerminalSession = class {
1171
1411
  }
1172
1412
  async send(raw) {
1173
1413
  if (this.exitCode !== null) {
1174
- return;
1414
+ throw new Error(`Terminal session "${this.id}" has already exited.`);
1175
1415
  }
1176
1416
  this.pty.write(raw);
1177
1417
  }
@@ -1179,22 +1419,26 @@ var TerminalSession = class {
1179
1419
  if (this.exitCode !== null) {
1180
1420
  return;
1181
1421
  }
1182
- this.signalRequested = true;
1183
1422
  this.pty.kill(sig);
1184
1423
  }
1185
1424
  async waitFor(pattern, opts) {
1186
1425
  const timeout = opts?.timeout ?? DEFAULT_TIMEOUT_MS;
1426
+ assertTimeout(timeout);
1187
1427
  const startedAt = Date.now();
1188
1428
  while (Date.now() - startedAt <= timeout) {
1189
1429
  const matched = matchPattern(this.rawBuffer, pattern);
1190
1430
  if (matched !== null) {
1191
1431
  return matched;
1192
1432
  }
1433
+ if (this.exitCode !== null) {
1434
+ throw new Error(`Terminal session "${this.id}" exited before matching pattern: ${String(pattern)}`);
1435
+ }
1193
1436
  await sleep(WAIT_FOR_POLL_MS);
1194
1437
  }
1195
1438
  throw new Error(`Timed out waiting for pattern after ${timeout}ms: ${String(pattern)}`);
1196
1439
  }
1197
1440
  async waitForQuiet(ms) {
1441
+ assertQuietPeriod(ms);
1198
1442
  while (true) {
1199
1443
  const remaining = ms - (Date.now() - this.lastDataAt);
1200
1444
  if (remaining <= 0) {
@@ -1227,10 +1471,14 @@ var TerminalSession = class {
1227
1471
  if (opts?.last === void 0) {
1228
1472
  return lines;
1229
1473
  }
1474
+ if (!Number.isInteger(opts.last) || opts.last < 0) {
1475
+ throw new Error("History last must be a non-negative integer.");
1476
+ }
1230
1477
  const start = Math.max(0, lines.length - opts.last);
1231
1478
  return lines.slice(start);
1232
1479
  }
1233
1480
  async resize(cols, rows) {
1481
+ assertTerminalGeometry(cols, rows);
1234
1482
  this.currentCols = cols;
1235
1483
  this.currentRows = rows;
1236
1484
  if (this.exitCode === null) {
@@ -1239,6 +1487,9 @@ var TerminalSession = class {
1239
1487
  this.terminal.resize(cols, rows);
1240
1488
  }
1241
1489
  async waitForExit(opts) {
1490
+ if (opts?.timeout !== void 0) {
1491
+ assertTimeout(opts.timeout);
1492
+ }
1242
1493
  if (this.exitCode !== null) {
1243
1494
  return this.exitCode;
1244
1495
  }
@@ -1255,32 +1506,52 @@ var TerminalSession = class {
1255
1506
  if (this.exitCode !== null) {
1256
1507
  return this.exitCode;
1257
1508
  }
1258
- if (!this.closeRequested) {
1259
- this.closeRequested = true;
1260
- const gracefulExitCode = await waitForExit(this.exitPromise, CLOSE_AFTER_SIGNAL_GRACE_MS);
1261
- if (gracefulExitCode !== null) {
1262
- return gracefulExitCode;
1263
- }
1264
- if (this.signalRequested) {
1265
- return this.exitPromise;
1266
- }
1267
- if (this.exitCode === null) {
1268
- this.pty.kill("SIGTERM");
1269
- const afterSigterm = await waitForExit(this.exitPromise, CLOSE_AFTER_SIGTERM_MS);
1270
- if (afterSigterm !== null) {
1271
- return afterSigterm;
1272
- }
1509
+ this.closePromise ??= this.closeProcess().catch((error) => {
1510
+ this.closePromise = null;
1511
+ throw error;
1512
+ });
1513
+ return this.closePromise;
1514
+ }
1515
+ async closeProcess() {
1516
+ const gracefulExitCode = await waitForExit(this.exitPromise, CLOSE_AFTER_SIGNAL_GRACE_MS);
1517
+ if (gracefulExitCode !== null) {
1518
+ return gracefulExitCode;
1519
+ }
1520
+ if (this.exitCode === null) {
1521
+ this.pty.kill("SIGTERM");
1522
+ const afterSigterm = await waitForExit(this.exitPromise, CLOSE_AFTER_SIGTERM_MS);
1523
+ if (afterSigterm !== null) {
1524
+ return afterSigterm;
1273
1525
  }
1274
- if (this.exitCode === null) {
1275
- this.pty.kill("SIGKILL");
1526
+ }
1527
+ if (this.exitCode === null) {
1528
+ this.pty.kill("SIGKILL");
1529
+ const afterSigkill = await waitForExit(this.exitPromise, CLOSE_AFTER_SIGKILL_MS);
1530
+ if (afterSigkill !== null) {
1531
+ return afterSigkill;
1276
1532
  }
1277
1533
  }
1278
- return this.exitPromise;
1534
+ throw new Error("Timed out waiting for process to exit after SIGKILL.");
1279
1535
  }
1280
1536
  on(event, cb) {
1281
1537
  this.emitter.on(event, cb);
1282
1538
  }
1283
1539
  };
1540
+ function assertTerminalGeometry(cols, rows) {
1541
+ if (!Number.isInteger(cols) || cols <= 0 || !Number.isInteger(rows) || rows <= 0) {
1542
+ throw new Error("Terminal columns and rows must be positive integers.");
1543
+ }
1544
+ }
1545
+ function assertTimeout(timeout) {
1546
+ if (!Number.isFinite(timeout) || timeout < 0) {
1547
+ throw new Error("Timeout must be a finite non-negative number.");
1548
+ }
1549
+ }
1550
+ function assertQuietPeriod(duration) {
1551
+ if (!Number.isFinite(duration) || duration < 0) {
1552
+ throw new Error("Quiet period must be a finite non-negative number.");
1553
+ }
1554
+ }
1284
1555
  function createPtyProcess({
1285
1556
  command,
1286
1557
  args,
@@ -1357,13 +1628,28 @@ function splitHistoryLines(input) {
1357
1628
  }
1358
1629
  function normalizeHistoryBuffer(input) {
1359
1630
  let output = "";
1631
+ let line = "";
1632
+ let cursor = 0;
1360
1633
  for (const character of input) {
1361
- if (character === "\r" || character === "\b") {
1634
+ if (character === "\r") {
1635
+ cursor = 0;
1636
+ continue;
1637
+ }
1638
+ if (character === "\b") {
1639
+ cursor = Math.max(0, cursor - 1);
1362
1640
  continue;
1363
1641
  }
1364
- output += character;
1642
+ if (character === "\n") {
1643
+ output += `${line}
1644
+ `;
1645
+ line = "";
1646
+ cursor = 0;
1647
+ continue;
1648
+ }
1649
+ line = `${line.slice(0, cursor)}${character}${line.slice(cursor + 1)}`;
1650
+ cursor += 1;
1365
1651
  }
1366
- return output;
1652
+ return output + line;
1367
1653
  }
1368
1654
  function sleep(ms) {
1369
1655
  return new Promise((resolve) => {
@@ -1428,11 +1714,12 @@ var TerminalPilot = class _TerminalPilot {
1428
1714
  }
1429
1715
  async close() {
1430
1716
  const sessions = [...this.sessionMap.values()];
1431
- try {
1432
- await Promise.all(sessions.map((session) => session.close()));
1433
- } finally {
1434
- this.sessionMap.clear();
1435
- }
1717
+ await Promise.all(
1718
+ sessions.map(async (session) => {
1719
+ await session.close();
1720
+ this.sessionMap.delete(session.id);
1721
+ })
1722
+ );
1436
1723
  }
1437
1724
  };
1438
1725
 
@@ -1450,6 +1737,7 @@ function createTerminalPilotRuntime(options = {}) {
1450
1737
  const launchPilot = options.launchPilot ?? TerminalPilot.launch;
1451
1738
  const nameToId = /* @__PURE__ */ new Map();
1452
1739
  const idToName = /* @__PURE__ */ new Map();
1740
+ const pendingNames = /* @__PURE__ */ new Set();
1453
1741
  let pilotPromise;
1454
1742
  function getRequestedName(name, env) {
1455
1743
  return name ?? env?.get(SESSION_ENV_VAR);
@@ -1460,7 +1748,7 @@ function createTerminalPilotRuntime(options = {}) {
1460
1748
  }
1461
1749
  function nextSessionName() {
1462
1750
  let index = 1;
1463
- while (nameToId.has(`s${index}`)) {
1751
+ while (nameToId.has(`s${index}`) || pendingNames.has(`s${index}`)) {
1464
1752
  index += 1;
1465
1753
  }
1466
1754
  return `s${index}`;
@@ -1471,8 +1759,12 @@ function createTerminalPilotRuntime(options = {}) {
1471
1759
  return { name, session };
1472
1760
  }
1473
1761
  function forgetSession(name, sessionId) {
1474
- nameToId.delete(name);
1475
- idToName.delete(sessionId);
1762
+ if (nameToId.get(name) === sessionId) {
1763
+ nameToId.delete(name);
1764
+ }
1765
+ if (idToName.get(sessionId) === name) {
1766
+ idToName.delete(sessionId);
1767
+ }
1476
1768
  }
1477
1769
  function formatAvailableSessions(names) {
1478
1770
  if (names.length === 0) {
@@ -1505,22 +1797,44 @@ function createTerminalPilotRuntime(options = {}) {
1505
1797
  return [{ name, session }];
1506
1798
  });
1507
1799
  }
1800
+ async function discardExitedSessionName(name) {
1801
+ const sessionId = nameToId.get(name);
1802
+ if (sessionId === void 0) {
1803
+ return;
1804
+ }
1805
+ const pilot = await getPilot();
1806
+ try {
1807
+ const session = pilot.getSession(sessionId);
1808
+ if (session.exitCode === null) {
1809
+ return;
1810
+ }
1811
+ pilot.deleteSession(sessionId);
1812
+ } catch {
1813
+ }
1814
+ forgetSession(name, sessionId);
1815
+ }
1508
1816
  return {
1509
1817
  async createSession(params2, env) {
1510
1818
  const requestedName = getRequestedName(params2.session, env) ?? nextSessionName();
1511
- if (nameToId.has(requestedName)) {
1819
+ await discardExitedSessionName(requestedName);
1820
+ if (nameToId.has(requestedName) || pendingNames.has(requestedName)) {
1512
1821
  throw new UserError(`Session "${requestedName}" already exists.`);
1513
1822
  }
1514
- const pilot = await getPilot();
1515
- const session = await pilot.newSession({
1516
- command: params2.command,
1517
- args: params2.args,
1518
- cwd: params2.cwd,
1519
- cols: params2.cols,
1520
- rows: params2.rows,
1521
- observe: params2.observe
1522
- });
1523
- return rememberSession(requestedName, session);
1823
+ pendingNames.add(requestedName);
1824
+ try {
1825
+ const pilot = await getPilot();
1826
+ const session = await pilot.newSession({
1827
+ command: params2.command,
1828
+ args: params2.args,
1829
+ cwd: params2.cwd,
1830
+ cols: params2.cols,
1831
+ rows: params2.rows,
1832
+ observe: params2.observe
1833
+ });
1834
+ return rememberSession(requestedName, session);
1835
+ } finally {
1836
+ pendingNames.delete(requestedName);
1837
+ }
1524
1838
  },
1525
1839
  async resolveSession(name, env) {
1526
1840
  const requestedName = getRequestedName(name, env);
@@ -1559,6 +1873,7 @@ function createTerminalPilotRuntime(options = {}) {
1559
1873
  pilotPromise = void 0;
1560
1874
  nameToId.clear();
1561
1875
  idToName.clear();
1876
+ pendingNames.clear();
1562
1877
  }
1563
1878
  };
1564
1879
  }