ptywright 0.4.0 → 0.5.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-PnG6UR43.mjs} +2519 -2472
- package/dist/cli.mjs +1 -1
- package/dist/config.mjs +1 -1
- 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 +1 -1
- package/dist/{runner-CembqDgJ.mjs → runner-C1gPRyCM.mjs} +2042 -1127
- package/dist/{runner-zApMYWZx.mjs → runner-wW_DCBX7.mjs} +1576 -1422
- package/dist/script.mjs +1 -1
- package/dist/{server-h--2U0Ic.mjs → server-DMnnXjWv.mjs} +2643 -2527
- package/dist/session.mjs +1 -1
- package/dist/{terminal_session-DopC7Xg6.mjs → terminal_session-DJKr-O3X.mjs} +349 -328
- package/package.json +2 -1
- package/dist/{config-B0r-JCFI.mjs → config-bGg636EW.mjs} +1 -1
- package/dist/{pty_like-DqCo7XdB.mjs → pty_like-BjeBibSL.mjs} +2 -2
|
@@ -143,6 +143,43 @@ function functionKeySpec(key) {
|
|
|
143
143
|
};
|
|
144
144
|
}
|
|
145
145
|
//#endregion
|
|
146
|
+
//#region src/terminal/mouse.ts
|
|
147
|
+
const ESC$2 = "\x1B";
|
|
148
|
+
function encodeSgrMouse(event) {
|
|
149
|
+
const x = clampInt(event.x, 1, 500);
|
|
150
|
+
const y = clampInt(event.y, 1, 300);
|
|
151
|
+
if (event.action === "click") return `${encodeSingleSgrMouse({
|
|
152
|
+
...event,
|
|
153
|
+
action: "down"
|
|
154
|
+
}, x, y)}${encodeSingleSgrMouse({
|
|
155
|
+
...event,
|
|
156
|
+
action: "up"
|
|
157
|
+
}, x, y)}`;
|
|
158
|
+
return encodeSingleSgrMouse(event, x, y);
|
|
159
|
+
}
|
|
160
|
+
function encodeSingleSgrMouse(event, x, y) {
|
|
161
|
+
const modifiers = event.modifiers;
|
|
162
|
+
const modifierBits = (modifiers?.shift ? 4 : 0) + (modifiers?.alt ? 8 : 0) + (modifiers?.ctrl ? 16 : 0);
|
|
163
|
+
const buttonCode = buttonToCode(event.button ?? "left");
|
|
164
|
+
if (event.action === "down") return `${ESC$2}[<${buttonCode + modifierBits};${x};${y}M`;
|
|
165
|
+
if (event.action === "up") return `${ESC$2}[<${buttonCode + modifierBits};${x};${y}m`;
|
|
166
|
+
if (event.action === "scroll_up") return `${ESC$2}[<${64 + modifierBits};${x};${y}M`;
|
|
167
|
+
if (event.action === "scroll_down") return `${ESC$2}[<${65 + modifierBits};${x};${y}M`;
|
|
168
|
+
return `${ESC$2}[<${32 + (event.button ? buttonCode : 3) + modifierBits};${x};${y}M`;
|
|
169
|
+
}
|
|
170
|
+
function buttonToCode(button) {
|
|
171
|
+
if (button === "left") return 0;
|
|
172
|
+
if (button === "middle") return 1;
|
|
173
|
+
return 2;
|
|
174
|
+
}
|
|
175
|
+
function clampInt(value, min, max) {
|
|
176
|
+
if (!Number.isFinite(value)) return min;
|
|
177
|
+
const int = Math.trunc(value);
|
|
178
|
+
if (int < min) return min;
|
|
179
|
+
if (int > max) return max;
|
|
180
|
+
return int;
|
|
181
|
+
}
|
|
182
|
+
//#endregion
|
|
146
183
|
//#region src/terminal/style.ts
|
|
147
184
|
const DEFAULT_STYLE = {
|
|
148
185
|
fg: { mode: "default" },
|
|
@@ -207,128 +244,64 @@ function extractColor(isDefault, isPalette, isRgb, value) {
|
|
|
207
244
|
return { mode: "default" };
|
|
208
245
|
}
|
|
209
246
|
//#endregion
|
|
210
|
-
//#region src/terminal/
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
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));
|
|
247
|
+
//#region src/terminal/snapshot_rows.ts
|
|
248
|
+
function snapshotPlainLine(line, cols, nullCell, trimRight) {
|
|
249
|
+
let endCol = cols;
|
|
250
|
+
if (trimRight) endCol = findMeaningfulEndCol(line, cols, nullCell);
|
|
251
|
+
let out = "";
|
|
252
|
+
for (let x = 0; x < endCol; x += 1) {
|
|
253
|
+
const cell = line?.getCell(x, nullCell);
|
|
254
|
+
if (!cell) {
|
|
255
|
+
out += " ";
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
if (cell.getWidth() === 0) continue;
|
|
259
|
+
out += cell.getChars() || " ";
|
|
229
260
|
}
|
|
230
261
|
return out;
|
|
231
262
|
}
|
|
232
|
-
function
|
|
263
|
+
function snapshotStyleRuns(line, cols, nullCell, trimRight) {
|
|
233
264
|
let endCol = cols;
|
|
234
265
|
if (trimRight) endCol = findMeaningfulEndCol(line, cols, nullCell);
|
|
235
|
-
|
|
236
|
-
let
|
|
237
|
-
let
|
|
238
|
-
let usedSgr = false;
|
|
239
|
-
const defaultKey = styleKey(DEFAULT_STYLE);
|
|
240
|
-
let currentKey = defaultKey;
|
|
266
|
+
const out = [];
|
|
267
|
+
let currentKey = null;
|
|
268
|
+
let currentRun = null;
|
|
241
269
|
for (let x = 0; x < endCol; x += 1) {
|
|
242
270
|
const cell = line?.getCell(x, nullCell);
|
|
243
271
|
if (!cell) {
|
|
244
|
-
|
|
245
|
-
|
|
272
|
+
if (currentRun) {
|
|
273
|
+
out.push(currentRun);
|
|
274
|
+
currentRun = null;
|
|
275
|
+
currentKey = null;
|
|
276
|
+
}
|
|
246
277
|
continue;
|
|
247
278
|
}
|
|
248
|
-
|
|
249
|
-
|
|
279
|
+
const width = cell.getWidth();
|
|
280
|
+
if (width === 0) continue;
|
|
250
281
|
const style = extractStyle(cell);
|
|
251
|
-
|
|
252
|
-
|
|
282
|
+
if (isDefaultStyle(style)) {
|
|
283
|
+
if (currentRun) {
|
|
284
|
+
out.push(currentRun);
|
|
285
|
+
currentRun = null;
|
|
286
|
+
currentKey = null;
|
|
287
|
+
}
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
253
290
|
const key = styleKey(style);
|
|
254
|
-
if (key
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
currentKey = key;
|
|
291
|
+
if (currentRun && key === currentKey) {
|
|
292
|
+
currentRun.endCol = x + width;
|
|
293
|
+
continue;
|
|
258
294
|
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
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}`);
|
|
295
|
+
if (currentRun) out.push(currentRun);
|
|
296
|
+
currentKey = key;
|
|
297
|
+
currentRun = {
|
|
298
|
+
startCol: x,
|
|
299
|
+
endCol: x + width,
|
|
300
|
+
style
|
|
301
|
+
};
|
|
286
302
|
}
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
function rgb(value) {
|
|
290
|
-
return [
|
|
291
|
-
value >> 16 & 255,
|
|
292
|
-
value >> 8 & 255,
|
|
293
|
-
value & 255
|
|
294
|
-
];
|
|
295
|
-
}
|
|
296
|
-
//#endregion
|
|
297
|
-
//#region src/terminal/mouse.ts
|
|
298
|
-
const ESC$1 = "\x1B";
|
|
299
|
-
function encodeSgrMouse(event) {
|
|
300
|
-
const x = clampInt(event.x, 1, 500);
|
|
301
|
-
const y = clampInt(event.y, 1, 300);
|
|
302
|
-
if (event.action === "click") return `${encodeSingleSgrMouse({
|
|
303
|
-
...event,
|
|
304
|
-
action: "down"
|
|
305
|
-
}, x, y)}${encodeSingleSgrMouse({
|
|
306
|
-
...event,
|
|
307
|
-
action: "up"
|
|
308
|
-
}, x, y)}`;
|
|
309
|
-
return encodeSingleSgrMouse(event, x, y);
|
|
310
|
-
}
|
|
311
|
-
function encodeSingleSgrMouse(event, x, y) {
|
|
312
|
-
const modifiers = event.modifiers;
|
|
313
|
-
const modifierBits = (modifiers?.shift ? 4 : 0) + (modifiers?.alt ? 8 : 0) + (modifiers?.ctrl ? 16 : 0);
|
|
314
|
-
const buttonCode = buttonToCode(event.button ?? "left");
|
|
315
|
-
if (event.action === "down") return `${ESC$1}[<${buttonCode + modifierBits};${x};${y}M`;
|
|
316
|
-
if (event.action === "up") return `${ESC$1}[<${buttonCode + modifierBits};${x};${y}m`;
|
|
317
|
-
if (event.action === "scroll_up") return `${ESC$1}[<${64 + modifierBits};${x};${y}M`;
|
|
318
|
-
if (event.action === "scroll_down") return `${ESC$1}[<${65 + modifierBits};${x};${y}M`;
|
|
319
|
-
return `${ESC$1}[<${32 + (event.button ? buttonCode : 3) + modifierBits};${x};${y}M`;
|
|
320
|
-
}
|
|
321
|
-
function buttonToCode(button) {
|
|
322
|
-
if (button === "left") return 0;
|
|
323
|
-
if (button === "middle") return 1;
|
|
324
|
-
return 2;
|
|
325
|
-
}
|
|
326
|
-
function clampInt(value, min, max) {
|
|
327
|
-
if (!Number.isFinite(value)) return min;
|
|
328
|
-
const int = Math.trunc(value);
|
|
329
|
-
if (int < min) return min;
|
|
330
|
-
if (int > max) return max;
|
|
331
|
-
return int;
|
|
303
|
+
if (currentRun) out.push(currentRun);
|
|
304
|
+
return out;
|
|
332
305
|
}
|
|
333
306
|
//#endregion
|
|
334
307
|
//#region src/terminal/snapshot.ts
|
|
@@ -383,108 +356,6 @@ function snapshotGrid(terminal, options) {
|
|
|
383
356
|
styleRuns: includeStyles ? snapshotVisibleStyleRuns(terminal, trimRight) : void 0
|
|
384
357
|
};
|
|
385
358
|
}
|
|
386
|
-
function snapshotPlainLine(line, cols, nullCell, trimRight) {
|
|
387
|
-
let endCol = cols;
|
|
388
|
-
if (trimRight) endCol = findMeaningfulEndCol(line, cols, nullCell);
|
|
389
|
-
let out = "";
|
|
390
|
-
for (let x = 0; x < endCol; x += 1) {
|
|
391
|
-
const cell = line?.getCell(x, nullCell);
|
|
392
|
-
if (!cell) {
|
|
393
|
-
out += " ";
|
|
394
|
-
continue;
|
|
395
|
-
}
|
|
396
|
-
if (cell.getWidth() === 0) continue;
|
|
397
|
-
out += cell.getChars() || " ";
|
|
398
|
-
}
|
|
399
|
-
return out;
|
|
400
|
-
}
|
|
401
|
-
function snapshotStyleRuns(line, cols, nullCell, trimRight) {
|
|
402
|
-
let endCol = cols;
|
|
403
|
-
if (trimRight) endCol = findMeaningfulEndCol(line, cols, nullCell);
|
|
404
|
-
const out = [];
|
|
405
|
-
let currentKey = null;
|
|
406
|
-
let currentRun = null;
|
|
407
|
-
for (let x = 0; x < endCol; x += 1) {
|
|
408
|
-
const cell = line?.getCell(x, nullCell);
|
|
409
|
-
if (!cell) {
|
|
410
|
-
if (currentRun) {
|
|
411
|
-
out.push(currentRun);
|
|
412
|
-
currentRun = null;
|
|
413
|
-
currentKey = null;
|
|
414
|
-
}
|
|
415
|
-
continue;
|
|
416
|
-
}
|
|
417
|
-
const width = cell.getWidth();
|
|
418
|
-
if (width === 0) continue;
|
|
419
|
-
const style = extractStyle(cell);
|
|
420
|
-
if (isDefaultStyle(style)) {
|
|
421
|
-
if (currentRun) {
|
|
422
|
-
out.push(currentRun);
|
|
423
|
-
currentRun = null;
|
|
424
|
-
currentKey = null;
|
|
425
|
-
}
|
|
426
|
-
continue;
|
|
427
|
-
}
|
|
428
|
-
const key = styleKey(style);
|
|
429
|
-
if (currentRun && key === currentKey) {
|
|
430
|
-
currentRun.endCol = x + width;
|
|
431
|
-
continue;
|
|
432
|
-
}
|
|
433
|
-
if (currentRun) out.push(currentRun);
|
|
434
|
-
currentKey = key;
|
|
435
|
-
currentRun = {
|
|
436
|
-
startCol: x,
|
|
437
|
-
endCol: x + width,
|
|
438
|
-
style
|
|
439
|
-
};
|
|
440
|
-
}
|
|
441
|
-
if (currentRun) out.push(currentRun);
|
|
442
|
-
return out;
|
|
443
|
-
}
|
|
444
|
-
//#endregion
|
|
445
|
-
//#region src/terminal/mask.ts
|
|
446
|
-
function applyTextMaskRules(lines, rules) {
|
|
447
|
-
if (!rules || rules.length === 0) return lines;
|
|
448
|
-
const compiled = compileMaskRules(rules);
|
|
449
|
-
if (compiled.length === 0) return lines;
|
|
450
|
-
return lines.map((line) => applyCompiledMaskRules(line, compiled));
|
|
451
|
-
}
|
|
452
|
-
function compileMaskRules(rules) {
|
|
453
|
-
const compiled = [];
|
|
454
|
-
for (const rule of rules) {
|
|
455
|
-
if (!rule.regex.trim()) continue;
|
|
456
|
-
const preserveLength = rule.preserveLength ?? false;
|
|
457
|
-
const replacement = preserveLength ? firstChar(rule.replacement) : rule.replacement ?? "<masked>";
|
|
458
|
-
const flags = normalizeFlags(rule.flags);
|
|
459
|
-
let regex;
|
|
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
|
-
});
|
|
470
|
-
}
|
|
471
|
-
return compiled;
|
|
472
|
-
}
|
|
473
|
-
function normalizeFlags(flags) {
|
|
474
|
-
const value = flags?.trim() ? flags.trim() : "g";
|
|
475
|
-
const set = new Set(value.split(""));
|
|
476
|
-
set.add("g");
|
|
477
|
-
return [...set].join("");
|
|
478
|
-
}
|
|
479
|
-
function firstChar(value) {
|
|
480
|
-
const trimmed = value?.trim();
|
|
481
|
-
return trimmed && trimmed.length > 0 ? trimmed[0] : "█";
|
|
482
|
-
}
|
|
483
|
-
function applyCompiledMaskRules(line, rules) {
|
|
484
|
-
let out = line;
|
|
485
|
-
for (const rule of rules) out = out.replace(rule.regex, (match) => rule.preserveLength ? rule.replacement.repeat(match.length) : rule.replacement);
|
|
486
|
-
return out;
|
|
487
|
-
}
|
|
488
359
|
//#endregion
|
|
489
360
|
//#region src/util/hash.ts
|
|
490
361
|
function fnv1a32(text) {
|
|
@@ -496,13 +367,6 @@ function fnv1a32(text) {
|
|
|
496
367
|
return (hash >>> 0).toString(16).padStart(8, "0");
|
|
497
368
|
}
|
|
498
369
|
//#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
370
|
//#region src/trace/asciicast.ts
|
|
507
371
|
function encodeAsciicast(header, events) {
|
|
508
372
|
const lines = [JSON.stringify(header)];
|
|
@@ -589,6 +453,269 @@ var TraceRecorder = class {
|
|
|
589
453
|
}
|
|
590
454
|
};
|
|
591
455
|
//#endregion
|
|
456
|
+
//#region src/terminal/ansi.ts
|
|
457
|
+
const ESC$1 = "\x1B";
|
|
458
|
+
const SGR_RESET = `${ESC$1}[0m`;
|
|
459
|
+
function renderAnsiLines(terminal, options) {
|
|
460
|
+
const scope = options?.scope ?? "visible";
|
|
461
|
+
const trimRight = options?.trimRight ?? false;
|
|
462
|
+
const buffer = terminal.buffer.active;
|
|
463
|
+
const nullCell = buffer.getNullCell();
|
|
464
|
+
let startY = 0;
|
|
465
|
+
let count = buffer.length;
|
|
466
|
+
if (scope === "visible") {
|
|
467
|
+
startY = buffer.viewportY;
|
|
468
|
+
count = terminal.rows;
|
|
469
|
+
}
|
|
470
|
+
const out = [];
|
|
471
|
+
for (let i = 0; i < count; i += 1) {
|
|
472
|
+
const y = startY + i;
|
|
473
|
+
const line = buffer.getLine(y);
|
|
474
|
+
out.push(renderLine(line, terminal.cols, nullCell, trimRight));
|
|
475
|
+
}
|
|
476
|
+
return out;
|
|
477
|
+
}
|
|
478
|
+
function renderLine(line, cols, nullCell, trimRight) {
|
|
479
|
+
let endCol = cols;
|
|
480
|
+
if (trimRight) endCol = findMeaningfulEndCol(line, cols, nullCell);
|
|
481
|
+
let ansi = "";
|
|
482
|
+
let plain = "";
|
|
483
|
+
let hasStyle = false;
|
|
484
|
+
let usedSgr = false;
|
|
485
|
+
const defaultKey = styleKey(DEFAULT_STYLE);
|
|
486
|
+
let currentKey = defaultKey;
|
|
487
|
+
for (let x = 0; x < endCol; x += 1) {
|
|
488
|
+
const cell = line?.getCell(x, nullCell);
|
|
489
|
+
if (!cell) {
|
|
490
|
+
plain += " ";
|
|
491
|
+
ansi += " ";
|
|
492
|
+
continue;
|
|
493
|
+
}
|
|
494
|
+
if (cell.getWidth() === 0) continue;
|
|
495
|
+
const chars = cell.getChars() || " ";
|
|
496
|
+
const style = extractStyle(cell);
|
|
497
|
+
const isDefault = isDefaultStyle(style);
|
|
498
|
+
if (!isDefault) hasStyle = true;
|
|
499
|
+
const key = styleKey(style);
|
|
500
|
+
if (key !== currentKey) {
|
|
501
|
+
ansi += isDefault ? SGR_RESET : toSgr(style);
|
|
502
|
+
usedSgr = true;
|
|
503
|
+
currentKey = key;
|
|
504
|
+
}
|
|
505
|
+
ansi += chars;
|
|
506
|
+
plain += chars;
|
|
507
|
+
}
|
|
508
|
+
if (usedSgr && currentKey !== defaultKey) ansi += SGR_RESET;
|
|
509
|
+
return {
|
|
510
|
+
ansi,
|
|
511
|
+
plain,
|
|
512
|
+
hasStyle
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
function toSgr(style) {
|
|
516
|
+
const codes = ["0"];
|
|
517
|
+
if (style.bold) codes.push("1");
|
|
518
|
+
if (style.dim) codes.push("2");
|
|
519
|
+
if (style.italic) codes.push("3");
|
|
520
|
+
if (style.underline) codes.push("4");
|
|
521
|
+
if (style.inverse) codes.push("7");
|
|
522
|
+
if (style.strikethrough) codes.push("9");
|
|
523
|
+
if (style.fg.mode === "palette") codes.push(`38;5;${style.fg.value}`);
|
|
524
|
+
else if (style.fg.mode === "rgb") {
|
|
525
|
+
const [r, g, b] = rgb(style.fg.value);
|
|
526
|
+
codes.push(`38;2;${r};${g};${b}`);
|
|
527
|
+
}
|
|
528
|
+
if (style.bg.mode === "palette") codes.push(`48;5;${style.bg.value}`);
|
|
529
|
+
else if (style.bg.mode === "rgb") {
|
|
530
|
+
const [r, g, b] = rgb(style.bg.value);
|
|
531
|
+
codes.push(`48;2;${r};${g};${b}`);
|
|
532
|
+
}
|
|
533
|
+
return `${ESC$1}[${codes.join(";")}m`;
|
|
534
|
+
}
|
|
535
|
+
function rgb(value) {
|
|
536
|
+
return [
|
|
537
|
+
value >> 16 & 255,
|
|
538
|
+
value >> 8 & 255,
|
|
539
|
+
value & 255
|
|
540
|
+
];
|
|
541
|
+
}
|
|
542
|
+
//#endregion
|
|
543
|
+
//#region src/terminal/mask.ts
|
|
544
|
+
function applyTextMaskRules(lines, rules) {
|
|
545
|
+
if (!rules || rules.length === 0) return lines;
|
|
546
|
+
const compiled = compileMaskRules(rules);
|
|
547
|
+
if (compiled.length === 0) return lines;
|
|
548
|
+
return lines.map((line) => applyCompiledMaskRules(line, compiled));
|
|
549
|
+
}
|
|
550
|
+
function compileMaskRules(rules) {
|
|
551
|
+
const compiled = [];
|
|
552
|
+
for (const rule of rules) {
|
|
553
|
+
if (!rule.regex.trim()) continue;
|
|
554
|
+
const preserveLength = rule.preserveLength ?? false;
|
|
555
|
+
const replacement = preserveLength ? firstChar(rule.replacement) : rule.replacement ?? "<masked>";
|
|
556
|
+
const flags = normalizeFlags(rule.flags);
|
|
557
|
+
let regex;
|
|
558
|
+
try {
|
|
559
|
+
regex = new RegExp(rule.regex, flags);
|
|
560
|
+
} catch (error) {
|
|
561
|
+
throw new Error(`invalid mask rule regex=${JSON.stringify(rule.regex)} flags=${JSON.stringify(rule.flags ?? "")}: ${error.message}`);
|
|
562
|
+
}
|
|
563
|
+
compiled.push({
|
|
564
|
+
regex,
|
|
565
|
+
replacement,
|
|
566
|
+
preserveLength
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
return compiled;
|
|
570
|
+
}
|
|
571
|
+
function normalizeFlags(flags) {
|
|
572
|
+
const value = flags?.trim() ? flags.trim() : "g";
|
|
573
|
+
const set = new Set(value.split(""));
|
|
574
|
+
set.add("g");
|
|
575
|
+
return [...set].join("");
|
|
576
|
+
}
|
|
577
|
+
function firstChar(value) {
|
|
578
|
+
const trimmed = value?.trim();
|
|
579
|
+
return trimmed && trimmed.length > 0 ? trimmed[0] : "█";
|
|
580
|
+
}
|
|
581
|
+
function applyCompiledMaskRules(line, rules) {
|
|
582
|
+
let out = line;
|
|
583
|
+
for (const rule of rules) out = out.replace(rule.regex, (match) => rule.preserveLength ? rule.replacement.repeat(match.length) : rule.replacement);
|
|
584
|
+
return out;
|
|
585
|
+
}
|
|
586
|
+
//#endregion
|
|
587
|
+
//#region src/session/terminal_session_snapshot.ts
|
|
588
|
+
function snapshotSessionText(terminal, options) {
|
|
589
|
+
if (options?.maxLines !== void 0 && options.tailLines !== void 0) throw new Error("snapshotText: maxLines and tailLines are mutually exclusive");
|
|
590
|
+
let lines = snapshotLines(terminal, {
|
|
591
|
+
scope: options?.scope,
|
|
592
|
+
trimRight: options?.trimRight
|
|
593
|
+
});
|
|
594
|
+
if (options?.trimBottom ?? true) lines = trimBottomEmptyLines(lines);
|
|
595
|
+
if (options?.maxLines !== void 0) {
|
|
596
|
+
const max = Math.max(0, Math.trunc(options.maxLines));
|
|
597
|
+
lines = lines.slice(0, max);
|
|
598
|
+
}
|
|
599
|
+
if (options?.tailLines !== void 0) {
|
|
600
|
+
const tail = Math.max(0, Math.trunc(options.tailLines));
|
|
601
|
+
lines = lines.slice(Math.max(0, lines.length - tail));
|
|
602
|
+
}
|
|
603
|
+
lines = applyTextMaskRules(lines, options?.mask);
|
|
604
|
+
const text = lines.join("\n");
|
|
605
|
+
return {
|
|
606
|
+
text,
|
|
607
|
+
hash: fnv1a32(text)
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
function snapshotSessionAnsi(terminal, options) {
|
|
611
|
+
if (options?.maxLines !== void 0 && options.tailLines !== void 0) throw new Error("snapshotAnsi: maxLines and tailLines are mutually exclusive");
|
|
612
|
+
let lines = renderAnsiLines(terminal, {
|
|
613
|
+
scope: options?.scope,
|
|
614
|
+
trimRight: options?.trimRight
|
|
615
|
+
});
|
|
616
|
+
if (options?.trimBottom ?? true) lines = trimBottomEmptyAnsiLines(lines);
|
|
617
|
+
if (options?.maxLines !== void 0) {
|
|
618
|
+
const max = Math.max(0, Math.trunc(options.maxLines));
|
|
619
|
+
lines = lines.slice(0, max);
|
|
620
|
+
}
|
|
621
|
+
if (options?.tailLines !== void 0) {
|
|
622
|
+
const tail = Math.max(0, Math.trunc(options.tailLines));
|
|
623
|
+
lines = lines.slice(Math.max(0, lines.length - tail));
|
|
624
|
+
}
|
|
625
|
+
if (options?.mask && options.mask.length > 0) {
|
|
626
|
+
const maskedPlain = applyTextMaskRules(lines.map((line) => line.plain), options.mask);
|
|
627
|
+
const maskedAnsi = applyTextMaskRules(lines.map((line) => line.ansi), options.mask);
|
|
628
|
+
lines = lines.map((line, idx) => ({
|
|
629
|
+
...line,
|
|
630
|
+
plain: maskedPlain[idx] ?? "",
|
|
631
|
+
ansi: maskedAnsi[idx] ?? ""
|
|
632
|
+
}));
|
|
633
|
+
}
|
|
634
|
+
const ansi = lines.map((l) => l.ansi).join("\n");
|
|
635
|
+
return {
|
|
636
|
+
ansi,
|
|
637
|
+
plain: lines.map((l) => l.plain).join("\n"),
|
|
638
|
+
hash: fnv1a32(ansi),
|
|
639
|
+
lines
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
function trimBottomEmptyLines(lines) {
|
|
643
|
+
let end = lines.length;
|
|
644
|
+
while (end > 0 && lines[end - 1] === "") end -= 1;
|
|
645
|
+
return end === lines.length ? lines : lines.slice(0, end);
|
|
646
|
+
}
|
|
647
|
+
function trimBottomEmptyAnsiLines(lines) {
|
|
648
|
+
let end = lines.length;
|
|
649
|
+
while (end > 0) {
|
|
650
|
+
const line = lines[end - 1];
|
|
651
|
+
if (!(!line?.hasStyle && (line?.plain ?? "").trim() === "")) break;
|
|
652
|
+
end -= 1;
|
|
653
|
+
}
|
|
654
|
+
return end === lines.length ? lines : lines.slice(0, end);
|
|
655
|
+
}
|
|
656
|
+
//#endregion
|
|
657
|
+
//#region src/util/sleep.ts
|
|
658
|
+
async function sleep(ms) {
|
|
659
|
+
await new Promise((resolve) => {
|
|
660
|
+
setTimeout(resolve, ms);
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
//#endregion
|
|
664
|
+
//#region src/session/terminal_session_wait.ts
|
|
665
|
+
async function waitForSessionText(host, args) {
|
|
666
|
+
const startedAt = Date.now();
|
|
667
|
+
let closedSince = null;
|
|
668
|
+
while (Date.now() - startedAt <= args.timeoutMs) {
|
|
669
|
+
const snapshot = await host.snapshotText({
|
|
670
|
+
captureFrame: true,
|
|
671
|
+
scope: args.scope
|
|
672
|
+
});
|
|
673
|
+
if (args.text && snapshot.text.includes(args.text)) return {
|
|
674
|
+
found: true,
|
|
675
|
+
...snapshot
|
|
676
|
+
};
|
|
677
|
+
if (args.regex && args.regex.test(snapshot.text)) return {
|
|
678
|
+
found: true,
|
|
679
|
+
...snapshot
|
|
680
|
+
};
|
|
681
|
+
if (host.isClosed()) {
|
|
682
|
+
closedSince ??= Date.now();
|
|
683
|
+
const drainMs = Math.max(500, args.intervalMs * 4);
|
|
684
|
+
if (Date.now() - closedSince >= drainMs) break;
|
|
685
|
+
}
|
|
686
|
+
await sleep(args.intervalMs);
|
|
687
|
+
}
|
|
688
|
+
return {
|
|
689
|
+
found: false,
|
|
690
|
+
...await host.snapshotText({
|
|
691
|
+
captureFrame: true,
|
|
692
|
+
scope: args.scope
|
|
693
|
+
})
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
async function waitForStableSessionScreen(host, args) {
|
|
697
|
+
const startedAt = Date.now();
|
|
698
|
+
let stableSince = null;
|
|
699
|
+
let lastHash = null;
|
|
700
|
+
while (Date.now() - startedAt <= args.timeoutMs) {
|
|
701
|
+
const snapshot = await host.snapshotText({ captureFrame: true });
|
|
702
|
+
if (snapshot.hash === lastHash) stableSince ??= Date.now();
|
|
703
|
+
else {
|
|
704
|
+
stableSince = null;
|
|
705
|
+
lastHash = snapshot.hash;
|
|
706
|
+
}
|
|
707
|
+
if (stableSince !== null && Date.now() - stableSince >= args.quietMs) return {
|
|
708
|
+
stable: true,
|
|
709
|
+
...snapshot
|
|
710
|
+
};
|
|
711
|
+
await sleep(args.intervalMs);
|
|
712
|
+
}
|
|
713
|
+
return {
|
|
714
|
+
stable: false,
|
|
715
|
+
...await host.snapshotText({ captureFrame: true })
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
//#endregion
|
|
592
719
|
//#region src/session/terminal_session.ts
|
|
593
720
|
const ESC = "\x1B";
|
|
594
721
|
var TerminalSession = class {
|
|
@@ -688,23 +815,7 @@ var TerminalSession = class {
|
|
|
688
815
|
}
|
|
689
816
|
async snapshotText(options) {
|
|
690
817
|
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);
|
|
818
|
+
const { text, hash } = snapshotSessionText(this.terminal, options);
|
|
708
819
|
if (options?.captureFrame ?? true) this.captureFrame(text, hash);
|
|
709
820
|
return {
|
|
710
821
|
text,
|
|
@@ -713,36 +824,7 @@ var TerminalSession = class {
|
|
|
713
824
|
}
|
|
714
825
|
async snapshotAnsi(options) {
|
|
715
826
|
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
|
-
};
|
|
827
|
+
return snapshotSessionAnsi(this.terminal, options);
|
|
746
828
|
}
|
|
747
829
|
async snapshotGrid(options) {
|
|
748
830
|
await this.flush();
|
|
@@ -771,57 +853,10 @@ var TerminalSession = class {
|
|
|
771
853
|
return [...this.rawOutputRing];
|
|
772
854
|
}
|
|
773
855
|
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
|
-
};
|
|
856
|
+
return waitForSessionText(this, args);
|
|
803
857
|
}
|
|
804
858
|
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
|
-
};
|
|
859
|
+
return waitForStableSessionScreen(this, args);
|
|
825
860
|
}
|
|
826
861
|
close() {
|
|
827
862
|
if (this.closed === null) this.closed = { type: "closed_by_user" };
|
|
@@ -875,19 +910,5 @@ var TerminalSession = class {
|
|
|
875
910
|
return true;
|
|
876
911
|
}
|
|
877
912
|
};
|
|
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
913
|
//#endregion
|
|
893
|
-
export {
|
|
914
|
+
export { fnv1a32 as a, extractStyle as c, styleKey as d, encodeSgrMouse as f, TraceRecorder as i, findMeaningfulEndCol as l, sleep as n, snapshotGrid as o, encodeKey as p, applyTextMaskRules as r, snapshotLines as s, TerminalSession as t, isDefaultStyle as u };
|