terminal-pilot 0.0.34 → 0.0.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.d.ts +4 -1
- package/dist/cli.js +576 -181
- package/dist/cli.js.map +4 -4
- package/dist/commands/daemon-runtime.d.ts +4 -0
- package/dist/commands/daemon-runtime.js +1944 -0
- package/dist/commands/daemon-runtime.js.map +7 -0
- package/dist/testing/cli-repl.js +587 -191
- package/dist/testing/cli-repl.js.map +4 -4
- package/dist/testing/qa-cli.js +599 -203
- package/dist/testing/qa-cli.js.map +4 -4
- package/node_modules/toolcraft-design/dist/explorer/layout.js +5 -3
- package/node_modules/toolcraft-design/dist/explorer/reducer.js +22 -6
- package/node_modules/toolcraft-design/dist/explorer/render/detail.js +19 -6
- package/node_modules/toolcraft-design/dist/explorer/render/list.js +28 -9
- package/node_modules/toolcraft-design/dist/explorer/render/pane.d.ts +5 -0
- package/node_modules/toolcraft-design/dist/explorer/render/pane.js +30 -0
- package/node_modules/toolcraft-design/dist/explorer/runtime.js +22 -4
- package/package.json +1 -1
|
@@ -0,0 +1,1944 @@
|
|
|
1
|
+
// src/commands/daemon-runtime.ts
|
|
2
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
import { mkdir, unlink } from "node:fs/promises";
|
|
5
|
+
import net from "node:net";
|
|
6
|
+
import os from "node:os";
|
|
7
|
+
import path2 from "node:path";
|
|
8
|
+
|
|
9
|
+
// ../toolcraft/src/index.ts
|
|
10
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
11
|
+
|
|
12
|
+
// ../toolcraft/src/user-error.ts
|
|
13
|
+
var UserError = class extends Error {
|
|
14
|
+
constructor(message, options) {
|
|
15
|
+
super(message, options);
|
|
16
|
+
this.name = "UserError";
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// ../toolcraft/src/runtime-logging.ts
|
|
21
|
+
var LOG_LEVELS = Object.freeze([
|
|
22
|
+
"silent",
|
|
23
|
+
"error",
|
|
24
|
+
"warn",
|
|
25
|
+
"info",
|
|
26
|
+
"debug",
|
|
27
|
+
"trace"
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
// ../toolcraft/src/package-metadata.ts
|
|
31
|
+
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
32
|
+
import path from "node:path";
|
|
33
|
+
import { fileURLToPath } from "node:url";
|
|
34
|
+
|
|
35
|
+
// src/terminal-pilot.ts
|
|
36
|
+
import { randomUUID } from "node:crypto";
|
|
37
|
+
|
|
38
|
+
// src/terminal-session.ts
|
|
39
|
+
import { EventEmitter } from "node:events";
|
|
40
|
+
import { accessSync, chmodSync, constants } from "node:fs";
|
|
41
|
+
import { createRequire } from "node:module";
|
|
42
|
+
import { dirname, join } from "node:path";
|
|
43
|
+
import * as nodePty from "node-pty";
|
|
44
|
+
|
|
45
|
+
// src/ansi.ts
|
|
46
|
+
var ESC = 27;
|
|
47
|
+
var BEL = 7;
|
|
48
|
+
var ST = 156;
|
|
49
|
+
var CSI = 155;
|
|
50
|
+
var OSC = 157;
|
|
51
|
+
var DCS = 144;
|
|
52
|
+
var SOS = 152;
|
|
53
|
+
var PM = 158;
|
|
54
|
+
var APC = 159;
|
|
55
|
+
function stripAnsi(input) {
|
|
56
|
+
let output = "";
|
|
57
|
+
for (let index = 0; index < input.length; index += 1) {
|
|
58
|
+
const code = input.charCodeAt(index);
|
|
59
|
+
if (code === ESC) {
|
|
60
|
+
const nextCode = input.charCodeAt(index + 1);
|
|
61
|
+
if (nextCode === 91) {
|
|
62
|
+
index = consumeCsi(input, index + 2);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (nextCode === 93) {
|
|
66
|
+
index = consumeTerminatedString(input, index + 2, true);
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (nextCode === 80 || nextCode === 88 || nextCode === 94 || nextCode === 95) {
|
|
70
|
+
index = consumeTerminatedString(input, index + 2, false);
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (!Number.isNaN(nextCode)) {
|
|
74
|
+
index += 1;
|
|
75
|
+
}
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (code === CSI) {
|
|
79
|
+
index = consumeCsi(input, index + 1);
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (code === OSC) {
|
|
83
|
+
index = consumeTerminatedString(input, index + 1, true);
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (code === DCS || code === SOS || code === PM || code === APC) {
|
|
87
|
+
index = consumeTerminatedString(input, index + 1, false);
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
output += input[index];
|
|
91
|
+
}
|
|
92
|
+
return output;
|
|
93
|
+
}
|
|
94
|
+
function consumeCsi(input, index) {
|
|
95
|
+
while (index < input.length) {
|
|
96
|
+
const code = input.charCodeAt(index);
|
|
97
|
+
if (code >= 64 && code <= 126) {
|
|
98
|
+
return index;
|
|
99
|
+
}
|
|
100
|
+
index += 1;
|
|
101
|
+
}
|
|
102
|
+
return input.length;
|
|
103
|
+
}
|
|
104
|
+
function consumeTerminatedString(input, index, allowBellTerminator) {
|
|
105
|
+
while (index < input.length) {
|
|
106
|
+
const code = input.charCodeAt(index);
|
|
107
|
+
if (code === ST || allowBellTerminator && code === BEL) {
|
|
108
|
+
return index;
|
|
109
|
+
}
|
|
110
|
+
if (code === ESC && input.charCodeAt(index + 1) === 92) {
|
|
111
|
+
return index + 1;
|
|
112
|
+
}
|
|
113
|
+
index += 1;
|
|
114
|
+
}
|
|
115
|
+
return input.length;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// src/errors.ts
|
|
119
|
+
function hasOwnErrorCode(error, code) {
|
|
120
|
+
return error instanceof Error && Object.prototype.hasOwnProperty.call(error, "code") && error.code === code;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// src/terminal-buffer.ts
|
|
124
|
+
var RESET_SGR = "\x1B[0m";
|
|
125
|
+
var DEC_SPECIAL_GRAPHICS = {
|
|
126
|
+
j: "\u2518",
|
|
127
|
+
k: "\u2510",
|
|
128
|
+
l: "\u250C",
|
|
129
|
+
m: "\u2514",
|
|
130
|
+
n: "\u253C",
|
|
131
|
+
q: "\u2500",
|
|
132
|
+
t: "\u251C",
|
|
133
|
+
u: "\u2524",
|
|
134
|
+
v: "\u2534",
|
|
135
|
+
w: "\u252C",
|
|
136
|
+
x: "\u2502"
|
|
137
|
+
};
|
|
138
|
+
var TerminalBuffer = class {
|
|
139
|
+
_cols;
|
|
140
|
+
_rows;
|
|
141
|
+
_screen;
|
|
142
|
+
_primaryScreen = null;
|
|
143
|
+
_cursorX = 0;
|
|
144
|
+
_cursorY = 0;
|
|
145
|
+
_savedCursor;
|
|
146
|
+
_scrollTop = 0;
|
|
147
|
+
_scrollBottom;
|
|
148
|
+
_state = 0 /* Normal */;
|
|
149
|
+
_csiParams = "";
|
|
150
|
+
_csiPrivate = "";
|
|
151
|
+
_autoWrap = true;
|
|
152
|
+
_pendingWrap = false;
|
|
153
|
+
_originMode = false;
|
|
154
|
+
_insertMode = false;
|
|
155
|
+
_tabStops;
|
|
156
|
+
_lastPrintedChar = null;
|
|
157
|
+
_style = createDefaultStyleState();
|
|
158
|
+
_styleSequence = "";
|
|
159
|
+
_stringState = 0 /* Normal */;
|
|
160
|
+
_charsetTarget = null;
|
|
161
|
+
_g0Charset = "ascii";
|
|
162
|
+
_g1Charset = "ascii";
|
|
163
|
+
_shiftOut = false;
|
|
164
|
+
displayBuffer;
|
|
165
|
+
constructor(cols, rows) {
|
|
166
|
+
this._cols = cols;
|
|
167
|
+
this._rows = rows;
|
|
168
|
+
this._scrollBottom = rows - 1;
|
|
169
|
+
this._screen = this._makeScreen(cols, rows);
|
|
170
|
+
this._tabStops = this._createDefaultTabStops(cols);
|
|
171
|
+
this._savedCursor = this._createSavedCursorState();
|
|
172
|
+
this.displayBuffer = Object.defineProperties(
|
|
173
|
+
{},
|
|
174
|
+
{
|
|
175
|
+
cursorX: { get: () => this._cursorX, enumerable: true },
|
|
176
|
+
cursorY: { get: () => this._cursorY, enumerable: true },
|
|
177
|
+
data: { get: () => this._screen, enumerable: true }
|
|
178
|
+
}
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
write(data) {
|
|
182
|
+
for (const ch of data) {
|
|
183
|
+
this._feed(ch);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
renderLine(row) {
|
|
187
|
+
const cells = this._screen[row] ?? [];
|
|
188
|
+
let lastVisibleCell = -1;
|
|
189
|
+
for (let index = cells.length - 1; index >= 0; index -= 1) {
|
|
190
|
+
if (cells[index] !== null) {
|
|
191
|
+
lastVisibleCell = index;
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (lastVisibleCell === -1) {
|
|
196
|
+
return "";
|
|
197
|
+
}
|
|
198
|
+
let line = "";
|
|
199
|
+
let activeStyle = "";
|
|
200
|
+
for (let index = 0; index <= lastVisibleCell; index += 1) {
|
|
201
|
+
const cell = cells[index];
|
|
202
|
+
const cellStyle = cell?.style ?? "";
|
|
203
|
+
if (cellStyle !== activeStyle) {
|
|
204
|
+
line += cellStyle.length > 0 ? cellStyle : RESET_SGR;
|
|
205
|
+
activeStyle = cellStyle;
|
|
206
|
+
}
|
|
207
|
+
line += cell?.[1] ?? " ";
|
|
208
|
+
}
|
|
209
|
+
if (activeStyle.length > 0) {
|
|
210
|
+
line += RESET_SGR;
|
|
211
|
+
}
|
|
212
|
+
return line;
|
|
213
|
+
}
|
|
214
|
+
resize(cols, rows) {
|
|
215
|
+
while (this._screen.length < rows) {
|
|
216
|
+
this._screen.push(this._makeRow(cols));
|
|
217
|
+
}
|
|
218
|
+
this._screen.length = rows;
|
|
219
|
+
for (let y = 0; y < rows; y++) {
|
|
220
|
+
const row = this._screen[y] ?? this._makeRow(cols);
|
|
221
|
+
while (row.length < cols) row.push(null);
|
|
222
|
+
row.length = cols;
|
|
223
|
+
this._screen[y] = row;
|
|
224
|
+
}
|
|
225
|
+
this._cols = cols;
|
|
226
|
+
this._rows = rows;
|
|
227
|
+
this._scrollTop = this._clamp(this._scrollTop, 0, rows - 1);
|
|
228
|
+
this._scrollBottom = this._clamp(this._scrollBottom, 0, rows - 1);
|
|
229
|
+
if (this._scrollTop >= this._scrollBottom) {
|
|
230
|
+
this._scrollTop = 0;
|
|
231
|
+
this._scrollBottom = rows - 1;
|
|
232
|
+
}
|
|
233
|
+
this._cursorX = this._clamp(this._cursorX, 0, cols - 1);
|
|
234
|
+
this._cursorY = this._clamp(this._cursorY, 0, rows - 1);
|
|
235
|
+
}
|
|
236
|
+
_makeScreen(cols, rows) {
|
|
237
|
+
return Array.from({ length: rows }, () => this._makeRow(cols));
|
|
238
|
+
}
|
|
239
|
+
_makeRow(cols) {
|
|
240
|
+
return Array(cols).fill(null);
|
|
241
|
+
}
|
|
242
|
+
_createDefaultTabStops(cols) {
|
|
243
|
+
const tabStops = /* @__PURE__ */ new Set();
|
|
244
|
+
for (let column = 8; column < cols; column += 8) {
|
|
245
|
+
tabStops.add(column);
|
|
246
|
+
}
|
|
247
|
+
return tabStops;
|
|
248
|
+
}
|
|
249
|
+
_clamp(value, min, max) {
|
|
250
|
+
return Math.max(min, Math.min(max, value));
|
|
251
|
+
}
|
|
252
|
+
_createSavedCursorState() {
|
|
253
|
+
return {
|
|
254
|
+
x: this._cursorX,
|
|
255
|
+
y: this._cursorY,
|
|
256
|
+
autoWrap: this._autoWrap,
|
|
257
|
+
style: { ...this._style, fg: this._style.fg?.slice(), bg: this._style.bg?.slice() },
|
|
258
|
+
styleSequence: this._styleSequence
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
_saveCursor() {
|
|
262
|
+
this._savedCursor = this._createSavedCursorState();
|
|
263
|
+
}
|
|
264
|
+
_restoreCursor() {
|
|
265
|
+
this._cursorX = this._clamp(this._savedCursor.x, 0, this._cols - 1);
|
|
266
|
+
this._cursorY = this._clamp(this._savedCursor.y, 0, this._rows - 1);
|
|
267
|
+
this._autoWrap = this._savedCursor.autoWrap;
|
|
268
|
+
this._style = {
|
|
269
|
+
...this._savedCursor.style,
|
|
270
|
+
fg: this._savedCursor.style.fg?.slice(),
|
|
271
|
+
bg: this._savedCursor.style.bg?.slice()
|
|
272
|
+
};
|
|
273
|
+
this._styleSequence = this._savedCursor.styleSequence;
|
|
274
|
+
}
|
|
275
|
+
_resetModes() {
|
|
276
|
+
this._autoWrap = true;
|
|
277
|
+
this._originMode = false;
|
|
278
|
+
this._insertMode = false;
|
|
279
|
+
this._pendingWrap = false;
|
|
280
|
+
this._tabStops = this._createDefaultTabStops(this._cols);
|
|
281
|
+
this._g0Charset = "ascii";
|
|
282
|
+
this._g1Charset = "ascii";
|
|
283
|
+
this._shiftOut = false;
|
|
284
|
+
this._resetStyle();
|
|
285
|
+
}
|
|
286
|
+
_writePrintable(ch) {
|
|
287
|
+
const width = this._cellWidth(ch);
|
|
288
|
+
if (width === 0) {
|
|
289
|
+
this._appendCombiningMark(ch);
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
if (this._autoWrap && this._pendingWrap) {
|
|
293
|
+
this._cursorX = 0;
|
|
294
|
+
this._newline();
|
|
295
|
+
} else if (this._autoWrap && this._cursorX + width > this._cols) {
|
|
296
|
+
this._cursorX = 0;
|
|
297
|
+
this._newline();
|
|
298
|
+
}
|
|
299
|
+
if (this._insertMode) {
|
|
300
|
+
const row = this._screen[this._cursorY];
|
|
301
|
+
if (row) {
|
|
302
|
+
row.splice(this._cursorX, 0, ...Array(width).fill(null));
|
|
303
|
+
row.splice(this._cols);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
this._setChar(this._cursorY, this._cursorX, ch);
|
|
307
|
+
for (let column = 1; column < width && this._cursorX + column < this._cols; column += 1) {
|
|
308
|
+
const row = this._screen[this._cursorY];
|
|
309
|
+
if (row) row[this._cursorX + column] = null;
|
|
310
|
+
}
|
|
311
|
+
this._lastPrintedChar = ch;
|
|
312
|
+
if (!this._autoWrap) {
|
|
313
|
+
this._cursorX = Math.min(this._cursorX + width, this._cols - 1);
|
|
314
|
+
this._pendingWrap = false;
|
|
315
|
+
} else if (this._cursorX + width >= this._cols) {
|
|
316
|
+
this._cursorX = this._cols - 1;
|
|
317
|
+
this._pendingWrap = true;
|
|
318
|
+
} else {
|
|
319
|
+
this._cursorX += width;
|
|
320
|
+
this._pendingWrap = false;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
_cellWidth(ch) {
|
|
324
|
+
const codePoint = ch.codePointAt(0);
|
|
325
|
+
if (codePoint === void 0) return 0;
|
|
326
|
+
if (isCombiningCodePoint(codePoint)) {
|
|
327
|
+
return 0;
|
|
328
|
+
}
|
|
329
|
+
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) {
|
|
330
|
+
return Math.min(2, this._cols);
|
|
331
|
+
}
|
|
332
|
+
return 1;
|
|
333
|
+
}
|
|
334
|
+
_appendCombiningMark(ch) {
|
|
335
|
+
const target = this._findCombiningTarget();
|
|
336
|
+
if (target === null) {
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
const cell = this._screen[target.y]?.[target.x];
|
|
340
|
+
if (cell === null || cell === void 0) {
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
cell[1] += ch;
|
|
344
|
+
}
|
|
345
|
+
_findCombiningTarget() {
|
|
346
|
+
const startX = this._pendingWrap ? this._cursorX : this._cursorX - 1;
|
|
347
|
+
for (let y = this._cursorY; y >= 0; y -= 1) {
|
|
348
|
+
const row = this._screen[y];
|
|
349
|
+
if (row === void 0) {
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
for (let x = y === this._cursorY ? startX : this._cols - 1; x >= 0; x -= 1) {
|
|
353
|
+
if (row[x] !== null) {
|
|
354
|
+
return { x, y };
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return null;
|
|
359
|
+
}
|
|
360
|
+
_setChar(y, x, ch) {
|
|
361
|
+
const row = this._screen[y];
|
|
362
|
+
if (row && x >= 0 && x < this._cols) {
|
|
363
|
+
const renderedChar = this._style.conceal ? " " : ch;
|
|
364
|
+
const cell = [renderedChar.charCodeAt(0), renderedChar];
|
|
365
|
+
if (this._styleSequence.length > 0) {
|
|
366
|
+
Object.defineProperty(cell, "style", {
|
|
367
|
+
value: this._styleSequence,
|
|
368
|
+
writable: true,
|
|
369
|
+
configurable: true
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
row[x] = cell;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
_eraseLine(y, fromX, toX) {
|
|
376
|
+
const row = this._screen[y];
|
|
377
|
+
if (!row) return;
|
|
378
|
+
for (let x = fromX; x <= toX && x < this._cols; x++) {
|
|
379
|
+
row[x] = null;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
_scrollUp(count) {
|
|
383
|
+
for (let i = 0; i < count; i++) {
|
|
384
|
+
this._screen.splice(this._scrollTop, 1);
|
|
385
|
+
this._screen.splice(this._scrollBottom, 0, this._makeRow(this._cols));
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
_scrollDown(count) {
|
|
389
|
+
for (let i = 0; i < count; i++) {
|
|
390
|
+
this._screen.splice(this._scrollBottom, 1);
|
|
391
|
+
this._screen.splice(this._scrollTop, 0, this._makeRow(this._cols));
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
_newline() {
|
|
395
|
+
this._pendingWrap = false;
|
|
396
|
+
if (this._cursorY === this._scrollBottom) {
|
|
397
|
+
this._scrollUp(1);
|
|
398
|
+
} else {
|
|
399
|
+
this._cursorY = Math.min(this._cursorY + 1, this._rows - 1);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
_parseCsiParams() {
|
|
403
|
+
if (!this._csiParams) return [];
|
|
404
|
+
const values = this._csiParams.split(";").flatMap((segment) => segment.split(":")).map((segment) => segment === "" ? 0 : parseInt(segment, 10));
|
|
405
|
+
if ((values[0] === 38 || values[0] === 48) && values[1] === 2 && values[2] === 0) {
|
|
406
|
+
values.splice(2, 1);
|
|
407
|
+
}
|
|
408
|
+
return values;
|
|
409
|
+
}
|
|
410
|
+
_execCsi(final) {
|
|
411
|
+
const params = this._parseCsiParams();
|
|
412
|
+
const p0 = params[0] ?? 0;
|
|
413
|
+
const p1 = params[1] ?? 0;
|
|
414
|
+
if (this._csiPrivate === "?") {
|
|
415
|
+
if (final === "h" || final === "l") {
|
|
416
|
+
if (params.includes(7)) {
|
|
417
|
+
this._autoWrap = final === "h";
|
|
418
|
+
}
|
|
419
|
+
if (params.includes(6)) {
|
|
420
|
+
this._originMode = final === "h";
|
|
421
|
+
this._cursorX = 0;
|
|
422
|
+
this._cursorY = this._originMode ? this._scrollTop : 0;
|
|
423
|
+
}
|
|
424
|
+
if (params.includes(1049)) {
|
|
425
|
+
if (final === "h") {
|
|
426
|
+
this._primaryScreen = {
|
|
427
|
+
screen: this._screen,
|
|
428
|
+
cursorX: this._cursorX,
|
|
429
|
+
cursorY: this._cursorY,
|
|
430
|
+
pendingWrap: this._pendingWrap,
|
|
431
|
+
style: { ...this._style, fg: this._style.fg?.slice(), bg: this._style.bg?.slice() },
|
|
432
|
+
styleSequence: this._styleSequence
|
|
433
|
+
};
|
|
434
|
+
this._screen = this._makeScreen(this._cols, this._rows);
|
|
435
|
+
this._cursorX = 0;
|
|
436
|
+
this._cursorY = 0;
|
|
437
|
+
this._pendingWrap = false;
|
|
438
|
+
} else {
|
|
439
|
+
if (this._primaryScreen !== null) {
|
|
440
|
+
this._screen = this._primaryScreen.screen;
|
|
441
|
+
this._cursorX = this._primaryScreen.cursorX;
|
|
442
|
+
this._cursorY = this._primaryScreen.cursorY;
|
|
443
|
+
this._pendingWrap = this._primaryScreen.pendingWrap;
|
|
444
|
+
this._style = this._primaryScreen.style;
|
|
445
|
+
this._styleSequence = this._primaryScreen.styleSequence;
|
|
446
|
+
this._primaryScreen = null;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
if (final === "h") this._resetStyle();
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
if ((final === "h" || final === "l") && params.includes(4)) {
|
|
455
|
+
this._insertMode = final === "h";
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
switch (final) {
|
|
459
|
+
case "A":
|
|
460
|
+
this._cursorY = this._clamp(this._cursorY - Math.max(1, p0), 0, this._rows - 1);
|
|
461
|
+
break;
|
|
462
|
+
case "B":
|
|
463
|
+
this._cursorY = this._clamp(this._cursorY + Math.max(1, p0), 0, this._rows - 1);
|
|
464
|
+
break;
|
|
465
|
+
case "C":
|
|
466
|
+
// cursor forward
|
|
467
|
+
case "a":
|
|
468
|
+
this._cursorX = this._clamp(this._cursorX + Math.max(1, p0), 0, this._cols - 1);
|
|
469
|
+
break;
|
|
470
|
+
case "D":
|
|
471
|
+
this._cursorX = this._clamp(this._cursorX - Math.max(1, p0), 0, this._cols - 1);
|
|
472
|
+
break;
|
|
473
|
+
case "E":
|
|
474
|
+
this._cursorY = this._clamp(this._cursorY + Math.max(1, p0), 0, this._rows - 1);
|
|
475
|
+
this._cursorX = 0;
|
|
476
|
+
break;
|
|
477
|
+
case "F":
|
|
478
|
+
this._cursorY = this._clamp(this._cursorY - Math.max(1, p0), 0, this._rows - 1);
|
|
479
|
+
this._cursorX = 0;
|
|
480
|
+
break;
|
|
481
|
+
case "G":
|
|
482
|
+
// cursor horizontal absolute
|
|
483
|
+
case "`":
|
|
484
|
+
this._cursorX = this._clamp(Math.max(1, p0) - 1, 0, this._cols - 1);
|
|
485
|
+
break;
|
|
486
|
+
case "H":
|
|
487
|
+
// cursor position
|
|
488
|
+
case "f":
|
|
489
|
+
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);
|
|
490
|
+
this._cursorX = this._clamp(Math.max(1, p1) - 1, 0, this._cols - 1);
|
|
491
|
+
break;
|
|
492
|
+
case "I":
|
|
493
|
+
for (let i = 0; i < Math.max(1, p0); i++) {
|
|
494
|
+
this._cursorX = Math.min(this._cols - 1, (Math.floor(this._cursorX / 8) + 1) * 8);
|
|
495
|
+
}
|
|
496
|
+
break;
|
|
497
|
+
case "J":
|
|
498
|
+
if (p0 === 0) {
|
|
499
|
+
this._eraseLine(this._cursorY, this._cursorX, this._cols - 1);
|
|
500
|
+
for (let y = this._cursorY + 1; y < this._rows; y++)
|
|
501
|
+
this._eraseLine(y, 0, this._cols - 1);
|
|
502
|
+
} else if (p0 === 1) {
|
|
503
|
+
for (let y = 0; y < this._cursorY; y++) this._eraseLine(y, 0, this._cols - 1);
|
|
504
|
+
this._eraseLine(this._cursorY, 0, this._cursorX);
|
|
505
|
+
} else if (p0 === 2) {
|
|
506
|
+
for (let y = 0; y < this._rows; y++) this._eraseLine(y, 0, this._cols - 1);
|
|
507
|
+
}
|
|
508
|
+
break;
|
|
509
|
+
case "K":
|
|
510
|
+
if (p0 === 0) this._eraseLine(this._cursorY, this._cursorX, this._cols - 1);
|
|
511
|
+
else if (p0 === 1) this._eraseLine(this._cursorY, 0, this._cursorX);
|
|
512
|
+
else if (p0 === 2) this._eraseLine(this._cursorY, 0, this._cols - 1);
|
|
513
|
+
break;
|
|
514
|
+
case "X":
|
|
515
|
+
this._eraseLine(this._cursorY, this._cursorX, this._cursorX + Math.max(1, p0) - 1);
|
|
516
|
+
break;
|
|
517
|
+
case "L": {
|
|
518
|
+
if (this._cursorY < this._scrollTop || this._cursorY > this._scrollBottom) {
|
|
519
|
+
break;
|
|
520
|
+
}
|
|
521
|
+
const n = Math.max(1, p0);
|
|
522
|
+
for (let i = 0; i < n; i++) {
|
|
523
|
+
this._screen.splice(this._scrollBottom, 1);
|
|
524
|
+
this._screen.splice(this._cursorY, 0, this._makeRow(this._cols));
|
|
525
|
+
}
|
|
526
|
+
break;
|
|
527
|
+
}
|
|
528
|
+
case "M": {
|
|
529
|
+
if (this._cursorY < this._scrollTop || this._cursorY > this._scrollBottom) {
|
|
530
|
+
break;
|
|
531
|
+
}
|
|
532
|
+
const n = Math.max(1, p0);
|
|
533
|
+
for (let i = 0; i < n; i++) {
|
|
534
|
+
this._screen.splice(this._cursorY, 1);
|
|
535
|
+
this._screen.splice(this._scrollBottom, 0, this._makeRow(this._cols));
|
|
536
|
+
}
|
|
537
|
+
break;
|
|
538
|
+
}
|
|
539
|
+
case "P": {
|
|
540
|
+
const row = this._screen[this._cursorY];
|
|
541
|
+
if (row) {
|
|
542
|
+
const n = Math.max(1, p0);
|
|
543
|
+
row.splice(this._cursorX, n);
|
|
544
|
+
while (row.length < this._cols) row.push(null);
|
|
545
|
+
}
|
|
546
|
+
break;
|
|
547
|
+
}
|
|
548
|
+
case "@": {
|
|
549
|
+
const row = this._screen[this._cursorY];
|
|
550
|
+
if (row) {
|
|
551
|
+
const n = Math.max(1, p0);
|
|
552
|
+
for (let i = 0; i < n; i++) row.splice(this._cursorX, 0, null);
|
|
553
|
+
row.splice(this._cols);
|
|
554
|
+
}
|
|
555
|
+
break;
|
|
556
|
+
}
|
|
557
|
+
case "S":
|
|
558
|
+
this._scrollUp(Math.max(1, p0));
|
|
559
|
+
break;
|
|
560
|
+
case "T":
|
|
561
|
+
if (params.length <= 1) this._scrollDown(Math.max(1, p0));
|
|
562
|
+
break;
|
|
563
|
+
case "Z": {
|
|
564
|
+
const n = Math.max(1, p0);
|
|
565
|
+
for (let i = 0; i < n; i++) {
|
|
566
|
+
this._cursorX = Math.max(0, (Math.ceil(this._cursorX / 8) - 1) * 8);
|
|
567
|
+
}
|
|
568
|
+
break;
|
|
569
|
+
}
|
|
570
|
+
case "b":
|
|
571
|
+
if (this._lastPrintedChar !== null) {
|
|
572
|
+
for (let index = 0; index < Math.max(1, p0); index += 1) {
|
|
573
|
+
this._writePrintable(this._lastPrintedChar);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
break;
|
|
577
|
+
case "d":
|
|
578
|
+
this._cursorY = this._clamp(Math.max(1, p0) - 1, 0, this._rows - 1);
|
|
579
|
+
break;
|
|
580
|
+
case "e":
|
|
581
|
+
this._cursorY = this._clamp(this._cursorY + Math.max(1, p0), 0, this._rows - 1);
|
|
582
|
+
break;
|
|
583
|
+
case "r": {
|
|
584
|
+
const top = this._clamp(Math.max(1, p0) - 1, 0, this._rows - 1);
|
|
585
|
+
const bottom = this._clamp((p1 === 0 ? this._rows : p1) - 1, 0, this._rows - 1);
|
|
586
|
+
if (top < bottom) {
|
|
587
|
+
this._scrollTop = top;
|
|
588
|
+
this._scrollBottom = bottom;
|
|
589
|
+
}
|
|
590
|
+
this._cursorX = 0;
|
|
591
|
+
this._cursorY = this._originMode ? this._scrollTop : 0;
|
|
592
|
+
break;
|
|
593
|
+
}
|
|
594
|
+
case "s":
|
|
595
|
+
this._saveCursor();
|
|
596
|
+
break;
|
|
597
|
+
case "u":
|
|
598
|
+
this._restoreCursor();
|
|
599
|
+
break;
|
|
600
|
+
case "p":
|
|
601
|
+
if (this._csiPrivate === "!") {
|
|
602
|
+
this._resetModes();
|
|
603
|
+
}
|
|
604
|
+
break;
|
|
605
|
+
case "m":
|
|
606
|
+
this._applySgr(params);
|
|
607
|
+
break;
|
|
608
|
+
default:
|
|
609
|
+
break;
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
_feed(ch) {
|
|
613
|
+
const code = ch.charCodeAt(0);
|
|
614
|
+
if (this._state !== 0 /* Normal */ && (code === 24 || code === 26)) {
|
|
615
|
+
this._state = 0 /* Normal */;
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
618
|
+
if (this._state === 2 /* Csi */ && code === 27) {
|
|
619
|
+
this._state = 1 /* Escape */;
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
switch (this._state) {
|
|
623
|
+
case 0 /* Normal */:
|
|
624
|
+
this._feedNormal(ch, code);
|
|
625
|
+
break;
|
|
626
|
+
case 1 /* Escape */:
|
|
627
|
+
this._feedEscape(ch, code);
|
|
628
|
+
break;
|
|
629
|
+
case 2 /* Csi */:
|
|
630
|
+
this._feedCsi(ch, code);
|
|
631
|
+
break;
|
|
632
|
+
case 3 /* Osc */:
|
|
633
|
+
if (code === 7 || code === 156) {
|
|
634
|
+
this._state = 0 /* Normal */;
|
|
635
|
+
} else if (code === 27) {
|
|
636
|
+
this._stringState = 3 /* Osc */;
|
|
637
|
+
this._state = 5 /* StringEscape */;
|
|
638
|
+
}
|
|
639
|
+
break;
|
|
640
|
+
case 4 /* Str */:
|
|
641
|
+
if (code === 156) {
|
|
642
|
+
this._state = 0 /* Normal */;
|
|
643
|
+
} else if (code === 27) {
|
|
644
|
+
this._stringState = 4 /* Str */;
|
|
645
|
+
this._state = 5 /* StringEscape */;
|
|
646
|
+
}
|
|
647
|
+
break;
|
|
648
|
+
case 5 /* StringEscape */:
|
|
649
|
+
this._state = ch === "\\" ? 0 /* Normal */ : this._stringState;
|
|
650
|
+
break;
|
|
651
|
+
case 6 /* EscCharset */:
|
|
652
|
+
if (this._charsetTarget === "g0") this._g0Charset = ch === "0" ? "graphics" : "ascii";
|
|
653
|
+
if (this._charsetTarget === "g1") this._g1Charset = ch === "0" ? "graphics" : "ascii";
|
|
654
|
+
this._charsetTarget = null;
|
|
655
|
+
this._state = 0 /* Normal */;
|
|
656
|
+
break;
|
|
657
|
+
case 7 /* EscHash */:
|
|
658
|
+
if (ch === "8") {
|
|
659
|
+
for (let row = 0; row < this._rows; row += 1) {
|
|
660
|
+
for (let column = 0; column < this._cols; column += 1) {
|
|
661
|
+
this._setChar(row, column, "E");
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
this._cursorX = 0;
|
|
665
|
+
this._cursorY = 0;
|
|
666
|
+
}
|
|
667
|
+
this._state = 0 /* Normal */;
|
|
668
|
+
break;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
_feedNormal(ch, code) {
|
|
672
|
+
if (code === 27) {
|
|
673
|
+
this._state = 1 /* Escape */;
|
|
674
|
+
} else if (code === 155) {
|
|
675
|
+
this._csiParams = "";
|
|
676
|
+
this._csiPrivate = "";
|
|
677
|
+
this._state = 2 /* Csi */;
|
|
678
|
+
} else if (code === 157) {
|
|
679
|
+
this._state = 3 /* Osc */;
|
|
680
|
+
} else if (code === 144 || code === 152 || code === 158 || code === 159) {
|
|
681
|
+
this._state = 4 /* Str */;
|
|
682
|
+
} else if (code === 133) {
|
|
683
|
+
this._cursorX = 0;
|
|
684
|
+
this._newline();
|
|
685
|
+
} else if (code === 156) {
|
|
686
|
+
} else if (code === 7 || code === 5 || code === 6) {
|
|
687
|
+
} else if (code === 8) {
|
|
688
|
+
this._pendingWrap = false;
|
|
689
|
+
if (this._cursorX > 0) this._cursorX--;
|
|
690
|
+
} else if (code === 127) {
|
|
691
|
+
} else if (code === 9) {
|
|
692
|
+
const nextTabStop = [...this._tabStops].sort((left, right) => left - right).find((tabStop) => tabStop > this._cursorX);
|
|
693
|
+
this._cursorX = nextTabStop ?? this._cols - 1;
|
|
694
|
+
} else if (code === 10 || code === 11 || code === 12) {
|
|
695
|
+
this._newline();
|
|
696
|
+
} else if (code === 13) {
|
|
697
|
+
this._pendingWrap = false;
|
|
698
|
+
this._cursorX = 0;
|
|
699
|
+
} else if (code === 14 || code === 15) {
|
|
700
|
+
this._shiftOut = code === 14;
|
|
701
|
+
} else if (code >= 32 && code !== 127) {
|
|
702
|
+
const charset = this._shiftOut ? this._g1Charset : this._g0Charset;
|
|
703
|
+
this._writePrintable(charset === "graphics" ? DEC_SPECIAL_GRAPHICS[ch] ?? ch : ch);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
_feedEscape(ch, code) {
|
|
707
|
+
this._state = 0 /* Normal */;
|
|
708
|
+
if (code === 91) {
|
|
709
|
+
this._csiParams = "";
|
|
710
|
+
this._csiPrivate = "";
|
|
711
|
+
this._state = 2 /* Csi */;
|
|
712
|
+
} else if (code === 93) {
|
|
713
|
+
this._state = 3 /* Osc */;
|
|
714
|
+
} else if (code === 80 || code === 88 || code === 94 || code === 95) {
|
|
715
|
+
this._state = 4 /* Str */;
|
|
716
|
+
} else if (code === 40 || code === 41 || code === 42 || code === 43 || code === 45 || code === 46) {
|
|
717
|
+
this._charsetTarget = code === 40 ? "g0" : code === 41 ? "g1" : null;
|
|
718
|
+
this._state = 6 /* EscCharset */;
|
|
719
|
+
} else if (code === 35) {
|
|
720
|
+
this._state = 7 /* EscHash */;
|
|
721
|
+
} else if (code === 55) {
|
|
722
|
+
this._saveCursor();
|
|
723
|
+
} else if (code === 56) {
|
|
724
|
+
this._restoreCursor();
|
|
725
|
+
} else if (code === 68) {
|
|
726
|
+
this._newline();
|
|
727
|
+
} else if (code === 69) {
|
|
728
|
+
this._cursorX = 0;
|
|
729
|
+
this._newline();
|
|
730
|
+
} else if (code === 77) {
|
|
731
|
+
if (this._cursorY === this._scrollTop) {
|
|
732
|
+
this._scrollDown(1);
|
|
733
|
+
} else {
|
|
734
|
+
this._cursorY = Math.max(0, this._cursorY - 1);
|
|
735
|
+
}
|
|
736
|
+
} else if (code === 72) {
|
|
737
|
+
this._tabStops.add(this._cursorX);
|
|
738
|
+
} else if (code === 99) {
|
|
739
|
+
this._screen = this._makeScreen(this._cols, this._rows);
|
|
740
|
+
this._cursorX = 0;
|
|
741
|
+
this._cursorY = 0;
|
|
742
|
+
this._scrollTop = 0;
|
|
743
|
+
this._scrollBottom = this._rows - 1;
|
|
744
|
+
this._resetModes();
|
|
745
|
+
this._savedCursor = this._createSavedCursorState();
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
_feedCsi(ch, code) {
|
|
749
|
+
if (code >= 64 && code <= 126) {
|
|
750
|
+
this._execCsi(ch);
|
|
751
|
+
this._state = 0 /* Normal */;
|
|
752
|
+
} else if (code === 63 || code === 33 || code === 62 || code === 32) {
|
|
753
|
+
this._csiPrivate = ch;
|
|
754
|
+
} else if (code >= 48 && code <= 57 || code === 59 || code === 58) {
|
|
755
|
+
this._csiParams += ch;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
_resetStyle() {
|
|
759
|
+
this._style = createDefaultStyleState();
|
|
760
|
+
this._styleSequence = "";
|
|
761
|
+
}
|
|
762
|
+
_applySgr(params) {
|
|
763
|
+
const normalizedParams = params.length === 0 ? [0] : params;
|
|
764
|
+
for (let index = 0; index < normalizedParams.length; index += 1) {
|
|
765
|
+
const value = normalizedParams[index] ?? 0;
|
|
766
|
+
switch (value) {
|
|
767
|
+
case 0:
|
|
768
|
+
this._resetStyle();
|
|
769
|
+
break;
|
|
770
|
+
case 1:
|
|
771
|
+
this._style.bold = true;
|
|
772
|
+
break;
|
|
773
|
+
case 2:
|
|
774
|
+
this._style.dim = true;
|
|
775
|
+
break;
|
|
776
|
+
case 3:
|
|
777
|
+
this._style.italic = true;
|
|
778
|
+
break;
|
|
779
|
+
case 4:
|
|
780
|
+
this._style.underline = true;
|
|
781
|
+
break;
|
|
782
|
+
case 7:
|
|
783
|
+
this._style.inverse = true;
|
|
784
|
+
break;
|
|
785
|
+
case 8:
|
|
786
|
+
this._style.conceal = true;
|
|
787
|
+
break;
|
|
788
|
+
case 9:
|
|
789
|
+
this._style.strikethrough = true;
|
|
790
|
+
break;
|
|
791
|
+
case 21:
|
|
792
|
+
case 22:
|
|
793
|
+
this._style.bold = false;
|
|
794
|
+
this._style.dim = false;
|
|
795
|
+
break;
|
|
796
|
+
case 23:
|
|
797
|
+
this._style.italic = false;
|
|
798
|
+
break;
|
|
799
|
+
case 24:
|
|
800
|
+
this._style.underline = false;
|
|
801
|
+
break;
|
|
802
|
+
case 27:
|
|
803
|
+
this._style.inverse = false;
|
|
804
|
+
break;
|
|
805
|
+
case 28:
|
|
806
|
+
this._style.conceal = false;
|
|
807
|
+
break;
|
|
808
|
+
case 29:
|
|
809
|
+
this._style.strikethrough = false;
|
|
810
|
+
break;
|
|
811
|
+
case 39:
|
|
812
|
+
this._style.fg = void 0;
|
|
813
|
+
break;
|
|
814
|
+
case 49:
|
|
815
|
+
this._style.bg = void 0;
|
|
816
|
+
break;
|
|
817
|
+
case 38:
|
|
818
|
+
case 48:
|
|
819
|
+
index = this._applyExtendedColor(value, normalizedParams, index);
|
|
820
|
+
break;
|
|
821
|
+
default:
|
|
822
|
+
if (value >= 30 && value <= 37 || value >= 90 && value <= 97) {
|
|
823
|
+
this._style.fg = [value];
|
|
824
|
+
} else if (value >= 40 && value <= 47 || value >= 100 && value <= 107) {
|
|
825
|
+
this._style.bg = [value];
|
|
826
|
+
}
|
|
827
|
+
break;
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
this._styleSequence = serializeStyleState(this._style);
|
|
831
|
+
}
|
|
832
|
+
_applyExtendedColor(control, params, index) {
|
|
833
|
+
const mode = params[index + 1];
|
|
834
|
+
const target = control === 38 ? "fg" : "bg";
|
|
835
|
+
if (mode === 5) {
|
|
836
|
+
const paletteIndex = params[index + 2];
|
|
837
|
+
if (paletteIndex !== void 0) {
|
|
838
|
+
this._style[target] = [control, 5, paletteIndex];
|
|
839
|
+
return index + 2;
|
|
840
|
+
}
|
|
841
|
+
return index;
|
|
842
|
+
}
|
|
843
|
+
if (mode === 2) {
|
|
844
|
+
const red = params[index + 2];
|
|
845
|
+
const green = params[index + 3];
|
|
846
|
+
const blue = params[index + 4];
|
|
847
|
+
if (red !== void 0 && green !== void 0 && blue !== void 0) {
|
|
848
|
+
this._style[target] = [control, 2, red, green, blue];
|
|
849
|
+
return index + 4;
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
return index;
|
|
853
|
+
}
|
|
854
|
+
};
|
|
855
|
+
function createDefaultStyleState() {
|
|
856
|
+
return {
|
|
857
|
+
bold: false,
|
|
858
|
+
dim: false,
|
|
859
|
+
italic: false,
|
|
860
|
+
underline: false,
|
|
861
|
+
inverse: false,
|
|
862
|
+
conceal: false,
|
|
863
|
+
strikethrough: false
|
|
864
|
+
};
|
|
865
|
+
}
|
|
866
|
+
function isCombiningCodePoint(codePoint) {
|
|
867
|
+
return codePoint >= 768 && codePoint <= 879 || codePoint >= 6832 && codePoint <= 6911 || codePoint >= 7616 && codePoint <= 7679 || codePoint >= 8400 && codePoint <= 8447 || codePoint >= 65056 && codePoint <= 65071;
|
|
868
|
+
}
|
|
869
|
+
function serializeStyleState(state) {
|
|
870
|
+
const codes = [];
|
|
871
|
+
if (state.bold) {
|
|
872
|
+
codes.push(1);
|
|
873
|
+
}
|
|
874
|
+
if (state.dim) {
|
|
875
|
+
codes.push(2);
|
|
876
|
+
}
|
|
877
|
+
if (state.italic) {
|
|
878
|
+
codes.push(3);
|
|
879
|
+
}
|
|
880
|
+
if (state.underline) {
|
|
881
|
+
codes.push(4);
|
|
882
|
+
}
|
|
883
|
+
if (state.inverse) {
|
|
884
|
+
codes.push(7);
|
|
885
|
+
}
|
|
886
|
+
if (state.conceal) {
|
|
887
|
+
codes.push(8);
|
|
888
|
+
}
|
|
889
|
+
if (state.strikethrough) {
|
|
890
|
+
codes.push(9);
|
|
891
|
+
}
|
|
892
|
+
if (state.fg !== void 0) {
|
|
893
|
+
codes.push(...state.fg);
|
|
894
|
+
}
|
|
895
|
+
if (state.bg !== void 0) {
|
|
896
|
+
codes.push(...state.bg);
|
|
897
|
+
}
|
|
898
|
+
return codes.length === 0 ? "" : `\x1B[${codes.join(";")}m`;
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
// src/keys.ts
|
|
902
|
+
var NAMED_KEY_SEQUENCES = {
|
|
903
|
+
Enter: "\r",
|
|
904
|
+
Tab: " ",
|
|
905
|
+
Escape: "\x1B",
|
|
906
|
+
Backspace: "\x7F",
|
|
907
|
+
Delete: "\x1B[3~",
|
|
908
|
+
ArrowUp: "\x1B[A",
|
|
909
|
+
ArrowDown: "\x1B[B",
|
|
910
|
+
ArrowRight: "\x1B[C",
|
|
911
|
+
ArrowLeft: "\x1B[D",
|
|
912
|
+
Home: "\x1B[H",
|
|
913
|
+
End: "\x1B[F",
|
|
914
|
+
PageUp: "\x1B[5~",
|
|
915
|
+
PageDown: "\x1B[6~",
|
|
916
|
+
Space: " "
|
|
917
|
+
};
|
|
918
|
+
var NAMED_KEY_LOWER = new Map(
|
|
919
|
+
Object.entries(NAMED_KEY_SEQUENCES).map(([k, v]) => [k.toLowerCase(), v])
|
|
920
|
+
);
|
|
921
|
+
var VALID_KEYS_HINT = `Valid keys: ${Object.keys(NAMED_KEY_SEQUENCES).join(", ")}, Control+<letter>, Alt+<key>`;
|
|
922
|
+
var NAMED_KEY_PATTERN = Object.keys(NAMED_KEY_SEQUENCES).map(caseInsensitivePattern).join("|");
|
|
923
|
+
var TERMINAL_KEY_PATTERN = `^(?:${NAMED_KEY_PATTERN}|.|[Cc][Oo][Nn][Tt][Rr][Oo][Ll]\\+[A-Za-z]|[Aa][Ll][Tt]\\+.+)$`;
|
|
924
|
+
function unknownKeyError(key) {
|
|
925
|
+
return new Error(`Unknown terminal key: ${key}. ${VALID_KEYS_HINT}`);
|
|
926
|
+
}
|
|
927
|
+
function keyToSequence(key) {
|
|
928
|
+
const lowerKey = key.toLowerCase();
|
|
929
|
+
const namedSequence = NAMED_KEY_LOWER.get(lowerKey);
|
|
930
|
+
if (namedSequence !== void 0) {
|
|
931
|
+
return namedSequence;
|
|
932
|
+
}
|
|
933
|
+
if (lowerKey.startsWith("control+")) {
|
|
934
|
+
return controlKeyToSequence(key.slice("control+".length));
|
|
935
|
+
}
|
|
936
|
+
if (lowerKey.startsWith("alt+")) {
|
|
937
|
+
const nestedKey = key.slice("alt+".length);
|
|
938
|
+
if (nestedKey.length === 0) {
|
|
939
|
+
throw unknownKeyError(key);
|
|
940
|
+
}
|
|
941
|
+
if (nestedKey.length === 1) {
|
|
942
|
+
return "\x1B" + nestedKey;
|
|
943
|
+
}
|
|
944
|
+
try {
|
|
945
|
+
return "\x1B" + keyToSequence(nestedKey);
|
|
946
|
+
} catch {
|
|
947
|
+
throw unknownKeyError(key);
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
if (key.length === 1) {
|
|
951
|
+
return key;
|
|
952
|
+
}
|
|
953
|
+
throw unknownKeyError(key);
|
|
954
|
+
}
|
|
955
|
+
function controlKeyToSequence(controlKey) {
|
|
956
|
+
if (controlKey.length !== 1) {
|
|
957
|
+
throw unknownKeyError(`Control+${controlKey}`);
|
|
958
|
+
}
|
|
959
|
+
const uppercaseLetter = controlKey.toUpperCase();
|
|
960
|
+
const charCode = uppercaseLetter.charCodeAt(0);
|
|
961
|
+
if (charCode < 65 || charCode > 90) {
|
|
962
|
+
throw unknownKeyError(`Control+${controlKey}`);
|
|
963
|
+
}
|
|
964
|
+
return String.fromCharCode(charCode - 64);
|
|
965
|
+
}
|
|
966
|
+
function caseInsensitivePattern(value) {
|
|
967
|
+
let pattern = "";
|
|
968
|
+
for (const character of value) {
|
|
969
|
+
const lower = character.toLowerCase();
|
|
970
|
+
const upper = character.toUpperCase();
|
|
971
|
+
pattern += lower === upper ? escapePatternCharacter(character) : `[${upper}${lower}]`;
|
|
972
|
+
}
|
|
973
|
+
return pattern;
|
|
974
|
+
}
|
|
975
|
+
function escapePatternCharacter(character) {
|
|
976
|
+
return character.replace(/[\\^$.*+?()[\]{}|]/g, "\\$&");
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
// src/terminal-screen.ts
|
|
980
|
+
var TerminalScreen = class {
|
|
981
|
+
lines;
|
|
982
|
+
rawLines;
|
|
983
|
+
cursor;
|
|
984
|
+
size;
|
|
985
|
+
constructor({
|
|
986
|
+
lines,
|
|
987
|
+
rawLines,
|
|
988
|
+
cursor,
|
|
989
|
+
size
|
|
990
|
+
}) {
|
|
991
|
+
this.lines = Object.freeze(lines.map((line) => stripAnsi(line)));
|
|
992
|
+
this.rawLines = Object.freeze([...rawLines]);
|
|
993
|
+
this.cursor = Object.freeze({ ...cursor });
|
|
994
|
+
this.size = Object.freeze({ ...size });
|
|
995
|
+
Object.freeze(this);
|
|
996
|
+
}
|
|
997
|
+
get text() {
|
|
998
|
+
return this.lines.join("\n");
|
|
999
|
+
}
|
|
1000
|
+
contains(substring) {
|
|
1001
|
+
return this.text.includes(substring);
|
|
1002
|
+
}
|
|
1003
|
+
line(index) {
|
|
1004
|
+
const normalizedIndex = index < 0 ? this.lines.length + index : index;
|
|
1005
|
+
const line = this.lines[normalizedIndex];
|
|
1006
|
+
if (line === void 0) {
|
|
1007
|
+
throw new RangeError(`Line index out of bounds: ${index}`);
|
|
1008
|
+
}
|
|
1009
|
+
return line;
|
|
1010
|
+
}
|
|
1011
|
+
};
|
|
1012
|
+
|
|
1013
|
+
// src/terminal-session.ts
|
|
1014
|
+
var DEFAULT_COLS = 120;
|
|
1015
|
+
var DEFAULT_ROWS = 40;
|
|
1016
|
+
var DEFAULT_TIMEOUT_MS = 1e4;
|
|
1017
|
+
var WAIT_FOR_POLL_MS = 10;
|
|
1018
|
+
var TYPE_DELAY_MS = 15;
|
|
1019
|
+
var CLOSE_AFTER_SIGNAL_GRACE_MS = 250;
|
|
1020
|
+
var CLOSE_AFTER_SIGTERM_MS = 1e3;
|
|
1021
|
+
var CLOSE_AFTER_SIGKILL_MS = 1e3;
|
|
1022
|
+
var TerminalSession = class {
|
|
1023
|
+
id;
|
|
1024
|
+
command;
|
|
1025
|
+
pid;
|
|
1026
|
+
exitCode = null;
|
|
1027
|
+
pty;
|
|
1028
|
+
terminal;
|
|
1029
|
+
emitter = new EventEmitter();
|
|
1030
|
+
exitPromise;
|
|
1031
|
+
rawBuffer = "";
|
|
1032
|
+
lastDataAt = Date.now();
|
|
1033
|
+
currentCols;
|
|
1034
|
+
currentRows;
|
|
1035
|
+
closePromise = null;
|
|
1036
|
+
constructor({
|
|
1037
|
+
id,
|
|
1038
|
+
command,
|
|
1039
|
+
args = [],
|
|
1040
|
+
cwd = process.cwd(),
|
|
1041
|
+
env = process.env,
|
|
1042
|
+
cols = DEFAULT_COLS,
|
|
1043
|
+
rows = DEFAULT_ROWS,
|
|
1044
|
+
observe = false
|
|
1045
|
+
}) {
|
|
1046
|
+
assertTerminalGeometry(cols, rows);
|
|
1047
|
+
this.id = id;
|
|
1048
|
+
this.command = command;
|
|
1049
|
+
this.currentCols = cols;
|
|
1050
|
+
this.currentRows = rows;
|
|
1051
|
+
this.terminal = new TerminalBuffer(cols, rows);
|
|
1052
|
+
this.pty = createPtyProcess({ command, args, cwd, env, cols, rows });
|
|
1053
|
+
this.pid = this.pty.pid;
|
|
1054
|
+
const dataSubscription = this.pty.onData((chunk) => {
|
|
1055
|
+
this.rawBuffer += chunk;
|
|
1056
|
+
this.lastDataAt = Date.now();
|
|
1057
|
+
this.terminal.write(chunk);
|
|
1058
|
+
if (observe) {
|
|
1059
|
+
process.stderr.write(chunk);
|
|
1060
|
+
}
|
|
1061
|
+
});
|
|
1062
|
+
let exitSubscription;
|
|
1063
|
+
this.exitPromise = new Promise((resolve) => {
|
|
1064
|
+
exitSubscription = this.pty.onExit(({ exitCode }) => {
|
|
1065
|
+
if (this.exitCode !== null) {
|
|
1066
|
+
resolve(this.exitCode);
|
|
1067
|
+
return;
|
|
1068
|
+
}
|
|
1069
|
+
this.exitCode = exitCode;
|
|
1070
|
+
dataSubscription.dispose();
|
|
1071
|
+
exitSubscription?.dispose();
|
|
1072
|
+
this.emitter.emit("exit", exitCode);
|
|
1073
|
+
resolve(exitCode);
|
|
1074
|
+
});
|
|
1075
|
+
});
|
|
1076
|
+
}
|
|
1077
|
+
async type(text) {
|
|
1078
|
+
for (const character of text) {
|
|
1079
|
+
await this.send(character);
|
|
1080
|
+
await sleep(TYPE_DELAY_MS);
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
async fill(text) {
|
|
1084
|
+
await this.send(text.replace(/\r?\n/g, "\r"));
|
|
1085
|
+
}
|
|
1086
|
+
async press(key) {
|
|
1087
|
+
await this.send(keyToSequence(key));
|
|
1088
|
+
}
|
|
1089
|
+
async send(raw) {
|
|
1090
|
+
if (this.exitCode !== null) {
|
|
1091
|
+
throw new Error(`Terminal session "${this.id}" has already exited.`);
|
|
1092
|
+
}
|
|
1093
|
+
this.pty.write(raw);
|
|
1094
|
+
}
|
|
1095
|
+
async signal(sig) {
|
|
1096
|
+
if (this.exitCode !== null) {
|
|
1097
|
+
return;
|
|
1098
|
+
}
|
|
1099
|
+
this.pty.kill(sig);
|
|
1100
|
+
}
|
|
1101
|
+
async waitFor(pattern, opts) {
|
|
1102
|
+
const timeout = opts?.timeout ?? DEFAULT_TIMEOUT_MS;
|
|
1103
|
+
assertTimeout(timeout);
|
|
1104
|
+
const startedAt = Date.now();
|
|
1105
|
+
while (Date.now() - startedAt <= timeout) {
|
|
1106
|
+
const matched = matchPattern(this.rawBuffer, pattern);
|
|
1107
|
+
if (matched !== null) {
|
|
1108
|
+
return matched;
|
|
1109
|
+
}
|
|
1110
|
+
if (this.exitCode !== null) {
|
|
1111
|
+
throw new Error(`Terminal session "${this.id}" exited before matching pattern: ${String(pattern)}`);
|
|
1112
|
+
}
|
|
1113
|
+
await sleep(WAIT_FOR_POLL_MS);
|
|
1114
|
+
}
|
|
1115
|
+
throw new Error(`Timed out waiting for pattern after ${timeout}ms: ${String(pattern)}`);
|
|
1116
|
+
}
|
|
1117
|
+
async waitForQuiet(ms) {
|
|
1118
|
+
assertQuietPeriod(ms);
|
|
1119
|
+
while (true) {
|
|
1120
|
+
const remaining = ms - (Date.now() - this.lastDataAt);
|
|
1121
|
+
if (remaining <= 0) {
|
|
1122
|
+
return;
|
|
1123
|
+
}
|
|
1124
|
+
await sleep(remaining);
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
async screen() {
|
|
1128
|
+
const rawLines = [];
|
|
1129
|
+
for (let row = 0; row < this.currentRows; row += 1) {
|
|
1130
|
+
rawLines.push(this.terminal.renderLine(row));
|
|
1131
|
+
}
|
|
1132
|
+
return new TerminalScreen({
|
|
1133
|
+
lines: rawLines,
|
|
1134
|
+
rawLines,
|
|
1135
|
+
cursor: {
|
|
1136
|
+
row: this.terminal.displayBuffer.cursorY,
|
|
1137
|
+
col: this.terminal.displayBuffer.cursorX
|
|
1138
|
+
},
|
|
1139
|
+
size: {
|
|
1140
|
+
rows: this.currentRows,
|
|
1141
|
+
cols: this.currentCols
|
|
1142
|
+
}
|
|
1143
|
+
});
|
|
1144
|
+
}
|
|
1145
|
+
async history(opts) {
|
|
1146
|
+
const normalized = normalizeHistoryBuffer(stripAnsi(this.rawBuffer));
|
|
1147
|
+
const lines = splitHistoryLines(normalized);
|
|
1148
|
+
if (opts?.last === void 0) {
|
|
1149
|
+
return lines;
|
|
1150
|
+
}
|
|
1151
|
+
if (!Number.isInteger(opts.last) || opts.last < 0) {
|
|
1152
|
+
throw new Error("History last must be a non-negative integer.");
|
|
1153
|
+
}
|
|
1154
|
+
const start = Math.max(0, lines.length - opts.last);
|
|
1155
|
+
return lines.slice(start);
|
|
1156
|
+
}
|
|
1157
|
+
async resize(cols, rows) {
|
|
1158
|
+
assertTerminalGeometry(cols, rows);
|
|
1159
|
+
this.currentCols = cols;
|
|
1160
|
+
this.currentRows = rows;
|
|
1161
|
+
if (this.exitCode === null) {
|
|
1162
|
+
this.pty.resize(cols, rows);
|
|
1163
|
+
}
|
|
1164
|
+
this.terminal.resize(cols, rows);
|
|
1165
|
+
}
|
|
1166
|
+
async waitForExit(opts) {
|
|
1167
|
+
if (opts?.timeout !== void 0) {
|
|
1168
|
+
assertTimeout(opts.timeout);
|
|
1169
|
+
}
|
|
1170
|
+
if (this.exitCode !== null) {
|
|
1171
|
+
return this.exitCode;
|
|
1172
|
+
}
|
|
1173
|
+
if (opts?.timeout !== void 0) {
|
|
1174
|
+
const result = await waitForExit(this.exitPromise, opts.timeout);
|
|
1175
|
+
if (result === null) {
|
|
1176
|
+
throw new Error(`Timed out waiting for process to exit after ${opts.timeout}ms`);
|
|
1177
|
+
}
|
|
1178
|
+
return result;
|
|
1179
|
+
}
|
|
1180
|
+
return this.exitPromise;
|
|
1181
|
+
}
|
|
1182
|
+
async close() {
|
|
1183
|
+
if (this.exitCode !== null) {
|
|
1184
|
+
return this.exitCode;
|
|
1185
|
+
}
|
|
1186
|
+
this.closePromise ??= this.closeProcess().catch((error) => {
|
|
1187
|
+
this.closePromise = null;
|
|
1188
|
+
throw error;
|
|
1189
|
+
});
|
|
1190
|
+
return this.closePromise;
|
|
1191
|
+
}
|
|
1192
|
+
async closeProcess() {
|
|
1193
|
+
const gracefulExitCode = await waitForExit(this.exitPromise, CLOSE_AFTER_SIGNAL_GRACE_MS);
|
|
1194
|
+
if (gracefulExitCode !== null) {
|
|
1195
|
+
return gracefulExitCode;
|
|
1196
|
+
}
|
|
1197
|
+
if (this.exitCode === null) {
|
|
1198
|
+
this.pty.kill("SIGTERM");
|
|
1199
|
+
const afterSigterm = await waitForExit(this.exitPromise, CLOSE_AFTER_SIGTERM_MS);
|
|
1200
|
+
if (afterSigterm !== null) {
|
|
1201
|
+
return afterSigterm;
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
if (this.exitCode === null) {
|
|
1205
|
+
this.pty.kill("SIGKILL");
|
|
1206
|
+
const afterSigkill = await waitForExit(this.exitPromise, CLOSE_AFTER_SIGKILL_MS);
|
|
1207
|
+
if (afterSigkill !== null) {
|
|
1208
|
+
return afterSigkill;
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
throw new Error("Timed out waiting for process to exit after SIGKILL.");
|
|
1212
|
+
}
|
|
1213
|
+
on(event, cb) {
|
|
1214
|
+
this.emitter.on(event, cb);
|
|
1215
|
+
}
|
|
1216
|
+
};
|
|
1217
|
+
function assertTerminalGeometry(cols, rows) {
|
|
1218
|
+
if (!Number.isInteger(cols) || cols <= 0 || !Number.isInteger(rows) || rows <= 0) {
|
|
1219
|
+
throw new Error("Terminal columns and rows must be positive integers.");
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
function assertTimeout(timeout) {
|
|
1223
|
+
if (!Number.isFinite(timeout) || timeout < 0) {
|
|
1224
|
+
throw new Error("Timeout must be a finite non-negative number.");
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
function assertQuietPeriod(duration) {
|
|
1228
|
+
if (!Number.isFinite(duration) || duration < 0) {
|
|
1229
|
+
throw new Error("Quiet period must be a finite non-negative number.");
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
function createPtyProcess({
|
|
1233
|
+
command,
|
|
1234
|
+
args,
|
|
1235
|
+
cwd,
|
|
1236
|
+
env,
|
|
1237
|
+
cols,
|
|
1238
|
+
rows
|
|
1239
|
+
}) {
|
|
1240
|
+
ensureSpawnHelperExecutable();
|
|
1241
|
+
return nodePty.spawn(command, args, {
|
|
1242
|
+
cwd,
|
|
1243
|
+
env,
|
|
1244
|
+
cols,
|
|
1245
|
+
rows,
|
|
1246
|
+
encoding: "utf8"
|
|
1247
|
+
});
|
|
1248
|
+
}
|
|
1249
|
+
var spawnHelperChecked = false;
|
|
1250
|
+
function ensureSpawnHelperExecutable() {
|
|
1251
|
+
if (spawnHelperChecked) return;
|
|
1252
|
+
spawnHelperChecked = true;
|
|
1253
|
+
const require2 = createRequire(import.meta.url);
|
|
1254
|
+
const nodePtyDir = dirname(require2.resolve("node-pty"));
|
|
1255
|
+
const helper = join(nodePtyDir, "..", "prebuilds", `${process.platform}-${process.arch}`, "spawn-helper");
|
|
1256
|
+
try {
|
|
1257
|
+
accessSync(helper, constants.X_OK);
|
|
1258
|
+
} catch (error) {
|
|
1259
|
+
if (isMissingFileError(error)) {
|
|
1260
|
+
return;
|
|
1261
|
+
}
|
|
1262
|
+
try {
|
|
1263
|
+
chmodSync(helper, 493);
|
|
1264
|
+
} catch (chmodError) {
|
|
1265
|
+
if (isMissingFileError(chmodError)) {
|
|
1266
|
+
return;
|
|
1267
|
+
}
|
|
1268
|
+
throw chmodError;
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
function isMissingFileError(error) {
|
|
1273
|
+
return hasOwnErrorCode(error, "ENOENT");
|
|
1274
|
+
}
|
|
1275
|
+
function matchPattern(buffer, pattern) {
|
|
1276
|
+
const clean = normalizeHistoryBuffer(stripAnsi(buffer));
|
|
1277
|
+
for (const line of clean.split("\n")) {
|
|
1278
|
+
if (typeof pattern === "string") {
|
|
1279
|
+
if (line.includes(pattern)) return line;
|
|
1280
|
+
} else {
|
|
1281
|
+
const flags = removeCharacter(pattern.flags, "g");
|
|
1282
|
+
if (new RegExp(pattern.source, flags).test(line)) return line;
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
return null;
|
|
1286
|
+
}
|
|
1287
|
+
function removeCharacter(input, charToRemove) {
|
|
1288
|
+
let output = "";
|
|
1289
|
+
for (const character of input) {
|
|
1290
|
+
if (character !== charToRemove) {
|
|
1291
|
+
output += character;
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
return output;
|
|
1295
|
+
}
|
|
1296
|
+
function splitHistoryLines(input) {
|
|
1297
|
+
const lines = input.split("\n");
|
|
1298
|
+
if (lines.length > 0 && lines[lines.length - 1] === "") {
|
|
1299
|
+
lines.pop();
|
|
1300
|
+
}
|
|
1301
|
+
return lines;
|
|
1302
|
+
}
|
|
1303
|
+
function normalizeHistoryBuffer(input) {
|
|
1304
|
+
let output = "";
|
|
1305
|
+
let line = "";
|
|
1306
|
+
let cursor = 0;
|
|
1307
|
+
for (const character of input) {
|
|
1308
|
+
if (character === "\r") {
|
|
1309
|
+
cursor = 0;
|
|
1310
|
+
continue;
|
|
1311
|
+
}
|
|
1312
|
+
if (character === "\b") {
|
|
1313
|
+
cursor = Math.max(0, cursor - 1);
|
|
1314
|
+
continue;
|
|
1315
|
+
}
|
|
1316
|
+
if (character === "\n") {
|
|
1317
|
+
output += `${line}
|
|
1318
|
+
`;
|
|
1319
|
+
line = "";
|
|
1320
|
+
cursor = 0;
|
|
1321
|
+
continue;
|
|
1322
|
+
}
|
|
1323
|
+
line = `${line.slice(0, cursor)}${character}${line.slice(cursor + 1)}`;
|
|
1324
|
+
cursor += 1;
|
|
1325
|
+
}
|
|
1326
|
+
return output + line;
|
|
1327
|
+
}
|
|
1328
|
+
function sleep(ms) {
|
|
1329
|
+
return new Promise((resolve) => {
|
|
1330
|
+
setTimeout(resolve, ms);
|
|
1331
|
+
});
|
|
1332
|
+
}
|
|
1333
|
+
async function waitForExit(exitPromise, timeout) {
|
|
1334
|
+
return new Promise((resolve) => {
|
|
1335
|
+
let settled = false;
|
|
1336
|
+
const timer = setTimeout(() => {
|
|
1337
|
+
if (settled) {
|
|
1338
|
+
return;
|
|
1339
|
+
}
|
|
1340
|
+
settled = true;
|
|
1341
|
+
resolve(null);
|
|
1342
|
+
}, timeout);
|
|
1343
|
+
void exitPromise.then((code) => {
|
|
1344
|
+
if (settled) {
|
|
1345
|
+
return;
|
|
1346
|
+
}
|
|
1347
|
+
settled = true;
|
|
1348
|
+
clearTimeout(timer);
|
|
1349
|
+
resolve(code);
|
|
1350
|
+
});
|
|
1351
|
+
});
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
// src/terminal-pilot.ts
|
|
1355
|
+
var DEFAULT_COLS2 = 120;
|
|
1356
|
+
var DEFAULT_ROWS2 = 40;
|
|
1357
|
+
var TerminalPilot = class _TerminalPilot {
|
|
1358
|
+
sessionMap = /* @__PURE__ */ new Map();
|
|
1359
|
+
static async launch() {
|
|
1360
|
+
return new _TerminalPilot();
|
|
1361
|
+
}
|
|
1362
|
+
async newSession(opts) {
|
|
1363
|
+
const session = new TerminalSession({
|
|
1364
|
+
id: randomUUID(),
|
|
1365
|
+
command: opts.command,
|
|
1366
|
+
args: opts.args,
|
|
1367
|
+
cwd: opts.cwd,
|
|
1368
|
+
env: opts.env,
|
|
1369
|
+
cols: opts.cols ?? DEFAULT_COLS2,
|
|
1370
|
+
rows: opts.rows ?? DEFAULT_ROWS2,
|
|
1371
|
+
observe: opts.observe ?? false
|
|
1372
|
+
});
|
|
1373
|
+
this.sessionMap.set(session.id, session);
|
|
1374
|
+
return session;
|
|
1375
|
+
}
|
|
1376
|
+
getSession(id) {
|
|
1377
|
+
const session = this.sessionMap.get(id);
|
|
1378
|
+
if (session === void 0) {
|
|
1379
|
+
throw new Error(`Session not found: ${id}`);
|
|
1380
|
+
}
|
|
1381
|
+
return session;
|
|
1382
|
+
}
|
|
1383
|
+
deleteSession(id) {
|
|
1384
|
+
this.sessionMap.delete(id);
|
|
1385
|
+
}
|
|
1386
|
+
sessions() {
|
|
1387
|
+
return [...this.sessionMap.values()].filter((s) => s.exitCode === null);
|
|
1388
|
+
}
|
|
1389
|
+
async close() {
|
|
1390
|
+
const sessions = [...this.sessionMap.values()];
|
|
1391
|
+
await Promise.all(
|
|
1392
|
+
sessions.map(async (session) => {
|
|
1393
|
+
await session.close();
|
|
1394
|
+
this.sessionMap.delete(session.id);
|
|
1395
|
+
})
|
|
1396
|
+
);
|
|
1397
|
+
}
|
|
1398
|
+
};
|
|
1399
|
+
|
|
1400
|
+
// src/commands/runtime.ts
|
|
1401
|
+
var SESSION_ENV_VAR = "TERMINAL_PILOT_SESSION";
|
|
1402
|
+
function createTerminalPilotRuntime(options = {}) {
|
|
1403
|
+
const launchPilot = options.launchPilot ?? TerminalPilot.launch;
|
|
1404
|
+
const nameToId = /* @__PURE__ */ new Map();
|
|
1405
|
+
const idToName = /* @__PURE__ */ new Map();
|
|
1406
|
+
const pendingNames = /* @__PURE__ */ new Set();
|
|
1407
|
+
let pilotPromise;
|
|
1408
|
+
function getRequestedName(name, env) {
|
|
1409
|
+
const requestedName = name ?? env?.get(SESSION_ENV_VAR);
|
|
1410
|
+
if (requestedName !== void 0 && requestedName.trim().length === 0) {
|
|
1411
|
+
throw new UserError("Session name must not be empty.");
|
|
1412
|
+
}
|
|
1413
|
+
return requestedName;
|
|
1414
|
+
}
|
|
1415
|
+
async function getPilot() {
|
|
1416
|
+
pilotPromise ??= launchPilot();
|
|
1417
|
+
return pilotPromise;
|
|
1418
|
+
}
|
|
1419
|
+
function nextSessionName() {
|
|
1420
|
+
let index = 1;
|
|
1421
|
+
while (nameToId.has(`s${index}`) || pendingNames.has(`s${index}`)) {
|
|
1422
|
+
index += 1;
|
|
1423
|
+
}
|
|
1424
|
+
return `s${index}`;
|
|
1425
|
+
}
|
|
1426
|
+
function rememberSession(name, session) {
|
|
1427
|
+
nameToId.set(name, session.id);
|
|
1428
|
+
idToName.set(session.id, name);
|
|
1429
|
+
return { name, session };
|
|
1430
|
+
}
|
|
1431
|
+
function forgetSession(name, sessionId) {
|
|
1432
|
+
if (nameToId.get(name) === sessionId) {
|
|
1433
|
+
nameToId.delete(name);
|
|
1434
|
+
}
|
|
1435
|
+
if (idToName.get(sessionId) === name) {
|
|
1436
|
+
idToName.delete(sessionId);
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
function formatAvailableSessions(names) {
|
|
1440
|
+
if (names.length === 0) {
|
|
1441
|
+
return "No active sessions are available.";
|
|
1442
|
+
}
|
|
1443
|
+
return `Available sessions: ${names.join(", ")}.`;
|
|
1444
|
+
}
|
|
1445
|
+
async function lookupNamedSession(name) {
|
|
1446
|
+
const sessionId = nameToId.get(name);
|
|
1447
|
+
if (sessionId === void 0) {
|
|
1448
|
+
const active = await listSessions();
|
|
1449
|
+
throw new UserError(
|
|
1450
|
+
`Session "${name}" was not found. ${formatAvailableSessions(active.map((entry) => entry.name))}`
|
|
1451
|
+
);
|
|
1452
|
+
}
|
|
1453
|
+
const pilot = await getPilot();
|
|
1454
|
+
try {
|
|
1455
|
+
return { name, session: pilot.getSession(sessionId) };
|
|
1456
|
+
} catch {
|
|
1457
|
+
forgetSession(name, sessionId);
|
|
1458
|
+
const active = await listSessions();
|
|
1459
|
+
throw new UserError(
|
|
1460
|
+
`Session "${name}" was not found. ${formatAvailableSessions(active.map((entry) => entry.name))}`
|
|
1461
|
+
);
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
async function listSessions() {
|
|
1465
|
+
const pilot = await getPilot();
|
|
1466
|
+
return pilot.sessions().flatMap((session) => {
|
|
1467
|
+
const name = idToName.get(session.id);
|
|
1468
|
+
if (name === void 0) {
|
|
1469
|
+
return [];
|
|
1470
|
+
}
|
|
1471
|
+
return [{ name, session }];
|
|
1472
|
+
});
|
|
1473
|
+
}
|
|
1474
|
+
async function discardExitedSessionName(name) {
|
|
1475
|
+
const sessionId = nameToId.get(name);
|
|
1476
|
+
if (sessionId === void 0) {
|
|
1477
|
+
return;
|
|
1478
|
+
}
|
|
1479
|
+
const pilot = await getPilot();
|
|
1480
|
+
try {
|
|
1481
|
+
const session = pilot.getSession(sessionId);
|
|
1482
|
+
if (session.exitCode === null) {
|
|
1483
|
+
return;
|
|
1484
|
+
}
|
|
1485
|
+
pilot.deleteSession(sessionId);
|
|
1486
|
+
} catch {
|
|
1487
|
+
}
|
|
1488
|
+
forgetSession(name, sessionId);
|
|
1489
|
+
}
|
|
1490
|
+
return {
|
|
1491
|
+
async createSession(params, env) {
|
|
1492
|
+
if (params.command.trim().length === 0) {
|
|
1493
|
+
throw new UserError("Command must not be empty.");
|
|
1494
|
+
}
|
|
1495
|
+
const requestedName = getRequestedName(params.session, env) ?? nextSessionName();
|
|
1496
|
+
await discardExitedSessionName(requestedName);
|
|
1497
|
+
if (nameToId.has(requestedName) || pendingNames.has(requestedName)) {
|
|
1498
|
+
throw new UserError(`Session "${requestedName}" already exists.`);
|
|
1499
|
+
}
|
|
1500
|
+
pendingNames.add(requestedName);
|
|
1501
|
+
try {
|
|
1502
|
+
const pilot = await getPilot();
|
|
1503
|
+
const session = await pilot.newSession({
|
|
1504
|
+
command: params.command,
|
|
1505
|
+
args: params.args,
|
|
1506
|
+
cwd: params.cwd,
|
|
1507
|
+
cols: params.cols,
|
|
1508
|
+
rows: params.rows,
|
|
1509
|
+
observe: params.observe
|
|
1510
|
+
});
|
|
1511
|
+
return rememberSession(requestedName, session);
|
|
1512
|
+
} finally {
|
|
1513
|
+
pendingNames.delete(requestedName);
|
|
1514
|
+
}
|
|
1515
|
+
},
|
|
1516
|
+
async resolveSession(name, env) {
|
|
1517
|
+
const requestedName = getRequestedName(name, env);
|
|
1518
|
+
if (requestedName !== void 0) {
|
|
1519
|
+
return lookupNamedSession(requestedName);
|
|
1520
|
+
}
|
|
1521
|
+
const active = await listSessions();
|
|
1522
|
+
if (active.length === 1) {
|
|
1523
|
+
return active[0];
|
|
1524
|
+
}
|
|
1525
|
+
if (active.length === 0) {
|
|
1526
|
+
throw new UserError("No active sessions. Create one with create-session.");
|
|
1527
|
+
}
|
|
1528
|
+
throw new UserError(
|
|
1529
|
+
`Multiple active sessions require an explicit session name. Pass --session or set ${SESSION_ENV_VAR}. ${formatAvailableSessions(active.map((entry) => entry.name))}`
|
|
1530
|
+
);
|
|
1531
|
+
},
|
|
1532
|
+
async closeSession(name, env) {
|
|
1533
|
+
const namedSession = await this.resolveSession(name, env);
|
|
1534
|
+
const exitCode = await namedSession.session.close();
|
|
1535
|
+
const pilot = await getPilot();
|
|
1536
|
+
pilot.deleteSession(namedSession.session.id);
|
|
1537
|
+
forgetSession(namedSession.name, namedSession.session.id);
|
|
1538
|
+
return {
|
|
1539
|
+
exitCode,
|
|
1540
|
+
name: namedSession.name
|
|
1541
|
+
};
|
|
1542
|
+
},
|
|
1543
|
+
listSessions,
|
|
1544
|
+
async close() {
|
|
1545
|
+
if (pilotPromise === void 0) {
|
|
1546
|
+
return;
|
|
1547
|
+
}
|
|
1548
|
+
const pilot = await pilotPromise;
|
|
1549
|
+
await pilot.close();
|
|
1550
|
+
pilotPromise = void 0;
|
|
1551
|
+
nameToId.clear();
|
|
1552
|
+
idToName.clear();
|
|
1553
|
+
pendingNames.clear();
|
|
1554
|
+
}
|
|
1555
|
+
};
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
// src/commands/daemon-runtime.ts
|
|
1559
|
+
var RUNTIME_DIR_ENV = "TERMINAL_PILOT_RUNTIME_DIR";
|
|
1560
|
+
var DAEMON_ARG = "__daemon";
|
|
1561
|
+
var START_TIMEOUT_MS = 5e3;
|
|
1562
|
+
var IDLE_SHUTDOWN_MS = 1e3;
|
|
1563
|
+
function isTerminalPilotDaemonArgv(argv) {
|
|
1564
|
+
return argv[2] === DAEMON_ARG;
|
|
1565
|
+
}
|
|
1566
|
+
async function runTerminalPilotDaemon() {
|
|
1567
|
+
const runtime = createTerminalPilotRuntime();
|
|
1568
|
+
const socketPath = resolveSocketPath(process.env);
|
|
1569
|
+
await mkdir(path2.dirname(socketPath), { recursive: true });
|
|
1570
|
+
if (process.platform !== "win32") {
|
|
1571
|
+
await unlink(socketPath).catch(() => void 0);
|
|
1572
|
+
}
|
|
1573
|
+
let idleTimer;
|
|
1574
|
+
const server = net.createServer((socket) => {
|
|
1575
|
+
let buffer = "";
|
|
1576
|
+
socket.setEncoding("utf8");
|
|
1577
|
+
socket.on("data", (chunk) => {
|
|
1578
|
+
buffer += chunk;
|
|
1579
|
+
while (true) {
|
|
1580
|
+
const newlineIndex = buffer.indexOf("\n");
|
|
1581
|
+
if (newlineIndex < 0) {
|
|
1582
|
+
break;
|
|
1583
|
+
}
|
|
1584
|
+
const line = buffer.slice(0, newlineIndex);
|
|
1585
|
+
buffer = buffer.slice(newlineIndex + 1);
|
|
1586
|
+
void handleRequestLine(runtime, line).then((response) => {
|
|
1587
|
+
socket.write(`${JSON.stringify(response)}
|
|
1588
|
+
`);
|
|
1589
|
+
scheduleIdleShutdown();
|
|
1590
|
+
});
|
|
1591
|
+
}
|
|
1592
|
+
});
|
|
1593
|
+
});
|
|
1594
|
+
async function shutdown() {
|
|
1595
|
+
if (idleTimer !== void 0) {
|
|
1596
|
+
clearTimeout(idleTimer);
|
|
1597
|
+
idleTimer = void 0;
|
|
1598
|
+
}
|
|
1599
|
+
await runtime.close();
|
|
1600
|
+
await new Promise((resolve) => {
|
|
1601
|
+
server.close(() => resolve());
|
|
1602
|
+
});
|
|
1603
|
+
if (process.platform !== "win32") {
|
|
1604
|
+
await unlink(socketPath).catch(() => void 0);
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
async function scheduleIdleShutdown() {
|
|
1608
|
+
if (idleTimer !== void 0) {
|
|
1609
|
+
clearTimeout(idleTimer);
|
|
1610
|
+
idleTimer = void 0;
|
|
1611
|
+
}
|
|
1612
|
+
const sessions = await runtime.listSessions();
|
|
1613
|
+
if (sessions.length > 0) {
|
|
1614
|
+
return;
|
|
1615
|
+
}
|
|
1616
|
+
idleTimer = setTimeout(() => {
|
|
1617
|
+
void shutdown().then(() => {
|
|
1618
|
+
process.exit(0);
|
|
1619
|
+
});
|
|
1620
|
+
}, IDLE_SHUTDOWN_MS);
|
|
1621
|
+
}
|
|
1622
|
+
await new Promise((resolve, reject) => {
|
|
1623
|
+
server.once("error", reject);
|
|
1624
|
+
server.listen(socketPath, () => {
|
|
1625
|
+
server.off("error", reject);
|
|
1626
|
+
resolve();
|
|
1627
|
+
});
|
|
1628
|
+
});
|
|
1629
|
+
}
|
|
1630
|
+
function createDaemonTerminalPilotRuntime() {
|
|
1631
|
+
let nextId = 1;
|
|
1632
|
+
async function request(method, params) {
|
|
1633
|
+
await ensureDaemon();
|
|
1634
|
+
return sendRequest(method, params, nextId++);
|
|
1635
|
+
}
|
|
1636
|
+
function proxySession(name, info) {
|
|
1637
|
+
return {
|
|
1638
|
+
id: info.id,
|
|
1639
|
+
command: info.command,
|
|
1640
|
+
pid: info.pid,
|
|
1641
|
+
exitCode: info.exitCode,
|
|
1642
|
+
fill: async (text) => {
|
|
1643
|
+
await request("sessionAction", { name, action: "fill", args: [text] });
|
|
1644
|
+
},
|
|
1645
|
+
type: async (text) => {
|
|
1646
|
+
await request("sessionAction", { name, action: "type", args: [text] });
|
|
1647
|
+
},
|
|
1648
|
+
press: async (key) => {
|
|
1649
|
+
await request("sessionAction", { name, action: "press", args: [key] });
|
|
1650
|
+
},
|
|
1651
|
+
signal: async (signal) => {
|
|
1652
|
+
await request("sessionAction", { name, action: "signal", args: [signal] });
|
|
1653
|
+
},
|
|
1654
|
+
waitFor: async (pattern, options) => request("sessionAction", {
|
|
1655
|
+
name,
|
|
1656
|
+
action: "waitFor",
|
|
1657
|
+
args: [serializePattern(pattern), options]
|
|
1658
|
+
}),
|
|
1659
|
+
waitForExit: async (options) => request("sessionAction", { name, action: "waitForExit", args: [options] }),
|
|
1660
|
+
screen: async () => request("sessionAction", { name, action: "screen", args: [] }),
|
|
1661
|
+
history: async (options) => request("sessionAction", { name, action: "history", args: [options] }),
|
|
1662
|
+
resize: async (cols, rows) => {
|
|
1663
|
+
await request("sessionAction", { name, action: "resize", args: [cols, rows] });
|
|
1664
|
+
},
|
|
1665
|
+
close: async () => {
|
|
1666
|
+
const result = await request("closeSession", { name });
|
|
1667
|
+
return result.exitCode;
|
|
1668
|
+
}
|
|
1669
|
+
};
|
|
1670
|
+
}
|
|
1671
|
+
return {
|
|
1672
|
+
async createSession(params, env) {
|
|
1673
|
+
const result = await request("createSession", {
|
|
1674
|
+
params,
|
|
1675
|
+
envSession: env?.get(SESSION_ENV_VAR)
|
|
1676
|
+
});
|
|
1677
|
+
return { name: result.name, session: proxySession(result.name, result.session) };
|
|
1678
|
+
},
|
|
1679
|
+
async resolveSession(name, env) {
|
|
1680
|
+
const result = await request("resolveSession", {
|
|
1681
|
+
name,
|
|
1682
|
+
envSession: env?.get(SESSION_ENV_VAR)
|
|
1683
|
+
});
|
|
1684
|
+
return { name: result.name, session: proxySession(result.name, result.session) };
|
|
1685
|
+
},
|
|
1686
|
+
async closeSession(name, env) {
|
|
1687
|
+
return request("closeSession", {
|
|
1688
|
+
name,
|
|
1689
|
+
envSession: env?.get(SESSION_ENV_VAR)
|
|
1690
|
+
});
|
|
1691
|
+
},
|
|
1692
|
+
async listSessions() {
|
|
1693
|
+
const sessions = await request("listSessions");
|
|
1694
|
+
return sessions.map((entry) => ({
|
|
1695
|
+
name: entry.name,
|
|
1696
|
+
session: proxySession(entry.name, entry.session)
|
|
1697
|
+
}));
|
|
1698
|
+
},
|
|
1699
|
+
async close() {
|
|
1700
|
+
await request("shutdown").catch(() => void 0);
|
|
1701
|
+
}
|
|
1702
|
+
};
|
|
1703
|
+
}
|
|
1704
|
+
async function handleRequestLine(runtime, line) {
|
|
1705
|
+
let request;
|
|
1706
|
+
try {
|
|
1707
|
+
request = JSON.parse(line);
|
|
1708
|
+
} catch (error) {
|
|
1709
|
+
return {
|
|
1710
|
+
id: 0,
|
|
1711
|
+
ok: false,
|
|
1712
|
+
error: { message: error instanceof Error ? error.message : "Invalid request" }
|
|
1713
|
+
};
|
|
1714
|
+
}
|
|
1715
|
+
try {
|
|
1716
|
+
const result = await handleRequest(runtime, request);
|
|
1717
|
+
return { id: request.id, ok: true, result };
|
|
1718
|
+
} catch (error) {
|
|
1719
|
+
return {
|
|
1720
|
+
id: request.id,
|
|
1721
|
+
ok: false,
|
|
1722
|
+
error: { message: error instanceof Error ? error.message : String(error) }
|
|
1723
|
+
};
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
async function handleRequest(runtime, request) {
|
|
1727
|
+
const params = isRecord(request.params) ? request.params : {};
|
|
1728
|
+
if (request.method === "ping") {
|
|
1729
|
+
return { ok: true };
|
|
1730
|
+
}
|
|
1731
|
+
if (request.method === "createSession") {
|
|
1732
|
+
const createParams = isRecord(params.params) ? params.params : {};
|
|
1733
|
+
const namedSession = await runtime.createSession(
|
|
1734
|
+
createParams,
|
|
1735
|
+
envFromValue(params.envSession)
|
|
1736
|
+
);
|
|
1737
|
+
return formatNamedSession(namedSession);
|
|
1738
|
+
}
|
|
1739
|
+
if (request.method === "resolveSession") {
|
|
1740
|
+
const namedSession = await runtime.resolveSession(
|
|
1741
|
+
optionalString(params.name),
|
|
1742
|
+
envFromValue(params.envSession)
|
|
1743
|
+
);
|
|
1744
|
+
return formatNamedSession(namedSession);
|
|
1745
|
+
}
|
|
1746
|
+
if (request.method === "listSessions") {
|
|
1747
|
+
const sessions = await runtime.listSessions();
|
|
1748
|
+
return sessions.map(formatNamedSession);
|
|
1749
|
+
}
|
|
1750
|
+
if (request.method === "closeSession") {
|
|
1751
|
+
return runtime.closeSession(optionalString(params.name), envFromValue(params.envSession));
|
|
1752
|
+
}
|
|
1753
|
+
if (request.method === "sessionAction") {
|
|
1754
|
+
return runSessionAction(runtime, params);
|
|
1755
|
+
}
|
|
1756
|
+
if (request.method === "shutdown") {
|
|
1757
|
+
await runtime.close();
|
|
1758
|
+
process.exit(0);
|
|
1759
|
+
}
|
|
1760
|
+
throw new UserError(`Unknown terminal-pilot daemon method: ${request.method}`);
|
|
1761
|
+
}
|
|
1762
|
+
async function runSessionAction(runtime, params) {
|
|
1763
|
+
const name = optionalString(params.name);
|
|
1764
|
+
const action = optionalString(params.action);
|
|
1765
|
+
const args = Array.isArray(params.args) ? params.args : [];
|
|
1766
|
+
const namedSession = await runtime.resolveSession(name);
|
|
1767
|
+
const session = namedSession.session;
|
|
1768
|
+
if (action === "fill") {
|
|
1769
|
+
await session.fill(String(args[0] ?? ""));
|
|
1770
|
+
return void 0;
|
|
1771
|
+
}
|
|
1772
|
+
if (action === "type") {
|
|
1773
|
+
await session.type(String(args[0] ?? ""));
|
|
1774
|
+
return void 0;
|
|
1775
|
+
}
|
|
1776
|
+
if (action === "press") {
|
|
1777
|
+
await session.press(String(args[0] ?? ""));
|
|
1778
|
+
return void 0;
|
|
1779
|
+
}
|
|
1780
|
+
if (action === "signal") {
|
|
1781
|
+
await session.signal(String(args[0] ?? ""));
|
|
1782
|
+
return void 0;
|
|
1783
|
+
}
|
|
1784
|
+
if (action === "waitFor") {
|
|
1785
|
+
return session.waitFor(deserializePattern(args[0]), optionalRecord(args[1]));
|
|
1786
|
+
}
|
|
1787
|
+
if (action === "waitForExit") {
|
|
1788
|
+
return session.waitForExit(optionalRecord(args[0]));
|
|
1789
|
+
}
|
|
1790
|
+
if (action === "screen") {
|
|
1791
|
+
const screen = await session.screen();
|
|
1792
|
+
return {
|
|
1793
|
+
lines: [...screen.lines],
|
|
1794
|
+
rawLines: [...screen.rawLines],
|
|
1795
|
+
cursor: { ...screen.cursor },
|
|
1796
|
+
size: { ...screen.size }
|
|
1797
|
+
};
|
|
1798
|
+
}
|
|
1799
|
+
if (action === "history") {
|
|
1800
|
+
return session.history(optionalRecord(args[0]));
|
|
1801
|
+
}
|
|
1802
|
+
if (action === "resize") {
|
|
1803
|
+
await session.resize(Number(args[0]), Number(args[1]));
|
|
1804
|
+
return void 0;
|
|
1805
|
+
}
|
|
1806
|
+
throw new UserError(`Unknown terminal-pilot session action: ${action ?? "<missing>"}`);
|
|
1807
|
+
}
|
|
1808
|
+
async function ensureDaemon() {
|
|
1809
|
+
try {
|
|
1810
|
+
await sendRequest("ping", void 0, 0, { start: false });
|
|
1811
|
+
return;
|
|
1812
|
+
} catch {
|
|
1813
|
+
}
|
|
1814
|
+
const socketPath = resolveSocketPath(process.env);
|
|
1815
|
+
await mkdir(path2.dirname(socketPath), { recursive: true });
|
|
1816
|
+
if (process.platform !== "win32") {
|
|
1817
|
+
await unlink(socketPath).catch(() => void 0);
|
|
1818
|
+
}
|
|
1819
|
+
const entryPoint = process.argv[1];
|
|
1820
|
+
if (typeof entryPoint !== "string" || entryPoint.length === 0) {
|
|
1821
|
+
throw new UserError("Cannot start terminal-pilot daemon: entrypoint is unknown.");
|
|
1822
|
+
}
|
|
1823
|
+
const child = spawn2(process.execPath, [...process.execArgv, entryPoint, DAEMON_ARG], {
|
|
1824
|
+
detached: true,
|
|
1825
|
+
stdio: "ignore",
|
|
1826
|
+
env: process.env
|
|
1827
|
+
});
|
|
1828
|
+
child.unref();
|
|
1829
|
+
const startedAt = Date.now();
|
|
1830
|
+
while (Date.now() - startedAt <= START_TIMEOUT_MS) {
|
|
1831
|
+
try {
|
|
1832
|
+
await sendRequest("ping", void 0, 0, { start: false });
|
|
1833
|
+
return;
|
|
1834
|
+
} catch {
|
|
1835
|
+
await sleep2(50);
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
throw new UserError("Timed out waiting for terminal-pilot daemon to start.");
|
|
1839
|
+
}
|
|
1840
|
+
function sendRequest(method, params, id, options = {}) {
|
|
1841
|
+
const socketPath = resolveSocketPath(process.env);
|
|
1842
|
+
return new Promise((resolve, reject) => {
|
|
1843
|
+
const socket = net.createConnection(socketPath);
|
|
1844
|
+
let buffer = "";
|
|
1845
|
+
socket.setEncoding("utf8");
|
|
1846
|
+
socket.on("connect", () => {
|
|
1847
|
+
const request = { id, method, params };
|
|
1848
|
+
socket.write(`${JSON.stringify(request)}
|
|
1849
|
+
`);
|
|
1850
|
+
});
|
|
1851
|
+
socket.on("data", (chunk) => {
|
|
1852
|
+
buffer += chunk;
|
|
1853
|
+
const newlineIndex = buffer.indexOf("\n");
|
|
1854
|
+
if (newlineIndex < 0) {
|
|
1855
|
+
return;
|
|
1856
|
+
}
|
|
1857
|
+
const line = buffer.slice(0, newlineIndex);
|
|
1858
|
+
socket.end();
|
|
1859
|
+
try {
|
|
1860
|
+
const response = JSON.parse(line);
|
|
1861
|
+
if (response.ok) {
|
|
1862
|
+
resolve(response.result);
|
|
1863
|
+
} else {
|
|
1864
|
+
reject(new UserError(response.error.message));
|
|
1865
|
+
}
|
|
1866
|
+
} catch (error) {
|
|
1867
|
+
reject(error);
|
|
1868
|
+
}
|
|
1869
|
+
});
|
|
1870
|
+
socket.on("error", (error) => {
|
|
1871
|
+
if (options.start === false) {
|
|
1872
|
+
reject(error);
|
|
1873
|
+
return;
|
|
1874
|
+
}
|
|
1875
|
+
reject(new UserError(error.message));
|
|
1876
|
+
});
|
|
1877
|
+
});
|
|
1878
|
+
}
|
|
1879
|
+
function resolveSocketPath(env) {
|
|
1880
|
+
const runtimeDir = env[RUNTIME_DIR_ENV] ?? path2.join(os.tmpdir(), `terminal-pilot-${process.getuid?.() ?? "user"}`);
|
|
1881
|
+
if (process.platform === "win32") {
|
|
1882
|
+
const hash = createHash("sha256").update(runtimeDir).digest("hex").slice(0, 16);
|
|
1883
|
+
return `\\\\.\\pipe\\terminal-pilot-${hash}`;
|
|
1884
|
+
}
|
|
1885
|
+
return path2.join(runtimeDir, "daemon.sock");
|
|
1886
|
+
}
|
|
1887
|
+
function formatNamedSession(namedSession) {
|
|
1888
|
+
return {
|
|
1889
|
+
name: namedSession.name,
|
|
1890
|
+
session: formatSession(namedSession.session)
|
|
1891
|
+
};
|
|
1892
|
+
}
|
|
1893
|
+
function formatSession(session) {
|
|
1894
|
+
return {
|
|
1895
|
+
id: session.id,
|
|
1896
|
+
command: session.command,
|
|
1897
|
+
pid: session.pid,
|
|
1898
|
+
exitCode: session.exitCode
|
|
1899
|
+
};
|
|
1900
|
+
}
|
|
1901
|
+
function envFromValue(value) {
|
|
1902
|
+
const sessionName = optionalString(value);
|
|
1903
|
+
if (sessionName === void 0) {
|
|
1904
|
+
return void 0;
|
|
1905
|
+
}
|
|
1906
|
+
return {
|
|
1907
|
+
get(key) {
|
|
1908
|
+
return key === SESSION_ENV_VAR ? sessionName : void 0;
|
|
1909
|
+
}
|
|
1910
|
+
};
|
|
1911
|
+
}
|
|
1912
|
+
function serializePattern(pattern) {
|
|
1913
|
+
if (typeof pattern === "string") {
|
|
1914
|
+
return { kind: "literal", value: pattern };
|
|
1915
|
+
}
|
|
1916
|
+
return { kind: "regex", source: pattern.source, flags: pattern.flags };
|
|
1917
|
+
}
|
|
1918
|
+
function deserializePattern(value) {
|
|
1919
|
+
if (!isRecord(value)) {
|
|
1920
|
+
return String(value ?? "");
|
|
1921
|
+
}
|
|
1922
|
+
if (value.kind === "regex") {
|
|
1923
|
+
return new RegExp(String(value.source ?? ""), String(value.flags ?? ""));
|
|
1924
|
+
}
|
|
1925
|
+
return String(value.value ?? "");
|
|
1926
|
+
}
|
|
1927
|
+
function optionalRecord(value) {
|
|
1928
|
+
return isRecord(value) ? value : void 0;
|
|
1929
|
+
}
|
|
1930
|
+
function optionalString(value) {
|
|
1931
|
+
return typeof value === "string" ? value : void 0;
|
|
1932
|
+
}
|
|
1933
|
+
function isRecord(value) {
|
|
1934
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1935
|
+
}
|
|
1936
|
+
function sleep2(ms) {
|
|
1937
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1938
|
+
}
|
|
1939
|
+
export {
|
|
1940
|
+
createDaemonTerminalPilotRuntime,
|
|
1941
|
+
isTerminalPilotDaemonArgv,
|
|
1942
|
+
runTerminalPilotDaemon
|
|
1943
|
+
};
|
|
1944
|
+
//# sourceMappingURL=daemon-runtime.js.map
|