terminal-pilot 0.0.17 → 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 (74) hide show
  1. package/dist/cli.js +6271 -2048
  2. package/dist/cli.js.map +4 -4
  3. package/dist/commands/close-session.d.ts +4 -4
  4. package/dist/commands/close-session.js +437 -108
  5. package/dist/commands/close-session.js.map +2 -2
  6. package/dist/commands/create-session.d.ts +16 -16
  7. package/dist/commands/create-session.js +437 -108
  8. package/dist/commands/create-session.js.map +2 -2
  9. package/dist/commands/fill.d.ts +6 -6
  10. package/dist/commands/fill.js +437 -108
  11. package/dist/commands/fill.js.map +2 -2
  12. package/dist/commands/get-session.d.ts +4 -4
  13. package/dist/commands/get-session.js +437 -108
  14. package/dist/commands/get-session.js.map +2 -2
  15. package/dist/commands/index.d.ts +330 -107
  16. package/dist/commands/index.js +1951 -336
  17. package/dist/commands/index.js.map +4 -4
  18. package/dist/commands/install.d.ts +8 -8
  19. package/dist/commands/install.js +1242 -134
  20. package/dist/commands/install.js.map +4 -4
  21. package/dist/commands/installer.d.ts +5 -0
  22. package/dist/commands/installer.js +606 -37
  23. package/dist/commands/installer.js.map +4 -4
  24. package/dist/commands/list-sessions.d.ts +2 -2
  25. package/dist/commands/list-sessions.js +437 -108
  26. package/dist/commands/list-sessions.js.map +2 -2
  27. package/dist/commands/press-key.d.ts +6 -6
  28. package/dist/commands/press-key.js +437 -108
  29. package/dist/commands/press-key.js.map +2 -2
  30. package/dist/commands/read-history.d.ts +6 -6
  31. package/dist/commands/read-history.js +437 -108
  32. package/dist/commands/read-history.js.map +2 -2
  33. package/dist/commands/read-screen.d.ts +4 -4
  34. package/dist/commands/read-screen.js +437 -108
  35. package/dist/commands/read-screen.js.map +2 -2
  36. package/dist/commands/resize.d.ts +8 -8
  37. package/dist/commands/resize.js +437 -108
  38. package/dist/commands/resize.js.map +2 -2
  39. package/dist/commands/runtime.js +356 -89
  40. package/dist/commands/runtime.js.map +2 -2
  41. package/dist/commands/screenshot.d.ts +10 -10
  42. package/dist/commands/screenshot.js +672 -148
  43. package/dist/commands/screenshot.js.map +3 -3
  44. package/dist/commands/send-signal.d.ts +6 -6
  45. package/dist/commands/send-signal.js +437 -108
  46. package/dist/commands/send-signal.js.map +2 -2
  47. package/dist/commands/type.d.ts +6 -6
  48. package/dist/commands/type.js +437 -108
  49. package/dist/commands/type.js.map +2 -2
  50. package/dist/commands/uninstall.d.ts +4 -4
  51. package/dist/commands/uninstall.js +708 -64
  52. package/dist/commands/uninstall.js.map +4 -4
  53. package/dist/commands/wait-for-exit.d.ts +6 -6
  54. package/dist/commands/wait-for-exit.js +437 -108
  55. package/dist/commands/wait-for-exit.js.map +2 -2
  56. package/dist/commands/wait-for.d.ts +10 -10
  57. package/dist/commands/wait-for.js +437 -108
  58. package/dist/commands/wait-for.js.map +2 -2
  59. package/dist/index.d.ts +1 -0
  60. package/dist/index.js +318 -80
  61. package/dist/index.js.map +3 -3
  62. package/dist/terminal-buffer.d.ts +18 -0
  63. package/dist/terminal-buffer.js +234 -43
  64. package/dist/terminal-buffer.js.map +2 -2
  65. package/dist/terminal-pilot.js +312 -73
  66. package/dist/terminal-pilot.js.map +2 -2
  67. package/dist/terminal-session.d.ts +2 -2
  68. package/dist/terminal-session.js +306 -68
  69. package/dist/terminal-session.js.map +2 -2
  70. package/dist/testing/cli-repl.js +6267 -2044
  71. package/dist/testing/cli-repl.js.map +4 -4
  72. package/dist/testing/qa-cli.js +6291 -2068
  73. package/dist/testing/qa-cli.js.map +4 -4
  74. package/package.json +6 -5
