ptywright 0.4.0 → 0.6.0
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/README.md +38 -31
- package/dist/agent.mjs +2 -2
- package/dist/bin/ptywright.mjs +1 -1
- package/dist/{cli-C40H_ElC.mjs → cli-IXmvK56U.mjs} +2519 -2472
- package/dist/cli.mjs +1 -1
- package/dist/config.mjs +1 -1
- package/dist/data-sdbf3IDh.mjs +18 -0
- package/dist/env-DPYHo-zH.mjs +36 -0
- package/dist/index.mjs +1 -1
- package/dist/manifest_files-DW80c1H7.mjs +77 -0
- package/dist/mcp.mjs +1 -1
- package/dist/pty-cassette.mjs +2 -1
- package/dist/{pty_like-DqCo7XdB.mjs → pty_like-DWIlWGgA.mjs} +4 -20
- package/dist/{runner-zApMYWZx.mjs → runner-BHXXwxYp.mjs} +1578 -1423
- package/dist/{runner-CembqDgJ.mjs → runner-GIEf0C6d.mjs} +2434 -1134
- package/dist/script.mjs +1 -1
- package/dist/{server-h--2U0Ic.mjs → server-ceZ1-s_J.mjs} +2643 -2527
- package/dist/session.mjs +1 -1
- package/dist/style-BtIUv5H0.mjs +65 -0
- package/dist/{terminal_session-DopC7Xg6.mjs → terminal_session-MX_vWpRG.mjs} +322 -364
- package/package.json +2 -1
- package/dist/{config-B0r-JCFI.mjs → config-bGg636EW.mjs} +1 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { a as styleKey, i as isDefaultStyle, n as extractStyle, r as findMeaningfulEndCol, t as DEFAULT_STYLE } from "./style-BtIUv5H0.mjs";
|
|
1
2
|
import { Terminal } from "@xterm/headless";
|
|
2
3
|
//#region src/terminal/keys.ts
|
|
3
4
|
const CSI = "\x1B[";
|
|
@@ -143,159 +144,8 @@ function functionKeySpec(key) {
|
|
|
143
144
|
};
|
|
144
145
|
}
|
|
145
146
|
//#endregion
|
|
146
|
-
//#region src/terminal/style.ts
|
|
147
|
-
const DEFAULT_STYLE = {
|
|
148
|
-
fg: { mode: "default" },
|
|
149
|
-
bg: { mode: "default" },
|
|
150
|
-
bold: false,
|
|
151
|
-
dim: false,
|
|
152
|
-
italic: false,
|
|
153
|
-
underline: false,
|
|
154
|
-
inverse: false,
|
|
155
|
-
strikethrough: false
|
|
156
|
-
};
|
|
157
|
-
function extractStyle(cell) {
|
|
158
|
-
const style = {
|
|
159
|
-
fg: extractColor(cell.isFgDefault(), cell.isFgPalette(), cell.isFgRGB(), cell.getFgColor()),
|
|
160
|
-
bg: extractColor(cell.isBgDefault(), cell.isBgPalette(), cell.isBgRGB(), cell.getBgColor()),
|
|
161
|
-
bold: cell.isBold() !== 0,
|
|
162
|
-
dim: cell.isDim() !== 0,
|
|
163
|
-
italic: cell.isItalic() !== 0,
|
|
164
|
-
underline: cell.isUnderline() !== 0,
|
|
165
|
-
inverse: cell.isInverse() !== 0,
|
|
166
|
-
strikethrough: cell.isStrikethrough() !== 0
|
|
167
|
-
};
|
|
168
|
-
return isDefaultStyle(style) ? DEFAULT_STYLE : style;
|
|
169
|
-
}
|
|
170
|
-
function isDefaultStyle(style) {
|
|
171
|
-
return style.fg.mode === "default" && style.bg.mode === "default" && !style.bold && !style.dim && !style.italic && !style.underline && !style.inverse && !style.strikethrough;
|
|
172
|
-
}
|
|
173
|
-
function styleKey(style) {
|
|
174
|
-
return [
|
|
175
|
-
style.fg.mode === "default" ? "d" : `${style.fg.mode}:${style.fg.value}`,
|
|
176
|
-
style.bg.mode === "default" ? "d" : `${style.bg.mode}:${style.bg.value}`,
|
|
177
|
-
style.bold ? "b" : "",
|
|
178
|
-
style.dim ? "d" : "",
|
|
179
|
-
style.italic ? "i" : "",
|
|
180
|
-
style.underline ? "u" : "",
|
|
181
|
-
style.inverse ? "r" : "",
|
|
182
|
-
style.strikethrough ? "s" : ""
|
|
183
|
-
].join("|");
|
|
184
|
-
}
|
|
185
|
-
function findMeaningfulEndCol(line, cols, nullCell) {
|
|
186
|
-
if (!line) return 0;
|
|
187
|
-
for (let x = cols - 1; x >= 0; x -= 1) {
|
|
188
|
-
const cell = line.getCell(x, nullCell);
|
|
189
|
-
if (!cell) continue;
|
|
190
|
-
if (cell.getWidth() === 0) continue;
|
|
191
|
-
const chars = cell.getChars();
|
|
192
|
-
const style = extractStyle(cell);
|
|
193
|
-
if (chars !== "" && chars !== " " || !isDefaultStyle(style)) return x + 1;
|
|
194
|
-
}
|
|
195
|
-
return 0;
|
|
196
|
-
}
|
|
197
|
-
function extractColor(isDefault, isPalette, isRgb, value) {
|
|
198
|
-
if (isDefault) return { mode: "default" };
|
|
199
|
-
if (isRgb) return {
|
|
200
|
-
mode: "rgb",
|
|
201
|
-
value
|
|
202
|
-
};
|
|
203
|
-
if (isPalette) return {
|
|
204
|
-
mode: "palette",
|
|
205
|
-
value
|
|
206
|
-
};
|
|
207
|
-
return { mode: "default" };
|
|
208
|
-
}
|
|
209
|
-
//#endregion
|
|
210
|
-
//#region src/terminal/ansi.ts
|
|
211
|
-
const ESC$2 = "\x1B";
|
|
212
|
-
const SGR_RESET = `${ESC$2}[0m`;
|
|
213
|
-
function renderAnsiLines(terminal, options) {
|
|
214
|
-
const scope = options?.scope ?? "visible";
|
|
215
|
-
const trimRight = options?.trimRight ?? false;
|
|
216
|
-
const buffer = terminal.buffer.active;
|
|
217
|
-
const nullCell = buffer.getNullCell();
|
|
218
|
-
let startY = 0;
|
|
219
|
-
let count = buffer.length;
|
|
220
|
-
if (scope === "visible") {
|
|
221
|
-
startY = buffer.viewportY;
|
|
222
|
-
count = terminal.rows;
|
|
223
|
-
}
|
|
224
|
-
const out = [];
|
|
225
|
-
for (let i = 0; i < count; i += 1) {
|
|
226
|
-
const y = startY + i;
|
|
227
|
-
const line = buffer.getLine(y);
|
|
228
|
-
out.push(renderLine(line, terminal.cols, nullCell, trimRight));
|
|
229
|
-
}
|
|
230
|
-
return out;
|
|
231
|
-
}
|
|
232
|
-
function renderLine(line, cols, nullCell, trimRight) {
|
|
233
|
-
let endCol = cols;
|
|
234
|
-
if (trimRight) endCol = findMeaningfulEndCol(line, cols, nullCell);
|
|
235
|
-
let ansi = "";
|
|
236
|
-
let plain = "";
|
|
237
|
-
let hasStyle = false;
|
|
238
|
-
let usedSgr = false;
|
|
239
|
-
const defaultKey = styleKey(DEFAULT_STYLE);
|
|
240
|
-
let currentKey = defaultKey;
|
|
241
|
-
for (let x = 0; x < endCol; x += 1) {
|
|
242
|
-
const cell = line?.getCell(x, nullCell);
|
|
243
|
-
if (!cell) {
|
|
244
|
-
plain += " ";
|
|
245
|
-
ansi += " ";
|
|
246
|
-
continue;
|
|
247
|
-
}
|
|
248
|
-
if (cell.getWidth() === 0) continue;
|
|
249
|
-
const chars = cell.getChars() || " ";
|
|
250
|
-
const style = extractStyle(cell);
|
|
251
|
-
const isDefault = isDefaultStyle(style);
|
|
252
|
-
if (!isDefault) hasStyle = true;
|
|
253
|
-
const key = styleKey(style);
|
|
254
|
-
if (key !== currentKey) {
|
|
255
|
-
ansi += isDefault ? SGR_RESET : toSgr(style);
|
|
256
|
-
usedSgr = true;
|
|
257
|
-
currentKey = key;
|
|
258
|
-
}
|
|
259
|
-
ansi += chars;
|
|
260
|
-
plain += chars;
|
|
261
|
-
}
|
|
262
|
-
if (usedSgr && currentKey !== defaultKey) ansi += SGR_RESET;
|
|
263
|
-
return {
|
|
264
|
-
ansi,
|
|
265
|
-
plain,
|
|
266
|
-
hasStyle
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
|
-
function toSgr(style) {
|
|
270
|
-
const codes = ["0"];
|
|
271
|
-
if (style.bold) codes.push("1");
|
|
272
|
-
if (style.dim) codes.push("2");
|
|
273
|
-
if (style.italic) codes.push("3");
|
|
274
|
-
if (style.underline) codes.push("4");
|
|
275
|
-
if (style.inverse) codes.push("7");
|
|
276
|
-
if (style.strikethrough) codes.push("9");
|
|
277
|
-
if (style.fg.mode === "palette") codes.push(`38;5;${style.fg.value}`);
|
|
278
|
-
else if (style.fg.mode === "rgb") {
|
|
279
|
-
const [r, g, b] = rgb(style.fg.value);
|
|
280
|
-
codes.push(`38;2;${r};${g};${b}`);
|
|
281
|
-
}
|
|
282
|
-
if (style.bg.mode === "palette") codes.push(`48;5;${style.bg.value}`);
|
|
283
|
-
else if (style.bg.mode === "rgb") {
|
|
284
|
-
const [r, g, b] = rgb(style.bg.value);
|
|
285
|
-
codes.push(`48;2;${r};${g};${b}`);
|
|
286
|
-
}
|
|
287
|
-
return `${ESC$2}[${codes.join(";")}m`;
|
|
288
|
-
}
|
|
289
|
-
function rgb(value) {
|
|
290
|
-
return [
|
|
291
|
-
value >> 16 & 255,
|
|
292
|
-
value >> 8 & 255,
|
|
293
|
-
value & 255
|
|
294
|
-
];
|
|
295
|
-
}
|
|
296
|
-
//#endregion
|
|
297
147
|
//#region src/terminal/mouse.ts
|
|
298
|
-
const ESC$
|
|
148
|
+
const ESC$2 = "\x1B";
|
|
299
149
|
function encodeSgrMouse(event) {
|
|
300
150
|
const x = clampInt(event.x, 1, 500);
|
|
301
151
|
const y = clampInt(event.y, 1, 300);
|
|
@@ -312,11 +162,11 @@ function encodeSingleSgrMouse(event, x, y) {
|
|
|
312
162
|
const modifiers = event.modifiers;
|
|
313
163
|
const modifierBits = (modifiers?.shift ? 4 : 0) + (modifiers?.alt ? 8 : 0) + (modifiers?.ctrl ? 16 : 0);
|
|
314
164
|
const buttonCode = buttonToCode(event.button ?? "left");
|
|
315
|
-
if (event.action === "down") return `${ESC$
|
|
316
|
-
if (event.action === "up") return `${ESC$
|
|
317
|
-
if (event.action === "scroll_up") return `${ESC$
|
|
318
|
-
if (event.action === "scroll_down") return `${ESC$
|
|
319
|
-
return `${ESC$
|
|
165
|
+
if (event.action === "down") return `${ESC$2}[<${buttonCode + modifierBits};${x};${y}M`;
|
|
166
|
+
if (event.action === "up") return `${ESC$2}[<${buttonCode + modifierBits};${x};${y}m`;
|
|
167
|
+
if (event.action === "scroll_up") return `${ESC$2}[<${64 + modifierBits};${x};${y}M`;
|
|
168
|
+
if (event.action === "scroll_down") return `${ESC$2}[<${65 + modifierBits};${x};${y}M`;
|
|
169
|
+
return `${ESC$2}[<${32 + (event.button ? buttonCode : 3) + modifierBits};${x};${y}M`;
|
|
320
170
|
}
|
|
321
171
|
function buttonToCode(button) {
|
|
322
172
|
if (button === "left") return 0;
|
|
@@ -331,58 +181,7 @@ function clampInt(value, min, max) {
|
|
|
331
181
|
return int;
|
|
332
182
|
}
|
|
333
183
|
//#endregion
|
|
334
|
-
//#region src/terminal/
|
|
335
|
-
function snapshotVisibleLines(terminal, trimRight) {
|
|
336
|
-
const buffer = terminal.buffer.active;
|
|
337
|
-
const nullCell = buffer.getNullCell();
|
|
338
|
-
const startY = buffer.viewportY;
|
|
339
|
-
const lines = [];
|
|
340
|
-
for (let row = 0; row < terminal.rows; row += 1) {
|
|
341
|
-
const line = buffer.getLine(startY + row);
|
|
342
|
-
lines.push(snapshotPlainLine(line, terminal.cols, nullCell, trimRight));
|
|
343
|
-
}
|
|
344
|
-
return lines;
|
|
345
|
-
}
|
|
346
|
-
function snapshotVisibleStyleRuns(terminal, trimRight) {
|
|
347
|
-
const buffer = terminal.buffer.active;
|
|
348
|
-
const nullCell = buffer.getNullCell();
|
|
349
|
-
const startY = buffer.viewportY;
|
|
350
|
-
const out = [];
|
|
351
|
-
for (let row = 0; row < terminal.rows; row += 1) {
|
|
352
|
-
const line = buffer.getLine(startY + row);
|
|
353
|
-
out.push(snapshotStyleRuns(line, terminal.cols, nullCell, trimRight));
|
|
354
|
-
}
|
|
355
|
-
return out;
|
|
356
|
-
}
|
|
357
|
-
function snapshotBufferLines(terminal, trimRight) {
|
|
358
|
-
const buffer = terminal.buffer.active;
|
|
359
|
-
const lines = [];
|
|
360
|
-
for (let y = 0; y < buffer.length; y += 1) {
|
|
361
|
-
const line = buffer.getLine(y);
|
|
362
|
-
lines.push(line ? line.translateToString(trimRight, 0, terminal.cols) : "");
|
|
363
|
-
}
|
|
364
|
-
return lines;
|
|
365
|
-
}
|
|
366
|
-
function snapshotLines(terminal, options) {
|
|
367
|
-
const trimRight = options?.trimRight ?? true;
|
|
368
|
-
return (options?.scope ?? "visible") === "buffer" ? snapshotBufferLines(terminal, trimRight) : snapshotVisibleLines(terminal, trimRight);
|
|
369
|
-
}
|
|
370
|
-
function snapshotGrid(terminal, options) {
|
|
371
|
-
const trimRight = options?.trimRight ?? true;
|
|
372
|
-
const includeStyles = options?.includeStyles ?? false;
|
|
373
|
-
const buffer = terminal.buffer.active;
|
|
374
|
-
const lines = snapshotVisibleLines(terminal, trimRight);
|
|
375
|
-
return {
|
|
376
|
-
cols: terminal.cols,
|
|
377
|
-
rows: terminal.rows,
|
|
378
|
-
bufferType: buffer.type,
|
|
379
|
-
cursorX: buffer.cursorX,
|
|
380
|
-
cursorY: buffer.cursorY,
|
|
381
|
-
viewportY: buffer.viewportY,
|
|
382
|
-
lines,
|
|
383
|
-
styleRuns: includeStyles ? snapshotVisibleStyleRuns(terminal, trimRight) : void 0
|
|
384
|
-
};
|
|
385
|
-
}
|
|
184
|
+
//#region src/terminal/snapshot_rows.ts
|
|
386
185
|
function snapshotPlainLine(line, cols, nullCell, trimRight) {
|
|
387
186
|
let endCol = cols;
|
|
388
187
|
if (trimRight) endCol = findMeaningfulEndCol(line, cols, nullCell);
|
|
@@ -442,48 +241,57 @@ function snapshotStyleRuns(line, cols, nullCell, trimRight) {
|
|
|
442
241
|
return out;
|
|
443
242
|
}
|
|
444
243
|
//#endregion
|
|
445
|
-
//#region src/terminal/
|
|
446
|
-
function
|
|
447
|
-
|
|
448
|
-
const
|
|
449
|
-
|
|
450
|
-
|
|
244
|
+
//#region src/terminal/snapshot.ts
|
|
245
|
+
function snapshotVisibleLines(terminal, trimRight) {
|
|
246
|
+
const buffer = terminal.buffer.active;
|
|
247
|
+
const nullCell = buffer.getNullCell();
|
|
248
|
+
const startY = buffer.viewportY;
|
|
249
|
+
const lines = [];
|
|
250
|
+
for (let row = 0; row < terminal.rows; row += 1) {
|
|
251
|
+
const line = buffer.getLine(startY + row);
|
|
252
|
+
lines.push(snapshotPlainLine(line, terminal.cols, nullCell, trimRight));
|
|
253
|
+
}
|
|
254
|
+
return lines;
|
|
451
255
|
}
|
|
452
|
-
function
|
|
453
|
-
const
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
const
|
|
459
|
-
|
|
460
|
-
try {
|
|
461
|
-
regex = new RegExp(rule.regex, flags);
|
|
462
|
-
} catch (error) {
|
|
463
|
-
throw new Error(`invalid mask rule regex=${JSON.stringify(rule.regex)} flags=${JSON.stringify(rule.flags ?? "")}: ${error.message}`);
|
|
464
|
-
}
|
|
465
|
-
compiled.push({
|
|
466
|
-
regex,
|
|
467
|
-
replacement,
|
|
468
|
-
preserveLength
|
|
469
|
-
});
|
|
256
|
+
function snapshotVisibleStyleRuns(terminal, trimRight) {
|
|
257
|
+
const buffer = terminal.buffer.active;
|
|
258
|
+
const nullCell = buffer.getNullCell();
|
|
259
|
+
const startY = buffer.viewportY;
|
|
260
|
+
const out = [];
|
|
261
|
+
for (let row = 0; row < terminal.rows; row += 1) {
|
|
262
|
+
const line = buffer.getLine(startY + row);
|
|
263
|
+
out.push(snapshotStyleRuns(line, terminal.cols, nullCell, trimRight));
|
|
470
264
|
}
|
|
471
|
-
return
|
|
265
|
+
return out;
|
|
472
266
|
}
|
|
473
|
-
function
|
|
474
|
-
const
|
|
475
|
-
const
|
|
476
|
-
|
|
477
|
-
|
|
267
|
+
function snapshotBufferLines(terminal, trimRight) {
|
|
268
|
+
const buffer = terminal.buffer.active;
|
|
269
|
+
const lines = [];
|
|
270
|
+
for (let y = 0; y < buffer.length; y += 1) {
|
|
271
|
+
const line = buffer.getLine(y);
|
|
272
|
+
lines.push(line ? line.translateToString(trimRight, 0, terminal.cols) : "");
|
|
273
|
+
}
|
|
274
|
+
return lines;
|
|
478
275
|
}
|
|
479
|
-
function
|
|
480
|
-
const
|
|
481
|
-
return
|
|
276
|
+
function snapshotLines(terminal, options) {
|
|
277
|
+
const trimRight = options?.trimRight ?? true;
|
|
278
|
+
return (options?.scope ?? "visible") === "buffer" ? snapshotBufferLines(terminal, trimRight) : snapshotVisibleLines(terminal, trimRight);
|
|
482
279
|
}
|
|
483
|
-
function
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
280
|
+
function snapshotGrid(terminal, options) {
|
|
281
|
+
const trimRight = options?.trimRight ?? true;
|
|
282
|
+
const includeStyles = options?.includeStyles ?? false;
|
|
283
|
+
const buffer = terminal.buffer.active;
|
|
284
|
+
const lines = snapshotVisibleLines(terminal, trimRight);
|
|
285
|
+
return {
|
|
286
|
+
cols: terminal.cols,
|
|
287
|
+
rows: terminal.rows,
|
|
288
|
+
bufferType: buffer.type,
|
|
289
|
+
cursorX: buffer.cursorX,
|
|
290
|
+
cursorY: buffer.cursorY,
|
|
291
|
+
viewportY: buffer.viewportY,
|
|
292
|
+
lines,
|
|
293
|
+
styleRuns: includeStyles ? snapshotVisibleStyleRuns(terminal, trimRight) : void 0
|
|
294
|
+
};
|
|
487
295
|
}
|
|
488
296
|
//#endregion
|
|
489
297
|
//#region src/util/hash.ts
|
|
@@ -496,13 +304,6 @@ function fnv1a32(text) {
|
|
|
496
304
|
return (hash >>> 0).toString(16).padStart(8, "0");
|
|
497
305
|
}
|
|
498
306
|
//#endregion
|
|
499
|
-
//#region src/util/sleep.ts
|
|
500
|
-
async function sleep(ms) {
|
|
501
|
-
await new Promise((resolve) => {
|
|
502
|
-
setTimeout(resolve, ms);
|
|
503
|
-
});
|
|
504
|
-
}
|
|
505
|
-
//#endregion
|
|
506
307
|
//#region src/trace/asciicast.ts
|
|
507
308
|
function encodeAsciicast(header, events) {
|
|
508
309
|
const lines = [JSON.stringify(header)];
|
|
@@ -589,6 +390,269 @@ var TraceRecorder = class {
|
|
|
589
390
|
}
|
|
590
391
|
};
|
|
591
392
|
//#endregion
|
|
393
|
+
//#region src/terminal/ansi.ts
|
|
394
|
+
const ESC$1 = "\x1B";
|
|
395
|
+
const SGR_RESET = `${ESC$1}[0m`;
|
|
396
|
+
function renderAnsiLines(terminal, options) {
|
|
397
|
+
const scope = options?.scope ?? "visible";
|
|
398
|
+
const trimRight = options?.trimRight ?? false;
|
|
399
|
+
const buffer = terminal.buffer.active;
|
|
400
|
+
const nullCell = buffer.getNullCell();
|
|
401
|
+
let startY = 0;
|
|
402
|
+
let count = buffer.length;
|
|
403
|
+
if (scope === "visible") {
|
|
404
|
+
startY = buffer.viewportY;
|
|
405
|
+
count = terminal.rows;
|
|
406
|
+
}
|
|
407
|
+
const out = [];
|
|
408
|
+
for (let i = 0; i < count; i += 1) {
|
|
409
|
+
const y = startY + i;
|
|
410
|
+
const line = buffer.getLine(y);
|
|
411
|
+
out.push(renderLine(line, terminal.cols, nullCell, trimRight));
|
|
412
|
+
}
|
|
413
|
+
return out;
|
|
414
|
+
}
|
|
415
|
+
function renderLine(line, cols, nullCell, trimRight) {
|
|
416
|
+
let endCol = cols;
|
|
417
|
+
if (trimRight) endCol = findMeaningfulEndCol(line, cols, nullCell);
|
|
418
|
+
let ansi = "";
|
|
419
|
+
let plain = "";
|
|
420
|
+
let hasStyle = false;
|
|
421
|
+
let usedSgr = false;
|
|
422
|
+
const defaultKey = styleKey(DEFAULT_STYLE);
|
|
423
|
+
let currentKey = defaultKey;
|
|
424
|
+
for (let x = 0; x < endCol; x += 1) {
|
|
425
|
+
const cell = line?.getCell(x, nullCell);
|
|
426
|
+
if (!cell) {
|
|
427
|
+
plain += " ";
|
|
428
|
+
ansi += " ";
|
|
429
|
+
continue;
|
|
430
|
+
}
|
|
431
|
+
if (cell.getWidth() === 0) continue;
|
|
432
|
+
const chars = cell.getChars() || " ";
|
|
433
|
+
const style = extractStyle(cell);
|
|
434
|
+
const isDefault = isDefaultStyle(style);
|
|
435
|
+
if (!isDefault) hasStyle = true;
|
|
436
|
+
const key = styleKey(style);
|
|
437
|
+
if (key !== currentKey) {
|
|
438
|
+
ansi += isDefault ? SGR_RESET : toSgr(style);
|
|
439
|
+
usedSgr = true;
|
|
440
|
+
currentKey = key;
|
|
441
|
+
}
|
|
442
|
+
ansi += chars;
|
|
443
|
+
plain += chars;
|
|
444
|
+
}
|
|
445
|
+
if (usedSgr && currentKey !== defaultKey) ansi += SGR_RESET;
|
|
446
|
+
return {
|
|
447
|
+
ansi,
|
|
448
|
+
plain,
|
|
449
|
+
hasStyle
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
function toSgr(style) {
|
|
453
|
+
const codes = ["0"];
|
|
454
|
+
if (style.bold) codes.push("1");
|
|
455
|
+
if (style.dim) codes.push("2");
|
|
456
|
+
if (style.italic) codes.push("3");
|
|
457
|
+
if (style.underline) codes.push("4");
|
|
458
|
+
if (style.inverse) codes.push("7");
|
|
459
|
+
if (style.strikethrough) codes.push("9");
|
|
460
|
+
if (style.fg.mode === "palette") codes.push(`38;5;${style.fg.value}`);
|
|
461
|
+
else if (style.fg.mode === "rgb") {
|
|
462
|
+
const [r, g, b] = rgb(style.fg.value);
|
|
463
|
+
codes.push(`38;2;${r};${g};${b}`);
|
|
464
|
+
}
|
|
465
|
+
if (style.bg.mode === "palette") codes.push(`48;5;${style.bg.value}`);
|
|
466
|
+
else if (style.bg.mode === "rgb") {
|
|
467
|
+
const [r, g, b] = rgb(style.bg.value);
|
|
468
|
+
codes.push(`48;2;${r};${g};${b}`);
|
|
469
|
+
}
|
|
470
|
+
return `${ESC$1}[${codes.join(";")}m`;
|
|
471
|
+
}
|
|
472
|
+
function rgb(value) {
|
|
473
|
+
return [
|
|
474
|
+
value >> 16 & 255,
|
|
475
|
+
value >> 8 & 255,
|
|
476
|
+
value & 255
|
|
477
|
+
];
|
|
478
|
+
}
|
|
479
|
+
//#endregion
|
|
480
|
+
//#region src/terminal/mask.ts
|
|
481
|
+
function applyTextMaskRules(lines, rules) {
|
|
482
|
+
if (!rules || rules.length === 0) return lines;
|
|
483
|
+
const compiled = compileMaskRules(rules);
|
|
484
|
+
if (compiled.length === 0) return lines;
|
|
485
|
+
return lines.map((line) => applyCompiledMaskRules(line, compiled));
|
|
486
|
+
}
|
|
487
|
+
function compileMaskRules(rules) {
|
|
488
|
+
const compiled = [];
|
|
489
|
+
for (const rule of rules) {
|
|
490
|
+
if (!rule.regex.trim()) continue;
|
|
491
|
+
const preserveLength = rule.preserveLength ?? false;
|
|
492
|
+
const replacement = preserveLength ? firstChar(rule.replacement) : rule.replacement ?? "<masked>";
|
|
493
|
+
const flags = normalizeFlags(rule.flags);
|
|
494
|
+
let regex;
|
|
495
|
+
try {
|
|
496
|
+
regex = new RegExp(rule.regex, flags);
|
|
497
|
+
} catch (error) {
|
|
498
|
+
throw new Error(`invalid mask rule regex=${JSON.stringify(rule.regex)} flags=${JSON.stringify(rule.flags ?? "")}: ${error.message}`);
|
|
499
|
+
}
|
|
500
|
+
compiled.push({
|
|
501
|
+
regex,
|
|
502
|
+
replacement,
|
|
503
|
+
preserveLength
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
return compiled;
|
|
507
|
+
}
|
|
508
|
+
function normalizeFlags(flags) {
|
|
509
|
+
const value = flags?.trim() ? flags.trim() : "g";
|
|
510
|
+
const set = new Set(value.split(""));
|
|
511
|
+
set.add("g");
|
|
512
|
+
return [...set].join("");
|
|
513
|
+
}
|
|
514
|
+
function firstChar(value) {
|
|
515
|
+
const trimmed = value?.trim();
|
|
516
|
+
return trimmed && trimmed.length > 0 ? trimmed[0] : "█";
|
|
517
|
+
}
|
|
518
|
+
function applyCompiledMaskRules(line, rules) {
|
|
519
|
+
let out = line;
|
|
520
|
+
for (const rule of rules) out = out.replace(rule.regex, (match) => rule.preserveLength ? rule.replacement.repeat(match.length) : rule.replacement);
|
|
521
|
+
return out;
|
|
522
|
+
}
|
|
523
|
+
//#endregion
|
|
524
|
+
//#region src/session/terminal_session_snapshot.ts
|
|
525
|
+
function snapshotSessionText(terminal, options) {
|
|
526
|
+
if (options?.maxLines !== void 0 && options.tailLines !== void 0) throw new Error("snapshotText: maxLines and tailLines are mutually exclusive");
|
|
527
|
+
let lines = snapshotLines(terminal, {
|
|
528
|
+
scope: options?.scope,
|
|
529
|
+
trimRight: options?.trimRight
|
|
530
|
+
});
|
|
531
|
+
if (options?.trimBottom ?? true) lines = trimBottomEmptyLines(lines);
|
|
532
|
+
if (options?.maxLines !== void 0) {
|
|
533
|
+
const max = Math.max(0, Math.trunc(options.maxLines));
|
|
534
|
+
lines = lines.slice(0, max);
|
|
535
|
+
}
|
|
536
|
+
if (options?.tailLines !== void 0) {
|
|
537
|
+
const tail = Math.max(0, Math.trunc(options.tailLines));
|
|
538
|
+
lines = lines.slice(Math.max(0, lines.length - tail));
|
|
539
|
+
}
|
|
540
|
+
lines = applyTextMaskRules(lines, options?.mask);
|
|
541
|
+
const text = lines.join("\n");
|
|
542
|
+
return {
|
|
543
|
+
text,
|
|
544
|
+
hash: fnv1a32(text)
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
function snapshotSessionAnsi(terminal, options) {
|
|
548
|
+
if (options?.maxLines !== void 0 && options.tailLines !== void 0) throw new Error("snapshotAnsi: maxLines and tailLines are mutually exclusive");
|
|
549
|
+
let lines = renderAnsiLines(terminal, {
|
|
550
|
+
scope: options?.scope,
|
|
551
|
+
trimRight: options?.trimRight
|
|
552
|
+
});
|
|
553
|
+
if (options?.trimBottom ?? true) lines = trimBottomEmptyAnsiLines(lines);
|
|
554
|
+
if (options?.maxLines !== void 0) {
|
|
555
|
+
const max = Math.max(0, Math.trunc(options.maxLines));
|
|
556
|
+
lines = lines.slice(0, max);
|
|
557
|
+
}
|
|
558
|
+
if (options?.tailLines !== void 0) {
|
|
559
|
+
const tail = Math.max(0, Math.trunc(options.tailLines));
|
|
560
|
+
lines = lines.slice(Math.max(0, lines.length - tail));
|
|
561
|
+
}
|
|
562
|
+
if (options?.mask && options.mask.length > 0) {
|
|
563
|
+
const maskedPlain = applyTextMaskRules(lines.map((line) => line.plain), options.mask);
|
|
564
|
+
const maskedAnsi = applyTextMaskRules(lines.map((line) => line.ansi), options.mask);
|
|
565
|
+
lines = lines.map((line, idx) => ({
|
|
566
|
+
...line,
|
|
567
|
+
plain: maskedPlain[idx] ?? "",
|
|
568
|
+
ansi: maskedAnsi[idx] ?? ""
|
|
569
|
+
}));
|
|
570
|
+
}
|
|
571
|
+
const ansi = lines.map((l) => l.ansi).join("\n");
|
|
572
|
+
return {
|
|
573
|
+
ansi,
|
|
574
|
+
plain: lines.map((l) => l.plain).join("\n"),
|
|
575
|
+
hash: fnv1a32(ansi),
|
|
576
|
+
lines
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
function trimBottomEmptyLines(lines) {
|
|
580
|
+
let end = lines.length;
|
|
581
|
+
while (end > 0 && lines[end - 1] === "") end -= 1;
|
|
582
|
+
return end === lines.length ? lines : lines.slice(0, end);
|
|
583
|
+
}
|
|
584
|
+
function trimBottomEmptyAnsiLines(lines) {
|
|
585
|
+
let end = lines.length;
|
|
586
|
+
while (end > 0) {
|
|
587
|
+
const line = lines[end - 1];
|
|
588
|
+
if (!(!line?.hasStyle && (line?.plain ?? "").trim() === "")) break;
|
|
589
|
+
end -= 1;
|
|
590
|
+
}
|
|
591
|
+
return end === lines.length ? lines : lines.slice(0, end);
|
|
592
|
+
}
|
|
593
|
+
//#endregion
|
|
594
|
+
//#region src/util/sleep.ts
|
|
595
|
+
async function sleep(ms) {
|
|
596
|
+
await new Promise((resolve) => {
|
|
597
|
+
setTimeout(resolve, ms);
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
//#endregion
|
|
601
|
+
//#region src/session/terminal_session_wait.ts
|
|
602
|
+
async function waitForSessionText(host, args) {
|
|
603
|
+
const startedAt = Date.now();
|
|
604
|
+
let closedSince = null;
|
|
605
|
+
while (Date.now() - startedAt <= args.timeoutMs) {
|
|
606
|
+
const snapshot = await host.snapshotText({
|
|
607
|
+
captureFrame: true,
|
|
608
|
+
scope: args.scope
|
|
609
|
+
});
|
|
610
|
+
if (args.text && snapshot.text.includes(args.text)) return {
|
|
611
|
+
found: true,
|
|
612
|
+
...snapshot
|
|
613
|
+
};
|
|
614
|
+
if (args.regex && args.regex.test(snapshot.text)) return {
|
|
615
|
+
found: true,
|
|
616
|
+
...snapshot
|
|
617
|
+
};
|
|
618
|
+
if (host.isClosed()) {
|
|
619
|
+
closedSince ??= Date.now();
|
|
620
|
+
const drainMs = Math.max(500, args.intervalMs * 4);
|
|
621
|
+
if (Date.now() - closedSince >= drainMs) break;
|
|
622
|
+
}
|
|
623
|
+
await sleep(args.intervalMs);
|
|
624
|
+
}
|
|
625
|
+
return {
|
|
626
|
+
found: false,
|
|
627
|
+
...await host.snapshotText({
|
|
628
|
+
captureFrame: true,
|
|
629
|
+
scope: args.scope
|
|
630
|
+
})
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
async function waitForStableSessionScreen(host, args) {
|
|
634
|
+
const startedAt = Date.now();
|
|
635
|
+
let stableSince = null;
|
|
636
|
+
let lastHash = null;
|
|
637
|
+
while (Date.now() - startedAt <= args.timeoutMs) {
|
|
638
|
+
const snapshot = await host.snapshotText({ captureFrame: true });
|
|
639
|
+
if (snapshot.hash === lastHash) stableSince ??= Date.now();
|
|
640
|
+
else {
|
|
641
|
+
stableSince = null;
|
|
642
|
+
lastHash = snapshot.hash;
|
|
643
|
+
}
|
|
644
|
+
if (stableSince !== null && Date.now() - stableSince >= args.quietMs) return {
|
|
645
|
+
stable: true,
|
|
646
|
+
...snapshot
|
|
647
|
+
};
|
|
648
|
+
await sleep(args.intervalMs);
|
|
649
|
+
}
|
|
650
|
+
return {
|
|
651
|
+
stable: false,
|
|
652
|
+
...await host.snapshotText({ captureFrame: true })
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
//#endregion
|
|
592
656
|
//#region src/session/terminal_session.ts
|
|
593
657
|
const ESC = "\x1B";
|
|
594
658
|
var TerminalSession = class {
|
|
@@ -688,23 +752,7 @@ var TerminalSession = class {
|
|
|
688
752
|
}
|
|
689
753
|
async snapshotText(options) {
|
|
690
754
|
await this.flush();
|
|
691
|
-
|
|
692
|
-
let lines = snapshotLines(this.terminal, {
|
|
693
|
-
scope: options?.scope,
|
|
694
|
-
trimRight: options?.trimRight
|
|
695
|
-
});
|
|
696
|
-
if (options?.trimBottom ?? true) lines = trimBottomEmptyLines(lines);
|
|
697
|
-
if (options?.maxLines !== void 0) {
|
|
698
|
-
const max = Math.max(0, Math.trunc(options.maxLines));
|
|
699
|
-
lines = lines.slice(0, max);
|
|
700
|
-
}
|
|
701
|
-
if (options?.tailLines !== void 0) {
|
|
702
|
-
const tail = Math.max(0, Math.trunc(options.tailLines));
|
|
703
|
-
lines = lines.slice(Math.max(0, lines.length - tail));
|
|
704
|
-
}
|
|
705
|
-
lines = applyTextMaskRules(lines, options?.mask);
|
|
706
|
-
const text = lines.join("\n");
|
|
707
|
-
const hash = fnv1a32(text);
|
|
755
|
+
const { text, hash } = snapshotSessionText(this.terminal, options);
|
|
708
756
|
if (options?.captureFrame ?? true) this.captureFrame(text, hash);
|
|
709
757
|
return {
|
|
710
758
|
text,
|
|
@@ -713,36 +761,7 @@ var TerminalSession = class {
|
|
|
713
761
|
}
|
|
714
762
|
async snapshotAnsi(options) {
|
|
715
763
|
await this.flush();
|
|
716
|
-
|
|
717
|
-
let lines = renderAnsiLines(this.terminal, {
|
|
718
|
-
scope: options?.scope,
|
|
719
|
-
trimRight: options?.trimRight
|
|
720
|
-
});
|
|
721
|
-
if (options?.trimBottom ?? true) lines = trimBottomEmptyAnsiLines(lines);
|
|
722
|
-
if (options?.maxLines !== void 0) {
|
|
723
|
-
const max = Math.max(0, Math.trunc(options.maxLines));
|
|
724
|
-
lines = lines.slice(0, max);
|
|
725
|
-
}
|
|
726
|
-
if (options?.tailLines !== void 0) {
|
|
727
|
-
const tail = Math.max(0, Math.trunc(options.tailLines));
|
|
728
|
-
lines = lines.slice(Math.max(0, lines.length - tail));
|
|
729
|
-
}
|
|
730
|
-
if (options?.mask && options.mask.length > 0) {
|
|
731
|
-
const maskedPlain = applyTextMaskRules(lines.map((line) => line.plain), options.mask);
|
|
732
|
-
const maskedAnsi = applyTextMaskRules(lines.map((line) => line.ansi), options.mask);
|
|
733
|
-
lines = lines.map((line, idx) => ({
|
|
734
|
-
...line,
|
|
735
|
-
plain: maskedPlain[idx] ?? "",
|
|
736
|
-
ansi: maskedAnsi[idx] ?? ""
|
|
737
|
-
}));
|
|
738
|
-
}
|
|
739
|
-
const ansi = lines.map((l) => l.ansi).join("\n");
|
|
740
|
-
return {
|
|
741
|
-
ansi,
|
|
742
|
-
plain: lines.map((l) => l.plain).join("\n"),
|
|
743
|
-
hash: fnv1a32(ansi),
|
|
744
|
-
lines
|
|
745
|
-
};
|
|
764
|
+
return snapshotSessionAnsi(this.terminal, options);
|
|
746
765
|
}
|
|
747
766
|
async snapshotGrid(options) {
|
|
748
767
|
await this.flush();
|
|
@@ -771,57 +790,10 @@ var TerminalSession = class {
|
|
|
771
790
|
return [...this.rawOutputRing];
|
|
772
791
|
}
|
|
773
792
|
async waitForText(args) {
|
|
774
|
-
|
|
775
|
-
let closedSince = null;
|
|
776
|
-
while (Date.now() - startedAt <= args.timeoutMs) {
|
|
777
|
-
const snapshot = await this.snapshotText({
|
|
778
|
-
captureFrame: true,
|
|
779
|
-
scope: args.scope
|
|
780
|
-
});
|
|
781
|
-
if (args.text && snapshot.text.includes(args.text)) return {
|
|
782
|
-
found: true,
|
|
783
|
-
...snapshot
|
|
784
|
-
};
|
|
785
|
-
if (args.regex && args.regex.test(snapshot.text)) return {
|
|
786
|
-
found: true,
|
|
787
|
-
...snapshot
|
|
788
|
-
};
|
|
789
|
-
if (this.isClosed()) {
|
|
790
|
-
closedSince ??= Date.now();
|
|
791
|
-
const drainMs = Math.max(500, args.intervalMs * 4);
|
|
792
|
-
if (Date.now() - closedSince >= drainMs) break;
|
|
793
|
-
}
|
|
794
|
-
await sleep(args.intervalMs);
|
|
795
|
-
}
|
|
796
|
-
return {
|
|
797
|
-
found: false,
|
|
798
|
-
...await this.snapshotText({
|
|
799
|
-
captureFrame: true,
|
|
800
|
-
scope: args.scope
|
|
801
|
-
})
|
|
802
|
-
};
|
|
793
|
+
return waitForSessionText(this, args);
|
|
803
794
|
}
|
|
804
795
|
async waitForStableScreen(args) {
|
|
805
|
-
|
|
806
|
-
let stableSince = null;
|
|
807
|
-
let lastHash = null;
|
|
808
|
-
while (Date.now() - startedAt <= args.timeoutMs) {
|
|
809
|
-
const snapshot = await this.snapshotText({ captureFrame: true });
|
|
810
|
-
if (snapshot.hash === lastHash) stableSince ??= Date.now();
|
|
811
|
-
else {
|
|
812
|
-
stableSince = null;
|
|
813
|
-
lastHash = snapshot.hash;
|
|
814
|
-
}
|
|
815
|
-
if (stableSince !== null && Date.now() - stableSince >= args.quietMs) return {
|
|
816
|
-
stable: true,
|
|
817
|
-
...snapshot
|
|
818
|
-
};
|
|
819
|
-
await sleep(args.intervalMs);
|
|
820
|
-
}
|
|
821
|
-
return {
|
|
822
|
-
stable: false,
|
|
823
|
-
...await this.snapshotText({ captureFrame: true })
|
|
824
|
-
};
|
|
796
|
+
return waitForStableSessionScreen(this, args);
|
|
825
797
|
}
|
|
826
798
|
close() {
|
|
827
799
|
if (this.closed === null) this.closed = { type: "closed_by_user" };
|
|
@@ -875,19 +847,5 @@ var TerminalSession = class {
|
|
|
875
847
|
return true;
|
|
876
848
|
}
|
|
877
849
|
};
|
|
878
|
-
function trimBottomEmptyLines(lines) {
|
|
879
|
-
let end = lines.length;
|
|
880
|
-
while (end > 0 && lines[end - 1] === "") end -= 1;
|
|
881
|
-
return end === lines.length ? lines : lines.slice(0, end);
|
|
882
|
-
}
|
|
883
|
-
function trimBottomEmptyAnsiLines(lines) {
|
|
884
|
-
let end = lines.length;
|
|
885
|
-
while (end > 0) {
|
|
886
|
-
const line = lines[end - 1];
|
|
887
|
-
if (!(!line?.hasStyle && (line?.plain ?? "").trim() === "")) break;
|
|
888
|
-
end -= 1;
|
|
889
|
-
}
|
|
890
|
-
return end === lines.length ? lines : lines.slice(0, end);
|
|
891
|
-
}
|
|
892
850
|
//#endregion
|
|
893
|
-
export {
|
|
851
|
+
export { fnv1a32 as a, encodeSgrMouse as c, TraceRecorder as i, encodeKey as l, sleep as n, snapshotGrid as o, applyTextMaskRules as r, snapshotLines as s, TerminalSession as t };
|