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
  };
@@ -99,27 +99,53 @@ function consumeTerminatedString(input, index, allowBellTerminator) {
99
99
 
100
100
  // src/terminal-buffer.ts
101
101
  var RESET_SGR = "\x1B[0m";
102
+ var DEC_SPECIAL_GRAPHICS = {
103
+ j: "\u2518",
104
+ k: "\u2510",
105
+ l: "\u250C",
106
+ m: "\u2514",
107
+ n: "\u253C",
108
+ q: "\u2500",
109
+ t: "\u251C",
110
+ u: "\u2524",
111
+ v: "\u2534",
112
+ w: "\u252C",
113
+ x: "\u2502"
114
+ };
102
115
  var TerminalBuffer = class {
103
116
  _cols;
104
117
  _rows;
105
118
  _screen;
119
+ _primaryScreen = null;
106
120
  _cursorX = 0;
107
121
  _cursorY = 0;
108
- _savedCursor = { x: 0, y: 0 };
122
+ _savedCursor;
109
123
  _scrollTop = 0;
110
124
  _scrollBottom;
111
125
  _state = 0 /* Normal */;
112
126
  _csiParams = "";
113
127
  _csiPrivate = "";
114
128
  _autoWrap = true;
129
+ _pendingWrap = false;
130
+ _originMode = false;
131
+ _insertMode = false;
132
+ _tabStops;
133
+ _lastPrintedChar = null;
115
134
  _style = createDefaultStyleState();
116
135
  _styleSequence = "";
136
+ _stringState = 0 /* Normal */;
137
+ _charsetTarget = null;
138
+ _g0Charset = "ascii";
139
+ _g1Charset = "ascii";
140
+ _shiftOut = false;
117
141
  displayBuffer;
118
142
  constructor(cols, rows) {
119
143
  this._cols = cols;
120
144
  this._rows = rows;
121
145
  this._scrollBottom = rows - 1;
122
146
  this._screen = this._makeScreen(cols, rows);
147
+ this._tabStops = this._createDefaultTabStops(cols);
148
+ this._savedCursor = this._createSavedCursorState();
123
149
  this.displayBuffer = Object.defineProperties(
124
150
  {},
125
151
  {
@@ -175,8 +201,12 @@ var TerminalBuffer = class {
175
201
  }
176
202
  this._cols = cols;
177
203
  this._rows = rows;
178
- this._scrollTop = 0;
179
- this._scrollBottom = rows - 1;
204
+ this._scrollTop = this._clamp(this._scrollTop, 0, rows - 1);
205
+ this._scrollBottom = this._clamp(this._scrollBottom, 0, rows - 1);
206
+ if (this._scrollTop >= this._scrollBottom) {
207
+ this._scrollTop = 0;
208
+ this._scrollBottom = rows - 1;
209
+ }
180
210
  this._cursorX = this._clamp(this._cursorX, 0, cols - 1);
181
211
  this._cursorY = this._clamp(this._cursorY, 0, rows - 1);
182
212
  }
@@ -186,13 +216,96 @@ var TerminalBuffer = class {
186
216
  _makeRow(cols) {
187
217
  return Array(cols).fill(null);
188
218
  }
219
+ _createDefaultTabStops(cols) {
220
+ const tabStops = /* @__PURE__ */ new Set();
221
+ for (let column = 8; column < cols; column += 8) {
222
+ tabStops.add(column);
223
+ }
224
+ return tabStops;
225
+ }
189
226
  _clamp(value, min, max) {
190
227
  return Math.max(min, Math.min(max, value));
191
228
  }
229
+ _createSavedCursorState() {
230
+ return {
231
+ x: this._cursorX,
232
+ y: this._cursorY,
233
+ autoWrap: this._autoWrap,
234
+ style: { ...this._style, fg: this._style.fg?.slice(), bg: this._style.bg?.slice() },
235
+ styleSequence: this._styleSequence
236
+ };
237
+ }
238
+ _saveCursor() {
239
+ this._savedCursor = this._createSavedCursorState();
240
+ }
241
+ _restoreCursor() {
242
+ this._cursorX = this._clamp(this._savedCursor.x, 0, this._cols - 1);
243
+ this._cursorY = this._clamp(this._savedCursor.y, 0, this._rows - 1);
244
+ this._autoWrap = this._savedCursor.autoWrap;
245
+ this._style = {
246
+ ...this._savedCursor.style,
247
+ fg: this._savedCursor.style.fg?.slice(),
248
+ bg: this._savedCursor.style.bg?.slice()
249
+ };
250
+ this._styleSequence = this._savedCursor.styleSequence;
251
+ }
252
+ _resetModes() {
253
+ this._autoWrap = true;
254
+ this._originMode = false;
255
+ this._insertMode = false;
256
+ this._pendingWrap = false;
257
+ this._tabStops = this._createDefaultTabStops(this._cols);
258
+ this._g0Charset = "ascii";
259
+ this._g1Charset = "ascii";
260
+ this._shiftOut = false;
261
+ this._resetStyle();
262
+ }
263
+ _writePrintable(ch) {
264
+ const width = this._cellWidth(ch);
265
+ if (this._autoWrap && this._pendingWrap) {
266
+ this._cursorX = 0;
267
+ this._newline();
268
+ } else if (this._autoWrap && this._cursorX + width > this._cols) {
269
+ this._cursorX = 0;
270
+ this._newline();
271
+ }
272
+ if (this._insertMode) {
273
+ const row = this._screen[this._cursorY];
274
+ if (row) {
275
+ row.splice(this._cursorX, 0, ...Array(width).fill(null));
276
+ row.splice(this._cols);
277
+ }
278
+ }
279
+ this._setChar(this._cursorY, this._cursorX, ch);
280
+ for (let column = 1; column < width && this._cursorX + column < this._cols; column += 1) {
281
+ const row = this._screen[this._cursorY];
282
+ if (row) row[this._cursorX + column] = null;
283
+ }
284
+ this._lastPrintedChar = ch;
285
+ if (!this._autoWrap) {
286
+ this._cursorX = Math.min(this._cursorX + width, this._cols - 1);
287
+ this._pendingWrap = false;
288
+ } else if (this._cursorX + width >= this._cols) {
289
+ this._cursorX = this._cols - 1;
290
+ this._pendingWrap = true;
291
+ } else {
292
+ this._cursorX += width;
293
+ this._pendingWrap = false;
294
+ }
295
+ }
296
+ _cellWidth(ch) {
297
+ const codePoint = ch.codePointAt(0);
298
+ if (codePoint === void 0) return 0;
299
+ 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) {
300
+ return Math.min(2, this._cols);
301
+ }
302
+ return 1;
303
+ }
192
304
  _setChar(y, x, ch) {
193
305
  const row = this._screen[y];
194
306
  if (row && x >= 0 && x < this._cols) {
195
- const cell = [ch.charCodeAt(0), ch];
307
+ const renderedChar = this._style.conceal ? " " : ch;
308
+ const cell = [renderedChar.charCodeAt(0), renderedChar];
196
309
  if (this._styleSequence.length > 0) {
197
310
  Object.defineProperty(cell, "style", {
198
311
  value: this._styleSequence,
@@ -223,6 +336,7 @@ var TerminalBuffer = class {
223
336
  }
224
337
  }
225
338
  _newline() {
339
+ this._pendingWrap = false;
226
340
  if (this._cursorY === this._scrollBottom) {
227
341
  this._scrollUp(1);
228
342
  } else {
@@ -231,7 +345,11 @@ var TerminalBuffer = class {
231
345
  }
232
346
  _parseCsiParams() {
233
347
  if (!this._csiParams) return [];
234
- return this._csiParams.split(";").map((s) => s === "" ? 0 : parseInt(s, 10));
348
+ const values = this._csiParams.split(";").flatMap((segment) => segment.split(":")).map((segment) => segment === "" ? 0 : parseInt(segment, 10));
349
+ if ((values[0] === 38 || values[0] === 48) && values[1] === 2 && values[2] === 0) {
350
+ values.splice(2, 1);
351
+ }
352
+ return values;
235
353
  }
236
354
  _execCsi(final) {
237
355
  const params = this._parseCsiParams();
@@ -242,21 +360,45 @@ var TerminalBuffer = class {
242
360
  if (params.includes(7)) {
243
361
  this._autoWrap = final === "h";
244
362
  }
363
+ if (params.includes(6)) {
364
+ this._originMode = final === "h";
365
+ this._cursorX = 0;
366
+ this._cursorY = this._originMode ? this._scrollTop : 0;
367
+ }
245
368
  if (params.includes(1049)) {
246
369
  if (final === "h") {
370
+ this._primaryScreen = {
371
+ screen: this._screen,
372
+ cursorX: this._cursorX,
373
+ cursorY: this._cursorY,
374
+ pendingWrap: this._pendingWrap,
375
+ style: { ...this._style, fg: this._style.fg?.slice(), bg: this._style.bg?.slice() },
376
+ styleSequence: this._styleSequence
377
+ };
247
378
  this._screen = this._makeScreen(this._cols, this._rows);
248
379
  this._cursorX = 0;
249
380
  this._cursorY = 0;
381
+ this._pendingWrap = false;
250
382
  } else {
251
- this._screen = this._makeScreen(this._cols, this._rows);
252
- this._cursorX = 0;
253
- this._cursorY = 0;
383
+ if (this._primaryScreen !== null) {
384
+ this._screen = this._primaryScreen.screen;
385
+ this._cursorX = this._primaryScreen.cursorX;
386
+ this._cursorY = this._primaryScreen.cursorY;
387
+ this._pendingWrap = this._primaryScreen.pendingWrap;
388
+ this._style = this._primaryScreen.style;
389
+ this._styleSequence = this._primaryScreen.styleSequence;
390
+ this._primaryScreen = null;
391
+ }
254
392
  }
255
- this._resetStyle();
393
+ if (final === "h") this._resetStyle();
256
394
  }
257
395
  }
258
396
  return;
259
397
  }
398
+ if ((final === "h" || final === "l") && params.includes(4)) {
399
+ this._insertMode = final === "h";
400
+ return;
401
+ }
260
402
  switch (final) {
261
403
  case "A":
262
404
  this._cursorY = this._clamp(this._cursorY - Math.max(1, p0), 0, this._rows - 1);
@@ -288,7 +430,7 @@ var TerminalBuffer = class {
288
430
  case "H":
289
431
  // cursor position
290
432
  case "f":
291
- this._cursorY = this._clamp(Math.max(1, p0) - 1, 0, this._rows - 1);
433
+ 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);
292
434
  this._cursorX = this._clamp(Math.max(1, p1) - 1, 0, this._cols - 1);
293
435
  break;
294
436
  case "I":
@@ -303,7 +445,7 @@ var TerminalBuffer = class {
303
445
  } else if (p0 === 1) {
304
446
  for (let y = 0; y < this._cursorY; y++) this._eraseLine(y, 0, this._cols - 1);
305
447
  this._eraseLine(this._cursorY, 0, this._cursorX);
306
- } else if (p0 === 2 || p0 === 3) {
448
+ } else if (p0 === 2) {
307
449
  for (let y = 0; y < this._rows; y++) this._eraseLine(y, 0, this._cols - 1);
308
450
  }
309
451
  break;
@@ -316,6 +458,9 @@ var TerminalBuffer = class {
316
458
  this._eraseLine(this._cursorY, this._cursorX, this._cursorX + Math.max(1, p0) - 1);
317
459
  break;
318
460
  case "L": {
461
+ if (this._cursorY < this._scrollTop || this._cursorY > this._scrollBottom) {
462
+ break;
463
+ }
319
464
  const n = Math.max(1, p0);
320
465
  for (let i = 0; i < n; i++) {
321
466
  this._screen.splice(this._scrollBottom, 1);
@@ -324,6 +469,9 @@ var TerminalBuffer = class {
324
469
  break;
325
470
  }
326
471
  case "M": {
472
+ if (this._cursorY < this._scrollTop || this._cursorY > this._scrollBottom) {
473
+ break;
474
+ }
327
475
  const n = Math.max(1, p0);
328
476
  for (let i = 0; i < n; i++) {
329
477
  this._screen.splice(this._cursorY, 1);
@@ -362,6 +510,13 @@ var TerminalBuffer = class {
362
510
  }
363
511
  break;
364
512
  }
513
+ case "b":
514
+ if (this._lastPrintedChar !== null) {
515
+ for (let index = 0; index < Math.max(1, p0); index += 1) {
516
+ this._writePrintable(this._lastPrintedChar);
517
+ }
518
+ }
519
+ break;
365
520
  case "d":
366
521
  this._cursorY = this._clamp(Math.max(1, p0) - 1, 0, this._rows - 1);
367
522
  break;
@@ -376,15 +531,19 @@ var TerminalBuffer = class {
376
531
  this._scrollBottom = bottom;
377
532
  }
378
533
  this._cursorX = 0;
379
- this._cursorY = 0;
534
+ this._cursorY = this._originMode ? this._scrollTop : 0;
380
535
  break;
381
536
  }
382
537
  case "s":
383
- this._savedCursor = { x: this._cursorX, y: this._cursorY };
538
+ this._saveCursor();
384
539
  break;
385
540
  case "u":
386
- this._cursorX = this._clamp(this._savedCursor.x, 0, this._cols - 1);
387
- this._cursorY = this._clamp(this._savedCursor.y, 0, this._rows - 1);
541
+ this._restoreCursor();
542
+ break;
543
+ case "p":
544
+ if (this._csiPrivate === "!") {
545
+ this._resetModes();
546
+ }
388
547
  break;
389
548
  case "m":
390
549
  this._applySgr(params);
@@ -395,6 +554,14 @@ var TerminalBuffer = class {
395
554
  }
396
555
  _feed(ch) {
397
556
  const code = ch.charCodeAt(0);
557
+ if (this._state !== 0 /* Normal */ && (code === 24 || code === 26)) {
558
+ this._state = 0 /* Normal */;
559
+ return;
560
+ }
561
+ if (this._state === 2 /* Csi */ && code === 27) {
562
+ this._state = 1 /* Escape */;
563
+ return;
564
+ }
398
565
  switch (this._state) {
399
566
  case 0 /* Normal */:
400
567
  this._feedNormal(ch, code);
@@ -409,20 +576,37 @@ var TerminalBuffer = class {
409
576
  if (code === 7 || code === 156) {
410
577
  this._state = 0 /* Normal */;
411
578
  } else if (code === 27) {
412
- this._state = 0 /* Normal */;
579
+ this._stringState = 3 /* Osc */;
580
+ this._state = 5 /* StringEscape */;
413
581
  }
414
582
  break;
415
583
  case 4 /* Str */:
416
- if (code === 156 || code === 7) {
584
+ if (code === 156) {
417
585
  this._state = 0 /* Normal */;
418
586
  } else if (code === 27) {
419
- this._state = 0 /* Normal */;
587
+ this._stringState = 4 /* Str */;
588
+ this._state = 5 /* StringEscape */;
420
589
  }
421
590
  break;
422
- case 5 /* EscCharset */:
591
+ case 5 /* StringEscape */:
592
+ this._state = ch === "\\" ? 0 /* Normal */ : this._stringState;
593
+ break;
594
+ case 6 /* EscCharset */:
595
+ if (this._charsetTarget === "g0") this._g0Charset = ch === "0" ? "graphics" : "ascii";
596
+ if (this._charsetTarget === "g1") this._g1Charset = ch === "0" ? "graphics" : "ascii";
597
+ this._charsetTarget = null;
423
598
  this._state = 0 /* Normal */;
424
599
  break;
425
- case 6 /* EscHash */:
600
+ case 7 /* EscHash */:
601
+ if (ch === "8") {
602
+ for (let row = 0; row < this._rows; row += 1) {
603
+ for (let column = 0; column < this._cols; column += 1) {
604
+ this._setChar(row, column, "E");
605
+ }
606
+ }
607
+ this._cursorX = 0;
608
+ this._cursorY = 0;
609
+ }
426
610
  this._state = 0 /* Normal */;
427
611
  break;
428
612
  }
@@ -438,32 +622,28 @@ var TerminalBuffer = class {
438
622
  this._state = 3 /* Osc */;
439
623
  } else if (code === 144 || code === 152 || code === 158 || code === 159) {
440
624
  this._state = 4 /* Str */;
625
+ } else if (code === 133) {
626
+ this._cursorX = 0;
627
+ this._newline();
628
+ } else if (code === 156) {
441
629
  } else if (code === 7 || code === 5 || code === 6) {
442
630
  } else if (code === 8) {
631
+ this._pendingWrap = false;
443
632
  if (this._cursorX > 0) this._cursorX--;
444
633
  } else if (code === 127) {
445
- if (this._cursorX > 0) {
446
- this._cursorX--;
447
- this._setChar(this._cursorY, this._cursorX, " ");
448
- }
449
634
  } else if (code === 9) {
450
- this._cursorX = Math.min(this._cols - 1, (Math.floor(this._cursorX / 8) + 1) * 8);
635
+ const nextTabStop = [...this._tabStops].sort((left, right) => left - right).find((tabStop) => tabStop > this._cursorX);
636
+ this._cursorX = nextTabStop ?? this._cols - 1;
451
637
  } else if (code === 10 || code === 11 || code === 12) {
452
638
  this._newline();
453
639
  } else if (code === 13) {
640
+ this._pendingWrap = false;
454
641
  this._cursorX = 0;
455
642
  } else if (code === 14 || code === 15) {
643
+ this._shiftOut = code === 14;
456
644
  } else if (code >= 32 && code !== 127) {
457
- this._setChar(this._cursorY, this._cursorX, ch);
458
- this._cursorX++;
459
- if (!this._autoWrap) {
460
- this._cursorX = Math.min(this._cursorX, this._cols - 1);
461
- return;
462
- }
463
- if (this._cursorX >= this._cols) {
464
- this._cursorX = 0;
465
- this._newline();
466
- }
645
+ const charset = this._shiftOut ? this._g1Charset : this._g0Charset;
646
+ this._writePrintable(charset === "graphics" ? DEC_SPECIAL_GRAPHICS[ch] ?? ch : ch);
467
647
  }
468
648
  }
469
649
  _feedEscape(ch, code) {
@@ -477,14 +657,14 @@ var TerminalBuffer = class {
477
657
  } else if (code === 80 || code === 88 || code === 94 || code === 95) {
478
658
  this._state = 4 /* Str */;
479
659
  } else if (code === 40 || code === 41 || code === 42 || code === 43 || code === 45 || code === 46) {
480
- this._state = 5 /* EscCharset */;
660
+ this._charsetTarget = code === 40 ? "g0" : code === 41 ? "g1" : null;
661
+ this._state = 6 /* EscCharset */;
481
662
  } else if (code === 35) {
482
- this._state = 6 /* EscHash */;
663
+ this._state = 7 /* EscHash */;
483
664
  } else if (code === 55) {
484
- this._savedCursor = { x: this._cursorX, y: this._cursorY };
665
+ this._saveCursor();
485
666
  } else if (code === 56) {
486
- this._cursorX = this._clamp(this._savedCursor.x, 0, this._cols - 1);
487
- this._cursorY = this._clamp(this._savedCursor.y, 0, this._rows - 1);
667
+ this._restoreCursor();
488
668
  } else if (code === 68) {
489
669
  this._newline();
490
670
  } else if (code === 69) {
@@ -497,14 +677,15 @@ var TerminalBuffer = class {
497
677
  this._cursorY = Math.max(0, this._cursorY - 1);
498
678
  }
499
679
  } else if (code === 72) {
680
+ this._tabStops.add(this._cursorX);
500
681
  } else if (code === 99) {
501
682
  this._screen = this._makeScreen(this._cols, this._rows);
502
683
  this._cursorX = 0;
503
684
  this._cursorY = 0;
504
- this._savedCursor = { x: 0, y: 0 };
505
685
  this._scrollTop = 0;
506
686
  this._scrollBottom = this._rows - 1;
507
- this._resetStyle();
687
+ this._resetModes();
688
+ this._savedCursor = this._createSavedCursorState();
508
689
  }
509
690
  }
510
691
  _feedCsi(ch, code) {
@@ -513,7 +694,7 @@ var TerminalBuffer = class {
513
694
  this._state = 0 /* Normal */;
514
695
  } else if (code === 63 || code === 33 || code === 62 || code === 32) {
515
696
  this._csiPrivate = ch;
516
- } else if (code >= 48 && code <= 57 || code === 59) {
697
+ } else if (code >= 48 && code <= 57 || code === 59 || code === 58) {
517
698
  this._csiParams += ch;
518
699
  }
519
700
  }
@@ -544,6 +725,9 @@ var TerminalBuffer = class {
544
725
  case 7:
545
726
  this._style.inverse = true;
546
727
  break;
728
+ case 8:
729
+ this._style.conceal = true;
730
+ break;
547
731
  case 9:
548
732
  this._style.strikethrough = true;
549
733
  break;
@@ -561,6 +745,9 @@ var TerminalBuffer = class {
561
745
  case 27:
562
746
  this._style.inverse = false;
563
747
  break;
748
+ case 28:
749
+ this._style.conceal = false;
750
+ break;
564
751
  case 29:
565
752
  this._style.strikethrough = false;
566
753
  break;
@@ -615,6 +802,7 @@ function createDefaultStyleState() {
615
802
  italic: false,
616
803
  underline: false,
617
804
  inverse: false,
805
+ conceal: false,
618
806
  strikethrough: false
619
807
  };
620
808
  }
@@ -635,6 +823,9 @@ function serializeStyleState(state) {
635
823
  if (state.inverse) {
636
824
  codes.push(7);
637
825
  }
826
+ if (state.conceal) {
827
+ codes.push(8);
828
+ }
638
829
  if (state.strikethrough) {
639
830
  codes.push(9);
640
831
  }
@@ -753,6 +944,7 @@ var WAIT_FOR_POLL_MS = 10;
753
944
  var TYPE_DELAY_MS = 15;
754
945
  var CLOSE_AFTER_SIGNAL_GRACE_MS = 250;
755
946
  var CLOSE_AFTER_SIGTERM_MS = 1e3;
947
+ var CLOSE_AFTER_SIGKILL_MS = 1e3;
756
948
  var TerminalSession = class {
757
949
  id;
758
950
  command;
@@ -766,8 +958,7 @@ var TerminalSession = class {
766
958
  lastDataAt = Date.now();
767
959
  currentCols;
768
960
  currentRows;
769
- closeRequested = false;
770
- signalRequested = false;
961
+ closePromise = null;
771
962
  constructor({
772
963
  id,
773
964
  command,
@@ -778,6 +969,7 @@ var TerminalSession = class {
778
969
  rows = DEFAULT_ROWS,
779
970
  observe = false
780
971
  }) {
972
+ assertTerminalGeometry(cols, rows);
781
973
  this.id = id;
782
974
  this.command = command;
783
975
  this.currentCols = cols;
@@ -822,7 +1014,7 @@ var TerminalSession = class {
822
1014
  }
823
1015
  async send(raw) {
824
1016
  if (this.exitCode !== null) {
825
- return;
1017
+ throw new Error(`Terminal session "${this.id}" has already exited.`);
826
1018
  }
827
1019
  this.pty.write(raw);
828
1020
  }
@@ -830,22 +1022,26 @@ var TerminalSession = class {
830
1022
  if (this.exitCode !== null) {
831
1023
  return;
832
1024
  }
833
- this.signalRequested = true;
834
1025
  this.pty.kill(sig);
835
1026
  }
836
1027
  async waitFor(pattern, opts) {
837
1028
  const timeout = opts?.timeout ?? DEFAULT_TIMEOUT_MS;
1029
+ assertTimeout(timeout);
838
1030
  const startedAt = Date.now();
839
1031
  while (Date.now() - startedAt <= timeout) {
840
1032
  const matched = matchPattern(this.rawBuffer, pattern);
841
1033
  if (matched !== null) {
842
1034
  return matched;
843
1035
  }
1036
+ if (this.exitCode !== null) {
1037
+ throw new Error(`Terminal session "${this.id}" exited before matching pattern: ${String(pattern)}`);
1038
+ }
844
1039
  await sleep(WAIT_FOR_POLL_MS);
845
1040
  }
846
1041
  throw new Error(`Timed out waiting for pattern after ${timeout}ms: ${String(pattern)}`);
847
1042
  }
848
1043
  async waitForQuiet(ms) {
1044
+ assertQuietPeriod(ms);
849
1045
  while (true) {
850
1046
  const remaining = ms - (Date.now() - this.lastDataAt);
851
1047
  if (remaining <= 0) {
@@ -878,10 +1074,14 @@ var TerminalSession = class {
878
1074
  if (opts?.last === void 0) {
879
1075
  return lines;
880
1076
  }
1077
+ if (!Number.isInteger(opts.last) || opts.last < 0) {
1078
+ throw new Error("History last must be a non-negative integer.");
1079
+ }
881
1080
  const start = Math.max(0, lines.length - opts.last);
882
1081
  return lines.slice(start);
883
1082
  }
884
1083
  async resize(cols, rows) {
1084
+ assertTerminalGeometry(cols, rows);
885
1085
  this.currentCols = cols;
886
1086
  this.currentRows = rows;
887
1087
  if (this.exitCode === null) {
@@ -890,6 +1090,9 @@ var TerminalSession = class {
890
1090
  this.terminal.resize(cols, rows);
891
1091
  }
892
1092
  async waitForExit(opts) {
1093
+ if (opts?.timeout !== void 0) {
1094
+ assertTimeout(opts.timeout);
1095
+ }
893
1096
  if (this.exitCode !== null) {
894
1097
  return this.exitCode;
895
1098
  }
@@ -906,32 +1109,52 @@ var TerminalSession = class {
906
1109
  if (this.exitCode !== null) {
907
1110
  return this.exitCode;
908
1111
  }
909
- if (!this.closeRequested) {
910
- this.closeRequested = true;
911
- const gracefulExitCode = await waitForExit(this.exitPromise, CLOSE_AFTER_SIGNAL_GRACE_MS);
912
- if (gracefulExitCode !== null) {
913
- return gracefulExitCode;
914
- }
915
- if (this.signalRequested) {
916
- return this.exitPromise;
917
- }
918
- if (this.exitCode === null) {
919
- this.pty.kill("SIGTERM");
920
- const afterSigterm = await waitForExit(this.exitPromise, CLOSE_AFTER_SIGTERM_MS);
921
- if (afterSigterm !== null) {
922
- return afterSigterm;
923
- }
1112
+ this.closePromise ??= this.closeProcess().catch((error) => {
1113
+ this.closePromise = null;
1114
+ throw error;
1115
+ });
1116
+ return this.closePromise;
1117
+ }
1118
+ async closeProcess() {
1119
+ const gracefulExitCode = await waitForExit(this.exitPromise, CLOSE_AFTER_SIGNAL_GRACE_MS);
1120
+ if (gracefulExitCode !== null) {
1121
+ return gracefulExitCode;
1122
+ }
1123
+ if (this.exitCode === null) {
1124
+ this.pty.kill("SIGTERM");
1125
+ const afterSigterm = await waitForExit(this.exitPromise, CLOSE_AFTER_SIGTERM_MS);
1126
+ if (afterSigterm !== null) {
1127
+ return afterSigterm;
924
1128
  }
925
- if (this.exitCode === null) {
926
- this.pty.kill("SIGKILL");
1129
+ }
1130
+ if (this.exitCode === null) {
1131
+ this.pty.kill("SIGKILL");
1132
+ const afterSigkill = await waitForExit(this.exitPromise, CLOSE_AFTER_SIGKILL_MS);
1133
+ if (afterSigkill !== null) {
1134
+ return afterSigkill;
927
1135
  }
928
1136
  }
929
- return this.exitPromise;
1137
+ throw new Error("Timed out waiting for process to exit after SIGKILL.");
930
1138
  }
931
1139
  on(event, cb) {
932
1140
  this.emitter.on(event, cb);
933
1141
  }
934
1142
  };
1143
+ function assertTerminalGeometry(cols, rows) {
1144
+ if (!Number.isInteger(cols) || cols <= 0 || !Number.isInteger(rows) || rows <= 0) {
1145
+ throw new Error("Terminal columns and rows must be positive integers.");
1146
+ }
1147
+ }
1148
+ function assertTimeout(timeout) {
1149
+ if (!Number.isFinite(timeout) || timeout < 0) {
1150
+ throw new Error("Timeout must be a finite non-negative number.");
1151
+ }
1152
+ }
1153
+ function assertQuietPeriod(duration) {
1154
+ if (!Number.isFinite(duration) || duration < 0) {
1155
+ throw new Error("Quiet period must be a finite non-negative number.");
1156
+ }
1157
+ }
935
1158
  function createPtyProcess({
936
1159
  command,
937
1160
  args,
@@ -1008,13 +1231,28 @@ function splitHistoryLines(input) {
1008
1231
  }
1009
1232
  function normalizeHistoryBuffer(input) {
1010
1233
  let output = "";
1234
+ let line = "";
1235
+ let cursor = 0;
1011
1236
  for (const character of input) {
1012
- if (character === "\r" || character === "\b") {
1237
+ if (character === "\r") {
1238
+ cursor = 0;
1239
+ continue;
1240
+ }
1241
+ if (character === "\b") {
1242
+ cursor = Math.max(0, cursor - 1);
1013
1243
  continue;
1014
1244
  }
1015
- output += character;
1245
+ if (character === "\n") {
1246
+ output += `${line}
1247
+ `;
1248
+ line = "";
1249
+ cursor = 0;
1250
+ continue;
1251
+ }
1252
+ line = `${line.slice(0, cursor)}${character}${line.slice(cursor + 1)}`;
1253
+ cursor += 1;
1016
1254
  }
1017
- return output;
1255
+ return output + line;
1018
1256
  }
1019
1257
  function sleep(ms) {
1020
1258
  return new Promise((resolve) => {
@@ -1079,11 +1317,12 @@ var TerminalPilot = class _TerminalPilot {
1079
1317
  }
1080
1318
  async close() {
1081
1319
  const sessions = [...this.sessionMap.values()];
1082
- try {
1083
- await Promise.all(sessions.map((session) => session.close()));
1084
- } finally {
1085
- this.sessionMap.clear();
1086
- }
1320
+ await Promise.all(
1321
+ sessions.map(async (session) => {
1322
+ await session.close();
1323
+ this.sessionMap.delete(session.id);
1324
+ })
1325
+ );
1087
1326
  }
1088
1327
  };
1089
1328
 
@@ -1101,6 +1340,7 @@ function createTerminalPilotRuntime(options = {}) {
1101
1340
  const launchPilot = options.launchPilot ?? TerminalPilot.launch;
1102
1341
  const nameToId = /* @__PURE__ */ new Map();
1103
1342
  const idToName = /* @__PURE__ */ new Map();
1343
+ const pendingNames = /* @__PURE__ */ new Set();
1104
1344
  let pilotPromise;
1105
1345
  function getRequestedName(name, env) {
1106
1346
  return name ?? env?.get(SESSION_ENV_VAR);
@@ -1111,7 +1351,7 @@ function createTerminalPilotRuntime(options = {}) {
1111
1351
  }
1112
1352
  function nextSessionName() {
1113
1353
  let index = 1;
1114
- while (nameToId.has(`s${index}`)) {
1354
+ while (nameToId.has(`s${index}`) || pendingNames.has(`s${index}`)) {
1115
1355
  index += 1;
1116
1356
  }
1117
1357
  return `s${index}`;
@@ -1122,8 +1362,12 @@ function createTerminalPilotRuntime(options = {}) {
1122
1362
  return { name, session };
1123
1363
  }
1124
1364
  function forgetSession(name, sessionId) {
1125
- nameToId.delete(name);
1126
- idToName.delete(sessionId);
1365
+ if (nameToId.get(name) === sessionId) {
1366
+ nameToId.delete(name);
1367
+ }
1368
+ if (idToName.get(sessionId) === name) {
1369
+ idToName.delete(sessionId);
1370
+ }
1127
1371
  }
1128
1372
  function formatAvailableSessions(names) {
1129
1373
  if (names.length === 0) {
@@ -1156,22 +1400,44 @@ function createTerminalPilotRuntime(options = {}) {
1156
1400
  return [{ name, session }];
1157
1401
  });
1158
1402
  }
1403
+ async function discardExitedSessionName(name) {
1404
+ const sessionId = nameToId.get(name);
1405
+ if (sessionId === void 0) {
1406
+ return;
1407
+ }
1408
+ const pilot = await getPilot();
1409
+ try {
1410
+ const session = pilot.getSession(sessionId);
1411
+ if (session.exitCode === null) {
1412
+ return;
1413
+ }
1414
+ pilot.deleteSession(sessionId);
1415
+ } catch {
1416
+ }
1417
+ forgetSession(name, sessionId);
1418
+ }
1159
1419
  return {
1160
1420
  async createSession(params, env) {
1161
1421
  const requestedName = getRequestedName(params.session, env) ?? nextSessionName();
1162
- if (nameToId.has(requestedName)) {
1422
+ await discardExitedSessionName(requestedName);
1423
+ if (nameToId.has(requestedName) || pendingNames.has(requestedName)) {
1163
1424
  throw new UserError(`Session "${requestedName}" already exists.`);
1164
1425
  }
1165
- const pilot = await getPilot();
1166
- const session = await pilot.newSession({
1167
- command: params.command,
1168
- args: params.args,
1169
- cwd: params.cwd,
1170
- cols: params.cols,
1171
- rows: params.rows,
1172
- observe: params.observe
1173
- });
1174
- return rememberSession(requestedName, session);
1426
+ pendingNames.add(requestedName);
1427
+ try {
1428
+ const pilot = await getPilot();
1429
+ const session = await pilot.newSession({
1430
+ command: params.command,
1431
+ args: params.args,
1432
+ cwd: params.cwd,
1433
+ cols: params.cols,
1434
+ rows: params.rows,
1435
+ observe: params.observe
1436
+ });
1437
+ return rememberSession(requestedName, session);
1438
+ } finally {
1439
+ pendingNames.delete(requestedName);
1440
+ }
1175
1441
  },
1176
1442
  async resolveSession(name, env) {
1177
1443
  const requestedName = getRequestedName(name, env);
@@ -1210,6 +1476,7 @@ function createTerminalPilotRuntime(options = {}) {
1210
1476
  pilotPromise = void 0;
1211
1477
  nameToId.clear();
1212
1478
  idToName.clear();
1479
+ pendingNames.clear();
1213
1480
  }
1214
1481
  };
1215
1482
  }