@@ -3,8 +3,8 @@ import { fileURLToPath as fileURLToPath2 } from "node:url";
3
3
 
4
4
  // ../toolcraft/src/user-error.ts
5
5
  var UserError = class extends Error {
6
- constructor(message) {
7
- super(message);
6
+ constructor(message, options) {
7
+ super(message, options);
8
8
  this.name = "UserError";
9
9
  }
10
10
  };
@@ -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,
@@ -66,22 +73,37 @@ function Record(value) {
66
73
  function isOptionalSchema(schema) {
67
74
  return schema.kind === "optional";
68
75
  }
69
- function getRequiredKeyFingerprint(schema) {
70
- const requiredKeys = Object.keys(schema.shape).filter((key) => !isOptionalSchema(schema.shape[key])).sort();
71
- return JSON.stringify(requiredKeys);
76
+ function getRequiredKeys(schema) {
77
+ return Object.keys(schema.shape).filter((key) => !isOptionalSchema(schema.shape[key])).sort();
78
+ }
79
+ function assertUniqueRequiredKeyFingerprints(branches) {
80
+ const fingerprints = /* @__PURE__ */ new Map();
81
+ branches.forEach((branch, index) => {
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);
93
+ });
94
+ for (const { display, indices } of fingerprints.values()) {
95
+ if (indices.length > 1) {
96
+ throw new Error(
97
+ `Union branches [${indices.join(", ")}] share required-key fingerprint "${display}". Each branch must require a distinct set of keys.`
98
+ );
99
+ }
100
+ }
72
101
  }
73
102
  function assertValidBranches2(branches) {
74
103
  if (branches.length === 0) {
75
104
  throw new Error("Union schema requires at least one branch");
76
105
  }
77
- const fingerprints = /* @__PURE__ */ new Set();
78
- for (const branch of branches) {
79
- const fingerprint = getRequiredKeyFingerprint(branch);
80
- if (fingerprints.has(fingerprint)) {
81
- throw new Error("Union schema branches must have unique required-key fingerprints");
82
- }
83
- fingerprints.add(fingerprint);
84
- }
106
+ assertUniqueRequiredKeyFingerprints(branches);
85
107
  }
86
108
  function Union(branches) {
87
109
  assertValidBranches2(branches);
@@ -100,15 +122,47 @@ function assertValidEnumValues(values) {
100
122
  if (uniqueValues.size !== values.length) {
101
123
  throw new Error("Enum schema values must be unique");
102
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
+ }
103
148
  }
104
149
  var S = {
105
150
  String(options = {}) {
151
+ assertNonNegativeInteger(options.minLength, "minLength");
152
+ assertNonNegativeInteger(options.maxLength, "maxLength");
153
+ assertPattern(options.pattern);
106
154
  return {
107
155
  kind: "string",
108
156
  ...options
109
157
  };
110
158
  },
111
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
+ }
112
166
  return {
113
167
  kind: "number",
114
168
  ...options
@@ -122,6 +176,9 @@ var S = {
122
176
  },
123
177
  Enum(values, options = {}) {
124
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
+ }
125
182
  return {
126
183
  kind: "enum",
127
184
  values,
@@ -129,6 +186,8 @@ var S = {
129
186
  };
130
187
  },
131
188
  Array(item, options = {}) {
189
+ assertNonNegativeInteger(options.minItems, "minItems");
190
+ assertNonNegativeInteger(options.maxItems, "maxItems");
132
191
  return {
133
192
  kind: "array",
134
193
  item,
@@ -341,12 +400,15 @@ function materializeCommand(command, inherited) {
341
400
  }
342
401
  function defineCommand(config) {
343
402
  validateHumanInLoopOnDefine(config);
344
- return materializeCommand(createBaseCommand(config), {
345
- scope: void 0,
346
- humanInLoop: void 0,
347
- secrets: {},
348
- requires: void 0
349
- });
403
+ return materializeCommand(
404
+ createBaseCommand(config),
405
+ {
406
+ scope: void 0,
407
+ humanInLoop: void 0,
408
+ secrets: {},
409
+ requires: void 0
410
+ }
411
+ );
350
412
  }
351
413
 
352
414
  // src/terminal-pilot.ts
@@ -434,27 +496,53 @@ function consumeTerminatedString(input, index, allowBellTerminator) {
434
496
 
435
497
  // src/terminal-buffer.ts
436
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
+ };
437
512
  var TerminalBuffer = class {
438
513
  _cols;
439
514
  _rows;
440
515
  _screen;
516
+ _primaryScreen = null;
441
517
  _cursorX = 0;
442
518
  _cursorY = 0;
443
- _savedCursor = { x: 0, y: 0 };
519
+ _savedCursor;
444
520
  _scrollTop = 0;
445
521
  _scrollBottom;
446
522
  _state = 0 /* Normal */;
447
523
  _csiParams = "";
448
524
  _csiPrivate = "";
449
525
  _autoWrap = true;
526
+ _pendingWrap = false;
527
+ _originMode = false;
528
+ _insertMode = false;
529
+ _tabStops;
530
+ _lastPrintedChar = null;
450
531
  _style = createDefaultStyleState();
451
532
  _styleSequence = "";
533
+ _stringState = 0 /* Normal */;
534
+ _charsetTarget = null;
535
+ _g0Charset = "ascii";
536
+ _g1Charset = "ascii";
537
+ _shiftOut = false;
452
538
  displayBuffer;
453
539
  constructor(cols, rows) {
454
540
  this._cols = cols;
455
541
  this._rows = rows;
456
542
  this._scrollBottom = rows - 1;
457
543
  this._screen = this._makeScreen(cols, rows);
544
+ this._tabStops = this._createDefaultTabStops(cols);
545
+ this._savedCursor = this._createSavedCursorState();
458
546
  this.displayBuffer = Object.defineProperties(
459
547
  {},
460
548
  {
@@ -510,8 +598,12 @@ var TerminalBuffer = class {
510
598
  }
511
599
  this._cols = cols;
512
600
  this._rows = rows;
513
- this._scrollTop = 0;
514
- 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
+ }
515
607
  this._cursorX = this._clamp(this._cursorX, 0, cols - 1);
516
608
  this._cursorY = this._clamp(this._cursorY, 0, rows - 1);
517
609
  }
@@ -521,13 +613,96 @@ var TerminalBuffer = class {
521
613
  _makeRow(cols) {
522
614
  return Array(cols).fill(null);
523
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
+ }
524
623
  _clamp(value, min, max) {
525
624
  return Math.max(min, Math.min(max, value));
526
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
+ }
527
701
  _setChar(y, x, ch) {
528
702
  const row = this._screen[y];
529
703
  if (row && x >= 0 && x < this._cols) {
530
- const cell = [ch.charCodeAt(0), ch];
704
+ const renderedChar = this._style.conceal ? " " : ch;
705
+ const cell = [renderedChar.charCodeAt(0), renderedChar];
531
706
  if (this._styleSequence.length > 0) {
532
707
  Object.defineProperty(cell, "style", {
533
708
  value: this._styleSequence,
@@ -558,6 +733,7 @@ var TerminalBuffer = class {
558
733
  }
559
734
  }
560
735
  _newline() {
736
+ this._pendingWrap = false;
561
737
  if (this._cursorY === this._scrollBottom) {
562
738
  this._scrollUp(1);
563
739
  } else {
@@ -566,7 +742,11 @@ var TerminalBuffer = class {
566
742
  }
567
743
  _parseCsiParams() {
568
744
  if (!this._csiParams) return [];
569
- 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;
570
750
  }
571
751
  _execCsi(final) {
572
752
  const params2 = this._parseCsiParams();
@@ -577,21 +757,45 @@ var TerminalBuffer = class {
577
757
  if (params2.includes(7)) {
578
758
  this._autoWrap = final === "h";
579
759
  }
760
+ if (params2.includes(6)) {
761
+ this._originMode = final === "h";
762
+ this._cursorX = 0;
763
+ this._cursorY = this._originMode ? this._scrollTop : 0;
764
+ }
580
765
  if (params2.includes(1049)) {
581
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
+ };
582
775
  this._screen = this._makeScreen(this._cols, this._rows);
583
776
  this._cursorX = 0;
584
777
  this._cursorY = 0;
778
+ this._pendingWrap = false;
585
779
  } else {
586
- this._screen = this._makeScreen(this._cols, this._rows);
587
- this._cursorX = 0;
588
- 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
+ }
589
789
  }
590
- this._resetStyle();
790
+ if (final === "h") this._resetStyle();
591
791
  }
592
792
  }
593
793
  return;
594
794
  }
795
+ if ((final === "h" || final === "l") && params2.includes(4)) {
796
+ this._insertMode = final === "h";
797
+ return;
798
+ }
595
799
  switch (final) {
596
800
  case "A":
597
801
  this._cursorY = this._clamp(this._cursorY - Math.max(1, p0), 0, this._rows - 1);
@@ -623,7 +827,7 @@ var TerminalBuffer = class {
623
827
  case "H":
624
828
  // cursor position
625
829
  case "f":
626
- 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);
627
831
  this._cursorX = this._clamp(Math.max(1, p1) - 1, 0, this._cols - 1);
628
832
  break;
629
833
  case "I":
@@ -638,7 +842,7 @@ var TerminalBuffer = class {
638
842
  } else if (p0 === 1) {
639
843
  for (let y = 0; y < this._cursorY; y++) this._eraseLine(y, 0, this._cols - 1);
640
844
  this._eraseLine(this._cursorY, 0, this._cursorX);
641
- } else if (p0 === 2 || p0 === 3) {
845
+ } else if (p0 === 2) {
642
846
  for (let y = 0; y < this._rows; y++) this._eraseLine(y, 0, this._cols - 1);
643
847
  }
644
848
  break;
@@ -651,6 +855,9 @@ var TerminalBuffer = class {
651
855
  this._eraseLine(this._cursorY, this._cursorX, this._cursorX + Math.max(1, p0) - 1);
652
856
  break;
653
857
  case "L": {
858
+ if (this._cursorY < this._scrollTop || this._cursorY > this._scrollBottom) {
859
+ break;
860
+ }
654
861
  const n = Math.max(1, p0);
655
862
  for (let i = 0; i < n; i++) {
656
863
  this._screen.splice(this._scrollBottom, 1);
@@ -659,6 +866,9 @@ var TerminalBuffer = class {
659
866
  break;
660
867
  }
661
868
  case "M": {
869
+ if (this._cursorY < this._scrollTop || this._cursorY > this._scrollBottom) {
870
+ break;
871
+ }
662
872
  const n = Math.max(1, p0);
663
873
  for (let i = 0; i < n; i++) {
664
874
  this._screen.splice(this._cursorY, 1);
@@ -697,6 +907,13 @@ var TerminalBuffer = class {
697
907
  }
698
908
  break;
699
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;
700
917
  case "d":
701
918
  this._cursorY = this._clamp(Math.max(1, p0) - 1, 0, this._rows - 1);
702
919
  break;
@@ -711,15 +928,19 @@ var TerminalBuffer = class {
711
928
  this._scrollBottom = bottom;
712
929
  }
713
930
  this._cursorX = 0;
714
- this._cursorY = 0;
931
+ this._cursorY = this._originMode ? this._scrollTop : 0;
715
932
  break;
716
933
  }
717
934
  case "s":
718
- this._savedCursor = { x: this._cursorX, y: this._cursorY };
935
+ this._saveCursor();
719
936
  break;
720
937
  case "u":
721
- this._cursorX = this._clamp(this._savedCursor.x, 0, this._cols - 1);
722
- 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
+ }
723
944
  break;
724
945
  case "m":
725
946
  this._applySgr(params2);
@@ -730,6 +951,14 @@ var TerminalBuffer = class {
730
951
  }
731
952
  _feed(ch) {
732
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
+ }
733
962
  switch (this._state) {
734
963
  case 0 /* Normal */:
735
964
  this._feedNormal(ch, code);
@@ -744,20 +973,37 @@ var TerminalBuffer = class {
744
973
  if (code === 7 || code === 156) {
745
974
  this._state = 0 /* Normal */;
746
975
  } else if (code === 27) {
747
- this._state = 0 /* Normal */;
976
+ this._stringState = 3 /* Osc */;
977
+ this._state = 5 /* StringEscape */;
748
978
  }
749
979
  break;
750
980
  case 4 /* Str */:
751
- if (code === 156 || code === 7) {
981
+ if (code === 156) {
752
982
  this._state = 0 /* Normal */;
753
983
  } else if (code === 27) {
754
- this._state = 0 /* Normal */;
984
+ this._stringState = 4 /* Str */;
985
+ this._state = 5 /* StringEscape */;
755
986
  }
756
987
  break;
757
- 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;
758
995
  this._state = 0 /* Normal */;
759
996
  break;
760
- 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
+ }
761
1007
  this._state = 0 /* Normal */;
762
1008
  break;
763
1009
  }
@@ -773,32 +1019,28 @@ var TerminalBuffer = class {
773
1019
  this._state = 3 /* Osc */;
774
1020
  } else if (code === 144 || code === 152 || code === 158 || code === 159) {
775
1021
  this._state = 4 /* Str */;
1022
+ } else if (code === 133) {
1023
+ this._cursorX = 0;
1024
+ this._newline();
1025
+ } else if (code === 156) {
776
1026
  } else if (code === 7 || code === 5 || code === 6) {
777
1027
  } else if (code === 8) {
1028
+ this._pendingWrap = false;
778
1029
  if (this._cursorX > 0) this._cursorX--;
779
1030
  } else if (code === 127) {
780
- if (this._cursorX > 0) {
781
- this._cursorX--;
782
- this._setChar(this._cursorY, this._cursorX, " ");
783
- }
784
1031
  } else if (code === 9) {
785
- 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;
786
1034
  } else if (code === 10 || code === 11 || code === 12) {
787
1035
  this._newline();
788
1036
  } else if (code === 13) {
1037
+ this._pendingWrap = false;
789
1038
  this._cursorX = 0;
790
1039
  } else if (code === 14 || code === 15) {
1040
+ this._shiftOut = code === 14;
791
1041
  } else if (code >= 32 && code !== 127) {
792
- this._setChar(this._cursorY, this._cursorX, ch);
793
- this._cursorX++;
794
- if (!this._autoWrap) {
795
- this._cursorX = Math.min(this._cursorX, this._cols - 1);
796
- return;
797
- }
798
- if (this._cursorX >= this._cols) {
799
- this._cursorX = 0;
800
- this._newline();
801
- }
1042
+ const charset = this._shiftOut ? this._g1Charset : this._g0Charset;
1043
+ this._writePrintable(charset === "graphics" ? DEC_SPECIAL_GRAPHICS[ch] ?? ch : ch);
802
1044
  }
803
1045
  }
804
1046
  _feedEscape(ch, code) {
@@ -812,14 +1054,14 @@ var TerminalBuffer = class {
812
1054
  } else if (code === 80 || code === 88 || code === 94 || code === 95) {
813
1055
  this._state = 4 /* Str */;
814
1056
  } else if (code === 40 || code === 41 || code === 42 || code === 43 || code === 45 || code === 46) {
815
- this._state = 5 /* EscCharset */;
1057
+ this._charsetTarget = code === 40 ? "g0" : code === 41 ? "g1" : null;
1058
+ this._state = 6 /* EscCharset */;
816
1059
  } else if (code === 35) {
817
- this._state = 6 /* EscHash */;
1060
+ this._state = 7 /* EscHash */;
818
1061
  } else if (code === 55) {
819
- this._savedCursor = { x: this._cursorX, y: this._cursorY };
1062
+ this._saveCursor();
820
1063
  } else if (code === 56) {
821
- this._cursorX = this._clamp(this._savedCursor.x, 0, this._cols - 1);
822
- this._cursorY = this._clamp(this._savedCursor.y, 0, this._rows - 1);
1064
+ this._restoreCursor();
823
1065
  } else if (code === 68) {
824
1066
  this._newline();
825
1067
  } else if (code === 69) {
@@ -832,14 +1074,15 @@ var TerminalBuffer = class {
832
1074
  this._cursorY = Math.max(0, this._cursorY - 1);
833
1075
  }
834
1076
  } else if (code === 72) {
1077
+ this._tabStops.add(this._cursorX);
835
1078
  } else if (code === 99) {
836
1079
  this._screen = this._makeScreen(this._cols, this._rows);
837
1080
  this._cursorX = 0;
838
1081
  this._cursorY = 0;
839
- this._savedCursor = { x: 0, y: 0 };
840
1082
  this._scrollTop = 0;
841
1083
  this._scrollBottom = this._rows - 1;
842
- this._resetStyle();
1084
+ this._resetModes();
1085
+ this._savedCursor = this._createSavedCursorState();
843
1086
  }
844
1087
  }
845
1088
  _feedCsi(ch, code) {
@@ -848,7 +1091,7 @@ var TerminalBuffer = class {
848
1091
  this._state = 0 /* Normal */;
849
1092
  } else if (code === 63 || code === 33 || code === 62 || code === 32) {
850
1093
  this._csiPrivate = ch;
851
- } else if (code >= 48 && code <= 57 || code === 59) {
1094
+ } else if (code >= 48 && code <= 57 || code === 59 || code === 58) {
852
1095
  this._csiParams += ch;
853
1096
  }
854
1097
  }
@@ -879,6 +1122,9 @@ var TerminalBuffer = class {
879
1122
  case 7:
880
1123
  this._style.inverse = true;
881
1124
  break;
1125
+ case 8:
1126
+ this._style.conceal = true;
1127
+ break;
882
1128
  case 9:
883
1129
  this._style.strikethrough = true;
884
1130
  break;
@@ -896,6 +1142,9 @@ var TerminalBuffer = class {
896
1142
  case 27:
897
1143
  this._style.inverse = false;
898
1144
  break;
1145
+ case 28:
1146
+ this._style.conceal = false;
1147
+ break;
899
1148
  case 29:
900
1149
  this._style.strikethrough = false;
901
1150
  break;
@@ -950,6 +1199,7 @@ function createDefaultStyleState() {
950
1199
  italic: false,
951
1200
  underline: false,
952
1201
  inverse: false,
1202
+ conceal: false,
953
1203
  strikethrough: false
954
1204
  };
955
1205
  }
@@ -970,6 +1220,9 @@ function serializeStyleState(state) {
970
1220
  if (state.inverse) {
971
1221
  codes.push(7);
972
1222
  }
1223
+ if (state.conceal) {
1224
+ codes.push(8);
1225
+ }
973
1226
  if (state.strikethrough) {
974
1227
  codes.push(9);
975
1228
  }
@@ -1088,6 +1341,7 @@ var WAIT_FOR_POLL_MS = 10;
1088
1341
  var TYPE_DELAY_MS = 15;
1089
1342
  var CLOSE_AFTER_SIGNAL_GRACE_MS = 250;
1090
1343
  var CLOSE_AFTER_SIGTERM_MS = 1e3;
1344
+ var CLOSE_AFTER_SIGKILL_MS = 1e3;
1091
1345
  var TerminalSession = class {
1092
1346
  id;
1093
1347
  command;
@@ -1101,8 +1355,7 @@ var TerminalSession = class {
1101
1355
  lastDataAt = Date.now();
1102
1356
  currentCols;
1103
1357
  currentRows;
1104
- closeRequested = false;
1105
- signalRequested = false;
1358
+ closePromise = null;
1106
1359
  constructor({
1107
1360
  id,
1108
1361
  command,
@@ -1113,6 +1366,7 @@ var TerminalSession = class {
1113
1366
  rows = DEFAULT_ROWS,
1114
1367
  observe = false
1115
1368
  }) {
1369
+ assertTerminalGeometry(cols, rows);
1116
1370
  this.id = id;
1117
1371
  this.command = command;
1118
1372
  this.currentCols = cols;
@@ -1157,7 +1411,7 @@ var TerminalSession = class {
1157
1411
  }
1158
1412
  async send(raw) {
1159
1413
  if (this.exitCode !== null) {
1160
- return;
1414
+ throw new Error(`Terminal session "${this.id}" has already exited.`);
1161
1415
  }
1162
1416
  this.pty.write(raw);
1163
1417
  }
@@ -1165,22 +1419,26 @@ var TerminalSession = class {
1165
1419
  if (this.exitCode !== null) {
1166
1420
  return;
1167
1421
  }
1168
- this.signalRequested = true;
1169
1422
  this.pty.kill(sig);
1170
1423
  }
1171
1424
  async waitFor(pattern, opts) {
1172
1425
  const timeout = opts?.timeout ?? DEFAULT_TIMEOUT_MS;
1426
+ assertTimeout(timeout);
1173
1427
  const startedAt = Date.now();
1174
1428
  while (Date.now() - startedAt <= timeout) {
1175
1429
  const matched = matchPattern(this.rawBuffer, pattern);
1176
1430
  if (matched !== null) {
1177
1431
  return matched;
1178
1432
  }
1433
+ if (this.exitCode !== null) {
1434
+ throw new Error(`Terminal session "${this.id}" exited before matching pattern: ${String(pattern)}`);
1435
+ }
1179
1436
  await sleep(WAIT_FOR_POLL_MS);
1180
1437
  }
1181
1438
  throw new Error(`Timed out waiting for pattern after ${timeout}ms: ${String(pattern)}`);
1182
1439
  }
1183
1440
  async waitForQuiet(ms) {
1441
+ assertQuietPeriod(ms);
1184
1442
  while (true) {
1185
1443
  const remaining = ms - (Date.now() - this.lastDataAt);
1186
1444
  if (remaining <= 0) {
@@ -1213,10 +1471,14 @@ var TerminalSession = class {
1213
1471
  if (opts?.last === void 0) {
1214
1472
  return lines;
1215
1473
  }
1474
+ if (!Number.isInteger(opts.last) || opts.last < 0) {
1475
+ throw new Error("History last must be a non-negative integer.");
1476
+ }
1216
1477
  const start = Math.max(0, lines.length - opts.last);
1217
1478
  return lines.slice(start);
1218
1479
  }
1219
1480
  async resize(cols, rows) {
1481
+ assertTerminalGeometry(cols, rows);
1220
1482
  this.currentCols = cols;
1221
1483
  this.currentRows = rows;
1222
1484
  if (this.exitCode === null) {
@@ -1225,6 +1487,9 @@ var TerminalSession = class {
1225
1487
  this.terminal.resize(cols, rows);
1226
1488
  }
1227
1489
  async waitForExit(opts) {
1490
+ if (opts?.timeout !== void 0) {
1491
+ assertTimeout(opts.timeout);
1492
+ }
1228
1493
  if (this.exitCode !== null) {
1229
1494
  return this.exitCode;
1230
1495
  }
@@ -1241,32 +1506,52 @@ var TerminalSession = class {
1241
1506
  if (this.exitCode !== null) {
1242
1507
  return this.exitCode;
1243
1508
  }
1244
- if (!this.closeRequested) {
1245
- this.closeRequested = true;
1246
- const gracefulExitCode = await waitForExit(this.exitPromise, CLOSE_AFTER_SIGNAL_GRACE_MS);
1247
- if (gracefulExitCode !== null) {
1248
- return gracefulExitCode;
1249
- }
1250
- if (this.signalRequested) {
1251
- return this.exitPromise;
1252
- }
1253
- if (this.exitCode === null) {
1254
- this.pty.kill("SIGTERM");
1255
- const afterSigterm = await waitForExit(this.exitPromise, CLOSE_AFTER_SIGTERM_MS);
1256
- if (afterSigterm !== null) {
1257
- return afterSigterm;
1258
- }
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;
1259
1525
  }
1260
- if (this.exitCode === null) {
1261
- 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;
1262
1532
  }
1263
1533
  }
1264
- return this.exitPromise;
1534
+ throw new Error("Timed out waiting for process to exit after SIGKILL.");
1265
1535
  }
1266
1536
  on(event, cb) {
1267
1537
  this.emitter.on(event, cb);
1268
1538
  }
1269
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
+ }
1270
1555
  function createPtyProcess({
1271
1556
  command,
1272
1557
  args,
@@ -1343,13 +1628,28 @@ function splitHistoryLines(input) {
1343
1628
  }
1344
1629
  function normalizeHistoryBuffer(input) {
1345
1630
  let output = "";
1631
+ let line = "";
1632
+ let cursor = 0;
1346
1633
  for (const character of input) {
1347
- if (character === "\r" || character === "\b") {
1634
+ if (character === "\r") {
1635
+ cursor = 0;
1348
1636
  continue;
1349
1637
  }
1350
- output += character;
1638
+ if (character === "\b") {
1639
+ cursor = Math.max(0, cursor - 1);
1640
+ continue;
1641
+ }
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;
1351
1651
  }
1352
- return output;
1652
+ return output + line;
1353
1653
  }
1354
1654
  function sleep(ms) {
1355
1655
  return new Promise((resolve) => {
@@ -1414,11 +1714,12 @@ var TerminalPilot = class _TerminalPilot {
1414
1714
  }
1415
1715
  async close() {
1416
1716
  const sessions = [...this.sessionMap.values()];
1417
- try {
1418
- await Promise.all(sessions.map((session) => session.close()));
1419
- } finally {
1420
- this.sessionMap.clear();
1421
- }
1717
+ await Promise.all(
1718
+ sessions.map(async (session) => {
1719
+ await session.close();
1720
+ this.sessionMap.delete(session.id);
1721
+ })
1722
+ );
1422
1723
  }
1423
1724
  };
1424
1725
 
@@ -1436,6 +1737,7 @@ function createTerminalPilotRuntime(options = {}) {
1436
1737
  const launchPilot = options.launchPilot ?? TerminalPilot.launch;
1437
1738
  const nameToId = /* @__PURE__ */ new Map();
1438
1739
  const idToName = /* @__PURE__ */ new Map();
1740
+ const pendingNames = /* @__PURE__ */ new Set();
1439
1741
  let pilotPromise;
1440
1742
  function getRequestedName(name, env) {
1441
1743
  return name ?? env?.get(SESSION_ENV_VAR);
@@ -1446,7 +1748,7 @@ function createTerminalPilotRuntime(options = {}) {
1446
1748
  }
1447
1749
  function nextSessionName() {
1448
1750
  let index = 1;
1449
- while (nameToId.has(`s${index}`)) {
1751
+ while (nameToId.has(`s${index}`) || pendingNames.has(`s${index}`)) {
1450
1752
  index += 1;
1451
1753
  }
1452
1754
  return `s${index}`;
@@ -1457,8 +1759,12 @@ function createTerminalPilotRuntime(options = {}) {
1457
1759
  return { name, session };
1458
1760
  }
1459
1761
  function forgetSession(name, sessionId) {
1460
- nameToId.delete(name);
1461
- 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
+ }
1462
1768
  }
1463
1769
  function formatAvailableSessions(names) {
1464
1770
  if (names.length === 0) {
@@ -1491,22 +1797,44 @@ function createTerminalPilotRuntime(options = {}) {
1491
1797
  return [{ name, session }];
1492
1798
  });
1493
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
+ }
1494
1816
  return {
1495
1817
  async createSession(params2, env) {
1496
1818
  const requestedName = getRequestedName(params2.session, env) ?? nextSessionName();
1497
- if (nameToId.has(requestedName)) {
1819
+ await discardExitedSessionName(requestedName);
1820
+ if (nameToId.has(requestedName) || pendingNames.has(requestedName)) {
1498
1821
  throw new UserError(`Session "${requestedName}" already exists.`);
1499
1822
  }
1500
- const pilot = await getPilot();
1501
- const session = await pilot.newSession({
1502
- command: params2.command,
1503
- args: params2.args,
1504
- cwd: params2.cwd,
1505
- cols: params2.cols,
1506
- rows: params2.rows,
1507
- observe: params2.observe
1508
- });
1509
- 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
+ }
1510
1838
  },
1511
1839
  async resolveSession(name, env) {
1512
1840
  const requestedName = getRequestedName(name, env);
@@ -1545,6 +1873,7 @@ function createTerminalPilotRuntime(options = {}) {
1545
1873
  pilotPromise = void 0;
1546
1874
  nameToId.clear();
1547
1875
  idToName.clear();
1876
+ pendingNames.clear();
1548
1877
  }
1549
1878
  };
1550
1879
  }